Merge lp:~dholbach/developer-ubuntu-com/just-one-rawhtml-plugin into lp:developer-ubuntu-com

Proposed by Daniel Holbach on 2016-04-21
Status: Merged
Approved by: David Callé on 2016-04-25
Approved revision: 222
Merged at revision: 215
Proposed branch: lp:~dholbach/developer-ubuntu-com/just-one-rawhtml-plugin
Merge into: lp:developer-ubuntu-com
Diff against target: 626 lines (+204/-76)
12 files modified
developer_portal/settings.py (+9/-3)
md_importer/importer/__init__.py (+3/-0)
md_importer/importer/article.py (+3/-3)
md_importer/importer/process.py (+2/-2)
md_importer/importer/publish.py (+46/-28)
md_importer/importer/repo.py (+4/-4)
md_importer/importer/source.py (+4/-3)
md_importer/management/commands/import_md.py (+3/-8)
md_importer/tests/test_branch_import.py (+2/-2)
md_importer/tests/test_publish.py (+77/-0)
md_importer/tests/test_website_import.py (+1/-4)
templates/snappy_hero_tour.html (+50/-19)
To merge this branch: bzr merge lp:~dholbach/developer-ubuntu-com/just-one-rawhtml-plugin
Reviewer Review Type Date Requested Status
David Callé 2016-04-21 Approve on 2016-04-25
Review via email: mp+292490@code.launchpad.net
To post a comment you must log in.
217. By Daniel Holbach on 2016-04-21

fix test by adapting to new find_text_plugin return value

218. By Daniel Holbach on 2016-04-21

add separate tests for importer.publish

219. By Daniel Holbach on 2016-04-21

add proper logging, configure so that module and time are mentioned as well

220. By Daniel Holbach on 2016-04-22

merged lp:~davidc3/developer-ubuntu-com/hero-tour-fixes

221. By Daniel Holbach on 2016-04-22

go back to importing markdown once a day

222. By Daniel Holbach on 2016-04-22

merged lp:~davidc3/developer-ubuntu-com/hero-tour-fixes again

David Callé (davidc3) wrote :

+1!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'developer_portal/settings.py'
2--- developer_portal/settings.py 2016-03-23 09:19:28 +0000
3+++ developer_portal/settings.py 2016-04-22 15:01:37 +0000
4@@ -98,7 +98,7 @@
5 'django.middleware.clickjacking.XFrameOptionsMiddleware',
6
7 'django.middleware.cache.FetchFromCacheMiddleware',
8-
9+
10 'cms.middleware.user.CurrentUserMiddleware',
11 'cms.middleware.page.CurrentPageMiddleware',
12 'cms.middleware.toolbar.ToolbarMiddleware',
13@@ -317,13 +317,13 @@
14 'DEFAULT_PERMISSION_CLASSES': [
15 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
16 ],
17-
18+
19 'DEFAULT_AUTHENTICATION_CLASSES': (
20 'rest_framework.authentication.BasicAuthentication',
21 'rest_framework.authentication.SessionAuthentication',
22 'rest_framework.authentication.TokenAuthentication',
23 ),
24-
25+
26 #'PAGINATE_BY': 10,
27 }
28
29@@ -347,11 +347,17 @@
30 LOGGING = {
31 'version': 1,
32 'disable_existing_loggers': False,
33+ 'formatters': {
34+ 'normal': {
35+ 'format': '%(levelname)s %(asctime)s %(module)s %(message)s'
36+ },
37+ },
38 'handlers': {
39 'errors': {
40 'level': 'ERROR',
41 'class': 'logging.FileHandler',
42 'filename': './error.log',
43+ 'formatter': 'normal',
44 },
45 },
46 'loggers': {
47
48=== modified file 'md_importer/importer/__init__.py'
49--- md_importer/importer/__init__.py 2016-03-08 13:07:19 +0000
50+++ md_importer/importer/__init__.py 2016-04-22 15:01:37 +0000
51@@ -2,6 +2,9 @@
52
53 from md_importer.models import ExternalDocsBranchImportDirective
54
55+import logging
56+logger = logging.getLogger('django')
57+
58 DEFAULT_LANG = LANGUAGE_CODE
59 HOME_PAGE_URL = '/{}/'.format(DEFAULT_LANG)
60 SUPPORTED_ARTICLE_TYPES = ['.md', '.html']
61
62=== modified file 'md_importer/importer/article.py'
63--- md_importer/importer/article.py 2016-04-18 11:53:26 +0000
64+++ md_importer/importer/article.py 2016-04-22 15:01:37 +0000
65@@ -1,6 +1,5 @@
66 from bs4 import BeautifulSoup
67 import codecs
68-import logging
69 import markdown
70 import os
71 import re
72@@ -9,6 +8,7 @@
73 from . import (
74 DEFAULT_LANG,
75 DEFAULT_TEMPLATE,
76+ logger,
77 MARKDOWN_EXTENSIONS,
78 SUPPORTED_ARTICLE_TYPES,
79 )
80@@ -51,7 +51,7 @@
81
82 def read(self):
83 if os.path.splitext(self.fn)[1] not in SUPPORTED_ARTICLE_TYPES:
84- logging.error("Don't know how to interpret '{}'.".format(
85+ logger.error("Don't know how to interpret '{}'.".format(
86 self.fn))
87 return False
88 with codecs.open(self.fn, 'r', encoding='utf-8') as f:
89@@ -64,7 +64,7 @@
90 self.html = f.read()
91 self._find_local_images()
92 if self.local_images:
93- logging.error('Found the following local image(s): {}'.format(
94+ logger.error('Found the following local image(s): {}'.format(
95 ', '.join(self.local_images)
96 ))
97 return False
98
99=== modified file 'md_importer/importer/process.py'
100--- md_importer/importer/process.py 2016-04-18 08:56:51 +0000
101+++ md_importer/importer/process.py 2016-04-22 15:01:37 +0000
102@@ -1,11 +1,11 @@
103 import datetime
104-import logging
105 import pytz
106 import shutil
107 import tempfile
108
109 from django.core.management import call_command
110
111+from md_importer.importer import logger
112 from md_importer.importer.publish import find_page
113 from md_importer.importer.repo import Repo
114 from md_importer.models import (
115@@ -51,7 +51,7 @@
116 branch=branch, last_import__lt=timestamp):
117 page = find_page(imported_article.url)
118 if not page:
119- logging.error('Page {} not found for deletion.'.format(url))
120+ logger.error('Page {} not found for deletion.'.format(url))
121 else:
122 page.delete()
123 imported_article.delete()
124
125=== modified file 'md_importer/importer/publish.py'
126--- md_importer/importer/publish.py 2016-04-18 15:28:00 +0000
127+++ md_importer/importer/publish.py 2016-04-22 15:01:37 +0000
128@@ -1,16 +1,18 @@
129 from md_importer.importer import (
130 DEFAULT_LANG,
131 DEFAULT_TEMPLATE,
132+ logger,
133 )
134 from md_importer.importer.tools import remove_leading_and_trailing_slash
135
136+from developer_portal.models import RawHtml
137+
138 from cms.api import create_page, add_plugin
139 from cms.models import Page
140 from cms.utils.page_resolver import get_page_from_path
141 from djangocms_text_ckeditor.html import clean_html
142
143 from bs4 import BeautifulSoup
144-import logging
145 import re
146 import os
147
148@@ -42,31 +44,21 @@
149 if self.draft.template != template:
150 self.draft.template = template
151 if html:
152- (self.draft_placeholder,
153- self.draft_text_plugin) = get_text_plugin(self.draft)
154 if self.page:
155- (self.placeholder,
156- self.text_plugin) = get_text_plugin(self.page)
157- if self.draft_text_plugin:
158- if self._text_plugin_needs_update(html):
159- self.draft_text_plugin.body = html
160- self.draft_text_plugin.save()
161- else:
162- # Reset draft
163- self.draft.revert(DEFAULT_LANG)
164+ self.text_plugin = find_text_plugin(self.page)
165+ if self._text_plugin_needs_update(html):
166+ self.draft_text_plugin.body = html
167+ self.draft_text_plugin.save()
168 else:
169- self.draft_plugin = add_plugin(
170- self.draft_placeholder, 'RawHtmlPlugin', DEFAULT_LANG,
171- body=html)
172+ # Reset draft
173+ self.draft.revert(DEFAULT_LANG)
174
175 def __init__(self, title, full_url, menu_title=None, in_navigation=True,
176 html=None, template=DEFAULT_TEMPLATE):
177 self.page = None
178 self.draft = None
179- self.draft_placeholder = None
180 self.draft_text_plugin = None
181 self.text_plugin = None
182- self.placeholder = None
183 self.full_url = full_url
184 self.title = title
185 self.menu_title = menu_title
186@@ -85,6 +77,11 @@
187 title=title, template=template, language=DEFAULT_LANG,
188 slug=slug, parent=parent, menu_title=menu_title,
189 in_navigation=in_navigation, position='last-child')
190+ else:
191+ remove_superfluous_placeholders(self.draft)
192+ remove_superfluous_plugins(self.draft)
193+ add_rawhtml_plugin(self.draft)
194+ self.draft_text_plugin = find_text_plugin(self.draft)
195 self.update(title, full_url, menu_title, in_navigation, html,
196 template)
197
198@@ -161,23 +158,44 @@
199 return root
200 parent = get_page_from_path(parent_url, draft=True)
201 if not parent:
202- logging.error('Parent {} not found.'.format(parent_url))
203+ logger.error('Parent {} not found.'.format(parent_url))
204 return None
205 return parent
206
207
208-def get_text_plugin(page):
209- '''Finds text plugin, creates it if necessary.'''
210+# More than one placeholder -> old style page
211+def remove_superfluous_placeholders(page):
212+ if page.placeholders.count() > 1:
213+ for placeholder in page.placeholders.all()[1:]:
214+ placeholder.delete()
215+ return page.placeholders.all()[0]
216+
217+
218+def remove_superfluous_plugins(page):
219+ placeholder = page.placeholders.all()[0]
220+ plugins = placeholder.get_plugins()
221+ if plugins.count() >= 1:
222+ for plugin in plugins[1:]:
223+ plugin.delete()
224+ if plugins.count() == 1 and \
225+ type(plugins[0].get_plugin_instance()[0]) != RawHtml:
226+ plugins[0].delete()
227+
228+
229+def add_rawhtml_plugin(page):
230+ placeholder = page.placeholders.all()[0]
231+ if not placeholder.get_plugins().count():
232+ add_plugin(
233+ placeholder, 'RawHtmlPlugin', DEFAULT_LANG,
234+ body='')
235+
236+
237+def find_text_plugin(page):
238 if not page:
239- return (None, None)
240+ return None
241 placeholders = page.placeholders.all()
242 if not placeholders:
243- return (None, None)
244+ return None
245 # We create the page, so we know there's just one placeholder
246 plugins = placeholders[0].get_plugins()
247- if plugins:
248- return (placeholders[0], plugins[0].get_plugin_instance()[0])
249- plugin = add_plugin(
250- placeholders[0], 'RawHtmlPlugin', DEFAULT_LANG,
251- body='')
252- return (placeholders[0], plugin)
253+ return plugins[0].get_plugin_instance()[0]
254
255=== modified file 'md_importer/importer/repo.py'
256--- md_importer/importer/repo.py 2016-04-19 08:40:41 +0000
257+++ md_importer/importer/repo.py 2016-04-22 15:01:37 +0000
258@@ -1,6 +1,7 @@
259 from . import (
260 SUPPORTED_ARTICLE_TYPES,
261 DEFAULT_TEMPLATE,
262+ logger,
263 )
264 from .article import Article
265 from .publish import (
266@@ -13,7 +14,6 @@
267 from md_importer.models import ExternalDocsBranchImportDirective
268
269 import glob
270-import logging
271 import os
272
273
274@@ -38,7 +38,7 @@
275 sourcecode = SourceCode(self.origin, self.checkout_location,
276 self.branch_name, self.post_checkout_command)
277 if sourcecode.get() != 0:
278- logging.error(
279+ logger.error(
280 'Could not check out branch "{}".'.format(self.origin))
281 return 1
282 return 0
283@@ -118,7 +118,7 @@
284 return True
285
286 def _abort_import(self, msg):
287- logging.error('Importing of {} aborted: {}.'.format(self.origin, msg))
288+ logger.error('Importing of {} aborted: {}.'.format(self.origin, msg))
289 return False
290
291 def _read_article(self, fn, write_to, advertise, template):
292@@ -130,7 +130,7 @@
293 def publish(self):
294 for article in self.imported_articles:
295 if not article.add_to_db():
296- logging.error('Publishing of {} aborted.'.format(self.origin))
297+ logger.error('Publishing of {} aborted.'.format(self.origin))
298 return False
299 article.replace_links(self.titles, self.url_map)
300 for article in self.imported_articles:
301
302=== modified file 'md_importer/importer/source.py'
303--- md_importer/importer/source.py 2016-01-15 20:03:15 +0000
304+++ md_importer/importer/source.py 2016-04-22 15:01:37 +0000
305@@ -1,4 +1,5 @@
306-import logging
307+from md_importer.importer import logger
308+
309 import os
310 import shutil
311 import subprocess
312@@ -43,7 +44,7 @@
313 self.branch_name])
314 os.chdir(pwd)
315 return retcode
316- logging.error(
317+ logger.error(
318 'Repo format "{}" not understood.'.format(self.origin))
319 return 1
320
321@@ -56,5 +57,5 @@
322 retcode = process.wait()
323 os.chdir(pwd)
324 if retcode != 0:
325- logging.error(out)
326+ logger.error(out)
327 return retcode
328
329=== modified file 'md_importer/management/commands/import_md.py'
330--- md_importer/management/commands/import_md.py 2016-04-11 07:04:35 +0000
331+++ md_importer/management/commands/import_md.py 2016-04-22 15:01:37 +0000
332@@ -1,15 +1,14 @@
333-import logging
334-
335 from django.core.management.base import BaseCommand
336
337+from md_importer.importer import logger
338 from md_importer.importer.process import process_branch
339 from md_importer.models import ExternalDocsBranch
340
341
342 def import_branches(selection):
343 if not ExternalDocsBranch.objects.count():
344- logging.error('No branches registered in the '
345- 'ExternalDocsBranch table yet.')
346+ logger.error('No branches registered in the '
347+ 'ExternalDocsBranch table yet.')
348 return
349 for branch in ExternalDocsBranch.objects.filter(
350 origin__regex=selection, active=True):
351@@ -24,10 +23,6 @@
352 parser.add_argument('branches', nargs='*')
353
354 def handle(*args, **options):
355- logging.basicConfig(
356- level=logging.ERROR,
357- format='%(asctime)s %(levelname)-8s %(message)s',
358- datefmt='%F %T')
359 branches = options['branches']
360 if not branches:
361 import_branches('.*')
362
363=== modified file 'md_importer/tests/test_branch_import.py'
364--- md_importer/tests/test_branch_import.py 2016-04-18 10:30:14 +0000
365+++ md_importer/tests/test_branch_import.py 2016-04-22 15:01:37 +0000
366@@ -10,7 +10,7 @@
367 TEMPLATE_CHOICES,
368 )
369 from md_importer.importer.article import Article
370-from md_importer.importer.publish import get_text_plugin
371+from md_importer.importer.publish import find_text_plugin
372 from md_importer.importer.tools import remove_trailing_slash
373
374 from .utils import (
375@@ -241,7 +241,7 @@
376 published_pages.update()
377 for page in published_pages.pages:
378 if page != self.root:
379- (dummy, plugin) = get_text_plugin(page)
380+ plugin = find_text_plugin(page)
381 self.assertGreater(now, plugin.changed_date)
382
383
384
385=== added file 'md_importer/tests/test_publish.py'
386--- md_importer/tests/test_publish.py 1970-01-01 00:00:00 +0000
387+++ md_importer/tests/test_publish.py 2016-04-22 15:01:37 +0000
388@@ -0,0 +1,77 @@
389+from django.test import TestCase
390+
391+from cms.api import add_plugin, publish_pages
392+
393+from developer_portal.models import RawHtml
394+
395+from md_importer.importer import DEFAULT_LANG
396+from md_importer.importer.publish import ArticlePage
397+
398+from .utils import (
399+ db_empty_page_list,
400+ db_create_root_page,
401+ db_add_empty_page,
402+ PublishedPages,
403+)
404+
405+
406+class TestCreateArticlePage(TestCase):
407+ def runTest(self):
408+ db_empty_page_list()
409+ db_create_root_page()
410+ published_pages = PublishedPages()
411+ self.assertTrue(published_pages.has_size(1))
412+ article_page = ArticlePage('Test page', 'test')
413+ self.assertIsNotNone(article_page)
414+ article_page.publish()
415+ published_pages.update()
416+ self.assertTrue(published_pages.has_size(2))
417+
418+
419+class TestNewArticlePageForCorrectPlaceholderAndPlugins(TestCase):
420+ def runTest(self):
421+ db_empty_page_list()
422+ db_create_root_page()
423+ published_pages = PublishedPages()
424+ self.assertTrue(published_pages.has_size(1))
425+ article_page = ArticlePage('Test page', 'test')
426+ self.assertIsNotNone(article_page)
427+ article_page.publish()
428+ published_pages.update()
429+ self.assertTrue(published_pages.has_size(2))
430+ placeholders = article_page.page.placeholders
431+ self.assertEqual(placeholders.count(), 1)
432+ plugins = placeholders.all()[0].get_plugins()
433+ self.assertEqual(plugins.count(), 1)
434+ self.assertIsInstance(plugins[0].get_plugin_instance()[0], RawHtml)
435+
436+
437+class TestOldArticlePageForCorrectPlaceholderAndPlugins(TestCase):
438+ def runTest(self):
439+ db_empty_page_list()
440+ root = db_create_root_page()
441+ published_pages = PublishedPages()
442+ self.assertTrue(published_pages.has_size(1))
443+ page = db_add_empty_page('Test', parent=root)
444+ publish_pages(page)
445+ self.assertIsNotNone(page)
446+ published_pages.update()
447+ self.assertTrue(published_pages.has_size(2))
448+ self.assertEqual(page.placeholders.count(), 1)
449+ placeholder = page.placeholders.all()[0]
450+ add_plugin(placeholder, 'TextPlugin', DEFAULT_LANG, body='')
451+ add_plugin(placeholder, 'TextPlugin', DEFAULT_LANG, body='')
452+ add_plugin(placeholder, 'TextPlugin', DEFAULT_LANG, body='')
453+ plugins = placeholder.get_plugins()
454+ self.assertEqual(plugins.count(), 3)
455+
456+ article_page = ArticlePage('Test', 'test')
457+ self.assertIsNotNone(article_page)
458+ article_page.publish()
459+ published_pages.update()
460+ self.assertTrue(published_pages.has_size(2))
461+ self.assertEqual(article_page.page.placeholders.count(), 1)
462+ placeholder = article_page.page.placeholders.all()[0]
463+ plugins = placeholder.get_plugins()
464+ self.assertEqual(plugins.count(), 1)
465+ self.assertIsInstance(plugins[0].get_plugin_instance()[0], RawHtml)
466
467=== modified file 'md_importer/tests/test_website_import.py'
468--- md_importer/tests/test_website_import.py 2016-04-18 10:30:14 +0000
469+++ md_importer/tests/test_website_import.py 2016-04-22 15:01:37 +0000
470@@ -1,4 +1,3 @@
471-from md_importer.importer import DEFAULT_LANG
472 from md_importer.importer.repo import Repo
473 from .utils import (
474 db_add_empty_page,
475@@ -6,7 +5,7 @@
476 TestLocalBranchImport,
477 )
478
479-from cms.api import add_plugin, publish_pages
480+from cms.api import publish_pages
481
482
483 class TestSnappyWebsiteRead(TestLocalBranchImport):
484@@ -29,8 +28,6 @@
485 'Get started', parent=snappy_page, slug='start')
486 as_dev = db_add_empty_page(
487 'As developer', parent=start, slug='as-dev')
488- placeholder = as_dev.placeholders.all()[0]
489- add_plugin(placeholder, 'RawHtmlPlugin', DEFAULT_LANG, body='')
490 page_16_04 = db_add_empty_page(
491 '16.04', parent=as_dev, slug='16-04')
492 publish_pages([snappy_page, start, as_dev, page_16_04])
493
494=== modified file 'templates/snappy_hero_tour.html'
495--- templates/snappy_hero_tour.html 2016-03-08 16:44:33 +0000
496+++ templates/snappy_hero_tour.html 2016-04-22 15:01:37 +0000
497@@ -99,31 +99,31 @@
498 document.getElementById("button-container-right").style.display = "block";
499 }
500
501- // Set next button url
502- nextBtn.href = getChoicesString();
503+ // Get cookies
504+ getChoicesString();
505
506 // Setup navigation dots
507- setDots()
508+ setNav()
509
510 // Dynamically update all nav urls to match user choices
511 if (!path.includes("/step")) {
512 var atags = document.getElementsByTagName('a');
513 for (var i = 0, length = atags.length; i < length; i++) {
514 atags[i].addEventListener('mouseover', function () {
515- nextBtn.href = getChoicesString();
516- setDots();
517+ getChoicesString();
518+ setNav();
519 });
520 atags[i].addEventListener('mousedown', function () {
521- nextBtn.href = getChoicesString();
522- setDots();
523+ getChoicesString();
524+ setNav();
525 });
526 }
527 }
528
529- function setDots() {
530+ function setNav() {
531 var dots = document.getElementsByClassName("dot");
532 for (var i = 0, length = dots.length; i < length; i++) {
533- dots[i].firstChild.href = createURLForDot(dots.length, i + 1)
534+ dots[i].firstChild.href = createURLForDot(i + 1)
535 var short_path = path.split("/snappy/get-started/")[1];
536 var short_url = dots[i].firstChild.href.split("/snappy/get-started/")[1];
537 if (short_path == short_url.replace("#", "")) {
538@@ -134,27 +134,59 @@
539 removeClass(dots[i].firstChild.children[1], "here");
540 }
541 }
542+ if (path.includes("/step")) {
543+ page = path.split("/").reverse()[1];
544+ current_step = page.split("-")[0].slice(-1);
545+ }
546+ else {
547+ current_step = 1;
548+ }
549+ nextBtn.href = createURLForDot(parseInt(current_step)+1)
550 }
551
552- function createURLForDot(length, position) {
553+ function createURLForDot(position) {
554 var host = getCookie("snappy-tour-host");
555 var version = getCookie("snappy-tour-version");
556 var target = getCookie("snappy-tour-target");
557 var url = "";
558 if (position == 1) {
559 if (path.includes("/step")) {
560- url = "..";
561+ url = "../..";
562 }
563 else {
564 url += "#";
565 }
566 }
567- else {
568- if (path.includes("/step")) {
569- url = "../step" + position + "-" + host + "-" + target + "/";
570- }
571- else {
572- url = version + "/step" + position + "-" + host + "-" + target + "/";
573+ else if (position == 2) {
574+ if (path.includes("/step")) {
575+ url = "../step" + position + "-setup-" + target + "-" + host + "/";
576+ }
577+ else {
578+ url = version + "/step" + position + "-setup-" + target + "-" + host + "/";
579+ }
580+ }
581+ else if (position == 3) {
582+ if (path.includes("/step")) {
583+ url = "../step" + position + "-get-familiar/";
584+ }
585+ else {
586+ url = version + "/step" + position + "-get-familiar/";
587+ }
588+ }
589+ else if (position == 4) {
590+ if (path.includes("/step")) {
591+ url = "../step" + position + "-first-snap/";
592+ }
593+ else {
594+ url = version + "/step" + position + "-first-snap/";
595+ }
596+ }
597+ else {
598+ if (path.includes("/step")) {
599+ url = "../step" + position + "-further-readings/";
600+ }
601+ else {
602+ url = version + "/step" + position + "-further-readings/";
603 }
604 }
605 return url;
606@@ -167,7 +199,6 @@
607 next_step = current_step.replace(/(\d+)/, function () {
608 return arguments[1] * 1 + 1;
609 });
610- return "../" + page.replace(current_step, next_step);
611 }
612 else {
613 var step = "step2";
614@@ -193,7 +224,6 @@
615 setCookie("snappy-tour-version", version);
616 setCookie("snappy-tour-host", host);
617 setCookie("snappy-tour-target", target);
618- return version + "/" + step + "-" + host + "-" + target + "/";
619 }
620 }
621
622@@ -332,3 +362,4 @@
623 {% endblock %}
624 {% block footer %}
625 {% endblock %}
626+

Subscribers

People subscribed via source and target branches