Merge lp:~developer-ubuntu-com-dev/developer-ubuntu-com/hero-tour-changes into lp:developer-ubuntu-com
- hero-tour-changes
- Merge into stable
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 |
Related bugs: |
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.
Commit message
Description of the change
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('<body>', 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 |