Merge lp:~developer-ubuntu-com-dev/developer-ubuntu-com/hero-tour-changes into lp:developer-ubuntu-com

Proposed by Daniel Holbach on 2016-03-08
Status: Superseded
Proposed branch: lp:~developer-ubuntu-com-dev/developer-ubuntu-com/hero-tour-changes
Merge into: lp:developer-ubuntu-com
Diff against target: 744 lines (+344/-87)
10 files modified
md_importer/importer/__init__.py (+18/-0)
md_importer/importer/article.py (+43/-24)
md_importer/importer/process.py (+3/-1)
md_importer/importer/publish.py (+66/-27)
md_importer/importer/repo.py (+18/-8)
md_importer/migrations/0002_hero_tour_changes.py (+24/-0)
md_importer/models.py (+21/-0)
md_importer/tests/test_branch_import.py (+106/-6)
md_importer/tests/test_link_rewrite.py (+42/-20)
md_importer/tests/utils.py (+3/-1)
To merge this branch: bzr merge lp:~developer-ubuntu-com-dev/developer-ubuntu-com/hero-tour-changes
Reviewer Review Type Date Requested Status
Ubuntu App Developer site developers 2016-03-08 Pending
Review via email: mp+288400@code.launchpad.net

This proposal has been superseded by a proposal from 2016-03-08.

To post a comment you must log in.
219. By David Callé on 2016-03-08

Add first iteration of the snappy hero tour

220. By Daniel Holbach on 2016-03-09

fix unicode craziness

221. By Daniel Holbach on 2016-03-09

extend db_add_empty_page do you can define your own slug

222. By Daniel Holbach on 2016-03-10

add tests for hero tour IA

223. By Daniel Holbach on 2016-03-21

merge trunk

224. By Daniel Holbach on 2016-03-22

merge trunk

225. By Daniel Holbach on 2016-03-22

adapt url expectation to a crash David was seeing

226. By Daniel Holbach on 2016-03-22

make pep8/pyflakes happy

227. By Daniel Holbach on 2016-03-22

apply fix from David - thanks

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'md_importer/importer/__init__.py'
2--- md_importer/importer/__init__.py 2016-01-12 11:44:04 +0000
3+++ md_importer/importer/__init__.py 2016-03-08 13:25:48 +0000
4@@ -1,5 +1,23 @@
5 from developer_portal.settings import LANGUAGE_CODE
6
7+from md_importer.models import ExternalDocsBranchImportDirective
8+
9 DEFAULT_LANG = LANGUAGE_CODE
10 HOME_PAGE_URL = '/{}/'.format(DEFAULT_LANG)
11 SUPPORTED_ARTICLE_TYPES = ['.md', '.html']
12+
13+# Instead of just using pymdownx.github, we go with these because of
14+# https://github.com/facelessuser/pymdown-extensions/issues/11
15+MARKDOWN_EXTENSIONS = [
16+ 'markdown.extensions.tables',
17+ 'pymdownx.magiclink',
18+ 'pymdownx.betterem',
19+ 'pymdownx.tilde',
20+ 'pymdownx.githubemoji',
21+ 'pymdownx.tasklist',
22+ 'pymdownx.superfences',
23+]
24+
25+model_info = ExternalDocsBranchImportDirective._meta
26+TEMPLATE_CHOICES = model_info.get_field('template').choices
27+DEFAULT_TEMPLATE = model_info.get_field('template').default
28
29=== modified file 'md_importer/importer/article.py'
30--- md_importer/importer/article.py 2016-01-15 13:56:34 +0000
31+++ md_importer/importer/article.py 2016-03-08 13:25:48 +0000
32@@ -8,9 +8,10 @@
33
34 from . import (
35 DEFAULT_LANG,
36+ MARKDOWN_EXTENSIONS,
37 SUPPORTED_ARTICLE_TYPES,
38 )
39-from .publish import get_or_create_page, slugify
40+from .publish import get_or_create_page, slugify, update_page
41
42 if sys.version_info.major == 2:
43 from urlparse import urlparse
44@@ -19,7 +20,7 @@
45
46
47 class Article:
48- def __init__(self, fn, write_to):
49+ def __init__(self, fn, write_to, advertise, template):
50 self.html = None
51 self.page = None
52 self.title = ""
53@@ -27,18 +28,20 @@
54 self.write_to = slugify(self.fn)
55 self.full_url = write_to
56 self.slug = os.path.basename(self.full_url)
57+ self.links_rewritten = False
58+ self.local_images = []
59+ self.advertise = advertise
60+ self.template = template
61
62 def _find_local_images(self):
63 '''Local images are currently not supported.'''
64 soup = BeautifulSoup(self.html, 'html5lib')
65- local_images = []
66 for img in soup.find_all('img'):
67 if img.has_attr('src'):
68 (scheme, netloc, path, params, query, fragment) = \
69 urlparse(img.attrs['src'])
70 if scheme not in ['http', 'https']:
71- local_images.extend([img.attrs['src']])
72- return local_images
73+ self.local_images.extend([img.attrs['src']])
74
75 def read(self):
76 if os.path.splitext(self.fn)[1] not in SUPPORTED_ARTICLE_TYPES:
77@@ -50,13 +53,13 @@
78 self.html = markdown.markdown(
79 f.read(),
80 output_format='html5',
81- extensions=['pymdownx.github'])
82+ extensions=MARKDOWN_EXTENSIONS)
83 elif self.fn.endswith('.html'):
84 self.html = f.read()
85- local_images = self._find_local_images()
86- if local_images:
87+ self._find_local_images()
88+ if self.local_images:
89 logging.error('Found the following local image(s): {}'.format(
90- ', '.join(local_images)
91+ ', '.join(self.local_images)
92 ))
93 return False
94 self.title = self._read_title()
95@@ -73,10 +76,15 @@
96 return slugify(self.fn).replace('-', ' ').title()
97
98 def _remove_body_and_html_tags(self):
99- self.html = re.sub(r"<html>\n\s<body>\n", "", self.html,
100- flags=re.MULTILINE)
101- self.html = re.sub(r"\s<\/body>\n<\/html>", "", self.html,
102- flags=re.MULTILINE)
103+ for regex in [
104+ # These are added by markdown.markdown
105+ r'\s*<html>\s*<body>\s*',
106+ r'\s*<\/body>\s*<\/html>\s*',
107+ # This is added by BeautifulSoup.prettify
108+ r'\s*<html>\s*<head>\s*<\/head>\s*<body>\s*',
109+ ]:
110+ self.html = re.sub(regex, '', self.html,
111+ flags=re.MULTILINE)
112
113 def _use_developer_site_style(self):
114 begin = (u"<div class=\"row no-border\">"
115@@ -92,7 +100,6 @@
116
117 def replace_links(self, titles, url_map):
118 soup = BeautifulSoup(self.html, 'html5lib')
119- change = False
120 for link in soup.find_all('a'):
121 if not link.has_attr('class') or \
122 'headeranchor-link' not in link.attrs['class']:
123@@ -100,25 +107,35 @@
124 if title.endswith(link.attrs['href']) and \
125 link.attrs['href'] != url_map[title].full_url:
126 link.attrs['href'] = url_map[title].full_url
127- change = True
128- if change:
129+ if not link.attrs['href'].startswith('/'):
130+ link.attrs['href'] = '/' + link.attrs['href']
131+ self.links_rewritten = True
132+ if self.links_rewritten:
133 self.html = soup.prettify()
134- return change
135+ self._remove_body_and_html_tags()
136
137 def add_to_db(self):
138 '''Publishes pages in their branch alias namespace.'''
139 self.page = get_or_create_page(
140 title=self.title, full_url=self.full_url, menu_title=self.title,
141- html=self.html)
142+ html=self.html, in_navigation=self.advertise,
143+ template=self.template)
144 if not self.page:
145 return False
146- self.full_url = self.page.get_absolute_url()
147+ self.full_url = re.sub(
148+ r'^\/None\/', '/{}/'.format(DEFAULT_LANG),
149+ self.page.get_absolute_url())
150 return True
151
152 def publish(self):
153+ if self.links_rewritten:
154+ update_page(self.page, title=self.title, full_url=self.full_url,
155+ menu_title=self.title, html=self.html,
156+ in_navigation=self.advertise, template=self.template)
157 if self.page.is_dirty(DEFAULT_LANG):
158 self.page.publish(DEFAULT_LANG)
159- self.page = self.page.get_public_object()
160+ if self.page.get_public_object():
161+ self.page = self.page.get_public_object()
162 return self.page
163
164
165@@ -128,14 +145,16 @@
166 def read(self):
167 if not Article.read(self):
168 return False
169- self.release_alias = re.findall(r'snappy/guides/(\S+?)/\S+?',
170- self.full_url)[0]
171+ matches = re.findall(r'snappy/guides/(\S+?)/\S+?',
172+ self.full_url)
173+ if matches:
174+ self.release_alias = matches[0]
175 self._make_snappy_mods()
176 return True
177
178 def _make_snappy_mods(self):
179 # Make sure the reader knows which documentation she is browsing
180- if self.release_alias != 'current':
181+ if self.release_alias and self.release_alias != 'current':
182 before = (u"<div class=\"row no-border\">\n"
183 "<div class=\"eight-col\">\n")
184 after = (u"<div class=\"row no-border\">\n"
185@@ -158,6 +177,6 @@
186 redirect="/snappy/guides/current/{}".format(self.slug))
187 if not page:
188 return False
189- else:
190+ elif self.release_alias:
191 self.title += " (%s)" % (self.release_alias,)
192 return Article.add_to_db(self)
193
194=== modified file 'md_importer/importer/process.py'
195--- md_importer/importer/process.py 2016-01-16 00:36:39 +0000
196+++ md_importer/importer/process.py 2016-03-08 13:25:48 +0000
197@@ -22,7 +22,9 @@
198 for directive in ExternalDocsBranchImportDirective.objects.filter(
199 external_docs_branch=branch):
200 repo.add_directive(directive.import_from,
201- directive.write_to)
202+ directive.write_to,
203+ directive.advertise,
204+ directive.template)
205 if not repo.execute_import_directives():
206 return False
207 if not repo.publish():
208
209=== modified file 'md_importer/importer/publish.py'
210--- md_importer/importer/publish.py 2016-01-15 13:58:39 +0000
211+++ md_importer/importer/publish.py 2016-03-08 13:25:48 +0000
212@@ -1,14 +1,25 @@
213-from md_importer.importer import DEFAULT_LANG, HOME_PAGE_URL
214+from md_importer.importer import (
215+ DEFAULT_LANG,
216+ DEFAULT_TEMPLATE,
217+ HOME_PAGE_URL,
218+)
219
220 from cms.api import create_page, add_plugin
221 from cms.models import Title
222 from djangocms_text_ckeditor.html import clean_html
223
224+from bs4 import BeautifulSoup
225 import logging
226 import re
227 import os
228
229
230+def _compare_html(html_a, html_b):
231+ soup_a = BeautifulSoup(html_a, 'html5lib')
232+ soup_b = BeautifulSoup(html_b, 'html5lib')
233+ return (clean_html(soup_a.prettify()) == clean_html(soup_b.prettify()))
234+
235+
236 def slugify(filename):
237 return os.path.basename(filename).replace('.md', '').replace('.html', '')
238
239@@ -32,44 +43,72 @@
240 return parent_pages[0].page
241
242
243+def find_text_plugin(page):
244+ # We create the page, so we know there's just one placeholder
245+ placeholder = page.placeholders.all()[0]
246+ if placeholder.get_plugins():
247+ return (
248+ placeholder,
249+ placeholder.get_plugins()[0].get_plugin_instance()[0]
250+ )
251+ return (placeholder, None)
252+
253+
254+def update_page(page, title, full_url, menu_title=None,
255+ in_navigation=True, redirect=None, html=None, template=None):
256+ if page.get_title() != title:
257+ page.title = title
258+ if page.get_menu_title() != menu_title:
259+ page.menu_title = menu_title
260+ if page.in_navigation != in_navigation:
261+ page.in_navigation = in_navigation
262+ if page.get_redirect() != redirect:
263+ page.redirect = redirect
264+ if page.template != template:
265+ page.template = template
266+ if html:
267+ update = True
268+ (placeholder, plugin) = find_text_plugin(page)
269+ if plugin:
270+ if _compare_html(html, plugin.body):
271+ update = False
272+ elif page.get_public_object():
273+ (dummy, published_plugin) = \
274+ find_text_plugin(page.get_public_object())
275+ if published_plugin:
276+ if _compare_html(html, published_plugin.body):
277+ update = False
278+ if update:
279+ plugin.body = html
280+ plugin.save()
281+ else:
282+ # Reset draft
283+ page.get_draft_object().revert(DEFAULT_LANG)
284+ else:
285+ add_plugin(
286+ placeholder, 'RawHtmlPlugin',
287+ DEFAULT_LANG, body=html)
288+
289+
290 def get_or_create_page(title, full_url, menu_title=None,
291- in_navigation=True, redirect=None, html=None):
292+ in_navigation=True, redirect=None, html=None,
293+ template=DEFAULT_TEMPLATE):
294 # First check if pages already exist.
295 pages = Title.objects.select_related('page').filter(
296 path__regex=full_url).filter(publisher_is_draft=True)
297 if pages:
298 page = pages[0].page
299- if page.get_title() != title:
300- page.title = title
301- if page.get_menu_title() != menu_title:
302- page.menu_title = menu_title
303- if page.in_navigation != in_navigation:
304- page.in_navigation = in_navigation
305- if page.get_redirect() != redirect:
306- page.redirect = redirect
307- if html:
308- # We create the page, so we know there's just one placeholder
309- placeholder = page.placeholders.all()[0]
310- if placeholder.get_plugins():
311- plugin = placeholder.get_plugins()[0].get_plugin_instance()[0]
312- if plugin.body != clean_html(html, full=False):
313- plugin.body = html
314- plugin.save()
315- else:
316- add_plugin(
317- placeholder, 'RawHtmlPlugin',
318- DEFAULT_LANG, body=html)
319+ update_page(page, title, full_url, menu_title, in_navigation,
320+ redirect, html, template)
321 else:
322 parent = _find_parent(full_url)
323 if not parent:
324 return None
325 slug = os.path.basename(full_url)
326 page = create_page(
327- title, 'default.html', DEFAULT_LANG, slug=slug, parent=parent,
328- menu_title=menu_title, in_navigation=in_navigation,
329+ title=title, template=template, language=DEFAULT_LANG, slug=slug,
330+ parent=parent, menu_title=menu_title, in_navigation=in_navigation,
331 position='last-child', redirect=redirect)
332- placeholder = page.placeholders.get()
333+ placeholder = page.placeholders.all()[0]
334 add_plugin(placeholder, 'RawHtmlPlugin', DEFAULT_LANG, body=html)
335- placeholder = page.placeholders.all()[0]
336- plugin = placeholder.get_plugins()[0].get_plugin_instance()[0]
337 return page
338
339=== modified file 'md_importer/importer/repo.py'
340--- md_importer/importer/repo.py 2016-01-15 18:54:50 +0000
341+++ md_importer/importer/repo.py 2016-03-08 13:25:48 +0000
342@@ -6,6 +6,8 @@
343 from .publish import get_or_create_page, slugify
344 from .source import SourceCode
345
346+from md_importer.models import ExternalDocsBranchImportDirective
347+
348 import glob
349 import logging
350 import os
351@@ -56,12 +58,18 @@
352 return 1
353 return 0
354
355- def add_directive(self, import_from, write_to):
356+ def add_directive(self, import_from, write_to, advertise=True,
357+ template=None):
358+ if template is None:
359+ model_info = ExternalDocsBranchImportDirective._meta
360+ template = model_info.get_field('template').default
361 self.directives += [
362 {
363 'import_from': os.path.join(self.checkout_location,
364 import_from),
365- 'write_to': write_to
366+ 'write_to': write_to,
367+ 'advertise': advertise,
368+ 'template': template,
369 }
370 ]
371
372@@ -71,7 +79,8 @@
373 for directive in [d for d in self.directives
374 if os.path.isfile(d['import_from'])]:
375 import_list += [
376- (directive['import_from'], directive['write_to'])
377+ (directive['import_from'], directive['write_to'],
378+ directive['advertise'], directive['template'])
379 ]
380 # Import directories next
381 for directive in [d for d in self.directives
382@@ -79,7 +88,8 @@
383 for fn in glob.glob('{}/*'.format(directive['import_from'])):
384 if fn not in [a[0] for a in import_list]:
385 import_list += [
386- (fn, os.path.join(directive['write_to'], slugify(fn)))
387+ (fn, os.path.join(directive['write_to'], slugify(fn)),
388+ directive['advertise'], directive['template'])
389 ]
390 # If we import into a namespace and don't have an index doc,
391 # we need to write one.
392@@ -91,7 +101,8 @@
393 return False
394 # The actual import
395 for entry in import_list:
396- article = self._read_article(entry[0], entry[1])
397+ article = self._read_article(
398+ entry[0], entry[1], entry[2], entry[3])
399 if article:
400 self.imported_articles += [article]
401 self.titles[article.fn] = article.title
402@@ -106,8 +117,8 @@
403 self._write_fake_index_doc()
404 return True
405
406- def _read_article(self, fn, write_to):
407- article = self.article_class(fn, write_to)
408+ def _read_article(self, fn, write_to, advertise, template):
409+ article = self.article_class(fn, write_to, advertise, template)
410 if article.read():
411 return article
412 return None
413@@ -118,7 +129,6 @@
414 logging.error('Publishing of {} aborted.'.format(self.origin))
415 return False
416 article.replace_links(self.titles, self.url_map)
417- self.pages = []
418 for article in self.imported_articles:
419 self.pages.extend([article.publish()])
420 if self.index_page:
421
422=== added file 'md_importer/migrations/0002_hero_tour_changes.py'
423--- md_importer/migrations/0002_hero_tour_changes.py 1970-01-01 00:00:00 +0000
424+++ md_importer/migrations/0002_hero_tour_changes.py 2016-03-08 13:25:48 +0000
425@@ -0,0 +1,24 @@
426+# -*- coding: utf-8 -*-
427+from __future__ import unicode_literals
428+
429+from django.db import migrations, models
430+
431+
432+class Migration(migrations.Migration):
433+
434+ dependencies = [
435+ ('md_importer', '0001_initial'),
436+ ]
437+
438+ operations = [
439+ migrations.AddField(
440+ model_name='externaldocsbranchimportdirective',
441+ name='advertise',
442+ field=models.BooleanField(default=True, help_text='Should the imported articles be listed in the navigation? Default: yes.'),
443+ ),
444+ migrations.AddField(
445+ model_name='externaldocsbranchimportdirective',
446+ name='template',
447+ field=models.CharField(default=b'default.html', help_text='Django CMS template to use for the imported articles. Default: default.html', max_length=50, choices=[(b'default.html', b'Default'), (b'landing_page.html', b'Landing Page'), (b'no_subnav.html', b'Without Subnav'), (b'with_hero.html', b'With Hero')]),
448+ ),
449+ ]
450
451=== modified file 'md_importer/models.py'
452--- md_importer/models.py 2016-01-11 08:10:08 +0000
453+++ md_importer/models.py 2016-03-08 13:25:48 +0000
454@@ -1,9 +1,18 @@
455+from django.conf import settings
456 from django.db import models
457 from django.utils.translation import ugettext_lazy as _
458
459 from cms.models import Page
460
461
462+if settings.CMS_TEMPLATES:
463+ cms_templates = settings.CMS_TEMPLATES
464+else:
465+ cms_templates = (
466+ ('default.html', 'Default'),
467+ )
468+
469+
470 class ExternalDocsBranch(models.Model):
471 origin = models.CharField(
472 max_length=200,
473@@ -43,6 +52,18 @@
474 help_text=_('Article URL (for a specific file) or article namespace '
475 'for a directory or a set of files.'),
476 blank=True)
477+ advertise = models.BooleanField(
478+ default=True,
479+ help_text=_('Should the imported articles be listed in the '
480+ 'navigation? Default: yes.'),
481+ )
482+ template = models.CharField(
483+ max_length=50,
484+ default=cms_templates[0][0],
485+ choices=cms_templates,
486+ help_text=_('Django CMS template to use for the imported articles. '
487+ 'Default: {}'.format(cms_templates[0][0])),
488+ )
489
490 def __str__(self):
491 return "{} -- {}".format(self.external_docs_branch,
492
493=== modified file 'md_importer/tests/test_branch_import.py'
494--- md_importer/tests/test_branch_import.py 2016-01-15 13:59:32 +0000
495+++ md_importer/tests/test_branch_import.py 2016-03-08 13:25:48 +0000
496@@ -2,9 +2,15 @@
497 import pytz
498 import shutil
499
500-from cms.models import CMSPlugin, Page
501+from cms.models import Page
502
503+from md_importer.importer import (
504+ DEFAULT_TEMPLATE,
505+ TEMPLATE_CHOICES,
506+)
507 from md_importer.importer.article import Article
508+from md_importer.importer.publish import find_text_plugin
509+
510 from .utils import TestLocalBranchImport
511
512
513@@ -66,6 +72,71 @@
514 self.assertEqual(page.parent_id, self.root.id)
515
516
517+class TestArticleHTMLTagsAfterImport(TestLocalBranchImport):
518+ def runTest(self):
519+ self.create_repo('data/snapcraft-test')
520+ self.repo.add_directive('docs', '')
521+ self.assertEqual(len(self.repo.directives), 1)
522+ self.assertTrue(self.repo.execute_import_directives())
523+ self.assertGreater(len(self.repo.imported_articles), 3)
524+ self.assertTrue(self.repo.publish())
525+ pages = Page.objects.all()
526+ self.assertGreater(pages.count(), len(self.repo.imported_articles))
527+ for article in self.repo.imported_articles:
528+ self.assertIsInstance(article, Article)
529+ self.assertNotIn('<body>', article.html)
530+ self.assertNotIn('&lt;body&gt;', article.html)
531+
532+
533+class TestNoneInURLAfterImport(TestLocalBranchImport):
534+ def runTest(self):
535+ self.create_repo('data/snapcraft-test')
536+ self.repo.add_directive('docs', '')
537+ self.assertEqual(len(self.repo.directives), 1)
538+ self.assertTrue(self.repo.execute_import_directives())
539+ self.assertGreater(len(self.repo.imported_articles), 3)
540+ self.assertTrue(self.repo.publish())
541+ pages = Page.objects.all()
542+ self.assertGreater(pages.count(), len(self.repo.imported_articles))
543+ for article in self.repo.imported_articles:
544+ self.assertIsInstance(article, Article)
545+ self.assertNotIn('/None/', article.full_url)
546+ for page in pages:
547+ self.assertIsNotNone(page.get_slug())
548+
549+
550+class TestAdvertiseImport(TestLocalBranchImport):
551+ '''Check if all imported articles are advertised in the navigation when
552+ using defaults.'''
553+ def runTest(self):
554+ self.create_repo('data/snapcraft-test')
555+ self.repo.add_directive('docs', '')
556+ self.assertTrue(self.repo.execute_import_directives())
557+ for article in self.repo.imported_articles:
558+ self.assertTrue(article.advertise)
559+ self.assertTrue(self.repo.publish())
560+ for page in Page.objects.filter(publisher_is_draft=False):
561+ if page.parent is not None:
562+ self.assertEqual(page.parent_id, self.root.id)
563+ self.assertTrue(page.in_navigation)
564+
565+
566+class TestNoAdvertiseImport(TestLocalBranchImport):
567+ '''Check if all imported articles are advertised in the navigation when
568+ using defaults.'''
569+ def runTest(self):
570+ self.create_repo('data/snapcraft-test')
571+ self.repo.add_directive('docs', '', advertise=False)
572+ self.assertTrue(self.repo.execute_import_directives())
573+ for article in self.repo.imported_articles:
574+ self.assertFalse(article.advertise)
575+ self.assertTrue(self.repo.publish())
576+ for page in Page.objects.filter(publisher_is_draft=False):
577+ if page.parent is not None:
578+ self.assertEqual(page.parent_id, self.root.id)
579+ self.assertFalse(page.in_navigation)
580+
581+
582 class TestTwiceImport(TestLocalBranchImport):
583 '''Run import on the same contents twice, make sure we don't
584 add new pages over and over again.'''
585@@ -101,9 +172,9 @@
586 self.assertEqual(
587 Page.objects.filter(publisher_is_draft=False).count(),
588 len(self.repo.imported_articles)+1) # articles + root
589+ shutil.rmtree(self.tempdir)
590 # Take the time before publishing the second import
591 now = datetime.now(pytz.utc)
592- shutil.rmtree(self.tempdir)
593 # Run second import
594 self.create_repo('data/snapcraft-test')
595 self.repo.add_directive('docs', '')
596@@ -112,7 +183,36 @@
597 self.assertTrue(self.repo.execute_import_directives())
598 self.assertTrue(self.repo.publish())
599 # Check the page's plugins
600- for plugin_change in CMSPlugin.objects.filter(
601- plugin_type='RawHtmlPlugin').order_by(
602- '-changed_date'):
603- self.assertGreater(now, plugin_change.changed_date)
604+ for page in Page.objects.filter(publisher_is_draft=False):
605+ if page != self.root:
606+ (dummy, plugin) = find_text_plugin(page)
607+ self.assertGreater(now, plugin.changed_date)
608+
609+
610+class TestImportNoTemplateChange(TestLocalBranchImport):
611+ '''Check if all imported articles use the default template.'''
612+ def runTest(self):
613+ self.create_repo('data/snapcraft-test')
614+ self.repo.add_directive('docs', '')
615+ self.assertTrue(self.repo.execute_import_directives())
616+ for article in self.repo.imported_articles:
617+ self.assertEqual(article.template, DEFAULT_TEMPLATE)
618+ self.assertTrue(self.repo.publish())
619+ for page in Page.objects.filter(publisher_is_draft=False):
620+ if page.parent is not None:
621+ self.assertEqual(page.template, DEFAULT_TEMPLATE)
622+
623+
624+class TestImportTemplateChange(TestLocalBranchImport):
625+ '''Check if all imported articles use the desired template.'''
626+ def runTest(self):
627+ self.create_repo('data/snapcraft-test')
628+ template_to_use = TEMPLATE_CHOICES[1][0]
629+ self.repo.add_directive('docs', '', template=template_to_use)
630+ self.assertTrue(self.repo.execute_import_directives())
631+ for article in self.repo.imported_articles:
632+ self.assertEqual(article.template, template_to_use)
633+ self.assertTrue(self.repo.publish())
634+ for page in Page.objects.filter(publisher_is_draft=False):
635+ if page.parent is not None:
636+ self.assertEqual(page.template, template_to_use)
637
638=== modified file 'md_importer/tests/test_link_rewrite.py'
639--- md_importer/tests/test_link_rewrite.py 2016-01-11 14:38:51 +0000
640+++ md_importer/tests/test_link_rewrite.py 2016-03-08 13:25:48 +0000
641@@ -30,6 +30,11 @@
642 link.attrs['href'],
643 ', '.join([p.get_absolute_url() for p in pages])))
644 self.assertIn(page, pages)
645+ if article.slug == 'file1':
646+ for link in soup.find_all('a'):
647+ if not link.has_attr('class') or \
648+ 'headeranchor-link' not in link.attrs['class']:
649+ self.assertEqual(link.attrs['href'], '/file2')
650
651
652 class TestLinkBrokenRewrite(TestLocalBranchImport):
653@@ -45,12 +50,33 @@
654 self.assertEqual(article.page.parent, self.root)
655 soup = BeautifulSoup(article.html, 'html5lib')
656 for link in soup.find_all('a'):
657- if link.has_attr('class') and \
658- 'headeranchor-link' in link.attrs['class']:
659- break
660- page = self.check_local_link(link.attrs['href'])
661- self.assertIsNone(page)
662- self.assertNotIn(page, pages)
663+ if not link.has_attr('class') or \
664+ 'headeranchor-link' not in link.attrs['class']:
665+ page = self.check_local_link(link.attrs['href'])
666+ self.assertIsNone(page)
667+ self.assertNotIn(page, pages)
668+
669+
670+class TestNoneNotInLinks(TestLocalBranchImport):
671+ def runTest(self):
672+ self.create_repo('data/snapcraft-test')
673+ snappy_page = db_add_empty_page('Snappy', self.root)
674+ self.assertFalse(snappy_page.publisher_is_draft)
675+ build_apps = db_add_empty_page('Build Apps', snappy_page)
676+ self.assertFalse(build_apps.publisher_is_draft)
677+ self.assertEqual(
678+ 3, Page.objects.filter(publisher_is_draft=False).count())
679+ self.repo.add_directive('docs/intro.md', 'snappy/build-apps/current')
680+ self.repo.add_directive('docs', 'snappy/build-apps/current')
681+ self.assertTrue(self.repo.execute_import_directives())
682+ self.assertTrue(self.repo.publish())
683+ for article in self.repo.imported_articles:
684+ self.assertTrue(isinstance(article, Article))
685+ self.assertGreater(len(article.html), 0)
686+ soup = BeautifulSoup(article.html, 'html5lib')
687+ for link in soup.find_all('a'):
688+ if is_local_link(link):
689+ self.assertFalse(link.attrs['href'].startswith('/None/'))
690
691
692 class TestSnapcraftLinkRewrite(TestLocalBranchImport):
693@@ -62,25 +88,21 @@
694 self.assertFalse(build_apps.publisher_is_draft)
695 self.assertEqual(
696 3, Page.objects.filter(publisher_is_draft=False).count())
697- self.repo.add_directive('docs', 'snappy/build-apps/devel')
698- self.repo.add_directive('README.md', 'snappy/build-apps/devel')
699- self.repo.add_directive(
700- 'HACKING.md', 'snappy/build-apps/devel/hacking')
701+ self.repo.add_directive('docs/intro.md', 'snappy/build-apps/current')
702+ self.repo.add_directive('docs', 'snappy/build-apps/current')
703 self.assertTrue(self.repo.execute_import_directives())
704 self.assertTrue(self.repo.publish())
705 pages = Page.objects.all()
706 for article in self.repo.imported_articles:
707 self.assertTrue(isinstance(article, Article))
708 self.assertGreater(len(article.html), 0)
709- for article in self.repo.imported_articles:
710 soup = BeautifulSoup(article.html, 'html5lib')
711 for link in soup.find_all('a'):
712- if not is_local_link(link):
713- break
714- page = self.check_local_link(link.attrs['href'])
715- self.assertIsNotNone(
716- page,
717- msg='Link {} not found. Available pages: {}'.format(
718- link.attrs['href'],
719- ', '.join([p.get_absolute_url() for p in pages])))
720- self.assertIn(page, pages)
721+ if is_local_link(link):
722+ page = self.check_local_link(link.attrs['href'])
723+ self.assertIsNotNone(
724+ page,
725+ msg='Link {} not found. Available pages: {}'.format(
726+ link.attrs['href'],
727+ ', '.join([p.get_absolute_url() for p in pages])))
728+ self.assertIn(page, pages)
729
730=== modified file 'md_importer/tests/utils.py'
731--- md_importer/tests/utils.py 2016-01-11 14:38:51 +0000
732+++ md_importer/tests/utils.py 2016-03-08 13:25:48 +0000
733@@ -55,8 +55,10 @@
734 self.assertEqual(self.fetch_retcode, 0)
735
736 def check_local_link(self, url):
737+ if not url.startswith('/'):
738+ url = '/' + url
739 if not url.startswith('/{}/'.format(DEFAULT_LANG)):
740- url = '/{}/{}/'.format(DEFAULT_LANG, url)
741+ url = '/{}'.format(DEFAULT_LANG) + url
742 request = self.get_request(url)
743 page = get_page_from_request(request)
744 return page

Subscribers

People subscribed via source and target branches