1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-24 07:39:43 +02:00
This commit is contained in:
Harvey Kandola 2016-10-24 19:20:29 -07:00
parent 7db618dea0
commit 4a17acce11
19 changed files with 322 additions and 203 deletions

View file

@ -10,16 +10,18 @@
// https://documize.com // https://documize.com
import Ember from 'ember'; import Ember from 'ember';
import TooltipMixin from '../../mixins/tooltip';
const { const {
inject: { service } inject: { service }
} = Ember; } = Ember;
export default Ember.Component.extend({ export default Ember.Component.extend(TooltipMixin, {
link: service(), link: service(),
hasSections: false, hasSections: false,
hasAttachments: false, hasAttachments: false,
linkName: '', linkName: '',
keywords: '',
selection: null, selection: null,
init() { init() {
@ -36,24 +38,37 @@ export default Ember.Component.extend({
}); });
}, },
didReceiveAttrs() {}, didRender() {
this.addTooltip(document.getElementById("content-linker-button"));
},
didInsertElement() {}, willDestroyElement() {
this.destroyTooltips();
willDestroyElement() {}, },
actions: { actions: {
setSelection(i) {
this.set('selection', i);
let candidates = this.get('candidates');
candidates.pages.forEach(c => {
Ember.set(c, 'selected', c.id === i.id);
});
candidates.attachments.forEach(c => {
Ember.set(c, 'selected', c.id === i.id);
});
},
onInsertLink() { onInsertLink() {
let selection = this.get('selection'); let selection = this.get('selection');
let linkName = this.get('linkName');
if (linkName.length) { if (is.null(selection)) {
selection.title = linkName; return;
} }
if (is.not.null(selection)) { return this.get('onInsertLink')(selection);
this.get('onInsertLink')(selection);
}
} }
} }
}); });

View file

@ -14,67 +14,63 @@ import NotifierMixin from '../../mixins/notifier';
import TooltipMixin from '../../mixins/tooltip'; import TooltipMixin from '../../mixins/tooltip';
const { const {
computed computed
} = Ember; } = Ember;
export default Ember.Component.extend(NotifierMixin, TooltipMixin, { export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
folderService: Ember.inject.service('folder'), folderService: Ember.inject.service('folder'),
documentService: Ember.inject.service('document'), documentService: Ember.inject.service('document'),
session: Ember.inject.service(), session: Ember.inject.service(),
appMeta: Ember.inject.service(), appMeta: Ember.inject.service(),
showToolbar: false, showToolbar: false,
folder: {}, folder: {},
busy: false, busy: false,
importedDocuments: [], importedDocuments: [],
isFolderOwner: computed.equal('folder.userId', 'session.user.id'), isFolderOwner: computed.equal('folder.userId', 'session.user.id'),
moveFolderId: "", moveFolderId: "",
drop: null, drop: null,
didReceiveAttrs() { didReceiveAttrs() {
this.set('isFolderOwner', this.get('folder.userId') === this.get("session.user.id")); this.set('isFolderOwner', this.get('folder.userId') === this.get("session.user.id"));
let show = this.get('isFolderOwner') || this.get('hasSelectedDocuments') || this.get('folderService').get('canEditCurrentFolder'); let show = this.get('isFolderOwner') || this.get('hasSelectedDocuments') || this.get('folderService').get('canEditCurrentFolder');
this.set('showToolbar', show); this.set('showToolbar', show);
let targets = _.reject(this.get('folders'), { let targets = _.reject(this.get('folders'), {
id: this.get('folder').get('id') id: this.get('folder').get('id')
}); });
this.set('movedFolderOptions', targets); this.set('movedFolderOptions', targets);
}, },
didRender() { didRender() {
if (this.get('hasSelectedDocuments')) { if (this.get('hasSelectedDocuments')) {
this.addTooltip(document.getElementById("move-documents-button")); this.addTooltip(document.getElementById("move-documents-button"));
this.addTooltip(document.getElementById("delete-documents-button")); this.addTooltip(document.getElementById("delete-documents-button"));
} else { } else {
if (this.get('isFolderOwner')) { if (this.get('isFolderOwner')) {
this.addTooltip(document.getElementById("folder-share-button")); this.addTooltip(document.getElementById("folder-share-button"));
this.addTooltip(document.getElementById("folder-settings-button")); this.addTooltip(document.getElementById("folder-settings-button"));
} }
if (this.get('folderService').get('canEditCurrentFolder')) { if (this.get('folderService').get('canEditCurrentFolder')) {
this.addTooltip(document.getElementById("import-document-button")); this.addTooltip(document.getElementById("import-document-button"));
} }
} }
}, },
didUpdate() { didUpdate() {
this.setupImport(); this.setupImport();
}, },
didInsertElement() { willDestroyElement() {
// this.setupImport();
},
willDestroyElement() {
if (is.not.null(this.get('drop'))) { if (is.not.null(this.get('drop'))) {
this.get('drop').destroy(); this.get('drop').destroy();
this.set('drop', null); this.set('drop', null);
} }
this.destroyTooltips(); this.destroyTooltips();
}, },
setupImport() { setupImport() {
// guard against unecessary file upload component init // guard against unecessary file upload component init
@ -135,52 +131,52 @@ export default Ember.Component.extend(NotifierMixin, TooltipMixin, {
this.set('drop', dzone); this.set('drop', dzone);
}, },
actions: { actions: {
onDocumentImporting(filename) { onDocumentImporting(filename) {
this.send("showNotification", `Importing ${filename}`); this.send("showNotification", `Importing ${filename}`);
let documents = this.get('importedDocuments'); let documents = this.get('importedDocuments');
documents.push(filename); documents.push(filename);
this.set('importedDocuments', documents); this.set('importedDocuments', documents);
}, },
onDocumentImported(filename /*, document*/ ) { onDocumentImported(filename /*, document*/ ) {
this.send("showNotification", `${filename} ready`); this.send("showNotification", `${filename} ready`);
let documents = this.get('importedDocuments'); let documents = this.get('importedDocuments');
documents.pop(filename); documents.pop(filename);
this.set('importedDocuments', documents); this.set('importedDocuments', documents);
this.attrs.refresh(); this.attrs.refresh();
if (documents.length === 0) { if (documents.length === 0) {
// this.get('showDocument')(this.get('folder'), document); // this.get('showDocument')(this.get('folder'), document);
} }
}, },
deleteDocuments() { deleteDocuments() {
this.attrs.onDeleteDocument(); this.attrs.onDeleteDocument();
}, },
setMoveFolder(folderId) { setMoveFolder(folderId) {
this.set('moveFolderId', folderId); this.set('moveFolderId', folderId);
let folders = this.get('folders'); let folders = this.get('folders');
folders.forEach(folder => { folders.forEach(folder => {
folder.set('selected', folder.id === folderId); folder.set('selected', folder.id === folderId);
}); });
}, },
moveDocuments() { moveDocuments() {
if (this.get("moveFolderId") === "") { if (this.get("moveFolderId") === "") {
return false; return false;
} }
this.attrs.onMoveDocument(this.get('moveFolderId')); this.attrs.onMoveDocument(this.get('moveFolderId'));
this.set("moveFolderId", ""); this.set("moveFolderId", "");
return true; return true;
} }
} }
}); });

View file

@ -19,25 +19,23 @@ export default Ember.Component.extend({
appMeta: Ember.inject.service(), appMeta: Ember.inject.service(),
link: service(), link: service(),
pageBody: "", 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 maxHeight = $(document).height() - $(".document-editor > .toolbar").height() - 200;
let options = { let options = {
selector: "#rich-text-editor", selector: "#rich-text-editor",
relative_urls: false, relative_urls: false,
cache_suffix: "?v=430", cache_suffix: "?v=443",
browser_spellcheck: false, browser_spellcheck: false,
gecko_spellcheck: false, gecko_spellcheck: false,
theme: "modern", theme: "modern",
statusbar: false, statusbar: false,
height: $(document).height() - $(".document-editor > .toolbar").height() - 200, height: maxHeight,
entity_encoding: "raw", entity_encoding: "raw",
paste_data_images: true, paste_data_images: true,
image_advtab: true, image_advtab: true,
@ -62,24 +60,9 @@ export default Ember.Component.extend({
menu: {}, menu: {},
menubar: false, menubar: false,
toolbar1: "bold italic underline strikethrough superscript subscript | outdent indent bullist numlist forecolor backcolor | alignleft aligncenter alignright alignjustify | link unlink | table image media | hr codesample", toolbar1: "bold italic underline strikethrough superscript subscript | outdent indent bullist numlist forecolor backcolor | alignleft aligncenter alignright alignjustify | link unlink | table image media | hr codesample",
toolbar2: "formatselect fontselect fontsizeselect | documizeLinkButton", toolbar2: "formatselect fontselect fontsizeselect",
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');
}
}
});
} }
}; };
@ -100,14 +83,17 @@ export default Ember.Component.extend({
}, },
actions: { actions: {
showSidebar() {
this.set('linkName', tinymce.activeEditor.selection.getContent());
},
onInsertLink(link) { onInsertLink(link) {
let userSelection = tinymce.activeEditor.selection.getContent();
if (is.not.empty(userSelection)) {
Ember.set(link, 'title', userSelection);
}
let linkHTML = this.get('link').buildLink(link); let linkHTML = this.get('link').buildLink(link);
tinymce.activeEditor.insertContent(linkHTML); tinymce.activeEditor.insertContent(linkHTML);
this.set('showSidebar', false);
return true;
}, },
isDirty() { isDirty() {

View file

@ -0,0 +1,16 @@
// 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';
export default Ember.Component.extend({
tagName: 'span'
});

View file

@ -52,3 +52,21 @@ export default Ember.Service.extend({
return result; return result;
} }
}); });
/*
we should not redirect to a link that is in the same document!
what happens if we delete attachment?
UpdatePage(): find and persist links from saved content
1. We need to deal with links server-side
2. We need to click on links in the browser and 'navigate' to linked content
*/

View file

@ -8,14 +8,12 @@
// by contacting <sales@documize.com>. // by contacting <sales@documize.com>.
// //
// https://documize.com // https://documize.com
@import "color.scss"; @import "color.scss";
@import "font.scss"; @import "font.scss";
@import "functions.scss"; @import "functions.scss";
@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";
@ -27,11 +25,11 @@
@import "view/document/wysiwyg.scss"; @import "view/document/wysiwyg.scss";
@import "view/document/editor.scss"; @import "view/document/editor.scss";
@import "view/document/wizard.scss"; @import "view/document/wizard.scss";
@import "view/document/edit-tools.scss";
@import "view/common.scss"; @import "view/common.scss";
@import "vendor.scss"; @import "vendor.scss";
@import "responsive.scss"; @import "responsive.scss";
@import "print.scss"; @import "print.scss";
@import "section/trello.scss"; @import "section/trello.scss";
@import "section/gemini.scss"; @import "section/gemini.scss";
@import "section/github.scss"; @import "section/github.scss";

View file

@ -196,7 +196,7 @@ $i: 100;
.width-#{$i} { .width-#{$i} {
width: #{$i}#{"%"}; width: #{$i}#{"%"};
} }
$i: $i - 5; $i: $i - 2;
} }
.no-outline { .no-outline {

View file

@ -1,16 +0,0 @@
.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;
}
}
}

View file

@ -0,0 +1,29 @@
.edit-tools {
margin: 15px 0 0 20px;
min-height: 500px;
}
.content-linker-dialog {
width: 300px;
height: 400px;
overflow-y: auto;
.link-list {
margin: 0;
padding: 0;
.link-item {
margin: 0;
padding: 5px 0;
font-size: 0.9rem;
color: $color-gray;
cursor: pointer;
.icon {
margin-right: 5px;
height: 15px;
width: 15px;
}
}
}
}

View file

@ -0,0 +1,21 @@
.checkbox-option {
vertical-align: bottom;
cursor: pointer;
font-size: 0.9rem;
overflow: hidden;
white-space: nowrap;
> .material-icons {
font-size: 0.9rem;
vertical-align: top;
margin-right: 5px;
}
&:hover {
color: $color-link;
}
}
.checkbox-option-selected {
color: $color-link;
}

View file

@ -2,64 +2,64 @@
// Material Design icons from https://design.google.com/icons/ // Material Design icons from https://design.google.com/icons/
.material-icons { .material-icons {
font-family : "Material Icons"; font-family: "Material Icons";
font-weight : normal; font-weight: normal;
font-style : normal; font-style: normal;
font-size : 1.2rem; font-size: 1.2rem;
display : inline-block; display: inline-block;
text-transform : none; text-transform: none;
letter-spacing : normal; letter-spacing: normal;
word-wrap : normal; word-wrap: normal;
-webkit-font-smoothing : antialiased; -webkit-font-smoothing: antialiased;
text-rendering : optimizeLegibility; text-rendering: optimizeLegibility;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
font-feature-settings : "liga"; font-feature-settings: "liga";
} }
.transition-shadow { .transition-shadow {
transition: box-shadow .25s; transition: box-shadow 0.25s;
} }
.transition-all { .transition-all {
transition: all .25s; transition: all 0.25s;
} }
.z-depth-0 { .z-depth-0 {
box-shadow: none !important; box-shadow: none !important;
} }
.z-depth-tiny { .z-depth-tiny {
box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.05), 0 1px 1px 0 rgba(0, 0, 0, 0.05); box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.05), 0 1px 1px 0 rgba(0, 0, 0, 0.05);
} }
.z-depth-half { .z-depth-half {
box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.16), 0 1px 5px 0 rgba(0, 0, 0, 0.12); box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.16), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
} }
.z-depth-1 { .z-depth-1 {
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12); box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.16), 0 2px 10px 0 rgba(0, 0, 0, 0.12);
} }
.z-depth-1-half { /* used on hover states */ .z-depth-1-half {
box-shadow: 0 5px 11px 0 rgba(0, 0, 0, 0.18), 0 4px 15px 0 rgba(0, 0, 0, 0.15); /* used on hover states */
box-shadow: 0 5px 11px 0 rgba(0, 0, 0, 0.18), 0 4px 15px 0 rgba(0, 0, 0, 0.15);
} }
.z-depth-2 { .z-depth-2 {
box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19); box-shadow: 0 8px 17px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
} }
.z-depth-3 { .z-depth-3 {
box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.24), 0 17px 50px 0 rgba(0, 0, 0, 0.19); box-shadow: 0 12px 15px 0 rgba(0, 0, 0, 0.24), 0 17px 50px 0 rgba(0, 0, 0, 0.19);
} }
.z-depth-4 { .z-depth-4 {
box-shadow: 0 16px 28px 0 rgba(0, 0, 0, 0.22), 0 25px 55px 0 rgba(0, 0, 0, 0.21); box-shadow: 0 16px 28px 0 rgba(0, 0, 0, 0.22), 0 25px 55px 0 rgba(0, 0, 0, 0.21);
} }
.z-depth-5 { .z-depth-5 {
box-shadow: 0 27px 24px 0 rgba(0, 0, 0, 0.2), 0 40px 77px 0 rgba(0, 0, 0, 0.22); box-shadow: 0 27px 24px 0 rgba(0, 0, 0, 0.2), 0 40px 77px 0 rgba(0, 0, 0, 0.22);
} }
@import "widget-avatar"; @import "widget-avatar";
@import "widget-button"; @import "widget-button";
@import "widget-card"; @import "widget-card";
@ -70,3 +70,4 @@
@import "widget-sidebar-menu"; @import "widget-sidebar-menu";
@import "widget-table"; @import "widget-table";
@import "widget-tooltip"; @import "widget-tooltip";
@import "widget-checkbox";

View file

@ -0,0 +1,44 @@
<div class="edit-tools">
<div class="square-button-mono button-gray" id="content-linker-button" data-tooltip="Insert content link" data-tooltip-position="left middle">
<i class="material-icons color-white">link</i>
</div>
{{#dropdown-dialog target="content-linker-button" focusOn="content-linker-search" position="bottom right" button="Insert" color="flat-blue" onAction=(action 'onInsertLink')}}
<div class="content-linker-dialog">
<form>
<div class="input-control">
<label>Insert Link</label>
<div class="tip">Choose content below or search</div>
{{focus-input id="content-linker-search" type="input" value=keywords placeholder="keyword search"}}
</div>
{{#if hasSections}}
<ul class="link-list">
{{#each candidates.pages as |p|}}
<li class="link-item" {{ action 'setSelection' p }}>
{{#ui/ui-checkbox selected=p.selected}}
{{p.title}}
{{/ui/ui-checkbox}}
</li>
{{/each}}
</ul>
{{/if}}
{{#if hasAttachments}}
<ul class="link-list">
{{#each candidates.attachments as |a|}}
<li class="link-item" {{ action 'setSelection' a }}>
{{#ui/ui-checkbox selected=a.selected}}
<img class="icon" src="/assets/img/attachments/{{document/file-icon a.attachmentExtension}}" />
{{ a.title }}
{{/ui/ui-checkbox}}
</li>
{{/each}}
</ul>
{{/if}}
<div class="hide regular-button button-blue pull-right" {{ action 'onInsertLink' }}>Insert</div>
<div class="hide clearfix" />
</form>
</div>
{{/dropdown-dialog}}
</div>

View file

@ -4,12 +4,7 @@
<i class="material-icons color-gray">close</i> <i class="material-icons color-gray">close</i>
</div> </div>
{{folder/start-document {{folder/start-document savedTemplates=savedTemplates folder=folder editor=folderService.canEditCurrentFolder onEditTemplate=(action 'onEditTemplate') onDocumentTemplate=(action 'onDocumentTemplate')}}
savedTemplates=savedTemplates
folder=folder
editor=folderService.canEditCurrentFolder
onEditTemplate=(action 'onEditTemplate')
onDocumentTemplate=(action 'onDocumentTemplate')}}
{{/if}} {{/if}}
{{#if showScrollTool}} {{#if showScrollTool}}
@ -35,7 +30,7 @@
<i class="material-icons">add</i> <i class="material-icons">add</i>
<div class="name">Space</div> <div class="name">Space</div>
</div> </div>
{{#dropdown-dialog target="add-space-button" position="bottom left" button="Add" color="flat-green" onAction=(action 'addFolder') focusOn="new-folder-name"}} {{#dropdown-dialog target="add-space-button" position="bottom left" button="Add" color="flat-green" onAction=(action 'addFolder') focusOn="new-folder-name" }}
<div> <div>
<div class="input-control"> <div class="input-control">
<label>New space</label> <label>New space</label>
@ -92,8 +87,6 @@
{{/if}} {{/if}}
</div> </div>
<div class="copyright hidden-xs hidden-sm"> <div class="copyright hidden-xs hidden-sm">
<a href="https://documize.com?ref=app-footer" target="_blank">Copyright&nbsp;&copy;&nbsp;2016 Documize Inc.</a> <a href="https://documize.com?ref=app-footer" target="_blank">Copyright&nbsp;&copy;&nbsp;2016 Documize Inc.</a>
</div> </div>

View file

@ -1,21 +0,0 @@
<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>

View file

@ -1,10 +1,11 @@
{{#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')}}
<div class="{{if showSidebar 'width-70' 'width-100'}} pull-left">
<div class="pull-left width-96">
{{focus-textarea value=pageBody id="rich-text-editor" class="mousetrap"}} {{focus-textarea value=pageBody id="rich-text-editor" class="mousetrap"}}
</div> </div>
<div class="{{if showSidebar 'width-30' 'no-display'}} pull-left"> <div class="pull-left">
{{link/content-linker document=document folder=folder page=page linkName=linkName onInsertLink=(action 'onInsertLink')}} {{document/edit-tools document=document folder=folder page=page onInsertLink=(action 'onInsertLink')}}
</div> </div>
{{/section/base-editor}} {{/section/base-editor}}

View file

@ -0,0 +1,8 @@
<div class="checkbox-option {{if selected 'checkbox-option-selected'}}">
{{#if selected}}
<i class="material-icons checkbox-option-selected">radio_button_checked</i>
{{else}}
<i class="material-icons">radio_button_unchecked</i>
{{/if}}
{{yield}}
</div>

View file

@ -94,11 +94,12 @@ func GetLinkCandidates(w http.ResponseWriter, r *http.Request) {
for _, f := range files { for _, f := range files {
c := entity.LinkCandidate{ c := entity.LinkCandidate{
RefID: util.UniqueID(), RefID: util.UniqueID(),
DocumentID: documentID, DocumentID: documentID,
AttachmentID: f.RefID, AttachmentID: f.RefID,
LinkType: "file", LinkType: "file",
Title: f.Filename, Title: f.Filename,
AttachmentExtension: f.Extension,
} }
fc = append(fc, c) fc = append(fc, c)

View file

@ -346,23 +346,25 @@ type SitemapDocument struct {
// Link defines a reference between a section and another document/section/attachment. // Link defines a reference between a section and another document/section/attachment.
type Link struct { type Link struct {
BaseEntity BaseEntity
OrgID string `json:"orgId"` OrgID string `json:"orgId"`
UserID string `json:"userId"` UserID string `json:"userId"`
SourceID string `json:"sourceId"` LinkType string `json:"linkType"`
DocumentID string `json:"documentId"` SourceID string `json:"sourceId"`
PageID string `json:"pageId"` DocumentID string `json:"documentId"`
LinkType string `json:"linkType"` PageID string `json:"pageId"`
Orphan bool `json:"orphan"` AttachmentID string `json:"attachmentId"`
Orphan bool `json:"orphan"`
} }
// LinkCandidate defines a potential link to a document/section/attachment. // LinkCandidate defines a potential link to a document/section/attachment.
type LinkCandidate struct { type LinkCandidate struct {
RefID string `json:"id"` RefID string `json:"id"`
OrgID string `json:"orgId"` OrgID string `json:"orgId"`
DocumentID string `json:"documentId"` LinkType string `json:"linkType"`
PageID string `json:"pageId"` DocumentID string `json:"documentId"`
AttachmentID string `json:"attachmentId"` PageID string `json:"pageId"`
LinkType string `json:"linkType"` AttachmentID string `json:"attachmentId"`
Title string `json:"title"` // what we label the link AttachmentExtension string `json:"attachmentExtension"`
Context string `json:"context"` // additional context (e.g. excerpt, parent) Title string `json:"title"` // what we label the link
Context string `json:"context"` // additional context (e.g. excerpt, parent)
} }

View file

@ -136,3 +136,30 @@ func (p *Persister) DeleteLink(id string) (rows int64, err error) {
// //
// return // return
// } // }
//
// package main
//
// import (
// "fmt"
// "regexp"
// )
//
// var imgRE = regexp.MustCompile(`<a[^>]+\bhref=["']([^"']+)["']`)
//
// func findImages(htm string) []string {
// imgs := imgRE.FindAllStringSubmatch(htm, -1)
// out := make([]string, len(imgs))
// for i := range out {
// out[i] = imgs[i][1]
// }
// return out
// }
//
// func main() {
// fmt.Printf("%q", findImages(data))
// }
//
// const data = `
// <p>dfdfdf</p><a href="/link/section/34354"><x><z?>
// <a czx zcxz href='/link/file/file.exe'><x><z?>
// `