mirror of
https://github.com/documize/community.git
synced 2025-07-21 06:09:42 +02:00
add section empty state
This commit is contained in:
parent
a7ac034d2c
commit
8d2dcf376f
12 changed files with 80 additions and 248 deletions
|
@ -21,7 +21,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
didReceiveAttrs() {
|
didReceiveAttrs() {
|
||||||
let toEdit = this.get('toEdit');
|
let toEdit = this.get('toEdit');
|
||||||
|
|
||||||
if (toEdit === this.get('page.id')) {
|
if (toEdit === this.get('page.id') && this.get('isEditor')) {
|
||||||
this.send('onEdit');
|
this.send('onEdit');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -22,7 +22,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
sectionService: Ember.inject.service('section'),
|
sectionService: Ember.inject.service('section'),
|
||||||
appMeta: Ember.inject.service(),
|
appMeta: Ember.inject.service(),
|
||||||
link: Ember.inject.service(),
|
link: Ember.inject.service(),
|
||||||
|
hasPages: computed.notEmpty('pages'),
|
||||||
newSectionName: '',
|
newSectionName: '',
|
||||||
newSectionNameMissing: computed.empty('newSectionName'),
|
newSectionNameMissing: computed.empty('newSectionName'),
|
||||||
newSectionLocation: '',
|
newSectionLocation: '',
|
||||||
|
@ -38,6 +38,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
this.set('blocks', blocks);
|
this.set('blocks', blocks);
|
||||||
this.set('hasBlocks', blocks.get('length') > 0);
|
this.set('hasBlocks', blocks.get('length') > 0);
|
||||||
|
|
||||||
|
// to test
|
||||||
blocks.forEach((b) => {
|
blocks.forEach((b) => {
|
||||||
b.set('deleteId', `delete-block-button-${b.id}`);
|
b.set('deleteId', `delete-block-button-${b.id}`);
|
||||||
});
|
});
|
||||||
|
@ -54,12 +55,12 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
$(".start-section").hoverIntent({interval: 100, over: function() {
|
$(".start-section:not(.start-section-empty-state)").hoverIntent({interval: 100, over: function() {
|
||||||
// in
|
// in
|
||||||
$(this).find('.start-button').css("display", "block").removeClass('fadeOut').addClass('fadeIn');
|
$(this).find('.start-button').velocity("transition.slideDownIn", {duration: 300});
|
||||||
}, out: function() {
|
}, out: function() {
|
||||||
//out
|
// out
|
||||||
$(this).find('.start-button').css("display", "none").removeClass('fadeIn').addClass('fadeOut');
|
$(this).find('.start-button').velocity("transition.slideUpOut", {duration: 300});
|
||||||
} });
|
} });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -109,12 +110,15 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
if (is.not.null(beforePage)) {
|
if (is.not.null(beforePage)) {
|
||||||
// get any page before the beforePage so we can insert this new section between them
|
// get any page before the beforePage so we can insert this new section between them
|
||||||
let index = _.findIndex(this.get('pages'), function(p) { return p.get('id') === beforePage.get('id'); });
|
let index = _.findIndex(this.get('pages'), function(p) { return p.get('id') === beforePage.get('id'); });
|
||||||
let beforeBeforePage = this.get('pages')[index-1];
|
|
||||||
|
|
||||||
if (is.not.undefined(beforeBeforePage)) {
|
if (index !== -1) {
|
||||||
sequence = (beforePage.get('sequence') + beforeBeforePage.get('sequence')) / 2;
|
let beforeBeforePage = this.get('pages')[index-1];
|
||||||
} else {
|
|
||||||
sequence = beforePage.get('sequence') / 2;
|
if (is.not.undefined(beforeBeforePage)) {
|
||||||
|
sequence = (beforePage.get('sequence') + beforeBeforePage.get('sequence')) / 2;
|
||||||
|
} else {
|
||||||
|
sequence = beforePage.get('sequence') / 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +128,7 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
|
|
||||||
const promise = this.get('onInsertSection')(model);
|
const promise = this.get('onInsertSection')(model);
|
||||||
promise.then((id) => {
|
promise.then((id) => {
|
||||||
if (model.page.contentType === 'section') {
|
if (model.page.pageType === 'section') {
|
||||||
this.set('toEdit', id);
|
this.set('toEdit', id);
|
||||||
} else {
|
} else {
|
||||||
this.get('onEditSection')(id);
|
this.get('onEditSection')(id);
|
||||||
|
@ -154,17 +158,26 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Section wizard related
|
// Section wizard related
|
||||||
|
|
||||||
onShowSectionWizard(page) {
|
onShowSectionWizard(page) {
|
||||||
let beforePage = this.get('beforePage');
|
if (is.undefined(page)) {
|
||||||
|
page = { id: '0' };
|
||||||
|
}
|
||||||
|
|
||||||
|
let beforePage = this.get('beforePage');
|
||||||
if (is.not.null(beforePage) && $("#new-section-wizard").is(':visible') && beforePage.get('id') === page.id) {
|
if (is.not.null(beforePage) && $("#new-section-wizard").is(':visible') && beforePage.get('id') === page.id) {
|
||||||
this.send('onHideSectionWizard');
|
this.send('onHideSectionWizard');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.set('newSectionLocation', page.id);
|
this.set('newSectionLocation', page.id);
|
||||||
this.set('beforePage', page);
|
|
||||||
|
if (page.id === '0') {
|
||||||
|
// this handles add section at the end of the document
|
||||||
|
// because we are not before another page
|
||||||
|
this.set('beforePage', null);
|
||||||
|
} else {
|
||||||
|
this.set('beforePage', page);
|
||||||
|
}
|
||||||
|
|
||||||
$("#new-section-wizard").insertAfter(`#add-section-button-${page.id}`);
|
$("#new-section-wizard").insertAfter(`#add-section-button-${page.id}`);
|
||||||
$("#new-section-wizard").velocity("transition.slideDownIn", {duration: 300, complete:
|
$("#new-section-wizard").velocity("transition.slideDownIn", {duration: 300, complete:
|
||||||
|
@ -250,7 +263,6 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
|
||||||
// to test
|
// to test
|
||||||
onDeleteBlock(id) {
|
onDeleteBlock(id) {
|
||||||
this.attrs.onDeleteBlock(id);
|
this.attrs.onDeleteBlock(id);
|
||||||
},
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,67 +0,0 @@
|
||||||
// Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
|
|
||||||
//
|
|
||||||
// This software (Documize Community Edition) is licensed under
|
|
||||||
// GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
|
|
||||||
//
|
|
||||||
// You can operate outside the AGPL restrictions by purchasing
|
|
||||||
// Documize Enterprise Edition and obtaining a commercial license
|
|
||||||
// by contacting <sales@documize.com>.
|
|
||||||
//
|
|
||||||
// https://documize.com
|
|
||||||
|
|
||||||
import Ember from 'ember';
|
|
||||||
import NotifierMixin from '../../mixins/notifier';
|
|
||||||
|
|
||||||
export default Ember.Component.extend(NotifierMixin, {
|
|
||||||
display: 'section', // which CSS to use
|
|
||||||
hasTemplates: false,
|
|
||||||
hasBlocks: false,
|
|
||||||
blockMode: false,
|
|
||||||
|
|
||||||
didReceiveAttrs() {
|
|
||||||
let blocks = this.get('blocks');
|
|
||||||
let blockMode = is.not.undefined(blocks);
|
|
||||||
|
|
||||||
this.set('blockMode', blockMode);
|
|
||||||
if (!blockMode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('hasBlocks', blocks.get('length') > 0);
|
|
||||||
|
|
||||||
blocks.forEach((b) => {
|
|
||||||
b.set('deleteId', `delete-block-button-${b.id}`);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
didRender() {
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
Mousetrap.bind('esc', function () {
|
|
||||||
if (self.get('isDestroyed') || self.get('isDestroying')) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.send('onCancel');
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
onCancel() {
|
|
||||||
this.attrs.onCancel();
|
|
||||||
},
|
|
||||||
|
|
||||||
addSection(section) {
|
|
||||||
this.attrs.onAddSection(section);
|
|
||||||
},
|
|
||||||
|
|
||||||
onDeleteBlock(id) {
|
|
||||||
this.attrs.onDeleteBlock(id);
|
|
||||||
},
|
|
||||||
|
|
||||||
onInsertBlock(block) {
|
|
||||||
this.attrs.onInsertBlock(block);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -40,7 +40,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="page-content-wrapper">
|
<div id="page-content-wrapper">
|
||||||
<div class="container">
|
<div class="{{if toggled 'container' 'container-fluid'}}">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12">
|
<div class="col-lg-12">
|
||||||
{{#if toggled}}
|
{{#if toggled}}
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import NotifierMixin from '../../../mixins/notifier';
|
|
||||||
|
|
||||||
export default Ember.Controller.extend(NotifierMixin, {
|
|
||||||
documentService: Ember.inject.service('document'),
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
onCancel() {
|
|
||||||
this.transitionToRoute('document');
|
|
||||||
},
|
|
||||||
|
|
||||||
onAddSection(section) {
|
|
||||||
this.audit.record("added-section-" + section.get('contentType'));
|
|
||||||
|
|
||||||
let page = {
|
|
||||||
documentId: this.get('model.document.id'),
|
|
||||||
title: `${section.get('title')}`,
|
|
||||||
level: 1,
|
|
||||||
sequence: 0,
|
|
||||||
body: "",
|
|
||||||
contentType: section.get('contentType'),
|
|
||||||
pageType: section.get('pageType')
|
|
||||||
};
|
|
||||||
|
|
||||||
let meta = {
|
|
||||||
documentId: this.get('model.document.id'),
|
|
||||||
rawBody: "",
|
|
||||||
config: "",
|
|
||||||
externaleSource: true
|
|
||||||
};
|
|
||||||
|
|
||||||
let model = {
|
|
||||||
page: page,
|
|
||||||
meta: meta
|
|
||||||
};
|
|
||||||
|
|
||||||
this.get('documentService').addPage(this.get('model.document.id'), model).then((newPage) => {
|
|
||||||
let data = this.get('store').normalize('page', newPage);
|
|
||||||
this.get('store').push(data);
|
|
||||||
|
|
||||||
this.get('documentService').getPages(this.get('model.document.id')).then((pages) => {
|
|
||||||
this.set('model.pages', pages.filterBy('pageType', 'section'));
|
|
||||||
this.set('model.tabs', pages.filterBy('pageType', 'tab'));
|
|
||||||
|
|
||||||
this.get('documentService').getPageMeta(this.get('model.document.id'), newPage.id).then(() => {
|
|
||||||
let options = {};
|
|
||||||
options['mode'] = 'edit';
|
|
||||||
this.transitionToRoute('document.section', newPage.id, { queryParams: options });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,21 +0,0 @@
|
||||||
import Ember from 'ember';
|
|
||||||
import AuthenticatedRouteMixin from 'ember-simple-auth/mixins/authenticated-route-mixin';
|
|
||||||
|
|
||||||
export default Ember.Route.extend(AuthenticatedRouteMixin, {
|
|
||||||
documentService: Ember.inject.service('document'),
|
|
||||||
folderService: Ember.inject.service('folder'),
|
|
||||||
sectionService: Ember.inject.service('section'),
|
|
||||||
|
|
||||||
model() {
|
|
||||||
return Ember.RSVP.hash({
|
|
||||||
folders: this.modelFor('document').folders,
|
|
||||||
folder: this.modelFor('document').folder,
|
|
||||||
document: this.modelFor('document').document,
|
|
||||||
pages: this.modelFor('document').pages,
|
|
||||||
tabs: this.modelFor('document').tabs,
|
|
||||||
sections: this.get('sectionService').getAll().then(function (sections) {
|
|
||||||
return sections.filterBy('pageType', 'tab');
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1 +0,0 @@
|
||||||
{{document/page-wizard display='tab' document=model.document folder=model.folder sections=model.sections onCancel=(action 'onCancel') onAddSection=(action 'onAddSection')}}
|
|
|
@ -151,7 +151,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.start-section {
|
.start-section {
|
||||||
@include ease-in();
|
|
||||||
@extend .no-select;
|
@extend .no-select;
|
||||||
height: 60px;
|
height: 60px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
|
@ -194,6 +193,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.start-section-empty-state {
|
||||||
|
> .start-button {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.new-section-wizard {
|
.new-section-wizard {
|
||||||
@include border-radius(2px);
|
@include border-radius(2px);
|
||||||
margin: 0 0 30px 0;
|
margin: 0 0 30px 0;
|
||||||
|
|
|
@ -1,35 +1,51 @@
|
||||||
<div class="document-view">
|
<div class="document-view">
|
||||||
{{#each pages key="id" as |page index|}}
|
{{#if hasPages}}
|
||||||
|
{{#each pages key="id" as |page index|}}
|
||||||
|
{{#if isEditor}}
|
||||||
|
<div class="start-section" data-index={{index}} data-before-id={{page.id}} id="add-section-button-{{page.id}}" {{action 'onShowSectionWizard' page}}>
|
||||||
|
<div class="start-button">
|
||||||
|
<div class="round-button round-button-small button-green">
|
||||||
|
<i class="material-icons">add</i>
|
||||||
|
</div>
|
||||||
|
<div class="label">section</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="section-divider" />
|
||||||
|
{{/if}}
|
||||||
|
{{#if (is-equal page.pageType 'section')}}
|
||||||
|
{{#document/document-page document=document folder=folder page=page isEditor=isEditor toEdit=toEdit
|
||||||
|
onSavePage=(action 'onSavePage') onSavePageAsBlock=(action 'onSavePageAsBlock')
|
||||||
|
onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}}
|
||||||
|
{{/document/document-page}}
|
||||||
|
{{/if}}
|
||||||
|
{{#if (is-equal page.pageType 'tab')}}
|
||||||
|
{{#document/document-tab document=document folder=folder page=page isEditor=isEditor
|
||||||
|
onSavePage=(action 'onSavePage') onSavePageAsBlock=(action 'onSavePageAsBlock')
|
||||||
|
onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}}
|
||||||
|
{{/document/document-tab}}
|
||||||
|
{{/if}}
|
||||||
|
{{/each}}
|
||||||
|
<div class="start-section" data-index="0" data-before-id="0" id="add-section-button-0" {{action 'onShowSectionWizard'}}>
|
||||||
|
<div class="start-button">
|
||||||
|
<div class="round-button round-button-small button-green">
|
||||||
|
<i class="material-icons">add</i>
|
||||||
|
</div>
|
||||||
|
<div class="label">section</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
{{#if isEditor}}
|
{{#if isEditor}}
|
||||||
<div class="start-section" data-index={{index}} data-before-id={{page.id}} id="add-section-button-{{page.id}}" {{action 'onShowSectionWizard' page}}>
|
<div class="start-section start-section-empty-state" data-index="-1" data-before-id="0" id="add-section-button-0" {{action 'onShowSectionWizard'}}>
|
||||||
<div class="start-button animated">
|
<div class="start-button">
|
||||||
<div class="round-button round-button-small button-green">
|
<div class="round-button round-button-small button-green">
|
||||||
<i class="material-icons">add</i>
|
<i class="material-icons">add</i>
|
||||||
</div>
|
</div>
|
||||||
<div class="label">section</div>
|
<div class="label">section</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{else}}
|
|
||||||
<div class="section-divider" />
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if (is-equal page.pageType 'section')}}
|
{{/if}}
|
||||||
{{#document/document-page document=document folder=folder page=page isEditor=isEditor toEdit=toEdit
|
|
||||||
onSavePage=(action 'onSavePage') onSavePageAsBlock=(action 'onSavePageAsBlock')
|
|
||||||
onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}}
|
|
||||||
{{/document/document-page}}
|
|
||||||
{{/if}}
|
|
||||||
{{#if (is-equal page.pageType 'tab')}}
|
|
||||||
{{#document/document-tab document=document folder=folder page=page isEditor=isEditor
|
|
||||||
onSavePage=(action 'onSavePage') onSavePageAsBlock=(action 'onSavePageAsBlock')
|
|
||||||
onCopyPage=(action 'onCopyPage') onMovePage=(action 'onMovePage') onDeletePage=(action 'onDeletePage')}}
|
|
||||||
{{/document/document-tab}}
|
|
||||||
{{/if}}
|
|
||||||
{{else}}
|
|
||||||
no sections!
|
|
||||||
<div class="empty-state-document">
|
|
||||||
<img src="/assets/img/empty-state-document.gif" />
|
|
||||||
</div>
|
|
||||||
{{/each}}
|
|
||||||
|
|
||||||
<div id="new-section-wizard" class="new-section-wizard">
|
<div id="new-section-wizard" class="new-section-wizard">
|
||||||
<div class="input-inline input-transparent pull-left width-80">
|
<div class="input-inline input-transparent pull-left width-80">
|
||||||
|
|
|
@ -1,63 +0,0 @@
|
||||||
<div class="{{display}}-wizard">
|
|
||||||
<div class="canvas">
|
|
||||||
<ul class="list">
|
|
||||||
{{#each sections as |section|}}
|
|
||||||
<li class="item" {{action 'addSection' section}}>
|
|
||||||
<div class="icon">
|
|
||||||
<img class="img" src="/sections/{{section.contentType}}.png" srcset="/sections/{{section.contentType}}@2x.png" />
|
|
||||||
</div>
|
|
||||||
<div class="details">
|
|
||||||
<div class='title'>
|
|
||||||
{{section.title}}
|
|
||||||
{{#if section.preview}}
|
|
||||||
<div class="preview">coming soon</div>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
<div class='desc'>{{section.description}}</div>
|
|
||||||
</div>
|
|
||||||
<div class="clearfix" />
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
{{#if blockMode}}
|
|
||||||
{{#if hasBlocks}}
|
|
||||||
<div class="divider"></div>
|
|
||||||
<div class="template-caption">Reusable content</div>
|
|
||||||
<ul class="list">
|
|
||||||
{{#each blocks as |block|}}
|
|
||||||
<li class="item min-height">
|
|
||||||
<div class="icon">
|
|
||||||
<div class="symbol" {{action 'onInsertBlock' block}}>
|
|
||||||
<i class="material-icons">view_agenda</i>
|
|
||||||
</div>
|
|
||||||
<div class="actions">
|
|
||||||
{{#link-to 'document.block' folder.id folder.slug document.id document.slug block.id}}
|
|
||||||
<i class="material-icons">mode_edit</i>
|
|
||||||
{{/link-to}}
|
|
||||||
<i class="material-icons" id={{block.deleteId}}>delete</i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="details" {{action 'onInsertBlock' block}}>
|
|
||||||
<div class='title'>
|
|
||||||
{{block.title}}
|
|
||||||
</div>
|
|
||||||
<div class='desc'>{{block.excerpt}}</div>
|
|
||||||
<div class='desc'>By {{block.firstname}} {{block.lastname}}, {{time-ago block.created}} (used: {{ block.used }})</div>
|
|
||||||
</div>
|
|
||||||
<div class="clearfix" />
|
|
||||||
{{#dropdown-dialog target=block.deleteId position="bottom left" button="Delete" color="flat-red" onAction=(action 'onDeleteBlock' block.id)}}
|
|
||||||
<p>
|
|
||||||
Are you sure you want to delete block<br/>
|
|
||||||
<span class="bold">{{block.title}}?</span>
|
|
||||||
</p>
|
|
||||||
{{/dropdown-dialog}}
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
{{else}}
|
|
||||||
<div class="divider"></div>
|
|
||||||
<div class="template-caption">Reusable content appears below</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
Binary file not shown.
Before Width: | Height: | Size: 13 KiB |
|
@ -12,6 +12,7 @@
|
||||||
package endpoint
|
package endpoint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -121,6 +122,10 @@ func RefreshSections(w http.ResponseWriter, r *http.Request) {
|
||||||
// Grab the page because we need content type and
|
// Grab the page because we need content type and
|
||||||
page, err2 := p.GetPage(pm.PageID)
|
page, err2 := p.GetPage(pm.PageID)
|
||||||
|
|
||||||
|
if err2 == sql.ErrNoRows {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
writeGeneralSQLError(w, method, err2)
|
writeGeneralSQLError(w, method, err2)
|
||||||
log.IfErr(tx.Rollback())
|
log.IfErr(tx.Rollback())
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue