From 55e1ed3a2b2f3018674369d5165d60978e4ddbfd Mon Sep 17 00:00:00 2001 From: Chase Sterling Date: Tue, 9 Aug 2016 00:52:08 -0400 Subject: [PATCH 1/5] WIP partial improvements --- realms/modules/wiki/models.py | 14 ++++---------- realms/modules/wiki/views.py | 31 ++++++++++++++++++++++++++++--- realms/static/js/editor.js | 20 ++++++++++++++++++-- realms/static/js/mdr.js | 13 +++++++++++-- realms/templates/wiki/edit.html | 2 +- realms/templates/wiki/page.html | 9 ++------- 6 files changed, 64 insertions(+), 25 deletions(-) diff --git a/realms/modules/wiki/models.py b/realms/modules/wiki/models.py index b32f076..5f2f2d7 100644 --- a/realms/modules/wiki/models.py +++ b/realms/modules/wiki/models.py @@ -187,16 +187,10 @@ class WikiPage(HookMixin): return len(cached_revs), True @property - def partials(self): - data = self.data - if not data: - return {} - partials = {} - meta = self._get_meta(data) - if meta and 'import' in meta: - for partial_name in meta['import']: - partials[partial_name] = self.wiki.get_page(partial_name, sha=self.sha) - return partials + def imports(self): + """Names""" + meta = self._get_meta(self.data) + return meta.get('import', []) @staticmethod def _get_meta(content): diff --git a/realms/modules/wiki/views.py b/realms/modules/wiki/views.py index c851741..b3de441 100644 --- a/realms/modules/wiki/views.py +++ b/realms/modules/wiki/views.py @@ -1,3 +1,4 @@ +import collections import itertools import sys from datetime import datetime @@ -97,7 +98,6 @@ def history_data(name): } - @blueprint.route("/_edit/") @login_required def edit(name): @@ -115,7 +115,32 @@ def edit(name): # TODO: Remove this? See #148 info=next(page.history), sha=page.sha, - partials=page.partials) + partials=_partials(page.imports)) + + +def _partials(imports): + page_queue = collections.deque(imports) + partials = collections.OrderedDict() + while page_queue: + page_name = page_queue.popleft() + if page_name in partials: + continue + page = g.current_wiki.get_page(page_name) + data = page.data + partials[page_name] = data + if not data: + continue + meta = page._get_meta(data) + if meta and meta.get('import'): + page_queue.extend(meta['import']) + return partials + + +@blueprint.route("/_partials") +def partials(): + if current_app.config.get('PRIVATE_WIKI') and current_user.is_anonymous(): + return current_app.login_manager.unauthorized() + return {'partials': _partials(request.args.getlist('imports[]'))} @blueprint.route("/_create/", defaults={'name': None}) @@ -239,6 +264,6 @@ def page(name): data = g.current_wiki.get_page(cname) if data: - return render_template('wiki/page.html', name=cname, page=data, partials=data.partials) + return render_template('wiki/page.html', name=cname, page=data, partials=_partials(data.imports)) else: return redirect(url_for('wiki.create', name=cname)) diff --git a/realms/static/js/editor.js b/realms/static/js/editor.js index 6340f58..bd9cf0c 100644 --- a/realms/static/js/editor.js +++ b/realms/static/js/editor.js @@ -82,10 +82,26 @@ var deletePage = function() { bootbox.alert('Error deleting page!'); }); }; - +var partials = {}; var aced = new Aced({ editor: $('#entry-markdown-content').find('.editor').attr('id'), - renderer: function(md) { return MDR.convert(md) }, + renderer: function(md) { + var doc = metaMarked(md); + if ('import' in doc.meta) { + if (!doc.meta['import'].every(function(val) {return val in partials;})) { + $.ajax({ + url: '/_partials', + data: {'imports': doc.meta['import']}, + async: true, + dataType: 'json', + success: function (response) { + $.extend(partials, response['partials']); + //TODO: Force editor rerender + }}); + } + } + return MDR.convert(md, partials) + }, info: Commit.info, submit: function(content) { var data = { diff --git a/realms/static/js/mdr.js b/realms/static/js/mdr.js index 445b425..b3e9ddb 100644 --- a/realms/static/js/mdr.js +++ b/realms/static/js/mdr.js @@ -58,11 +58,12 @@ var MDR = { parse: function(md){ return marked(md, { renderer: this.renderer }); }, - convert: function(md, sanitize) { + convert: function(md, partials, sanitize) { if (this.sanitize !== null) { sanitize = this.sanitize; } this.md = md; + this.partials = partials; this.processMeta(); try { var html = this.parse(this.md); @@ -93,7 +94,15 @@ var MDR = { processMeta: function() { var doc = metaMarked(this.md); this.md = doc.md; - this.meta = doc.meta; + var meta = this.meta = {}; + if (this.partials) { + $.each(this.partials, function(key, value) { + var doc = metaMarked(value); + Handlebars.registerPartial(key, doc.md); + $.extend(meta, doc.meta); + }) + } + $.extend(this.meta, doc.meta); if (this.meta) { try { var template = Handlebars.compile(this.md); diff --git a/realms/templates/wiki/edit.html b/realms/templates/wiki/edit.html index 398cbae..479a44b 100644 --- a/realms/templates/wiki/edit.html +++ b/realms/templates/wiki/edit.html @@ -14,7 +14,7 @@ {% for name, value in partials.items() %} {% if name and value %} try { - Handlebars.registerPartial({{ name|tojson|safe }}, {{ value.data|tojson|safe }}); + Handlebars.registerPartial({{ name|tojson|safe }}, {{ value|tojson|safe }}); } catch (e) { // no data? } diff --git a/realms/templates/wiki/page.html b/realms/templates/wiki/page.html index bd40d7f..4529d23 100644 --- a/realms/templates/wiki/page.html +++ b/realms/templates/wiki/page.html @@ -23,12 +23,7 @@ {% block js %} -{% endblock %} \ No newline at end of file +{% endblock %} From 55e2ceccaad08779f928b6fc3242105bf3d1dfb3 Mon Sep 17 00:00:00 2001 From: Chase Sterling Date: Tue, 9 Aug 2016 20:14:29 -0400 Subject: [PATCH 2/5] Fix live preview of imports in editor --- realms/modules/wiki/models.py | 2 +- realms/static/js/editor.js | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/realms/modules/wiki/models.py b/realms/modules/wiki/models.py index 5f2f2d7..e5b3ca2 100644 --- a/realms/modules/wiki/models.py +++ b/realms/modules/wiki/models.py @@ -189,7 +189,7 @@ class WikiPage(HookMixin): @property def imports(self): """Names""" - meta = self._get_meta(self.data) + meta = self._get_meta(self.data) or {} return meta.get('import', []) @staticmethod diff --git a/realms/static/js/editor.js b/realms/static/js/editor.js index bd9cf0c..71a17c9 100644 --- a/realms/static/js/editor.js +++ b/realms/static/js/editor.js @@ -87,17 +87,15 @@ var aced = new Aced({ editor: $('#entry-markdown-content').find('.editor').attr('id'), renderer: function(md) { var doc = metaMarked(md); - if ('import' in doc.meta) { + if (doc.meta && 'import' in doc.meta) { + // If we don't have all the imports loaded as partials, get them from server if (!doc.meta['import'].every(function(val) {return val in partials;})) { - $.ajax({ - url: '/_partials', - data: {'imports': doc.meta['import']}, - async: true, - dataType: 'json', - success: function (response) { + $.getJSON('/partials', {'imports': doc.meta['import']}, function (response) { $.extend(partials, response['partials']); - //TODO: Force editor rerender - }}); + // TODO: Better way to force update of the preview here than this fake signal? + aced.editor.session.doc._signal('change', + {'action': 'insert', 'lines': [''], 'start': {'row': 0}, 'end': {'row': 0}}); + }); } } return MDR.convert(md, partials) From 9a730a947e34c3d96eda5ee77dc04d265e66c9ff Mon Sep 17 00:00:00 2001 From: Chase Sterling Date: Tue, 9 Aug 2016 21:45:07 -0400 Subject: [PATCH 3/5] Fix scope order when importing partials with metadata. Render error when importing wiki pages that do not exist. --- realms/modules/wiki/views.py | 17 ++++++++--------- realms/static/js/editor.js | 13 +++++++------ realms/static/js/mdr.js | 6 +++--- realms/templates/wiki/edit.html | 16 ---------------- 4 files changed, 18 insertions(+), 34 deletions(-) diff --git a/realms/modules/wiki/views.py b/realms/modules/wiki/views.py index b3de441..ab7d99f 100644 --- a/realms/modules/wiki/views.py +++ b/realms/modules/wiki/views.py @@ -114,8 +114,7 @@ def edit(name): content=page.data, # TODO: Remove this? See #148 info=next(page.history), - sha=page.sha, - partials=_partials(page.imports)) + sha=page.sha) def _partials(imports): @@ -126,14 +125,14 @@ def _partials(imports): if page_name in partials: continue page = g.current_wiki.get_page(page_name) - data = page.data - partials[page_name] = data - if not data: + try: + partials[page_name] = page.data + except KeyError: + partials[page_name] = "`Error importing wiki page '{0}'`".format(page_name) continue - meta = page._get_meta(data) - if meta and meta.get('import'): - page_queue.extend(meta['import']) - return partials + page_queue.extend(page.imports) + # We want to retain the order (and reverse it) so that combining metadata from the imports works + return list(reversed(partials.items())) @blueprint.route("/_partials") diff --git a/realms/static/js/editor.js b/realms/static/js/editor.js index 71a17c9..fed5e73 100644 --- a/realms/static/js/editor.js +++ b/realms/static/js/editor.js @@ -82,19 +82,20 @@ var deletePage = function() { bootbox.alert('Error deleting page!'); }); }; -var partials = {}; +var partials = []; var aced = new Aced({ editor: $('#entry-markdown-content').find('.editor').attr('id'), renderer: function(md) { var doc = metaMarked(md); if (doc.meta && 'import' in doc.meta) { - // If we don't have all the imports loaded as partials, get them from server - if (!doc.meta['import'].every(function(val) {return val in partials;})) { - $.getJSON('/partials', {'imports': doc.meta['import']}, function (response) { - $.extend(partials, response['partials']); + // If the imports have changed, refresh them from the server + if (partials.length < doc.meta['import'].length || + !doc.meta['import'].every(function(impname, index) {return partials[partials.length-index-1][0] == impname})) { + $.getJSON('/_partials', {'imports': doc.meta['import']}, function (response) { + partials = response['partials']; // TODO: Better way to force update of the preview here than this fake signal? aced.editor.session.doc._signal('change', - {'action': 'insert', 'lines': [''], 'start': {'row': 0}, 'end': {'row': 0}}); + {'action': 'insert', 'lines': [], 'start': {'row': 0}, 'end': {'row': 0}}); }); } } diff --git a/realms/static/js/mdr.js b/realms/static/js/mdr.js index b3e9ddb..0f077b3 100644 --- a/realms/static/js/mdr.js +++ b/realms/static/js/mdr.js @@ -96,9 +96,9 @@ var MDR = { this.md = doc.md; var meta = this.meta = {}; if (this.partials) { - $.each(this.partials, function(key, value) { - var doc = metaMarked(value); - Handlebars.registerPartial(key, doc.md); + $.each(this.partials, function(index, item) { + var doc = metaMarked(item[1]); + Handlebars.registerPartial(item[0], doc.md); $.extend(meta, doc.meta); }) } diff --git a/realms/templates/wiki/edit.html b/realms/templates/wiki/edit.html index 479a44b..dd1dc86 100644 --- a/realms/templates/wiki/edit.html +++ b/realms/templates/wiki/edit.html @@ -8,22 +8,6 @@ - {% if partials %} - - {% endif %} - {% if config.get('COLLABORATION') %} {% endif %} From b13f7661761dd9b34de9275c150b29b6dcdd29db Mon Sep 17 00:00:00 2001 From: Chase Sterling Date: Tue, 9 Aug 2016 23:20:06 -0400 Subject: [PATCH 4/5] Prevent overzealous requesting of partials while editing pages --- realms/static/js/editor.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/realms/static/js/editor.js b/realms/static/js/editor.js index fed5e73..fb17988 100644 --- a/realms/static/js/editor.js +++ b/realms/static/js/editor.js @@ -82,6 +82,7 @@ var deletePage = function() { bootbox.alert('Error deleting page!'); }); }; +var last_imports = ''; var partials = []; var aced = new Aced({ editor: $('#entry-markdown-content').find('.editor').attr('id'), @@ -89,8 +90,8 @@ var aced = new Aced({ var doc = metaMarked(md); if (doc.meta && 'import' in doc.meta) { // If the imports have changed, refresh them from the server - if (partials.length < doc.meta['import'].length || - !doc.meta['import'].every(function(impname, index) {return partials[partials.length-index-1][0] == impname})) { + if (doc.meta['import'].toString() != last_imports) { + last_imports = doc.meta['import'].toString(); $.getJSON('/_partials', {'imports': doc.meta['import']}, function (response) { partials = response['partials']; // TODO: Better way to force update of the preview here than this fake signal? From f34eabd7df1ee32cd061851277e4a0f30ea8311a Mon Sep 17 00:00:00 2001 From: Chase Sterling Date: Sun, 4 Sep 2016 16:35:56 -0400 Subject: [PATCH 5/5] Viewing historical versions of pages now works, and show historical version of partials as well --- realms/modules/wiki/templates/wiki/page.html | 2 +- realms/modules/wiki/views.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/realms/modules/wiki/templates/wiki/page.html b/realms/modules/wiki/templates/wiki/page.html index 4529d23..167f113 100644 --- a/realms/modules/wiki/templates/wiki/page.html +++ b/realms/modules/wiki/templates/wiki/page.html @@ -23,7 +23,7 @@ {% block js %} {% endblock %} diff --git a/realms/modules/wiki/views.py b/realms/modules/wiki/views.py index 73eee22..480775b 100644 --- a/realms/modules/wiki/views.py +++ b/realms/modules/wiki/views.py @@ -23,7 +23,9 @@ def commit(name, sha): if not data: abort(404) - return render_template('wiki/page.html', name=name, page=data, commit=sha) + partials = _partials(data.imports, sha=sha) + + return render_template('wiki/page.html', name=name, page=data, commit=sha, partials=partials) @blueprint.route(r"/_compare//") @@ -118,14 +120,14 @@ def edit(name): sha=page.sha) -def _partials(imports): +def _partials(imports, sha='HEAD'): page_queue = collections.deque(imports) partials = collections.OrderedDict() while page_queue: page_name = page_queue.popleft() if page_name in partials: continue - page = g.current_wiki.get_page(page_name) + page = g.current_wiki.get_page(page_name, sha=sha) try: partials[page_name] = page.data except KeyError: