mirror of
https://github.com/documize/community.git
synced 2025-07-22 22:59:43 +02:00
foundational layer for inserting content and attachment links into content
This commit is contained in:
parent
5ca53ecb04
commit
7db618dea0
20 changed files with 1397 additions and 721 deletions
59
app/app/components/link/content-linker.js
Normal file
59
app/app/components/link/content-linker.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
// 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';
|
||||||
|
|
||||||
|
const {
|
||||||
|
inject: { service }
|
||||||
|
} = Ember;
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
link: service(),
|
||||||
|
hasSections: false,
|
||||||
|
hasAttachments: false,
|
||||||
|
linkName: '',
|
||||||
|
selection: null,
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
let documentId = this.get('document.id');
|
||||||
|
let pageId = this.get('page.id');
|
||||||
|
|
||||||
|
this.get('link').getCandidates(documentId, pageId).then(function (candidates) {
|
||||||
|
self.set('candidates', candidates);
|
||||||
|
self.set('hasSections', is.not.null(candidates.pages) && candidates.pages.length);
|
||||||
|
self.set('hasAttachments', is.not.null(candidates.attachments) && candidates.attachments.length);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
didReceiveAttrs() {},
|
||||||
|
|
||||||
|
didInsertElement() {},
|
||||||
|
|
||||||
|
willDestroyElement() {},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
onInsertLink() {
|
||||||
|
let selection = this.get('selection');
|
||||||
|
let linkName = this.get('linkName');
|
||||||
|
|
||||||
|
if (linkName.length) {
|
||||||
|
selection.title = linkName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is.not.null(selection)) {
|
||||||
|
this.get('onInsertLink')(selection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -11,15 +11,24 @@
|
||||||
|
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
const {
|
||||||
|
inject: { service }
|
||||||
|
} = Ember;
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
pageBody: "",
|
|
||||||
appMeta: Ember.inject.service(),
|
appMeta: Ember.inject.service(),
|
||||||
|
link: service(),
|
||||||
|
pageBody: "",
|
||||||
|
drop: null,
|
||||||
|
showSidebar: false,
|
||||||
|
|
||||||
didReceiveAttrs() {
|
didReceiveAttrs() {
|
||||||
this.set('pageBody', this.get('meta.rawBody'));
|
this.set('pageBody', this.get('meta.rawBody'));
|
||||||
},
|
},
|
||||||
|
|
||||||
didInsertElement() {
|
didInsertElement() {
|
||||||
|
let self = this;
|
||||||
|
|
||||||
let options = {
|
let options = {
|
||||||
selector: "#rich-text-editor",
|
selector: "#rich-text-editor",
|
||||||
relative_urls: false,
|
relative_urls: false,
|
||||||
|
@ -34,7 +43,7 @@ export default Ember.Component.extend({
|
||||||
image_advtab: true,
|
image_advtab: true,
|
||||||
image_caption: true,
|
image_caption: true,
|
||||||
media_live_embeds: true,
|
media_live_embeds: true,
|
||||||
fontsize_formats: "8pt 10pt 12pt 14pt 16pt 18pt 20pt 22pt 24pt 26pt 28pt 30pt 32pt 34pt 36pt",
|
fontsize_formats: "8px 10px 12px 14px 18px 24px 36px 40px 50px 60px",
|
||||||
formats: {
|
formats: {
|
||||||
bold: {
|
bold: {
|
||||||
inline: 'b'
|
inline: 'b'
|
||||||
|
@ -48,29 +57,29 @@ export default Ember.Component.extend({
|
||||||
'advlist autolink lists link image charmap print preview hr anchor pagebreak',
|
'advlist autolink lists link image charmap print preview hr anchor pagebreak',
|
||||||
'searchreplace wordcount visualblocks visualchars code codesample fullscreen',
|
'searchreplace wordcount visualblocks visualchars code codesample fullscreen',
|
||||||
'insertdatetime media nonbreaking save table directionality',
|
'insertdatetime media nonbreaking save table directionality',
|
||||||
'emoticons template paste textcolor colorpicker textpattern imagetools'
|
'template paste textcolor colorpicker textpattern imagetools'
|
||||||
],
|
],
|
||||||
menu: {
|
menu: {},
|
||||||
edit: {
|
menubar: false,
|
||||||
title: 'Edit',
|
toolbar1: "bold italic underline strikethrough superscript subscript | outdent indent bullist numlist forecolor backcolor | alignleft aligncenter alignright alignjustify | link unlink | table image media | hr codesample",
|
||||||
items: 'undo redo | cut copy paste pastetext | selectall | searchreplace'
|
toolbar2: "formatselect fontselect fontsizeselect | documizeLinkButton",
|
||||||
},
|
|
||||||
insert: {
|
|
||||||
title: 'Insert',
|
|
||||||
items: 'anchor link media | hr | charmap emoticons | blockquote'
|
|
||||||
},
|
|
||||||
format: {
|
|
||||||
title: 'Format',
|
|
||||||
items: 'bold italic underline strikethrough superscript subscript | formats fonts | removeformat'
|
|
||||||
},
|
|
||||||
table: {
|
|
||||||
title: 'Table',
|
|
||||||
items: 'inserttable tableprops deletetable | cell row column'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toolbar1: "formatselect fontselect fontsizeselect | bold italic underline | link unlink | image media | codesample | outdent indent | alignleft aligncenter alignright alignjustify | bullist numlist | forecolor backcolor",
|
|
||||||
save_onsavecallback: function () {
|
save_onsavecallback: function () {
|
||||||
Mousetrap.trigger('ctrl+s');
|
Mousetrap.trigger('ctrl+s');
|
||||||
|
},
|
||||||
|
setup: function (editor) {
|
||||||
|
editor.addButton('documizeLinkButton', {
|
||||||
|
title: 'Insert Link',
|
||||||
|
icon: false,
|
||||||
|
image: '/favicon.ico',
|
||||||
|
onclick: function () {
|
||||||
|
let showSidebar = !self.get('showSidebar');
|
||||||
|
self.set('showSidebar', showSidebar);
|
||||||
|
|
||||||
|
if (showSidebar) {
|
||||||
|
self.send('showSidebar');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -91,6 +100,16 @@ export default Ember.Component.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
showSidebar() {
|
||||||
|
this.set('linkName', tinymce.activeEditor.selection.getContent());
|
||||||
|
},
|
||||||
|
|
||||||
|
onInsertLink(link) {
|
||||||
|
let linkHTML = this.get('link').buildLink(link);
|
||||||
|
tinymce.activeEditor.insertContent(linkHTML);
|
||||||
|
this.set('showSidebar', false);
|
||||||
|
},
|
||||||
|
|
||||||
isDirty() {
|
isDirty() {
|
||||||
return is.not.undefined(tinymce) && is.not.undefined(tinymce.activeEditor) && tinymce.activeEditor.isDirty();
|
return is.not.undefined(tinymce) && is.not.undefined(tinymce.activeEditor) && tinymce.activeEditor.isDirty();
|
||||||
},
|
},
|
||||||
|
@ -110,3 +129,7 @@ export default Ember.Component.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// editor.insertContent(' <b>It\'s my button!</b> ');
|
||||||
|
// Selects the first paragraph found
|
||||||
|
// tinyMCE.activeEditor.selection.select(tinyMCE.activeEditor.dom.select('p')[0]);
|
||||||
|
|
54
app/app/services/link.js
Normal file
54
app/app/services/link.js
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
// 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';
|
||||||
|
|
||||||
|
const {
|
||||||
|
inject: { service }
|
||||||
|
} = Ember;
|
||||||
|
|
||||||
|
export default Ember.Service.extend({
|
||||||
|
sessionService: service('session'),
|
||||||
|
ajax: service(),
|
||||||
|
appMeta: service(),
|
||||||
|
|
||||||
|
// Returns candidate links using provided parameters
|
||||||
|
getCandidates(documentId, pageId /*, keywords*/ ) {
|
||||||
|
return this.get('ajax').request(`links/${documentId}/${pageId}`, {
|
||||||
|
method: 'GET'
|
||||||
|
}).then((response) => {
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
buildLink(link) {
|
||||||
|
let result = "";
|
||||||
|
let href = "";
|
||||||
|
let endpoint = this.get('appMeta').get('endpoint');
|
||||||
|
let orgId = this.get('appMeta').get('orgId');
|
||||||
|
|
||||||
|
if (link.linkType === "section") {
|
||||||
|
href = `/link/${link.linkType}/${link.id}`;
|
||||||
|
}
|
||||||
|
if (link.linkType === "file") {
|
||||||
|
href = `${endpoint}/public/attachments/${orgId}/${link.attachmentId}`;
|
||||||
|
}
|
||||||
|
if (link.linkType === "document") {
|
||||||
|
href = `/link/${link.linkType}/${link.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = `<a data-link-id='${link.id}' data-link-type='${link.linkType}' href='${href}'>${link.title}</a>`;
|
||||||
|
|
||||||
|
console.log(link);
|
||||||
|
console.log(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
});
|
|
@ -15,6 +15,7 @@
|
||||||
@import "base.scss";
|
@import "base.scss";
|
||||||
@import "widget/widget.scss";
|
@import "widget/widget.scss";
|
||||||
@import "view/layout.scss";
|
@import "view/layout.scss";
|
||||||
|
@import "view/content-linker.scss";
|
||||||
@import "view/page-search.scss";
|
@import "view/page-search.scss";
|
||||||
@import "view/page-documents.scss";
|
@import "view/page-documents.scss";
|
||||||
@import "view/page-settings.scss";
|
@import "view/page-settings.scss";
|
||||||
|
|
|
@ -9,26 +9,100 @@
|
||||||
//
|
//
|
||||||
// https://documize.com
|
// https://documize.com
|
||||||
|
|
||||||
.cursor-pointer { cursor: pointer; }
|
.cursor-pointer {
|
||||||
.cursor-not-allowed { cursor: not-allowed !important; }
|
cursor: pointer;
|
||||||
.cursor-auto { cursor: auto; }
|
}
|
||||||
.vertical-top { vertical-align: top; }
|
|
||||||
.inline-block { display: inline-block; }
|
.cursor-not-allowed {
|
||||||
.text-left { text-align: left; }
|
cursor: not-allowed !important;
|
||||||
.text-right { text-align: right; }
|
}
|
||||||
.text-center { text-align: center; }
|
|
||||||
.center { margin: 0 auto; }
|
.cursor-auto {
|
||||||
.bold { font-weight: bold; }
|
cursor: auto;
|
||||||
.italic { font-style: italic; }
|
}
|
||||||
.text-uppercase { text-transform: uppercase; }
|
|
||||||
.truncate { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } // requires element to specify width
|
.vertical-top {
|
||||||
.absolute-center { margin: auto; position: absolute; top: 0; left: 0; bottom: 0; right: 0; }
|
vertical-align: top;
|
||||||
.no-width { white-space: nowrap; width: 1%; }
|
}
|
||||||
.no-float { float: none !important; }
|
|
||||||
.no-overflow-x { overflow-x: visible !important; }
|
.inline-block {
|
||||||
input:-webkit-autofill { -webkit-box-shadow: 0 0 0px 1000px white inset; }
|
display: inline-block;
|
||||||
img.responsive-img, video.responsive-video { max-width: 100%; height: auto; }
|
}
|
||||||
.bordered { border: 1px solid $color-border; }
|
|
||||||
|
.text-left {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.center {
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bold {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-uppercase {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.truncate {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
// requires element to specify width
|
||||||
|
|
||||||
|
.absolute-center {
|
||||||
|
margin: auto;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-width {
|
||||||
|
white-space: nowrap;
|
||||||
|
width: 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-float {
|
||||||
|
float: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-overflow-x {
|
||||||
|
overflow-x: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-display {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input:-webkit-autofill {
|
||||||
|
-webkit-box-shadow: 0 0 0 1000px white inset;
|
||||||
|
}
|
||||||
|
|
||||||
|
img.responsive-img,
|
||||||
|
video.responsive-video {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bordered {
|
||||||
|
border: 1px solid $color-border;
|
||||||
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
@ -52,7 +126,8 @@ a {
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
text-shadow: 1px 1px 1px rgba(0,0,0,0.004);
|
text-shadow: 1px 1px 1px rgba(0,0,0,0.004);
|
||||||
|
|
||||||
a:hover, a:focus {
|
a:focus,
|
||||||
|
a:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,52 +137,81 @@ a.alt {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
a:hover, a:focus {
|
a:focus,
|
||||||
|
a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$i: 150;
|
$i: 150;
|
||||||
@while $i > 0 {
|
@while $i > 0 {
|
||||||
.margin-#{$i} { margin: #{$i}px; }
|
.margin-#{$i} {
|
||||||
.margin-top-#{$i} { margin-top: #{$i}px; }
|
margin: #{$i}px;
|
||||||
.margin-bottom-#{$i} { margin-bottom: #{$i}px; }
|
}
|
||||||
.margin-right-#{$i} { margin-right: #{$i}px; }
|
|
||||||
.margin-left-#{$i} { margin-left: #{$i}px; }
|
.margin-top-#{$i} {
|
||||||
|
margin-top: #{$i}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-bottom-#{$i} {
|
||||||
|
margin-bottom: #{$i}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-right-#{$i} {
|
||||||
|
margin-right: #{$i}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.margin-left-#{$i} {
|
||||||
|
margin-left: #{$i}px;
|
||||||
|
}
|
||||||
$i: $i - 5;
|
$i: $i - 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
$i: 150;
|
$i: 150;
|
||||||
@while $i > 0 {
|
@while $i > 0 {
|
||||||
.padding-#{$i} { padding: #{$i}px; }
|
.padding-#{$i} {
|
||||||
.padding-top-#{$i} { padding-top: #{$i}px; }
|
padding: #{$i}px;
|
||||||
.padding-bottom-#{$i} { padding-bottom: #{$i}px; }
|
}
|
||||||
.padding-right-#{$i} { padding-right: #{$i}px; }
|
|
||||||
.padding-left-#{$i} { padding-left: #{$i}px; }
|
.padding-top-#{$i} {
|
||||||
|
padding-top: #{$i}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-bottom-#{$i} {
|
||||||
|
padding-bottom: #{$i}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-right-#{$i} {
|
||||||
|
padding-right: #{$i}px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.padding-left-#{$i} {
|
||||||
|
padding-left: #{$i}px;
|
||||||
|
}
|
||||||
$i: $i - 5;
|
$i: $i - 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
$i: 100;
|
$i: 100;
|
||||||
@while $i > 0 {
|
@while $i > 0 {
|
||||||
.width-#{$i} { width: #{$i}#{"%"}; }
|
.width-#{$i} {
|
||||||
|
width: #{$i}#{"%"};
|
||||||
|
}
|
||||||
$i: $i - 5;
|
$i: $i - 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-outline
|
.no-outline {
|
||||||
{
|
outline: none !important;
|
||||||
outline:none !important;
|
border: none !important;
|
||||||
border:none !important;
|
box-shadow: none !important;
|
||||||
box-shadow:none !important;
|
|
||||||
|
|
||||||
&:focus, &:active
|
&:active,
|
||||||
{
|
&:focus {
|
||||||
border:none !important;
|
border: none !important;
|
||||||
box-shadow:none !important;
|
box-shadow: none !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-select
|
.no-select {
|
||||||
{
|
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-khtml-user-select: none;
|
-khtml-user-select: none;
|
||||||
|
@ -127,11 +231,12 @@ ul {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.clearfix:before,
|
.clearfix:after,
|
||||||
.clearfix:after {
|
.clearfix:before {
|
||||||
content: " ";
|
content: " ";
|
||||||
display: table;
|
display: table;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clearfix:after {
|
.clearfix:after {
|
||||||
clear: both;
|
clear: both;
|
||||||
}
|
}
|
||||||
|
|
16
app/app/styles/view/content-linker.scss
Normal file
16
app/app/styles/view/content-linker.scss
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
.content-linker {
|
||||||
|
margin: 0 10px;
|
||||||
|
padding: 20px;
|
||||||
|
@include border(1px);
|
||||||
|
|
||||||
|
.input-control > .page-list {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
> .item {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,7 +20,7 @@
|
||||||
{{#each attachments key="id" as |a index|}}
|
{{#each attachments key="id" as |a index|}}
|
||||||
<li class="item">
|
<li class="item">
|
||||||
<img class="icon" src="/assets/img/attachments/{{document/file-icon a.extension}}" />
|
<img class="icon" src="/assets/img/attachments/{{document/file-icon a.extension}}" />
|
||||||
<a href="{{ appMeta.endpoint }}/public/attachments/{{ appMeta.orgId }}/{{ a.job }}/{{ a.fileId }}">
|
<a href="{{ appMeta.endpoint }}/public/attachments/{{ appMeta.orgId }}/{{ a.id }}">
|
||||||
<span class="file">{{ a.filename }}</span>
|
<span class="file">{{ a.filename }}</span>
|
||||||
</a>
|
</a>
|
||||||
{{#if isEditor}}
|
{{#if isEditor}}
|
||||||
|
@ -34,42 +34,41 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="pages">
|
<div class="pages">
|
||||||
{{#each pages key="id" as |page index|}}
|
{{#each pages key="id" as |page index|}}
|
||||||
<div class="wysiwyg">
|
<div class="wysiwyg">
|
||||||
<div id="page-{{ page.id }}" class="is-a-page" data-id="{{ page.id }}" data-type="{{ page.contentType }}">
|
<div id="page-{{ page.id }}" class="is-a-page" data-id="{{ page.id }}" data-type="{{ page.contentType }}">
|
||||||
{{document/page-heading tagName=page.tagName document=document folder=folder page=page isEditor=isEditor onDeletePage=(action 'onDeletePage')}}
|
{{document/page-heading tagName=page.tagName document=document folder=folder page=page isEditor=isEditor onDeletePage=(action 'onDeletePage')}} {{section/base-renderer page=page}}
|
||||||
{{section/base-renderer page=page}}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{{/each}}
|
||||||
{{/each}}
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="dropdown-dialog delete-attachment-dialog">
|
<div class="dropdown-dialog delete-attachment-dialog">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<p>Are you sure you want to delete <span class="bold">{{deleteAttachment.name}}?</span></p>
|
<p>Are you sure you want to delete <span class="bold">{{deleteAttachment.name}}?</span></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
<div class="flat-button" {{action 'cancel'}}>
|
<div class="flat-button" {{action 'cancel'}}>
|
||||||
cancel
|
cancel
|
||||||
</div>
|
</div>
|
||||||
<div class="flat-button flat-red" {{action 'deleteAttachment'}}>
|
<div class="flat-button flat-red" {{action 'deleteAttachment'}}>
|
||||||
delete
|
delete
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearfix"></div>
|
<div class="clearfix"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if noSections}}
|
{{#if noSections}}
|
||||||
<div class="no-sections">
|
<div class="no-sections">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="message">Click the
|
<div class="message">Click the
|
||||||
<div class="round-button-mono">
|
<div class="round-button-mono">
|
||||||
<i class="material-icons color-gray">add</i>
|
<i class="material-icons color-gray">add</i>
|
||||||
<div class="name">section</div>
|
<div class="name">section</div>
|
||||||
</div>
|
</div>
|
||||||
to add a new section to this document</div>
|
to add a new section to this document</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
21
app/app/templates/components/link/content-linker.hbs
Normal file
21
app/app/templates/components/link/content-linker.hbs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<div class="content-linker">
|
||||||
|
<form>
|
||||||
|
<div class="input-control">
|
||||||
|
<label>Insert Link</label>
|
||||||
|
<div class="tip">Give the link a clickable name</div>
|
||||||
|
{{focus-input type="input" value=linkName}}
|
||||||
|
</div>
|
||||||
|
{{#if hasSections}}
|
||||||
|
<div class="input-control">
|
||||||
|
{{ui-select id="content-linker-section-list" content=candidates.pages action=(action (mut selection)) prompt="Link to existing section" optionValuePath="id" optionLabelPath="title" }}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if hasAttachments}}
|
||||||
|
<div class="input-control">
|
||||||
|
{{ui-select id="content-linker-attachment-list" content=candidates.attachments action=(action (mut selection)) prompt="Link to file attachment" optionValuePath="id" optionLabelPath="title" }}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
<div class="regular-button button-blue pull-right" {{ action 'onInsertLink' }}>Insert</div>
|
||||||
|
<div class="clearfix" />
|
||||||
|
</form>
|
||||||
|
</div>
|
|
@ -1,3 +1,10 @@
|
||||||
{{#section/base-editor document=document folder=folder page=page isDirty=(action 'isDirty') onCancel=(action 'onCancel') onAction=(action 'onAction')}}
|
{{#section/base-editor document=document folder=folder page=page isDirty=(action 'isDirty') onCancel=(action 'onCancel') onAction=(action 'onAction')}}
|
||||||
{{focus-textarea value=pageBody id="rich-text-editor" class="mousetrap"}}
|
<div class="{{if showSidebar 'width-70' 'width-100'}} pull-left">
|
||||||
|
{{focus-textarea value=pageBody id="rich-text-editor" class="mousetrap"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="{{if showSidebar 'width-30' 'no-display'}} pull-left">
|
||||||
|
{{link/content-linker document=document folder=folder page=page linkName=linkName onInsertLink=(action 'onInsertLink')}}
|
||||||
|
</div>
|
||||||
|
|
||||||
{{/section/base-editor}}
|
{{/section/base-editor}}
|
||||||
|
|
10
app/vendor/markdown-it.min.js
vendored
10
app/vendor/markdown-it.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -40,7 +40,7 @@ func AttachmentDownload(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
params := mux.Vars(r)
|
params := mux.Vars(r)
|
||||||
|
|
||||||
attachment, err := p.GetAttachmentByJobAndFileID(params["orgID"], params["job"], params["fileID"])
|
attachment, err := p.GetAttachment(params["orgID"], params["attachmentID"])
|
||||||
|
|
||||||
if err == sql.ErrNoRows {
|
if err == sql.ErrNoRows {
|
||||||
writeNotFoundError(w, method, params["fileID"])
|
writeNotFoundError(w, method, params["fileID"])
|
||||||
|
|
135
core/api/endpoint/link_endpoint.go
Normal file
135
core/api/endpoint/link_endpoint.go
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
package endpoint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
"github.com/documize/community/core/api/entity"
|
||||||
|
"github.com/documize/community/core/api/request"
|
||||||
|
"github.com/documize/community/core/api/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetLinkCandidates returns references to documents/sections/attachments.
|
||||||
|
func GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
|
||||||
|
method := "GetLinkCandidates"
|
||||||
|
p := request.GetPersister(r)
|
||||||
|
|
||||||
|
params := mux.Vars(r)
|
||||||
|
documentID := params["documentID"]
|
||||||
|
pageID := params["pageID"]
|
||||||
|
|
||||||
|
// parameter check
|
||||||
|
if len(documentID) == 0 {
|
||||||
|
util.WriteMissingDataError(w, method, "documentID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pageID) == 0 {
|
||||||
|
util.WriteMissingDataError(w, method, "pageID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// permission check
|
||||||
|
if !p.CanViewDocument(documentID) {
|
||||||
|
util.WriteForbiddenError(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can link to a section within the same document so
|
||||||
|
// let's get all pages for the document and remove "us".
|
||||||
|
pages, err := p.GetPagesWithoutContent(documentID)
|
||||||
|
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
util.WriteServerError(w, method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pages) == 0 {
|
||||||
|
pages = []entity.Page{}
|
||||||
|
}
|
||||||
|
|
||||||
|
pc := []entity.LinkCandidate{}
|
||||||
|
|
||||||
|
for _, p := range pages {
|
||||||
|
if p.RefID != pageID {
|
||||||
|
c := entity.LinkCandidate{
|
||||||
|
RefID: util.UniqueID(),
|
||||||
|
DocumentID: documentID,
|
||||||
|
PageID: p.RefID,
|
||||||
|
LinkType: "section",
|
||||||
|
Title: p.Title,
|
||||||
|
}
|
||||||
|
pc = append(pc, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We can link to attachment within the same document so
|
||||||
|
// let's get all attachments for the document.
|
||||||
|
files, err := p.GetAttachments(documentID)
|
||||||
|
|
||||||
|
if err != nil && err != sql.ErrNoRows {
|
||||||
|
util.WriteServerError(w, method, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(files) == 0 {
|
||||||
|
files = []entity.Attachment{}
|
||||||
|
}
|
||||||
|
|
||||||
|
fc := []entity.LinkCandidate{}
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
c := entity.LinkCandidate{
|
||||||
|
RefID: util.UniqueID(),
|
||||||
|
DocumentID: documentID,
|
||||||
|
AttachmentID: f.RefID,
|
||||||
|
LinkType: "file",
|
||||||
|
Title: f.Filename,
|
||||||
|
}
|
||||||
|
|
||||||
|
fc = append(fc, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// send back the payload
|
||||||
|
var payload struct {
|
||||||
|
Pages []entity.LinkCandidate `json:"pages"`
|
||||||
|
Attachments []entity.LinkCandidate `json:"attachments"`
|
||||||
|
Matches []entity.LinkCandidate `json:"matches"`
|
||||||
|
}
|
||||||
|
|
||||||
|
payload.Pages = pc
|
||||||
|
payload.Attachments = fc
|
||||||
|
|
||||||
|
json, err := json.Marshal(payload)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
util.WriteMarshalError(w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
util.WriteSuccessBytes(w, json)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
DocumentID string `json:"documentId"`
|
||||||
|
PageID string `json:"pageId"`
|
||||||
|
FileID string `json:"fileId"`
|
||||||
|
LinkType string `json:"linkType"`
|
||||||
|
Title string `json:"caption"` // what we label the link
|
||||||
|
Context string `json:"context"` // additional context (e.g. excerpt, parent)
|
||||||
|
|
||||||
|
*/
|
|
@ -136,7 +136,7 @@ func init() {
|
||||||
log.IfErr(Add(RoutePrefixPublic, "forgot", []string{"POST", "OPTIONS"}, nil, ForgotUserPassword))
|
log.IfErr(Add(RoutePrefixPublic, "forgot", []string{"POST", "OPTIONS"}, nil, ForgotUserPassword))
|
||||||
log.IfErr(Add(RoutePrefixPublic, "reset/{token}", []string{"POST", "OPTIONS"}, nil, ResetUserPassword))
|
log.IfErr(Add(RoutePrefixPublic, "reset/{token}", []string{"POST", "OPTIONS"}, nil, ResetUserPassword))
|
||||||
log.IfErr(Add(RoutePrefixPublic, "share/{folderID}", []string{"POST", "OPTIONS"}, nil, AcceptSharedFolder))
|
log.IfErr(Add(RoutePrefixPublic, "share/{folderID}", []string{"POST", "OPTIONS"}, nil, AcceptSharedFolder))
|
||||||
log.IfErr(Add(RoutePrefixPublic, "attachments/{orgID}/{job}/{fileID}", []string{"GET", "OPTIONS"}, nil, AttachmentDownload))
|
log.IfErr(Add(RoutePrefixPublic, "attachments/{orgID}/{attachmentID}", []string{"GET", "OPTIONS"}, nil, AttachmentDownload))
|
||||||
log.IfErr(Add(RoutePrefixPublic, "version", []string{"GET", "OPTIONS"}, nil, version))
|
log.IfErr(Add(RoutePrefixPublic, "version", []string{"GET", "OPTIONS"}, nil, version))
|
||||||
|
|
||||||
// **** add secure routes
|
// **** add secure routes
|
||||||
|
@ -212,12 +212,14 @@ func init() {
|
||||||
log.IfErr(Add(RoutePrefixPrivate, "sections", []string{"POST", "OPTIONS"}, nil, RunSectionCommand))
|
log.IfErr(Add(RoutePrefixPrivate, "sections", []string{"POST", "OPTIONS"}, nil, RunSectionCommand))
|
||||||
log.IfErr(Add(RoutePrefixPrivate, "sections/refresh", []string{"GET", "OPTIONS"}, nil, RefreshSections))
|
log.IfErr(Add(RoutePrefixPrivate, "sections/refresh", []string{"GET", "OPTIONS"}, nil, RefreshSections))
|
||||||
|
|
||||||
|
// Links
|
||||||
|
log.IfErr(Add(RoutePrefixPrivate, "links/{documentID}/{pageID}", []string{"GET", "OPTIONS"}, nil, GetLinkCandidates))
|
||||||
|
|
||||||
// Global installation-wide config
|
// Global installation-wide config
|
||||||
log.IfErr(Add(RoutePrefixPrivate, "global", []string{"GET", "OPTIONS"}, nil, GetGlobalConfig))
|
log.IfErr(Add(RoutePrefixPrivate, "global", []string{"GET", "OPTIONS"}, nil, GetGlobalConfig))
|
||||||
log.IfErr(Add(RoutePrefixPrivate, "global", []string{"PUT", "OPTIONS"}, nil, SaveGlobalConfig))
|
log.IfErr(Add(RoutePrefixPrivate, "global", []string{"PUT", "OPTIONS"}, nil, SaveGlobalConfig))
|
||||||
|
|
||||||
// **** configure single page app handler.
|
// Single page app handler
|
||||||
|
|
||||||
log.IfErr(Add(RoutePrefixRoot, "robots.txt", []string{"GET", "OPTIONS"}, nil, GetRobots))
|
log.IfErr(Add(RoutePrefixRoot, "robots.txt", []string{"GET", "OPTIONS"}, nil, GetRobots))
|
||||||
log.IfErr(Add(RoutePrefixRoot, "sitemap.xml", []string{"GET", "OPTIONS"}, nil, GetSitemap))
|
log.IfErr(Add(RoutePrefixRoot, "sitemap.xml", []string{"GET", "OPTIONS"}, nil, GetSitemap))
|
||||||
log.IfErr(Add(RoutePrefixRoot, "{rest:.*}", nil, nil, web.EmberHandler))
|
log.IfErr(Add(RoutePrefixRoot, "{rest:.*}", nil, nil, web.EmberHandler))
|
||||||
|
|
|
@ -342,3 +342,27 @@ type SitemapDocument struct {
|
||||||
Folder string
|
Folder string
|
||||||
Revised time.Time
|
Revised time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Link defines a reference between a section and another document/section/attachment.
|
||||||
|
type Link struct {
|
||||||
|
BaseEntity
|
||||||
|
OrgID string `json:"orgId"`
|
||||||
|
UserID string `json:"userId"`
|
||||||
|
SourceID string `json:"sourceId"`
|
||||||
|
DocumentID string `json:"documentId"`
|
||||||
|
PageID string `json:"pageId"`
|
||||||
|
LinkType string `json:"linkType"`
|
||||||
|
Orphan bool `json:"orphan"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// LinkCandidate defines a potential link to a document/section/attachment.
|
||||||
|
type LinkCandidate struct {
|
||||||
|
RefID string `json:"id"`
|
||||||
|
OrgID string `json:"orgId"`
|
||||||
|
DocumentID string `json:"documentId"`
|
||||||
|
PageID string `json:"pageId"`
|
||||||
|
AttachmentID string `json:"attachmentId"`
|
||||||
|
LinkType string `json:"linkType"`
|
||||||
|
Title string `json:"title"` // what we label the link
|
||||||
|
Context string `json:"context"` // additional context (e.g. excerpt, parent)
|
||||||
|
}
|
||||||
|
|
|
@ -47,23 +47,23 @@ func (p *Persister) AddAttachment(a entity.Attachment) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAttachmentByJobAndFileID returns the database attachment record specified by the parameters.
|
// GetAttachment returns the database attachment record specified by the parameters.
|
||||||
func (p *Persister) GetAttachmentByJobAndFileID(orgID, job, fileID string) (attachment entity.Attachment, err error) {
|
func (p *Persister) GetAttachment(orgID, attachmentID string) (attachment entity.Attachment, err error) {
|
||||||
|
|
||||||
err = nil
|
err = nil
|
||||||
|
|
||||||
stmt, err := Db.Preparex("SELECT id, refid, orgid, documentid, job, fileid, filename, data, extension, created, revised FROM attachment WHERE orgid=? and job=? and fileid=?")
|
stmt, err := Db.Preparex("SELECT id, refid, orgid, documentid, job, fileid, filename, data, extension, created, revised FROM attachment WHERE orgid=? and refid=?")
|
||||||
defer utility.Close(stmt)
|
defer utility.Close(stmt)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(fmt.Sprintf("Unable to prepare select for attachment %s/%s", job, fileID), err)
|
log.Error(fmt.Sprintf("Unable to prepare select for attachment %s", attachmentID), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = stmt.Get(&attachment, orgID, job, fileID)
|
err = stmt.Get(&attachment, orgID, attachmentID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(fmt.Sprintf("Unable to execute select for attachment %s/%s", job, fileID), err)
|
log.Error(fmt.Sprintf("Unable to execute select for attachment %s", attachmentID), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
138
core/api/request/link.go
Normal file
138
core/api/request/link.go
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
package request
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/documize/community/core/api/entity"
|
||||||
|
"github.com/documize/community/core/log"
|
||||||
|
"github.com/documize/community/core/utility"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AddLink inserts wiki-link into the store.
|
||||||
|
// These links exist when content references another document or content.
|
||||||
|
func (p *Persister) AddLink(l entity.Link) (err error) {
|
||||||
|
l.UserID = p.Context.UserID
|
||||||
|
l.Created = time.Now().UTC()
|
||||||
|
l.Revised = time.Now().UTC()
|
||||||
|
|
||||||
|
stmt, err := p.Context.Transaction.Preparex("INSERT INTO link (refid, orgid, userid, sourceid, documentid, pageid, linktype, created, revised) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)")
|
||||||
|
defer utility.Close(stmt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to prepare insert for link", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec(l.RefID, l.OrgID, l.UserID, l.SourceID, l.DocumentID, l.PageID, l.LinkType, l.Created, l.Revised)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Unable to execute insert for link", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetReferencedLinks returns all links that the specified section is referencing.
|
||||||
|
func (p *Persister) GetReferencedLinks(sectionID string) (links []entity.Link, err error) {
|
||||||
|
err = nil
|
||||||
|
|
||||||
|
sql := "SELECT id,refid,orgid,userid,sourceid,documentid,sectionid,linktype,orphan,created,revised from link WHERE orgid=? AND sourceid=?"
|
||||||
|
|
||||||
|
err = Db.Select(&links, sql, p.Context.OrgID, sectionID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Unable to execute select links for org %s", p.Context.OrgID), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLinksToSection returns all links that are linking to the specified section.
|
||||||
|
func (p *Persister) GetLinksToSection(sectionID string) (links []entity.Link, err error) {
|
||||||
|
err = nil
|
||||||
|
|
||||||
|
sql := "SELECT id,refid,orgid,userid,sourceid,documentid,sectionid,linktype,orphan,created,revised from link WHERE orgid=? AND sectionid=?"
|
||||||
|
|
||||||
|
err = Db.Select(&links, sql, p.Context.OrgID, sectionID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Unable to execute select links for org %s", p.Context.OrgID), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLinksToDocument returns all links that are linking to the specified document.
|
||||||
|
func (p *Persister) GetLinksToDocument(documentID string) (links []entity.Link, err error) {
|
||||||
|
err = nil
|
||||||
|
|
||||||
|
sql := "SELECT id,refid,orgid,userid,sourceid,documentid,sectionid,linktype,orphan,created,revised from link WHERE orgid=? AND documentid=?"
|
||||||
|
|
||||||
|
err = Db.Select(&links, sql, p.Context.OrgID, documentID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Unable to execute select links for org %s", p.Context.OrgID), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarkLinkAsOrphan marks the link record as being invalid.
|
||||||
|
func (p *Persister) MarkLinkAsOrphan(l entity.Link) (err error) {
|
||||||
|
l.Orphan = true
|
||||||
|
l.Revised = time.Now().UTC()
|
||||||
|
|
||||||
|
stmt, err := p.Context.Transaction.PrepareNamed("UPDATE link SET orphan=1 revised=:revised WHERE orgid=:orgid AND refid=:refid")
|
||||||
|
defer utility.Close(stmt)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Unable to prepare update for link %s", l.RefID), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stmt.Exec(&l)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Error(fmt.Sprintf("Unable to execute update for link %s", l.RefID), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteLink removes saved link from the store.
|
||||||
|
func (p *Persister) DeleteLink(id string) (rows int64, err error) {
|
||||||
|
return p.Base.DeleteConstrained(p.Context.Transaction, "link", p.Context.OrgID, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLinkCandidates returns matching results based upon specified parameters.
|
||||||
|
// func (p *Persister) GetLinkCandidates(keywords string) (c []entity.LinkCandidate, err error) {
|
||||||
|
// err = nil
|
||||||
|
//
|
||||||
|
// sql := "SELECT id,refid,orgid,userid,sourceid,documentid,sectionid,linktype,orphan,created,revised from link WHERE orgid=? AND sectionid=?"
|
||||||
|
//
|
||||||
|
// err = Db.Select(&links, sql, p.Context.OrgID, sectionID)
|
||||||
|
//
|
||||||
|
// if err != nil {
|
||||||
|
// log.Error(fmt.Sprintf("Unable to execute select links for org %s", p.Context.OrgID), err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return
|
||||||
|
// }
|
|
@ -279,3 +279,54 @@ CREATE TABLE IF NOT EXISTS `userconfig` (
|
||||||
UNIQUE INDEX `idx_userconfig_orguserkey` (`orgid`, `userid`, `key` ASC))
|
UNIQUE INDEX `idx_userconfig_orguserkey` (`orgid`, `userid`, `key` ASC))
|
||||||
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
|
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
|
||||||
ENGINE = InnoDB;
|
ENGINE = InnoDB;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `share`;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `share` (
|
||||||
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`userid` CHAR(16) DEFAULT '' COLLATE utf8_bin,
|
||||||
|
`email` NVARCHAR(250) NOT NULL DEFAULT '',
|
||||||
|
`message` NVARCHAR(500) NOT NULL DEFAULT '',
|
||||||
|
`viewed` VARCHAR(500) NOT NULL DEFAULT '',
|
||||||
|
`secret` VARCHAR(200) NOT NULL DEFAULT '',
|
||||||
|
`expires` CHAR(16) DEFAULT '' COLLATE utf8_bin,
|
||||||
|
`active` BOOL NOT NULL DEFAULT 1,
|
||||||
|
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT pk_id PRIMARY KEY (id))
|
||||||
|
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
|
||||||
|
ENGINE = InnoDB;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `feedback`;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `feedback` (
|
||||||
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`userid` CHAR(16) DEFAULT '' COLLATE utf8_bin,
|
||||||
|
`email` NVARCHAR(250) NOT NULL DEFAULT '',
|
||||||
|
`feedback` LONGTEXT,
|
||||||
|
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT pk_id PRIMARY KEY (id))
|
||||||
|
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
|
||||||
|
ENGINE = InnoDB;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `link`;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `link` (
|
||||||
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`sourceid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`sectionid` CHAR(16) DEFAULT '' COLLATE utf8_bin,
|
||||||
|
`userid` CHAR(16) DEFAULT '' COLLATE utf8_bin,
|
||||||
|
`linktype` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`orphan` BOOL NOT NULL DEFAULT 0,
|
||||||
|
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT pk_id PRIMARY KEY (id))
|
||||||
|
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
|
||||||
|
ENGINE = InnoDB;
|
||||||
|
|
18
core/database/scripts/autobuild/db_00004.sql
Normal file
18
core/database/scripts/autobuild/db_00004.sql
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
/* community edition */
|
||||||
|
DROP TABLE IF EXISTS `link`;
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `link` (
|
||||||
|
`id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`refid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`orgid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`sourceid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`documentid` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`sectionid` CHAR(16) DEFAULT '' COLLATE utf8_bin,
|
||||||
|
`userid` CHAR(16) DEFAULT '' COLLATE utf8_bin,
|
||||||
|
`linktype` CHAR(16) NOT NULL COLLATE utf8_bin,
|
||||||
|
`orphan` BOOL NOT NULL DEFAULT 0,
|
||||||
|
`created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`revised` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
CONSTRAINT pk_id PRIMARY KEY (id))
|
||||||
|
DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci
|
||||||
|
ENGINE = InnoDB;
|
|
@ -26,7 +26,7 @@ type ProdInfo struct {
|
||||||
// Product returns product edition details
|
// Product returns product edition details
|
||||||
func Product() (p ProdInfo) {
|
func Product() (p ProdInfo) {
|
||||||
p.Major = "0"
|
p.Major = "0"
|
||||||
p.Minor = "27"
|
p.Minor = "28"
|
||||||
p.Patch = "0"
|
p.Patch = "0"
|
||||||
p.Version = fmt.Sprintf("%s.%s.%s", p.Major, p.Minor, p.Patch)
|
p.Version = fmt.Sprintf("%s.%s.%s", p.Major, p.Minor, p.Patch)
|
||||||
p.Edition = "Community"
|
p.Edition = "Community"
|
||||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue