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

Proposed by Daniel Holbach
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 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é

Add first iteration of the snappy hero tour

220. By Daniel Holbach

fix unicode craziness

221. By Daniel Holbach

extend db_add_empty_page do you can define your own slug

222. By Daniel Holbach

add tests for hero tour IA

223. By Daniel Holbach

merge trunk

224. By Daniel Holbach

merge trunk

225. By Daniel Holbach

adapt url expectation to a crash David was seeing

226. By Daniel Holbach

make pep8/pyflakes happy

227. By Daniel Holbach

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