Skip to content

Commit 41af8cb

Browse files
authored
Merge pull request #148 from UCL-ARC/feature/footnotes-functionality
Feature/footnotes functionality
2 parents 0317c26 + 8f1193d commit 41af8cb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

86 files changed

+3649
-4
lines changed

.github/workflows/django.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
python-version: ${{ matrix.python-version }}
3535

3636
- name: Cache dependencies
37-
uses: actions/cache@v2
37+
uses: actions/cache@v4
3838
with:
3939
path: |
4040
~/.cache/pip

dashboard.py

+18
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,24 @@ def init_with_context(self, context):
122122
"external": True,
123123
"header": True,
124124
},
125+
{
126+
"title": _("How to add MediaCentral & Flickr links"),
127+
"url": "https://github.com/UCL-ARC/museum_of_dreams/wiki/Adding-links-from-mediacentral-&-flickr",
128+
"external": True,
129+
"header": True,
130+
},
131+
{
132+
"title": _("Adding Images"),
133+
"url": "https://github.com/UCL-ARC/museum_of_dreams/wiki/Adding-Images",
134+
"external": True,
135+
"header": True,
136+
},
137+
{
138+
"title": _("Importing from Word"),
139+
"url": "https://github.com/UCL-ARC/museum_of_dreams/wiki/Importing-from-Word",
140+
"external": True,
141+
"header": True,
142+
},
125143
],
126144
)
127145
)

docs/addingCKEPlugins.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Adding Plugins to CK Editor
2+
3+
- Download the plugin from CKE 4
4+
- unpack the zip file
5+
- in the codebase, create a new folder with the plugin name under `/mod_app/static/ckeditor/plugins/`
6+
- from the unpacked zip, you mainly need the plugin.js file but some other files can/should be included, especially if you need icons
7+
- in `/museum_of_dreams_project/settings/base.py` update `CKEDITOR_CONFIGS.default.extraPlugins` to include the name of the plugin and if it should have a button, add it in the toolbar section as well

mod_app/admin/note_admin.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class WritInline(s3BrowserButtonMixin, admin.TabularInline):
6666

6767

6868
@admin.register(VisualInfluences)
69-
class VisualInfluencesAdmin(s3BrowserButtonMixin, admin.ModelAdmin):
69+
class VisualInfluencesAdmin(admin.ModelAdmin):
7070
class Media:
7171
js = ("admin/js/mentionsPluginConfig.js",)
7272

@@ -85,7 +85,7 @@ def safe_content(self, obj):
8585

8686

8787
@admin.register(WrittenInfluences)
88-
class WrittenInfluencesAdmin(s3BrowserButtonMixin, admin.ModelAdmin):
88+
class WrittenInfluencesAdmin(admin.ModelAdmin):
8989
class Media:
9090
js = ("admin/js/mentionsPluginConfig.js",)
9191

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
CKEditorFootnotes
2+
==================
3+
4+
Maintainers Required
5+
--------------------
6+
7+
Unfortunately I don't have the time to give this project the attention it deserves. I'm happy to hand this over to someone or add contributors to help keep this ticking over.
8+
If you're interested, please get in touch.
9+
10+
---
11+
12+
Footnotes plugin for CKEditor.
13+
14+
Demo: http://demo.gridlight-design.co.uk/ckeditor-footnotes.html
15+
16+
CKEditor Addon: http://ckeditor.com/addon/footnotes
17+
18+
Configuring multiple instances
19+
------------------------------
20+
21+
As of 1.0.5 the plugin accepts a configuration option to allow you to prefix all your footnotes when the editor is instantiated.
22+
23+
E.g.
24+
25+
~~~
26+
CKEDITOR.replace( 'editor1', {
27+
footnotesPrefix: 'a'
28+
} );
29+
~~~
30+
31+
This could be set dynamically to allow you to ensure that all chunks of text can contain unique ID's, allowing you to include multiple chunks of text on any given page with ID clashes.
32+
33+
For example, it should be possible to use a server-side script to set this variable to the id of a database row.
34+
35+
36+
Other configuration
37+
-------------------
38+
39+
In master, it's now possible to to set configuration for the Footnotes title and the titles elements:
40+
41+
E.g.
42+
43+
~~~
44+
CKEDITOR.replace( 'editor1', {
45+
footnotesDisableHeader: true, // Defaults to false
46+
footnotesHeaderEls: ['<p><b>', '</b></p>'], // Defaults to ['<h2>', '</h2>']
47+
footnotesTitle: 'References', // Defaults to 'Footnotes'
48+
footnotesDialogEditorExtraConfig: { height: 150 } // Will be merged with the default options for the footnote editor
49+
} );
50+
~~~
51+
52+
Paste From Word
53+
---------------
54+
55+
A complimentary plugin that allows automatic conversion from content pasted from word is now available:
56+
[CKEditorFootnotes-PasteFromWord](https://github.com/andykirk/CKEditorFootnotes-PasteFromWord)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/**
2+
* The footnotes dialog definition.
3+
*
4+
* Version 1.0.9
5+
* https://github.com/andykirk/CKEditorFootnotes
6+
*
7+
*/
8+
9+
(function() {
10+
"use strict";
11+
12+
// Dialog definition.
13+
CKEDITOR.dialog.add( 'footnotesDialog', function( editor ) {
14+
15+
return {
16+
editor_name: false,
17+
footnotes_editor: false,
18+
dialog_dom_id: false,
19+
// Basic properties of the dialog window: title, minimum size.
20+
title: 'Manage Footnotes',
21+
minWidth: 400,
22+
minHeight: 200,
23+
24+
// Dialog window contents definition.
25+
contents: [
26+
{
27+
// Definition of the Basic Settings dialog tab (page).
28+
id: 'tab-basic',
29+
label: 'Basic Settings',
30+
31+
// The tab contents.
32+
elements: [
33+
{
34+
// Text input field for the footnotes text.
35+
type: 'textarea',
36+
id: 'new_footnote',
37+
'class': 'footnote_text',
38+
label: 'New footnote:',
39+
inputStyle: 'height: 100px',
40+
},
41+
{
42+
// Text input field for the footnotes title (explanation).
43+
type: 'text',
44+
id: 'footnote_id',
45+
name: 'footnote_id',
46+
label: 'No existing footnotes',
47+
48+
// Called by the main setupContent call on dialog initialization.
49+
setup: function( element ) {
50+
51+
var dialog = this.getDialog(),
52+
editor = dialog.getParentEditor(),
53+
el = dialog.getElement().findOne('#' + this.domId),
54+
footnotes = editor.editable().findOne('.footnotes ol');
55+
56+
dialog.dialog_dom_id = this.domId;
57+
58+
if (footnotes !== null) {
59+
60+
if (el.findOne('p') === null) {
61+
el.appendHtml('<p style="margin-bottom: 10px;"><strong>OR:</strong> Choose footnote:</p><ol class="footnotes_list"></ol>');
62+
} else {
63+
el.findOne('ol').getChildren().toArray().forEach(function(item){
64+
item.remove();
65+
});
66+
}
67+
68+
var radios = '';
69+
70+
footnotes.find('li').toArray().forEach(function(item){
71+
72+
var footnote_id = item.getAttribute('data-footnote-id');
73+
var cite_text = item.findOne('cite').getText();
74+
75+
radios += '<li style="margin-left: 15px;"><input type="radio" name="footnote_id" value="' + footnote_id + '" id="fn_' + footnote_id + '" /> <label for="fn_' + footnote_id + '" style="white-space: normal; display: inline-block; padding: 0 25px 0 5px; vertical-align: top; margin-bottom: 10px;">' + cite_text + '</label></li>';
76+
});
77+
78+
el.find('label,div').toArray().forEach(function(item){
79+
item.setStyle('display', 'none');
80+
});
81+
el.findOne('ol').appendHtml(radios);
82+
83+
el.find('input[type="radio"]').toArray().forEach(function(item){
84+
item.on('change', function(){
85+
86+
// Set the hidden input with the radio ident for the
87+
// footnote links to use:
88+
el.findOne('input[type="text"]').setValue(item.getValue());
89+
90+
// Also clear the editor to avoid any confusion:
91+
dialog.footnotes_editor.setData('');
92+
});
93+
});
94+
95+
} else {
96+
el.find('div').toArray().forEach(function(item){
97+
item.setStyle('display', 'none');
98+
});
99+
}
100+
}
101+
}
102+
]
103+
},
104+
],
105+
106+
// Invoked when the dialog is loaded.
107+
onShow: function() {
108+
this.setupContent();
109+
110+
var dialog = this;
111+
CKEDITOR.on( 'instanceLoaded', function( evt ) {
112+
dialog.editor_name = evt.editor.name;
113+
dialog.footnotes_editor = evt.editor;
114+
} );
115+
116+
// Allow page to scroll with dialog to allow for many/long footnotes
117+
// (https://github.com/andykirk/CKEditorFootnotes/issues/12)
118+
/*this.getElement().findOne('.cke_dialog').setStyles({
119+
'position': 'absolute',
120+
'top': '2%'
121+
});*/
122+
// Note that it seems core CKEditor Dialog CSS now solves this for me so I don't
123+
// need the above code. I'll keep it here for reference for now though.
124+
125+
var current_editor_id = dialog.getParentEditor().id;
126+
127+
CKEDITOR.replaceAll( function( textarea, config ) {
128+
// Make sure the textarea has the correct class:
129+
if (!textarea.className.match(/footnote_text/)) {
130+
return false;
131+
}
132+
133+
// Make sure we only instantiate the relevant editor:
134+
var el = textarea;
135+
while ((el = el.parentElement) && !el.classList.contains(current_editor_id));
136+
if (!el) {
137+
return false;
138+
}
139+
140+
config.toolbarGroups = [
141+
{ name: 'editing', groups: [ 'undo', 'find', 'selection', 'spellchecker' ] },
142+
{ name: 'clipboard', groups: [ 'clipboard' ] },
143+
{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
144+
]
145+
config.allowedContent = 'br em strong; a[!href]';
146+
config.enterMode = CKEDITOR.ENTER_BR;
147+
config.autoParagraph = false;
148+
config.height = 80;
149+
config.resize_enabled = false;
150+
config.autoGrow_minHeight = 80;
151+
config.removePlugins = 'footnotes';
152+
153+
var extra_config = editor.config.footnotesDialogEditorExtraConfig;
154+
if (extra_config) {
155+
for (var attribute in extra_config) {
156+
config[attribute] = extra_config[attribute];
157+
}
158+
}
159+
160+
// If we focus on the dialog editor we should clear the radios to avoid any
161+
// confusion. Similarly, if we focus on a radio, we should clear the editor
162+
// (see setup above for radio change event handler for that)
163+
config.on = {
164+
focus: function( evt ){
165+
var form_row = evt.editor.element.getAscendant('tr').getNext();
166+
form_row.find('input[type="radio"]').toArray().forEach(function(item){
167+
item.$.checked = false;
168+
});
169+
form_row.findOne('input[type="text"]').setValue('');
170+
}
171+
};
172+
return true;
173+
});
174+
175+
},
176+
177+
// This method is invoked once a user clicks the OK button, confirming the dialog.
178+
onOk: function() {
179+
var dialog = this;
180+
var footnote_editor = CKEDITOR.instances[dialog.editor_name];
181+
var footnote_id = dialog.getValueOf('tab-basic', 'footnote_id');
182+
var footnote_data = footnote_editor.getData();
183+
184+
185+
if (footnote_id == '') {
186+
// No existing id selected, check for new footnote:
187+
if (footnote_data == '') {
188+
// Nothing entered, so quit:
189+
return;
190+
} else {
191+
// Insert new footnote:
192+
editor.plugins.footnotes.build(footnote_data, true, editor);
193+
}
194+
} else {
195+
// Insert existing footnote:
196+
editor.plugins.footnotes.build(footnote_id, false, editor);
197+
}
198+
// Destroy the editor so it's rebuilt properly next time:
199+
footnote_editor.destroy();
200+
// Destroy the list of footnotes so it's rebuilt properly next time:
201+
var list = dialog.getElement().findOne('#' + dialog.dialog_dom_id).findOne('ol');
202+
if (list) {
203+
list.getChildren().toArray().forEach(function(item){
204+
item.remove();
205+
});
206+
}
207+
return;
208+
},
209+
210+
onCancel: function() {
211+
var dialog = this;
212+
var footnote_editor = CKEDITOR.instances[dialog.editor_name];
213+
footnote_editor.destroy();
214+
}
215+
};
216+
});
217+
}());
Loading
Loading

0 commit comments

Comments
 (0)