From 2e6da9157c5784f267694397a3991700a55d81af Mon Sep 17 00:00:00 2001 From: groovecoder Date: Tue, 20 Mar 2012 14:27:18 -0500 Subject: [PATCH 1/5] Bug 734187 add a templates dumpdata view with smuggler --- apps/wiki/admin.py | 26 ++++ apps/wiki/fixtures/wiki/templates.json | 130 ++++++++++++++++++ .../admin/wiki/document/change_list.html | 17 +++ 3 files changed, 173 insertions(+) create mode 100644 apps/wiki/fixtures/wiki/templates.json create mode 100644 apps/wiki/templates/admin/wiki/document/change_list.html diff --git a/apps/wiki/admin.py b/apps/wiki/admin.py index 469df74b9b6..380d6f02bb1 100644 --- a/apps/wiki/admin.py +++ b/apps/wiki/admin.py @@ -1,9 +1,35 @@ +from datetime import datetime + from django.contrib import admin +from django.core import serializers +from django.http import HttpResponse + +from smuggler.settings import SMUGGLER_FORMAT +from smuggler.utils import serialize_to_response from wiki.models import Document, Revision, EditorToolbar +def dump_selected(modeladmin, request, queryset): + objects = [] + for doc in queryset.all(): + rev = Revision.objects.get(id=doc.current_revision.id) + doc.current_revision = None + objects.append(doc) + objects.append(rev) + serializers.get_serializer('json') + filename = "documents_%s.%s" % ( + datetime.now().isoformat(), SMUGGLER_FORMAT) + response = HttpResponse(mimetype="text/plain") + response['Content-Disposition'] = 'attachment; filename=%s' % filename + return serialize_to_response(objects, response) + +dump_selected.short_description = "Dump selected objects as JSON data" + + class DocumentAdmin(admin.ModelAdmin): + actions = [dump_selected, ] + change_list_template = 'admin/wiki/document/change_list.html' exclude = ('tags',) list_display = ('id', 'locale', 'slug', 'title', 'category', 'is_localizable') diff --git a/apps/wiki/fixtures/wiki/templates.json b/apps/wiki/fixtures/wiki/templates.json new file mode 100644 index 00000000000..d54a7f0d12d --- /dev/null +++ b/apps/wiki/fixtures/wiki/templates.json @@ -0,0 +1,130 @@ +[ + { + "pk": 8, + "model": "wiki.document", + "fields": { + "category": 10, + "parent": null, + "title": "Template:test", + "locale": "en-US", + "mindtouch_page_id": null, + "html": "

{%= new Date() %}

", + "current_revision": 24, + "modified": "2012-03-20 10:09:16", + "is_template": true, + "is_localizable": false, + "slug": "Template:test" + } + }, + { + "pk": 9, + "model": "wiki.document", + "fields": { + "category": 10, + "parent": null, + "title": "test-template", + "locale": "en-US", + "mindtouch_page_id": null, + "html": "

{{ test() }}

", + "current_revision": 25, + "modified": "2012-03-20 10:09:38", + "is_template": false, + "is_localizable": false, + "slug": "test-template" + } + }, + { + "pk": 1, + "model": "wiki.reviewtag", + "fields": { + "name": "technical", + "slug": "technical" + } + }, + { + "pk": 2, + "model": "wiki.reviewtag", + "fields": { + "name": "editorial", + "slug": "editorial" + } + }, + { + "pk": 1, + "model": "wiki.reviewtaggedrevision", + "fields": { + "content_object": 24, + "tag": 1 + } + }, + { + "pk": 2, + "model": "wiki.reviewtaggedrevision", + "fields": { + "content_object": 24, + "tag": 2 + } + }, + { + "pk": 3, + "model": "wiki.reviewtaggedrevision", + "fields": { + "content_object": 25, + "tag": 1 + } + }, + { + "pk": 4, + "model": "wiki.reviewtaggedrevision", + "fields": { + "content_object": 25, + "tag": 2 + } + }, + { + "pk": 24, + "model": "wiki.revision", + "fields": { + "comment": "", + "based_on": null, + "is_approved": true, + "reviewed": null, + "title": "Template:test", + "tags": "", + "summary": "", + "content": "

{%= new Date() %}

", + "mindtouch_old_id": null, + "is_mindtouch_migration": false, + "created": "2012-03-20 10:09:16", + "significance": null, + "keywords": "", + "reviewer": null, + "document": 8, + "creator": 7, + "slug": "Template:test" + } + }, + { + "pk": 25, + "model": "wiki.revision", + "fields": { + "comment": "", + "based_on": null, + "is_approved": true, + "reviewed": null, + "title": "test-template", + "tags": "", + "summary": "", + "content": "

{{ test() }}

", + "mindtouch_old_id": null, + "is_mindtouch_migration": false, + "created": "2012-03-20 10:09:38", + "significance": null, + "keywords": "", + "reviewer": null, + "document": 9, + "creator": 7, + "slug": "test-template" + } + } +] diff --git a/apps/wiki/templates/admin/wiki/document/change_list.html b/apps/wiki/templates/admin/wiki/document/change_list.html new file mode 100644 index 00000000000..5f99c1ff740 --- /dev/null +++ b/apps/wiki/templates/admin/wiki/document/change_list.html @@ -0,0 +1,17 @@ +{% extends "admin/change_list.html" %} +{% load i18n %} + {% block object-tools %} + + {% endblock %} From 1d7775d6d59014a75d37e507a29e3df58103b722 Mon Sep 17 00:00:00 2001 From: Les Orchard Date: Tue, 27 Mar 2012 16:56:41 -0400 Subject: [PATCH 2/5] bug 671886: clean up and migrate templates * Defer bleach sanitation of template content all the way until the kumascript service response * --withtemplates option to select templates for migration * Migrate documents in Template: namespace, with some content tweaking to ease transition to KumaScript * Add "template" review tag to mark templates in need of review * Tweaks to review list page to use tag list styles * Index Template: in slug as is_template, rather than title * Skip Template:MindTouch/* in migration, since they're out-of-box templates and not really used * Tweaks to wiki admin to allow easier navigation between documents and related revisions * More fields included in admin search --- .../commands/migrate_to_kuma_wiki.py | 84 +++++++++++++++++-- apps/wiki/admin.py | 22 +++-- apps/wiki/models.py | 42 +++++++++- .../wiki/list_documents_for_review.html | 28 ++++--- apps/wiki/tests/test_models.py | 4 +- apps/wiki/views.py | 57 +++++++++---- 6 files changed, 190 insertions(+), 47 deletions(-) diff --git a/apps/dekicompat/management/commands/migrate_to_kuma_wiki.py b/apps/dekicompat/management/commands/migrate_to_kuma_wiki.py index afc4c2f912e..f97aa9b08c6 100644 --- a/apps/dekicompat/management/commands/migrate_to_kuma_wiki.py +++ b/apps/dekicompat/management/commands/migrate_to_kuma_wiki.py @@ -67,7 +67,8 @@ MT_NS_NAME_TO_ID = dict(MT_NAMESPACES) MT_NS_ID_TO_NAME = dict((x[1], x[0]) for x in MT_NAMESPACES) MT_MIGRATED_NS_IDS = (MT_NS_NAME_TO_ID[x] for x in ( - '', 'Talk:', 'User:', 'User_talk:', 'Project:', 'Project_talk:' + '', 'Talk:', 'User:', 'User_talk:', 'Project:', 'Project_talk:', + 'Template:', 'Template_talk:', )) # NOTE: These are MD5 hashes of garbage User page content. The criteria is that @@ -143,6 +144,8 @@ class Command(BaseCommand): help="Migrate # of documents with syntax blocks"), make_option('--withscripts', dest="withscripts", type="int", default=0, help="Migrate # of documents that use scripts"), + make_option('--withtemplates', dest="withtemplates", type="int", default=0, + help="Migrate # of template documents"), make_option('--syntax-metrics', action="store_true", dest="syntax_metrics", default=False, help="Measure syntax highlighter usage, skip migration"), @@ -474,6 +477,18 @@ def gather_pages(self): LIMIT %s """ % (ns_list, '%s'), self.options['withscripts'])) + if self.options['withtemplates'] > 0: + log.info("Gathering %s templates" % + self.options['withtemplates']) + iters.append(self._query(""" + SELECT * + FROM pages + WHERE page_namespace=%s + ORDER BY page_timestamp DESC + LIMIT %s + """, MT_NS_NAME_TO_ID['Template:'], + self.options['withtemplates'])) + return itertools.chain(*iters) @transaction.commit_on_success @@ -511,6 +526,12 @@ def update_document(self, r): (locale, slug, r['page_display_name'])) return False + # Skip migrating Template:MindTouch/* templates + if slug.startswith('Template:MindTouch'): + log.debug("\t%s/%s (%s) skipped, was a MindTouch default template" % + (locale, slug, r['page_display_name'])) + return False + # Check to see if this page's content is too long, skip if so. if len(r['page_text']) > self.options['maxlength']: log.debug("\t%s/%s (%s) skipped, page too long (%s > %s max)" % @@ -596,7 +617,7 @@ def update_past_revisions(self, r_page, doc, tags): significance=SIGNIFICANCES[0][0], summary='', keywords='', - content=self.convert_page_text(r['old_text']), + content=self.convert_page_text(r_page, r['old_text']), comment=r['old_comment'], created=ts, creator_id=self.get_django_user_id_for_deki_id(r['old_user']), @@ -657,7 +678,7 @@ def update_current_revision(self, r, doc, tags): rev.slug = doc.slug rev.title = doc.title rev.tags = tags - rev.content = self.convert_page_text(r['page_text']) + rev.content = self.convert_page_text(r, r['page_text']) # HACK: Some comments end up being too long, but just truncate. rev.comment = r['page_comment'][:255] @@ -666,20 +687,31 @@ def update_current_revision(self, r, doc, tags): rev.save() rev.make_current() + # If this is a template, set it as in need of template review + if doc.slug.startswith('Template:'): + rev.review_tags.set('template') + if created: log.info("\t\tCurrent revision created. (ID=%s)" % rev.pk) else: log.info("\t\tCurrent revision updated. (ID=%s)" % rev.pk) - def convert_page_text(self, pt): + def convert_page_text(self, r, pt): """Given a page row from MindTouch, do whatever needs doing to convert the page content for Kuma.""" + # If this is a redirect, just convert the redirect. if pt.startswith('#REDIRECT'): - pt = self.convert_redirect(pt) + return self.convert_redirect(pt) + + # If this is a template, just do template conversion + ns_name = MT_NS_ID_TO_NAME.get(r['page_namespace'], '') + if ns_name == 'Template:': + return self.convert_dekiscript_template(pt) + # Otherwise, run through the rest of the conversions. pt = self.convert_code_blocks(pt) - pt = self.convert_dekiscript_template_calls(pt) + pt = self.convert_dekiscript_calls(pt) # TODO: bug 710726 - Convert intra-wiki links? return pt @@ -699,10 +731,48 @@ def convert_code_blocks(self, pt): pt = ContentSectionTool(pt).filter(CodeSyntaxFilter).serialize() return pt - def convert_dekiscript_template_calls(self, pt): + def convert_dekiscript_calls(self, pt): return (wiki.content.parse(pt).filter(DekiscriptMacroFilter) .serialize()) + def convert_dekiscript_template(self, pt): + """Do what we can to convert DekiScript templates into EJS templates. + + This is an incomplete process, but it tries to take care off as much as + it can so that human intervention is minimized.""" + + # Many templates start with this prefix, which corresponds to {% in EJS + pre = '
'
+        if pt.startswith(pre):
+            pt = "{%%\n%s" % pt[len(pre):]
+
+        # Many templates end with this postfix, which corresponds to %} in EJS
+        post = '
' + if pt.endswith(post): + pt = "%s\n%%}" % pt[:0-len(post)] + + # Template source is usually HTML encoded inside the
+        pt = (pt.replace('&', '&')
+                .replace('<', '<')
+                .replace('>', '>')
+                .replace('"', '"'))
+
+        # String concatenation is '..' in DS, '+' in EJS
+        pt = pt.replace('..', '+')
+
+        # ?? in DS is pretty much || in EJS
+        pt = pt.replace('??', '||')
+
+        # No need for DS 'let' in EJS
+        pt = pt.replace('let ', '')
+
+        # This is a common sequence at the start of many templates. It clobbers
+        # the url API, and needs correcting.
+        pt = (pt.replace('var uri =', 'var u =')
+                .replace('uri.path[', 'u.path['))
+
+        return pt
+
     def get_tags_for_page(self, r):
         """For a given page row, get the list of tags from MindTouch and build
         a string representation for Kuma revisions."""
diff --git a/apps/wiki/admin.py b/apps/wiki/admin.py
index 380d6f02bb1..a995b964b49 100644
--- a/apps/wiki/admin.py
+++ b/apps/wiki/admin.py
@@ -30,16 +30,28 @@ def dump_selected(modeladmin, request, queryset):
 class DocumentAdmin(admin.ModelAdmin):
     actions = [dump_selected, ]
     change_list_template = 'admin/wiki/document/change_list.html'
-    exclude = ('tags',)
-    list_display = ('id', 'locale', 'slug', 'title', 'category',
-                    'is_localizable')
+    fields = ('title', 'slug', 'locale', 'parent', 'category')
+    list_display = ('id', 'locale', 'slug', 'title', 'is_localizable',
+                    'modified', 'parent_document_link',
+                    'current_revision_link', 'related_revisions_link',)
     list_display_links = ('id', 'slug',)
     list_filter = ('is_template', 'is_localizable', 'category', 'locale')
     raw_id_fields = ('parent',)
     readonly_fields = ('id', 'current_revision')
-    search_fields = ('title',)
+    search_fields = ('title', 'slug', 'html')
+
+
+class RevisionAdmin(admin.ModelAdmin):
+    fields = ('title', 'slug', 'summary', 'content', 'keywords', 'tags',
+              'reviewed', 'comment', 'is_approved')
+    list_display = ('id', 'slug', 'title', 'is_approved', 'created',
+                    'creator',)
+    list_display_links = ('id', 'slug')
+    list_filter = ('is_approved', )
+    ordering = ('-created',)
+    search_fields = ('title', 'slug', 'summary', 'content', 'tags')
 
 
 admin.site.register(Document, DocumentAdmin)
-admin.site.register(Revision, admin.ModelAdmin)
+admin.site.register(Revision, RevisionAdmin)
 admin.site.register(EditorToolbar, admin.ModelAdmin)
diff --git a/apps/wiki/models.py b/apps/wiki/models.py
index b63e4493956..d20bd8456cb 100644
--- a/apps/wiki/models.py
+++ b/apps/wiki/models.py
@@ -1,3 +1,4 @@
+import logging
 from collections import namedtuple
 from datetime import datetime
 from itertools import chain
@@ -137,7 +138,9 @@
 REVIEW_FLAG_TAGS = (
     ('technical', _('Technical - code samples, APIs, or technologies')),
     ('editorial', _('Editorial - prose, grammar, or content')),
+    ('template',  _('Template - KumaScript code')),
 )
+REVIEW_FLAG_TAGS_DEFAULT = ['technical', 'editorial']
 
 # TODO: This is info derived from urls.py, but unsure how to DRY it
 RESERVED_SLUGS = (
@@ -410,7 +413,7 @@ def unique_attr():
             return unique_attr()
 
     def save(self, *args, **kwargs):
-        self.is_template = self.title.startswith(TEMPLATE_TITLE_PREFIX)
+        self.is_template = self.slug.startswith(TEMPLATE_TITLE_PREFIX)
 
         try:
             # Check if the slug would collide with an existing doc
@@ -697,6 +700,39 @@ def is_watched_by(self, user):
         from wiki.events import EditDocumentEvent
         return EditDocumentEvent.is_notifying(user, self)
 
+    def related_revisions_link(self):
+        """HTML link to related revisions for admin change list"""
+        link = '%s?%s' % (
+            reverse('admin:wiki_revision_changelist', args=[]),
+            'document__exact=%s' % (self.id)
+        )
+        count = self.revisions.count()
+        what = (count == 1) and 'revision' or 'revisons'
+        return '%s %s' % (link, count, what)
+
+    related_revisions_link.allow_tags = True
+    related_revisions_link.short_description = "All Revisions"
+        
+    def current_revision_link(self):
+        """HTML link to the current revision for the admin change list"""
+        if not self.current_revision:
+            return "None"
+        rev = self.current_revision
+        rev_url = reverse('admin:wiki_revision_change', args=[rev.id])
+        return 'Revision #%s' % (rev_url, rev.id)
+
+    current_revision_link.allow_tags = True
+    current_revision_link.short_description = "Current Revision"
+        
+    def parent_document_link(self):
+        """HTML link to the parent document for admin change list"""
+        if not self.parent:
+            return "None"
+        url = reverse('admin:wiki_document_change', args=[self.parent.id])
+        return 'Document #%s' % (url, self.parent.id)
+
+    parent_document_link.allow_tags = True
+    parent_document_link.short_description = "Parent Document"
 
 class ReviewTag(TagBase):
     """A tag indicating review status, mainly for revisions"""
@@ -828,7 +864,7 @@ def save(self, *args, **kwargs):
                                    'to a revision of the default-'
                                    'language document.')
 
-        if self.content:
+        if self.content and not self.document.is_template:
             self.content = (wiki.content
                             .parse(self.content)
                             .injectSectionIDs()
@@ -874,6 +910,8 @@ def get_section_content(self, section_id):
 
     @property
     def content_cleaned(self):
+        if self.document.is_template:
+            return self.content
         return bleach.clean(
             self.content, attributes=ALLOWED_ATTRIBUTES, tags=ALLOWED_TAGS,
             strip_comments=False
diff --git a/apps/wiki/templates/wiki/list_documents_for_review.html b/apps/wiki/templates/wiki/list_documents_for_review.html
index 19d3f46f9ac..ed26b465d92 100644
--- a/apps/wiki/templates/wiki/list_documents_for_review.html
+++ b/apps/wiki/templates/wiki/list_documents_for_review.html
@@ -20,19 +20,21 @@
 
-
-

{{ title }}

- {% if documents.object_list %} -
    - {% for doc in documents.object_list %} -
  • {{ doc.title }}
  • - {% endfor %} -
- {{ documents|paginator }} - {% else %} -

{{ _('There are no articles.') }}

- {% endif %} -
+ +
+

{{ title }}

+ {% if documents.object_list %} +
    + {% for doc in documents.object_list %} +
  • {{ doc.title }}
  • + {% endfor %} +
+ {{ documents|paginator }} + {% else %} +

{{ _('There are no articles.') }}

+ {% endif %} +
+
diff --git a/apps/wiki/tests/test_models.py b/apps/wiki/tests/test_models.py index ab51f1c3e6b..80044bad7c9 100644 --- a/apps/wiki/tests/test_models.py +++ b/apps/wiki/tests/test_models.py @@ -40,12 +40,12 @@ def test_document_is_template(self): assert not d.is_template - d.title = 'Template:test' + d.slug = 'Template:test' d.save() assert d.is_template - d.title = 'Back to document' + d.slug = 'Back-to-document' d.save() assert not d.is_template diff --git a/apps/wiki/views.py b/apps/wiki/views.py index afa4d00e45b..ae26b6bed5d 100644 --- a/apps/wiki/views.py +++ b/apps/wiki/views.py @@ -7,6 +7,7 @@ from string import ascii_letters import requests +import bleach try: from functools import wraps @@ -46,7 +47,9 @@ CATEGORIES, OPERATING_SYSTEMS, GROUPED_OPERATING_SYSTEMS, FIREFOX_VERSIONS, GROUPED_FIREFOX_VERSIONS, - REVIEW_FLAG_TAGS, get_current_or_latest_revision) + REVIEW_FLAG_TAGS, REVIEW_FLAG_TAGS_DEFAULT, + ALLOWED_ATTRIBUTES, ALLOWED_TAGS, + get_current_or_latest_revision) from wiki.parser import wiki_to_html from wiki.tasks import send_reviewed_notification, schedule_rebuild_kb import wiki.content @@ -245,27 +248,34 @@ def set_common_headers(r): if resp_errors: ks_errors = resp_errors - # Start applying some filters to the document HTML - tool = wiki.content.parse(doc_html) + if not doc.is_template: - # If a section ID is specified, extract that section. - if section_id: - tool.extractSection(section_id) + # Start applying some filters to the document HTML + tool = wiki.content.parse(doc_html) - # If this user can edit the document, inject some section editing links. - if ((need_edit_links or not show_raw) and - doc.allows_editing_by(request.user)): - tool.injectSectionEditingLinks(doc.full_path, doc.locale) + # If a section ID is specified, extract that section. + if section_id: + tool.extractSection(section_id) + + # If this user can edit the document, inject some section editing links. + if ((need_edit_links or not show_raw) and + doc.allows_editing_by(request.user)): + tool.injectSectionEditingLinks(doc.full_path, doc.locale) + + doc_html = tool.serialize() # if ?raw parameter is supplied, then we respond with raw page source # without template wrapping or edit links. This is also permissive for # iframe inclusion if show_raw: - response = HttpResponse(tool.serialize()) + response = HttpResponse(doc_html) response['x-frame-options'] = 'Allow' + if doc.is_template: + # Treat raw, un-bleached template source as plain text, not HTML. + response['Content-Type'] = 'text/plain; charset=utf-8' return set_common_headers(response) - data = {'document': doc, 'document_html': tool.serialize(), + data = {'document': doc, 'document_html': doc_html, 'redirected_from': redirected_from, 'related': related, 'contributors': contributors, 'fallback_reason': fallback_reason, @@ -394,6 +404,14 @@ def _perform_kumascript_request(request, response_headers, document_locale, response_headers['X-Kumascript-Caching'] = ( '200 OK, Age: %s' % resp.headers.get('age', 0)) + # We defer bleach sanitation of kumascript content all the way + # through editing, source display, and raw output. But, we still + # want sanitation, so it finally gets picked up here. + resp_body = bleach.clean( + resp_body, attributes=ALLOWED_ATTRIBUTES, tags=ALLOWED_TAGS, + strip_comments=False + ) + # Cache the request for conditional GET, but use the max_age for # the cache timeout here too. cache.set_many({ @@ -496,7 +514,7 @@ def new_document(request): if request.method == 'GET': doc_form = DocumentForm() rev_form = RevisionForm( - initial={'review_tags': [t[0] for t in REVIEW_FLAG_TAGS]}) + initial={'review_tags': REVIEW_FLAG_TAGS_DEFAULT}) return jingo.render(request, 'wiki/new_document.html', {'document_form': doc_form, 'revision_form': rev_form}) @@ -705,11 +723,14 @@ def _edit_document_collision(request, orig_rev, curr_rev, is_iframe_target, # Process the original content for a diff, extracting a section if we're # editing one. - tool = wiki.content.parse(curr_rev.content) - tool.injectSectionIDs() - if section_id: - tool.extractSection(section_id) - curr_content = tool.serialize() + if (doc.is_template): + curr_content = curr_rev.content + else: + tool = wiki.content.parse(curr_rev.content) + tool.injectSectionIDs() + if section_id: + tool.extractSection(section_id) + curr_content = tool.serialize() if is_raw: # When dealing with the raw content API, we need to signal the conflict From b3ad28d0ed512edeac22ed014817aa24a8cc61e5 Mon Sep 17 00:00:00 2001 From: groovecoder Date: Thu, 29 Mar 2012 13:01:01 -0500 Subject: [PATCH 3/5] disable sql devserver modules --- puppet/files/vagrant/settings_local.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/puppet/files/vagrant/settings_local.py b/puppet/files/vagrant/settings_local.py index 10db4bfcd0e..e78b21e9394 100644 --- a/puppet/files/vagrant/settings_local.py +++ b/puppet/files/vagrant/settings_local.py @@ -60,6 +60,19 @@ 'debug_toolbar.panels.logger.LoggingPanel', ) +DEVSERVER_MODULES = ( + # sql modules interfere with saving some KumaScript templates + #'devserver.modules.sql.SQLRealTimeModule', + #'devserver.modules.sql.SQLSummaryModule', + 'devserver.modules.profile.ProfileSummaryModule', + + # Modules not enabled by default + #'devserver.modules.ajax.AjaxDumpModule', + #'devserver.modules.profile.MemoryUseModule', + #'devserver.modules.cache.CacheSummaryModule', + #'devserver.modules.profile.LineProfilerModule', +) + # The default database should point to the master. DATABASES = { 'default': { From 7065ae5f77853dbd2558819adb7937e351459dbf Mon Sep 17 00:00:00 2001 From: Les Orchard Date: Mon, 2 Apr 2012 12:10:36 -0400 Subject: [PATCH 4/5] Refrain from processing template source as HTML in RevisionForm --- apps/wiki/forms.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/wiki/forms.py b/apps/wiki/forms.py index 3cfb4f64def..7d297d73e26 100644 --- a/apps/wiki/forms.py +++ b/apps/wiki/forms.py @@ -251,11 +251,13 @@ def __init__(self, *args, **kwargs): self.initial['slug'] = self.instance.document.slug content = self.instance.content - tool = wiki.content.parse(content) - tool.injectSectionIDs() - if self.section_id: - tool.extractSection(self.section_id) - self.initial['content'] = tool.serialize() + if not self.instance.document.is_template: + tool = wiki.content.parse(content) + tool.injectSectionIDs() + if self.section_id: + tool.extractSection(self.section_id) + content = tool.serialize() + self.initial['content'] = content self.initial['review_tags'] = [x.name for x in self.instance.review_tags.all()] From 0d2f83932f56f69ea83e5d52d198bc1354f4d13f Mon Sep 17 00:00:00 2001 From: John Karahalis Date: Tue, 3 Apr 2012 00:01:54 -0400 Subject: [PATCH 5/5] Added links to the April Derby tab copy. --- apps/demos/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/demos/__init__.py b/apps/demos/__init__.py index 5836c72694f..472c212838d 100644 --- a/apps/demos/__init__.py +++ b/apps/demos/__init__.py @@ -153,7 +153,7 @@ "summary": _("The HTML5 audio element lets you embed sound in webpages without requiring your users to rely on plug-ins."), "description": _("The HTML5 audio element lets you embed sound in webpages without requiring your users to rely on plug-ins."), "learn_more": [], - "tab_copy": _("

The HTML5 <audio> element lets you embed sound in Web pages. More importantly, it lets you do so without requiring your users to rely on plug-ins. This means sound for everyone, everywhere, in the most open way possible. In particular, you can play sounds in games with very low latency, making for a responsive, immersive game experience.

What else can you do with the audio element? Show us by submitting to the Dev Derby today.

"), + "tab_copy": _("

The HTML5 <audio> element lets you embed sound in Web pages. More importantly, it lets you do so without requiring your users to rely on plug-ins. This means sound for everyone, everywhere, in the most open way possible. In particular, you can play sounds in games with very low latency, making for a responsive, immersive game experience.

What else can you do with the audio element? Show us by submitting to the Dev Derby today.

"), }, { "tag_name": "challenge:2012:may",