automatically copy linked static files

This commit is contained in:
Oliver Urs Lenz 2018-07-11 15:54:47 +02:00
commit 048ea4dc0c
32 changed files with 424 additions and 224 deletions

View file

@ -14,6 +14,9 @@ Next release
and author pages 2) feeds 3) draft and hidden articles and pages
* New ``ARTICLE_TRANSLATION_ID`` and ``PAGE_TRANSLATION_ID`` settings to specify
metadata attributes used to identify translations; or to disable translations
* New ``{static}`` syntax to link to static content; content linked to by
``{static}`` and ``{attach}`` is automatically copied over even if not in
``STATIC_PATHS``
3.7.1 (2017-01-10)
==================

View file

@ -168,6 +168,34 @@ If you want to exclude any pages from being linked to or listed in the menu
then add a ``status: hidden`` attribute to its metadata. This is useful for
things like making error pages that fit the generated theme of your site.
Static content
==============
Static files are files other than articles and pages that are copied to the
output folder as-is, without processing. You can control which static files
are copied over with the ``STATIC_PATHS`` setting of the project's
``pelicanconf.py`` file. Pelican's default configuration includes the
``images`` directory for this, but others must be added manually. In addition,
static files that are explicitly linked to are included (see below).
Mixed content in the same directory
-----------------------------------
Starting with Pelican 3.5, static files can safely share a source directory with
page source files, without exposing the page sources in the generated site.
Any such directory must be added to both ``STATIC_PATHS`` and ``PAGE_PATHS``
(or ``STATIC_PATHS`` and ``ARTICLE_PATHS``). Pelican will identify and process
the page source files normally, and copy the remaining files as if they lived
in a separate directory reserved for static files.
Note: Placing static and content source files together in the same source
directory does not guarantee that they will end up in the same place in the
generated site. The easiest way to do this is by using the ``{attach}`` link
syntax (described below). Alternatively, the ``STATIC_SAVE_AS``,
``PAGE_SAVE_AS``, and ``ARTICLE_SAVE_AS`` settings (and the corresponding
``*_URL`` settings) can be configured to place files of different types
together, just as they could in earlier versions of Pelican.
.. _ref-linking-to-internal-content:
Linking to internal content
@ -221,13 +249,10 @@ and ``article2.md``::
Linking to static files
-----------------------
Linking to non-article or non-page content uses the same ``{filename}`` syntax
as described above. It is important to remember that those files will not be
copied to the output directory unless the source directories containing them
are included in the ``STATIC_PATHS`` setting of the project's ``pelicanconf.py``
file. Pelican's default configuration includes the ``images`` directory for
this, but others must be added manually. Forgetting to do so will result in
broken links.
You can link to static content using ``{static}path/to/file``. Files linked to
with this syntax will automatically be copied to the output directory, even if
the source directories containing them are not included in the ``STATIC_PATHS``
setting of the project's ``pelicanconf.py`` file.
For example, a project's content directory might be structured like this::
@ -241,48 +266,28 @@ For example, a project's content directory might be structured like this::
``test.md`` would include::
![Alt Text]({filename}/images/han.jpg)
[Our Menu]({filename}/pdfs/menu.pdf)
``pelicanconf.py`` would include::
STATIC_PATHS = ['images', 'pdfs']
![Alt Text]({static}/images/han.jpg)
[Our Menu]({static}/pdfs/menu.pdf)
Site generation would then copy ``han.jpg`` to ``output/images/han.jpg``,
``menu.pdf`` to ``output/pdfs/menu.pdf``, and write the appropriate links
in ``test.md``.
Mixed content in the same directory
-----------------------------------
Starting with Pelican 3.5, static files can safely share a source directory with
page source files, without exposing the page sources in the generated site.
Any such directory must be added to both ``STATIC_PATHS`` and ``PAGE_PATHS``
(or ``STATIC_PATHS`` and ``ARTICLE_PATHS``). Pelican will identify and process
the page source files normally, and copy the remaining files as if they lived
in a separate directory reserved for static files.
Note: Placing static and content source files together in the same source
directory does not guarantee that they will end up in the same place in the
generated site. The easiest way to do this is by using the ``{attach}`` link
syntax (described below). Alternatively, the ``STATIC_SAVE_AS``,
``PAGE_SAVE_AS``, and ``ARTICLE_SAVE_AS`` settings (and the corresponding
``*_URL`` settings) can be configured to place files of different types
together, just as they could in earlier versions of Pelican.
If you use ``{static}`` to link to an article or a page, this will be turned into
a link to its source code.
Attaching static files
----------------------
Starting with Pelican 3.5, static files can be "attached" to a page or article
using this syntax for the link target: ``{attach}path/to/file`` This works
like the ``{filename}`` syntax, but also relocates the static file into the
like the ``{static}`` syntax, but also relocates the static file into the
linking document's output directory. If the static file originates from a
subdirectory beneath the linking document's source, that relationship will be
preserved on output. Otherwise, it will become a sibling of the linking
document.
This only works for linking to static files, and only when they originate from
a directory included in the ``STATIC_PATHS`` setting.
This only works for linking to static files.
For example, a project's content directory might be structured like this::
@ -298,7 +303,6 @@ For example, a project's content directory might be structured like this::
``pelicanconf.py`` would include::
PATH = 'content'
STATIC_PATHS = ['blog', 'downloads']
ARTICLE_PATHS = ['blog']
ARTICLE_SAVE_AS = '{date:%Y}/{slug}.html'
ARTICLE_URL = '{date:%Y}/{slug}.html'
@ -328,7 +332,7 @@ the article's output directory.
If a static file is linked multiple times, the relocating feature of
``{attach}`` will only work in the first of those links to be processed.
After the first link, Pelican will treat ``{attach}`` like ``{filename}``.
After the first link, Pelican will treat ``{attach}`` like ``{static}``.
This avoids breaking the already-processed links.
**Be careful when linking to a file from multiple documents:**
@ -342,7 +346,7 @@ file's old location might then find their links broken. **It is therefore
advisable to use {attach} only if you use it in all links to a file, and only
if the linking documents share a single directory.** Under these conditions,
the file's output location will not change in future builds. In cases where
these precautions are not possible, consider using ``{filename}`` links instead
these precautions are not possible, consider using ``{static}`` links instead
of ``{attach}``, and letting the file's location be determined by the project's
``STATIC_SAVE_AS`` and ``STATIC_URL`` settings. (Per-file ``save_as`` and
``url`` overrides can still be set in ``EXTRA_PATH_METADATA``.)
@ -360,8 +364,11 @@ To remain compatible with earlier versions, Pelican still supports vertical bars
(``||``) in addition to curly braces (``{}``) for internal links. For example:
``|filename|an_article.rst``, ``|tag|tagname``, ``|category|foobar``.
The syntax was changed from ``||`` to ``{}`` to avoid collision with Markdown
extensions or reST directives. Support for the old syntax may eventually be
removed.
extensions or reST directives. Similarly, Pelican also still supports linking to
static content with ``{filename}``. The syntax was changed to ``{static}`` to allow
linking to both generated articles and pages and their static sources.
Support for the old syntax may eventually be removed.
Importing an existing site

View file

@ -150,8 +150,11 @@ class Pelican(object):
start_time = time.time()
context = self.settings.copy()
# Share these among all the generators and content objects:
context['filenames'] = {} # maps source path to Content object or None
# Share these among all the generators and content objects
# They map source paths to Content objects or None
context['generated_content'] = {}
context['static_links'] = set()
context['static_content'] = {}
context['localsiteurl'] = self.settings['SITEURL']
generators = [

View file

@ -142,7 +142,8 @@ class Content(object):
if not hasattr(self, 'status'):
self.status = getattr(self, 'default_status', None)
if len(self._context.get('filenames', [])) > 0:
if (len(self._context.get('generated_content', [])) > 0 or
len(self._context.get('static_content', [])) > 0):
self.refresh_metadata_intersite_links()
signals.content_object_init.send(self)
@ -255,7 +256,7 @@ class Content(object):
siteurl += '/'
# XXX Put this in a different location.
if what in {'filename', 'attach'}:
if what in {'filename', 'static', 'attach'}:
if path.startswith('/'):
path = path[1:]
else:
@ -264,22 +265,33 @@ class Content(object):
os.path.join(self.relative_dir, path)
)
if path not in self._context['filenames']:
unquoted_path = path.replace('%20', ' ')
key = 'static_content' if what in ('static', 'attach')\
else 'generated_content'
if unquoted_path in self._context['filenames']:
path = unquoted_path
def _get_linked_content(key, path):
try:
return self._context[key][path]
except KeyError:
try:
# Markdown escapes spaces, try unescaping
return self._context[key][path.replace('%20', ' ')]
except KeyError:
if what == 'filename' and key == 'generated_content':
key = 'static_content'
linked_content = _get_linked_content(key, path)
if linked_content:
logger.warning(
'{filename} used for linking to static'
'content %s in %s. Use {static} instead',
path,
self.get_relative_source_path())
return linked_content
return None
linked_content = self._context['filenames'].get(path)
linked_content = _get_linked_content(key, path)
if linked_content:
if what == 'attach':
if isinstance(linked_content, Static):
linked_content.attach_to(self)
else:
logger.warning(
"%s used {attach} link syntax on a "
"non-static file. Use {filename} instead.",
self.get_relative_source_path())
linked_content.attach_to(self)
origin = joiner(siteurl, linked_content.url)
origin = origin.replace('\\', '/') # for Windows paths.
else:
@ -310,6 +322,17 @@ class Content(object):
return ''.join((m.group('markup'), m.group('quote'), origin,
m.group('quote')))
def _get_intrasite_link_regex(self):
intrasite_link_regex = self.settings['INTRASITE_LINK_REGEX']
regex = r"""
(?P<markup><[^\>]+ # match tag with all url-value attributes
(?:href|src|poster|data|cite|formaction|action)\s*=\s*)
(?P<quote>["\']) # require value to be quoted
(?P<path>{0}(?P<value>.*?)) # the url value
\2""".format(intrasite_link_regex)
return re.compile(regex, re.X)
def _update_content(self, content, siteurl):
"""Update the content attribute.
@ -323,18 +346,29 @@ class Content(object):
if not content:
return content
instrasite_link_regex = self.settings['INTRASITE_LINK_REGEX']
regex = r"""
(?P<markup><[^\>]+ # match tag with all url-value attributes
(?:href|src|poster|data|cite|formaction|action)\s*=\s*)
(?P<quote>["\']) # require value to be quoted
(?P<path>{0}(?P<value>.*?)) # the url value
\2""".format(instrasite_link_regex)
hrefs = re.compile(regex, re.X)
hrefs = self._get_intrasite_link_regex()
return hrefs.sub(lambda m: self._link_replacer(siteurl, m), content)
def get_static_links(self):
static_links = set()
hrefs = self._get_intrasite_link_regex()
for m in hrefs.finditer(self._content):
what = m.group('what')
value = urlparse(m.group('value'))
path = value.path
if what not in {'static', 'attach'}:
continue
if path.startswith('/'):
path = path[1:]
else:
# relative to the source path of this content
path = self.get_relative_source_path(
os.path.join(self.relative_dir, path)
)
path = path.replace('%20', ' ')
static_links.add(path)
return static_links
def get_siteurl(self):
return self._context.get('localsiteurl', '')

View file

@ -147,7 +147,7 @@ class Generator(object):
parent_path, subdir = os.path.split(os.path.join(self.path, e))
exclusions_by_dirpath.setdefault(parent_path, set()).add(subdir)
files = []
files = set()
ignores = self.settings['IGNORE_FILES']
for path in paths:
# careful: os.path.join() will add a slash when path == ''.
@ -170,33 +170,41 @@ class Generator(object):
for f in temp_files:
fp = os.path.join(reldir, f)
if self._include_path(fp, extensions):
files.append(fp)
files.add(fp)
elif os.path.exists(root) and self._include_path(path, extensions):
files.append(path) # can't walk non-directories
files.add(path) # can't walk non-directories
return files
def add_source_path(self, content):
def add_source_path(self, content, static=False):
"""Record a source file path that a Generator found and processed.
Store a reference to its Content object, for url lookups later.
"""
location = content.get_relative_source_path()
self.context['filenames'][location] = content
key = 'static_content' if static else 'generated_content'
self.context[key][location] = content
def _add_failed_source_path(self, path):
def _add_failed_source_path(self, path, static=False):
"""Record a source file path that a Generator failed to process.
(For example, one that was missing mandatory metadata.)
The path argument is expected to be relative to self.path.
"""
self.context['filenames'][posixize_path(os.path.normpath(path))] = None
key = 'static_content' if static else 'generated_content'
self.context[key][posixize_path(os.path.normpath(path))] = None
def _is_potential_source_path(self, path):
def _is_potential_source_path(self, path, static=False):
"""Return True if path was supposed to be used as a source file.
(This includes all source files that have been found by generators
before this method is called, even if they failed to process.)
The path argument is expected to be relative to self.path.
"""
return (posixize_path(os.path.normpath(path))
in self.context['filenames'])
key = 'static_content' if static else 'generated_content'
return (posixize_path(os.path.normpath(path)) in self.context[key])
def add_static_links(self, content):
"""Add file links in content to context to be processed as Static
content.
"""
self.context['static_links'] |= content.get_static_links()
def _update_context(self, items):
"""Update the context with the given items from the currrent
@ -596,6 +604,7 @@ class ArticlesGenerator(CachingGenerator):
elif article.status == "draft":
all_drafts.append(article)
self.add_source_path(article)
self.add_static_links(article)
def _process(arts):
origs, translations = process_translations(
@ -702,6 +711,7 @@ class PagesGenerator(CachingGenerator):
elif page.status == "draft":
draft_pages.append(page)
self.add_source_path(page)
self.add_static_links(page)
def _process(pages):
origs, translations = process_translations(
@ -753,9 +763,12 @@ class StaticGenerator(Generator):
def generate_context(self):
self.staticfiles = []
for f in self.get_files(self.settings['STATIC_PATHS'],
exclude=self.settings['STATIC_EXCLUDES'],
extensions=False):
linked_files = {os.path.join(self.path, path)
for path in self.context['static_links']}
found_files = self.get_files(self.settings['STATIC_PATHS'],
exclude=self.settings['STATIC_EXCLUDES'],
extensions=False)
for f in linked_files | found_files:
# skip content source files unless the user explicitly wants them
if self.settings['STATIC_EXCLUDE_SOURCES']:
@ -770,7 +783,7 @@ class StaticGenerator(Generator):
context_signal=signals.static_generator_context,
context_sender=self)
self.staticfiles.append(static)
self.add_source_path(static)
self.add_source_path(static, static=True)
self._update_context(('staticfiles',))
signals.static_generator_finalized.send(self)

View file

@ -0,0 +1,7 @@
Title: Page with static links
My links:
[Link 0]({static}image0.jpg)
[Link 1]({attach}image1.jpg)

View file

@ -42,8 +42,8 @@
<div class="section" id="this-is-a-simple-title">
<h2>This is a simple title</h2>
<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<pre class="literal-block">
&gt;&gt;&gt; from ipdb import set_trace
&gt;&gt;&gt; set_trace()
@ -78,7 +78,7 @@
<h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div>
<a class="readmore" href="/oh-yeah.html">read more</a>

View file

@ -38,7 +38,7 @@
<h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div>
</article>
</aside><!-- /#featured -->

View file

@ -42,8 +42,8 @@
<div class="section" id="this-is-a-simple-title">
<h2>This is a simple title</h2>
<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<pre class="literal-block">
&gt;&gt;&gt; from ipdb import set_trace
&gt;&gt;&gt; set_trace()

View file

@ -5,8 +5,8 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="section" id="this-is-a-simple-title"&gt;
&lt;h2&gt;This is a simple title&lt;/h2&gt;
&lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace
&amp;gt;&amp;gt;&amp;gt; set_trace()
@ -17,12 +17,12 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt;
</summary><content type="html">&lt;div class="section" id="why-not"&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt;
</content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry></feed>

View file

@ -5,6 +5,6 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt;
</description><dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/">Alexis Métaireau</dc:creator><pubDate>Wed, 20 Oct 2010 10:14:00 +0000</pubDate><guid isPermaLink="false">tag:None,2010-10-20:/oh-yeah.html</guid><category>oh</category><category>bar</category><category>yeah</category></item></channel></rss>

View file

@ -19,8 +19,8 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="section" id="this-is-a-simple-title"&gt;
&lt;h2&gt;This is a simple title&lt;/h2&gt;
&lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace
&amp;gt;&amp;gt;&amp;gt; set_trace()
@ -31,13 +31,13 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt;
</summary><content type="html">&lt;div class="section" id="why-not"&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt;
</content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry><entry><title>Unbelievable !</title><link href="/unbelievable.html" rel="alternate"></link><published>2010-10-15T20:30:00+00:00</published><updated>2010-10-15T20:30:00+00:00</updated><author><name></name></author><id>tag:None,2010-10-15:/unbelievable.html</id><summary type="html">&lt;p&gt;Or completely awesome. Depends the needs.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/a-markdown-powered-article.html"&gt;a root-relative link to markdown-article&lt;/a&gt;

View file

@ -21,8 +21,8 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="section" id="this-is-a-simple-title"&gt;
&lt;h2&gt;This is a simple title&lt;/h2&gt;
&lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace
&amp;gt;&amp;gt;&amp;gt; set_trace()
@ -33,13 +33,13 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt;
</summary><content type="html">&lt;div class="section" id="why-not"&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt;
</content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry><entry><title>Unbelievable !</title><link href="/unbelievable.html" rel="alternate"></link><published>2010-10-15T20:30:00+00:00</published><updated>2010-10-15T20:30:00+00:00</updated><author><name></name></author><id>tag:None,2010-10-15:/unbelievable.html</id><summary type="html">&lt;p&gt;Or completely awesome. Depends the needs.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/a-markdown-powered-article.html"&gt;a root-relative link to markdown-article&lt;/a&gt;

View file

@ -3,12 +3,12 @@
&lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt;
</summary><content type="html">&lt;div class="section" id="why-not"&gt;
&lt;h2&gt;Why not ?&lt;/h2&gt;
&lt;p&gt;After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;/div&gt;
</content><category term="oh"></category><category term="bar"></category><category term="yeah"></category></entry></feed>

View file

@ -5,8 +5,8 @@ as well as &lt;strong&gt;inline markup&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="section" id="this-is-a-simple-title"&gt;
&lt;h2&gt;This is a simple title&lt;/h2&gt;
&lt;p&gt;And here comes the cool &lt;a class="reference external" href="http://books.couchdb.org/relax/design-documents/views"&gt;stuff&lt;/a&gt;.&lt;/p&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" /&gt;
&lt;pre class="literal-block"&gt;
&amp;gt;&amp;gt;&amp;gt; from ipdb import set_trace
&amp;gt;&amp;gt;&amp;gt; set_trace()

View file

@ -191,7 +191,7 @@ as well as <strong>inline markup</strong>.</p>
<h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div>
<a class="readmore" href="/oh-yeah.html">read more</a>

View file

@ -43,7 +43,7 @@
<h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div>
</div><!-- /.entry-content -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

View file

@ -90,7 +90,7 @@ as well as <strong>inline markup</strong>.</p>
<h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div>
<a class="readmore" href="/oh-yeah.html">read more</a>

View file

@ -42,8 +42,8 @@
<div class="section" id="this-is-a-simple-title">
<h2>This is a simple title</h2>
<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<pre class="literal-block">
&gt;&gt;&gt; from ipdb import set_trace
&gt;&gt;&gt; set_trace()

View file

@ -38,7 +38,7 @@
<h2>Why not ?</h2>
<p>After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
</div>
</article>
</aside><!-- /#featured -->

View file

@ -47,8 +47,8 @@
<div class="section" id="this-is-a-simple-title">
<h2>This is a simple title</h2>
<p>And here comes the cool <a class="reference external" href="http://books.couchdb.org/relax/design-documents/views">stuff</a>.</p>
<img alt="alternate text" src="|filename|/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="|filename|/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi.jpg" style="width: 600px; height: 450px;" />
<img alt="alternate text" src="/pictures/Sushi_Macro.jpg" style="width: 600px; height: 450px;" />
<pre class="literal-block">
&gt;&gt;&gt; from ipdb import set_trace
&gt;&gt;&gt; set_trace()

View file

@ -175,6 +175,15 @@ def get_settings(**kwargs):
return settings
def get_context(settings=None, **kwargs):
context = settings.copy() if settings else {}
context['generated_content'] = {}
context['static_links'] = set()
context['static_content'] = {}
context.update(kwargs)
return context
class LogCountHandler(BufferingHandler):
"""Capturing and counting logged messages."""

View file

@ -6,7 +6,7 @@ from shutil import rmtree
from tempfile import mkdtemp
from pelican.generators import ArticlesGenerator, PagesGenerator
from pelican.tests.support import get_settings, unittest
from pelican.tests.support import get_context, get_settings, unittest
try:
from unittest.mock import MagicMock
@ -29,7 +29,7 @@ class TestCache(unittest.TestCase):
rmtree(self.temp_cache)
def _get_cache_enabled_settings(self):
settings = get_settings(filenames={})
settings = get_settings()
settings['CACHE_CONTENT'] = True
settings['LOAD_CONTENT_CACHE'] = True
settings['CACHE_PATH'] = self.temp_cache
@ -42,20 +42,21 @@ class TestCache(unittest.TestCase):
settings['PAGE_PATHS'] = ['TestPages']
settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['READERS'] = {'asc': None}
context = get_context(settings)
def sorted_titles(items):
return sorted(item.title for item in items)
# Articles
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
uncached_articles = sorted_titles(generator.articles)
uncached_drafts = sorted_titles(generator.drafts)
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
cached_articles = sorted_titles(generator.articles)
@ -66,7 +67,7 @@ class TestCache(unittest.TestCase):
# Pages
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
uncached_pages = sorted_titles(generator.pages)
@ -74,7 +75,7 @@ class TestCache(unittest.TestCase):
uncached_draft_pages = sorted_titles(generator.draft_pages)
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
cached_pages = sorted_titles(generator.pages)
@ -92,20 +93,21 @@ class TestCache(unittest.TestCase):
settings['PAGE_PATHS'] = ['TestPages']
settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['READERS'] = {'asc': None}
context = get_context(settings)
def sorted_titles(items):
return sorted(item.title for item in items)
# Articles
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
uncached_articles = sorted_titles(generator.articles)
uncached_drafts = sorted_titles(generator.drafts)
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
cached_articles = sorted_titles(generator.articles)
@ -116,14 +118,14 @@ class TestCache(unittest.TestCase):
# Pages
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
uncached_pages = sorted_titles(generator.pages)
uncached_hidden_pages = sorted_titles(generator.hidden_pages)
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
cached_pages = sorted_titles(generator.pages)
@ -139,15 +141,16 @@ class TestCache(unittest.TestCase):
settings['CONTENT_CACHING_LAYER'] = 'generator'
settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
self.assertTrue(hasattr(generator, '_cache'))
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock()
generator.generate_context()
@ -167,15 +170,16 @@ class TestCache(unittest.TestCase):
"""Test raw article content caching at the reader level"""
settings = self._get_cache_enabled_settings()
settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
self.assertTrue(hasattr(generator.readers, '_cache'))
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
readers = generator.readers.readers
for reader in readers.values():
@ -191,9 +195,10 @@ class TestCache(unittest.TestCase):
used in --ignore-cache or autoreload mode"""
settings = self._get_cache_enabled_settings()
settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock()
generator.generate_context()
@ -202,7 +207,7 @@ class TestCache(unittest.TestCase):
settings['LOAD_CONTENT_CACHE'] = False
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock()
generator.generate_context()
@ -217,15 +222,16 @@ class TestCache(unittest.TestCase):
settings['CONTENT_CACHING_LAYER'] = 'generator'
settings['PAGE_PATHS'] = ['TestPages']
settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
self.assertTrue(hasattr(generator, '_cache'))
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock()
generator.generate_context()
@ -241,15 +247,16 @@ class TestCache(unittest.TestCase):
settings = self._get_cache_enabled_settings()
settings['PAGE_PATHS'] = ['TestPages']
settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
self.assertTrue(hasattr(generator.readers, '_cache'))
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
readers = generator.readers.readers
for reader in readers.values():
@ -266,9 +273,10 @@ class TestCache(unittest.TestCase):
settings = self._get_cache_enabled_settings()
settings['PAGE_PATHS'] = ['TestPages']
settings['READERS'] = {'asc': None}
context = get_context(settings)
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock()
generator.generate_context()
@ -277,7 +285,7 @@ class TestCache(unittest.TestCase):
settings['LOAD_CONTENT_CACHE'] = False
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.readers.read_file = MagicMock()
generator.generate_context()

View file

@ -14,7 +14,8 @@ import six
from pelican.contents import Article, Author, Category, Page, Static, Tag
from pelican.settings import DEFAULT_CONFIG
from pelican.signals import content_object_init
from pelican.tests.support import LoggedTestCase, get_settings, unittest
from pelican.tests.support import LoggedTestCase, get_context, get_settings,\
unittest
from pelican.utils import SafeDatetime, path_to_url, truncate_html_words
@ -264,7 +265,7 @@ class TestPage(LoggedTestCase):
args = self.page_kwargs.copy()
args['settings'] = get_settings()
args['source_path'] = 'content'
args['context']['filenames'] = {'article.rst': article}
args['context']['generated_content'] = {'article.rst': article}
# Classic intrasite link via filename
args['content'] = (
@ -343,22 +344,24 @@ class TestPage(LoggedTestCase):
args = self.page_kwargs.copy()
args['settings'] = get_settings()
args['source_path'] = 'content'
args['context']['filenames'] = {
'images/poster.jpg': type(
cls_name, (object,), {'url': 'images/poster.jpg'}),
'assets/video.mp4': type(
cls_name, (object,), {'url': 'assets/video.mp4'}),
'images/graph.svg': type(
cls_name, (object,), {'url': 'images/graph.svg'}),
'reference.rst': type(
cls_name, (object,), {'url': 'reference.html'}),
args['context']['static_content'] = {
'images/poster.jpg':
type(cls_name, (object,), {'url': 'images/poster.jpg'}),
'assets/video.mp4':
type(cls_name, (object,), {'url': 'assets/video.mp4'}),
'images/graph.svg':
type(cls_name, (object,), {'url': 'images/graph.svg'}),
}
args['context']['generated_content'] = {
'reference.rst':
type(cls_name, (object,), {'url': 'reference.html'}),
}
# video.poster
args['content'] = (
'There is a video with poster '
'<video controls poster="{filename}/images/poster.jpg">'
'<source src="|filename|/assets/video.mp4" type="video/mp4">'
'<video controls poster="{static}/images/poster.jpg">'
'<source src="|static|/assets/video.mp4" type="video/mp4">'
'</video>'
)
content = Page(**args).get_content('http://notmyidea.org')
@ -374,7 +377,7 @@ class TestPage(LoggedTestCase):
# object.data
args['content'] = (
'There is a svg object '
'<object data="{filename}/images/graph.svg"'
'<object data="{static}/images/graph.svg"'
' type="image/svg+xml">'
'</object>'
)
@ -409,14 +412,15 @@ class TestPage(LoggedTestCase):
STATIC_URL='http://static.cool.site/{path}',
ARTICLE_URL='http://blog.cool.site/{slug}.html')
args['source_path'] = 'content'
args['context']['filenames'] = {
'images/poster.jpg': Static('',
settings=args['settings'],
source_path='images/poster.jpg'),
'article.rst': Article('',
settings=args['settings'],
metadata={'slug': 'article',
'title': 'Article'})
args['context']['static_content'] = {
'images/poster.jpg':
Static('', settings=args['settings'],
source_path='images/poster.jpg'),
}
args['context']['generated_content'] = {
'article.rst':
Article('', settings=args['settings'], metadata={
'slug': 'article', 'title': 'Article'})
}
# Article link will go to blog
@ -441,7 +445,7 @@ class TestPage(LoggedTestCase):
# Image link will go to static
args['content'] = (
'<img src="{filename}/images/poster.jpg"/>'
'<img src="{static}/images/poster.jpg"/>'
)
content = Page(**args).get_content('http://cool.site')
self.assertEqual(
@ -458,7 +462,7 @@ class TestPage(LoggedTestCase):
args = self.page_kwargs.copy()
args['settings'] = get_settings()
args['source_path'] = 'content'
args['context']['filenames'] = {'article spaces.rst': article}
args['context']['generated_content'] = {'article spaces.rst': article}
# An intrasite link via filename with %20 as a space
args['content'] = (
@ -472,6 +476,55 @@ class TestPage(LoggedTestCase):
'<a href="http://notmyidea.org/article-spaces.html">link</a>'
)
def test_intrasite_link_source_and_generated(self):
"""Test linking both to the source and the generated article
"""
cls_name = '_DummyAsset' if six.PY3 else b'_DummyAsset'
args = self.page_kwargs.copy()
args['settings'] = get_settings()
args['source_path'] = 'content'
args['context']['generated_content'] = {
'article.rst': type(cls_name, (object,), {'url': 'article.html'})}
args['context']['static_content'] = {
'article.rst': type(cls_name, (object,), {'url': 'article.rst'})}
args['content'] = (
'A simple test, with a link to an'
'<a href="{filename}article.rst">article</a> and its'
'<a href="{static}article.rst">source</a>'
)
content = Page(**args).get_content('http://notmyidea.org')
self.assertEqual(
content,
'A simple test, with a link to an'
'<a href="http://notmyidea.org/article.html">article</a> and its'
'<a href="http://notmyidea.org/article.rst">source</a>'
)
def test_intrasite_link_to_static_content_with_filename(self):
"""Test linking to a static resource with deprecated {filename}
"""
cls_name = '_DummyAsset' if six.PY3 else b'_DummyAsset'
args = self.page_kwargs.copy()
args['settings'] = get_settings()
args['source_path'] = 'content'
args['context']['static_content'] = {
'poster.jpg':
type(cls_name, (object,), {'url': 'images/poster.jpg'})}
args['content'] = (
'A simple test, with a link to a'
'<a href="{filename}poster.jpg">poster</a>'
)
content = Page(**args).get_content('http://notmyidea.org')
self.assertEqual(
content,
'A simple test, with a link to a'
'<a href="http://notmyidea.org/images/poster.jpg">poster</a>'
)
def test_multiple_authors(self):
"""Test article with multiple authors."""
args = self.page_kwargs.copy()
@ -599,13 +652,13 @@ class TestStatic(LoggedTestCase):
STATIC_URL='{path}',
PAGE_SAVE_AS=os.path.join('outpages', '{slug}.html'),
PAGE_URL='outpages/{slug}.html')
self.context = self.settings.copy()
self.context = get_context(self.settings)
self.static = Static(content=None, metadata={}, settings=self.settings,
source_path=posix_join('dir', 'foo.jpg'),
context=self.context)
self.context['filenames'] = {self.static.source_path: self.static}
self.context['static_content'][self.static.source_path] = self.static
def tearDown(self):
pass
@ -674,7 +727,7 @@ class TestStatic(LoggedTestCase):
def test_attach_to_does_nothing_after_save_as_referenced(self):
"""attach_to() does nothing if the save_as was already referenced.
(For example, by a {filename} link an a document processed earlier.)
(For example, by a {static} link an a document processed earlier.)
"""
original_save_as = self.static.save_as
@ -690,7 +743,7 @@ class TestStatic(LoggedTestCase):
def test_attach_to_does_nothing_after_url_referenced(self):
"""attach_to() does nothing if the url was already referenced.
(For example, by a {filename} link an a document processed earlier.)
(For example, by a {static} link an a document processed earlier.)
"""
original_url = self.static.url

View file

@ -10,7 +10,7 @@ from tempfile import mkdtemp
from pelican.generators import (ArticlesGenerator, Generator, PagesGenerator,
PelicanTemplateNotFound, StaticGenerator,
TemplatePagesGenerator)
from pelican.tests.support import get_settings, unittest
from pelican.tests.support import get_context, get_settings, unittest
from pelican.writers import Writer
try:
@ -178,14 +178,15 @@ class TestArticlesGenerator(unittest.TestCase):
@classmethod
def setUpClass(cls):
settings = get_settings(filenames={})
settings = get_settings()
settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['READERS'] = {'asc': None}
settings['CACHE_CONTENT'] = False
context = get_context(settings)
cls.generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
cls.generator.generate_context()
cls.articles = cls.distill_articles(cls.generator.articles)
@ -299,15 +300,15 @@ class TestArticlesGenerator(unittest.TestCase):
self.assertEqual(sorted(categories), sorted(categories_expected))
def test_do_not_use_folder_as_category(self):
settings = get_settings(filenames={})
settings = get_settings()
settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['USE_FOLDER_AS_CATEGORY'] = False
settings['CACHE_PATH'] = self.temp_cache
settings['READERS'] = {'asc': None}
settings['filenames'] = {}
context = get_context(settings)
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
# test for name
@ -328,15 +329,16 @@ class TestArticlesGenerator(unittest.TestCase):
@unittest.skipUnless(MagicMock, 'Needs Mock module')
def test_direct_templates_save_as_url_default(self):
settings = get_settings(filenames={})
settings = get_settings()
settings['CACHE_PATH'] = self.temp_cache
context = get_context(settings)
generator = ArticlesGenerator(
context=settings, settings=settings,
context=context, settings=settings,
path=None, theme=settings['THEME'], output_path=None)
write = MagicMock()
generator.generate_direct_templates(write)
write.assert_called_with("archives.html",
generator.get_template("archives"), settings,
generator.get_template("archives"), context,
articles=generator.articles,
dates=generator.dates, blog=True,
template_name='archives',
@ -396,13 +398,14 @@ class TestArticlesGenerator(unittest.TestCase):
"""
old_locale = locale.setlocale(locale.LC_ALL)
locale.setlocale(locale.LC_ALL, str('C'))
settings = get_settings(filenames={})
settings = get_settings()
settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html'
settings['YEAR_ARCHIVE_URL'] = 'posts/{date:%Y}/'
settings['CACHE_PATH'] = self.temp_cache
context = get_context(settings)
generator = ArticlesGenerator(
context=settings, settings=settings,
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
write = MagicMock()
@ -411,20 +414,20 @@ class TestArticlesGenerator(unittest.TestCase):
articles = [d for d in generator.articles if d.date.year == 1970]
self.assertEqual(len(dates), 1)
# among other things it must have at least been called with this
settings["period"] = (1970,)
context["period"] = (1970,)
write.assert_called_with("posts/1970/index.html",
generator.get_template("period_archives"),
settings, blog=True, articles=articles,
context, blog=True, articles=articles,
dates=dates, template_name='period_archives',
url="posts/1970/")
del settings["period"]
settings['MONTH_ARCHIVE_SAVE_AS'] = \
'posts/{date:%Y}/{date:%b}/index.html'
settings['MONTH_ARCHIVE_URL'] = \
'posts/{date:%Y}/{date:%b}/'
context = get_context(settings)
generator = ArticlesGenerator(
context=settings, settings=settings,
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
write = MagicMock()
@ -434,21 +437,21 @@ class TestArticlesGenerator(unittest.TestCase):
articles = [d for d in generator.articles
if d.date.year == 1970 and d.date.month == 1]
self.assertEqual(len(dates), 1)
settings["period"] = (1970, "January")
context["period"] = (1970, "January")
# among other things it must have at least been called with this
write.assert_called_with("posts/1970/Jan/index.html",
generator.get_template("period_archives"),
settings, blog=True, articles=articles,
context, blog=True, articles=articles,
dates=dates, template_name='period_archives',
url="posts/1970/Jan/")
del settings["period"]
settings['DAY_ARCHIVE_SAVE_AS'] = \
'posts/{date:%Y}/{date:%b}/{date:%d}/index.html'
settings['DAY_ARCHIVE_URL'] = \
'posts/{date:%Y}/{date:%b}/{date:%d}/'
context = get_context(settings)
generator = ArticlesGenerator(
context=settings, settings=settings,
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
write = MagicMock()
@ -466,20 +469,21 @@ class TestArticlesGenerator(unittest.TestCase):
d.date.day == 1
]
self.assertEqual(len(dates), 1)
settings["period"] = (1970, "January", 1)
context["period"] = (1970, "January", 1)
# among other things it must have at least been called with this
write.assert_called_with("posts/1970/Jan/01/index.html",
generator.get_template("period_archives"),
settings, blog=True, articles=articles,
context, blog=True, articles=articles,
dates=dates, template_name='period_archives',
url="posts/1970/Jan/01/")
locale.setlocale(locale.LC_ALL, old_locale)
def test_nonexistent_template(self):
"""Attempt to load a non-existent template"""
settings = get_settings(filenames={})
settings = get_settings()
context = get_context(settings)
generator = ArticlesGenerator(
context=settings, settings=settings,
context=context, settings=settings,
path=None, theme=settings['THEME'], output_path=None)
self.assertRaises(Exception, generator.get_template, "not_a_template")
@ -497,7 +501,7 @@ class TestArticlesGenerator(unittest.TestCase):
self.assertEqual(sorted(authors), sorted(authors_expected))
def test_standard_metadata_in_default_metadata(self):
settings = get_settings(filenames={})
settings = get_settings()
settings['CACHE_CONTENT'] = False
settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1)
@ -506,8 +510,9 @@ class TestArticlesGenerator(unittest.TestCase):
# DEFAULT_CATEGORY
('category', 'Random'),
('tags', 'general, untagged'))
context = get_context(settings)
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
@ -530,13 +535,14 @@ class TestArticlesGenerator(unittest.TestCase):
self.assertEqual(tags, tags_expected)
def test_article_order_by(self):
settings = get_settings(filenames={})
settings = get_settings()
settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['ARTICLE_ORDER_BY'] = 'title'
context = get_context(settings)
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
@ -575,13 +581,14 @@ class TestArticlesGenerator(unittest.TestCase):
self.assertEqual(articles, expected)
# reversed title
settings = get_settings(filenames={})
settings = get_settings()
settings['DEFAULT_CATEGORY'] = 'Default'
settings['DEFAULT_DATE'] = (1970, 1, 1)
settings['ARTICLE_ORDER_BY'] = 'reversed-title'
context = get_context(settings)
generator = ArticlesGenerator(
context=settings.copy(), settings=settings,
context=context, settings=settings,
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
@ -605,13 +612,14 @@ class TestPageGenerator(unittest.TestCase):
return [[page.title, page.status, page.template] for page in pages]
def test_generate_context(self):
settings = get_settings(filenames={})
settings = get_settings()
settings['CACHE_PATH'] = self.temp_cache
settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
settings['DEFAULT_DATE'] = (1970, 1, 1)
context = get_context(settings)
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context, settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
pages = self.distill_pages(generator.pages)
@ -624,6 +632,7 @@ class TestPageGenerator(unittest.TestCase):
['This is a test page with a preset template', 'published',
'custom'],
['Page with a bunch of links', 'published', 'page'],
['Page with static links', 'published', 'page'],
['A Page (Test) for sorting', 'published', 'page'],
]
hidden_pages_expected = [
@ -653,10 +662,11 @@ class TestPageGenerator(unittest.TestCase):
sorted(self.distill_pages(generator.context['draft_pages'])))
def test_generate_sorted(self):
settings = get_settings(filenames={})
settings = get_settings()
settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
settings['CACHE_PATH'] = self.temp_cache
settings['DEFAULT_DATE'] = (1970, 1, 1)
context = get_context(settings)
# default sort (filename)
pages_expected_sorted_by_filename = [
@ -664,11 +674,12 @@ class TestPageGenerator(unittest.TestCase):
['This is a markdown test page', 'published', 'page'],
['A Page (Test) for sorting', 'published', 'page'],
['Page with a bunch of links', 'published', 'page'],
['Page with static links', 'published', 'page'],
['This is a test page with a preset template', 'published',
'custom'],
]
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context, settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
pages = self.distill_pages(generator.pages)
@ -678,14 +689,16 @@ class TestPageGenerator(unittest.TestCase):
pages_expected_sorted_by_title = [
['A Page (Test) for sorting', 'published', 'page'],
['Page with a bunch of links', 'published', 'page'],
['Page with static links', 'published', 'page'],
['This is a markdown test page', 'published', 'page'],
['This is a test page', 'published', 'page'],
['This is a test page with a preset template', 'published',
'custom'],
]
settings['PAGE_ORDER_BY'] = 'title'
context = get_context(settings)
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context.copy(), settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
pages = self.distill_pages(generator.pages)
@ -697,12 +710,14 @@ class TestPageGenerator(unittest.TestCase):
'custom'],
['This is a test page', 'published', 'page'],
['This is a markdown test page', 'published', 'page'],
['Page with static links', 'published', 'page'],
['Page with a bunch of links', 'published', 'page'],
['A Page (Test) for sorting', 'published', 'page'],
]
settings['PAGE_ORDER_BY'] = 'reversed-title'
context = get_context(settings)
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context, settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
pages = self.distill_pages(generator.pages)
@ -713,21 +728,43 @@ class TestPageGenerator(unittest.TestCase):
Test to ensure links of the form {tag}tagname and {category}catname
are generated correctly on pages
"""
settings = get_settings(filenames={})
settings = get_settings()
settings['PAGE_PATHS'] = ['TestPages'] # relative to CUR_DIR
settings['CACHE_PATH'] = self.temp_cache
settings['DEFAULT_DATE'] = (1970, 1, 1)
context = get_context(settings)
generator = PagesGenerator(
context=settings.copy(), settings=settings,
context=context, settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
pages_by_title = {p.title: p.content for p in generator.pages}
pages_by_title = {p.title: p for p in generator.pages}
test_content = pages_by_title['Page with a bunch of links']
test_content = pages_by_title['Page with a bunch of links'].content
self.assertIn('<a href="/category/yeah.html">', test_content)
self.assertIn('<a href="/tag/matsuku.html">', test_content)
def test_static_and_attach_links_on_generated_pages(self):
"""
Test to ensure links of the form {static}filename and {attach}filename
are included in context['static_links']
"""
settings = get_settings()
settings['PAGE_PATHS'] = ['TestPages/page_with_static_links.md']
settings['CACHE_PATH'] = self.temp_cache
settings['DEFAULT_DATE'] = (1970, 1, 1)
context = get_context(settings)
generator = PagesGenerator(
context=context, settings=settings,
path=CUR_DIR, theme=settings['THEME'], output_path=None)
generator.generate_context()
self.assertIn('pelican/tests/TestPages/image0.jpg',
context['static_links'])
self.assertIn('pelican/tests/TestPages/image1.jpg',
context['static_links'])
class TestTemplatePagesGenerator(unittest.TestCase):
@ -791,7 +828,7 @@ class TestStaticGenerator(unittest.TestCase):
"static", "staticfile")
self.endfile = os.path.join(self.temp_output, "static", "staticfile")
self.generator = StaticGenerator(
context={'filenames': {}},
context=get_context(),
settings=self.settings,
path=self.temp_content,
theme="",
@ -808,11 +845,8 @@ class TestStaticGenerator(unittest.TestCase):
def test_theme_static_paths_dirs(self):
"""Test that StaticGenerator properly copies also files mentioned in
TEMPLATE_STATIC_PATHS, not just directories."""
settings = get_settings(
PATH=self.content_path,
filenames={})
context = settings.copy()
context['staticfiles'] = []
settings = get_settings(PATH=self.content_path)
context = get_context(settings, staticfiles=[])
StaticGenerator(
context=context, settings=settings,
@ -831,10 +865,8 @@ class TestStaticGenerator(unittest.TestCase):
TEMPLATE_STATIC_PATHS, not just directories."""
settings = get_settings(
PATH=self.content_path,
THEME_STATIC_PATHS=['static/css/fonts.css', 'static/fonts/'],
filenames={})
context = settings.copy()
context['staticfiles'] = []
THEME_STATIC_PATHS=['static/css/fonts.css', 'static/fonts/'],)
context = get_context(settings, staticfiles=[])
StaticGenerator(
context=context, settings=settings,
@ -869,9 +901,8 @@ class TestStaticGenerator(unittest.TestCase):
settings = get_settings(
STATIC_EXCLUDES=['subdir'],
PATH=self.content_path,
STATIC_PATHS=[''],
filenames={})
context = settings.copy()
STATIC_PATHS=[''],)
context = get_context(settings)
StaticGenerator(
context=context, settings=settings,
@ -897,9 +928,8 @@ class TestStaticGenerator(unittest.TestCase):
PATH=self.content_path,
PAGE_PATHS=[''],
STATIC_PATHS=[''],
CACHE_CONTENT=False,
filenames={})
context = settings.copy()
CACHE_CONTENT=False,)
context = get_context(settings)
for generator_class in (PagesGenerator, StaticGenerator):
generator_class(
@ -915,8 +945,7 @@ class TestStaticGenerator(unittest.TestCase):
"STATIC_EXCLUDE_SOURCES=True failed to exclude a markdown file")
settings.update(STATIC_EXCLUDE_SOURCES=False)
context = settings.copy()
context['filenames'] = {}
context = get_context(settings)
for generator_class in (PagesGenerator, StaticGenerator):
generator_class(
@ -931,6 +960,40 @@ class TestStaticGenerator(unittest.TestCase):
any(name.endswith(".md") for name in staticnames),
"STATIC_EXCLUDE_SOURCES=False failed to include a markdown file")
def test_static_links(self):
"""Test that StaticGenerator uses files in static_links
"""
settings = get_settings(
STATIC_EXCLUDES=['subdir'],
PATH=self.content_path,
STATIC_PATHS=[],)
context = get_context(settings)
context['static_links'] |= {'short_page.md', 'subdir_fake_image.jpg'}
StaticGenerator(
context=context, settings=settings,
path=settings['PATH'], output_path=self.temp_output,
theme=settings['THEME']).generate_context()
staticfiles_names = [
os.path.basename(c.source_path) for c in context['staticfiles']]
static_content_names = [
os.path.basename(c) for c in context['static_content']]
self.assertIn(
'short_page.md', staticfiles_names,
"StaticGenerator skipped a file that it should have included")
self.assertIn(
'short_page.md', static_content_names,
"StaticGenerator skipped a file that it should have included")
self.assertIn(
'subdir_fake_image.jpg', staticfiles_names,
"StaticGenerator skipped a file that it should have included")
self.assertIn(
'subdir_fake_image.jpg', static_content_names,
"StaticGenerator skipped a file that it should have included")
def test_copy_one_file(self):
with open(self.startfile, "w") as f:
f.write("staticcontent")

View file

@ -112,7 +112,7 @@ class TestPelican(LoggedTestCase):
self.assertDirsEqual(
self.temp_path, os.path.join(OUTPUT_PATH, 'basic'))
self.assertLogCountEqual(
count=3,
count=1,
msg="Unable to find.*skipping url replacement",
level=logging.WARNING)

View file

@ -761,7 +761,7 @@ def update_links_to_attached_files(content, attachments):
http_url = old_url.replace('https://', 'http://')
https_url = old_url.replace('http://', 'https://')
for url in [http_url, https_url]:
content = content.replace(url, '{filename}' + new_path)
content = content.replace(url, '{static}' + new_path)
return content

View file

@ -435,7 +435,7 @@ def path_to_url(path):
def posixize_path(rel_path):
"""Use '/' as path separator, so that source references,
like '{filename}/foo/bar.jpg' or 'extras/favicon.ico',
like '{static}/foo/bar.jpg' or 'extras/favicon.ico',
will work on Windows as well as on Mac and Linux."""
return rel_path.replace(os.sep, '/')

View file

@ -14,7 +14,7 @@ Why not ?
After all, why not ? It's pretty simple to do it, and it will allow me to write my blogposts in rst !
YEAH !
.. image:: |filename|/pictures/Sushi.jpg
.. image:: |static|/pictures/Sushi.jpg
:height: 450 px
:width: 600 px
:alt: alternate text

View file

@ -17,12 +17,12 @@ This is a simple title
And here comes the cool stuff_.
.. image:: |filename|/pictures/Sushi.jpg
.. image:: |static|/pictures/Sushi.jpg
:height: 450 px
:width: 600 px
:alt: alternate text
.. image:: |filename|/pictures/Sushi_Macro.jpg
.. image:: |static|/pictures/Sushi_Macro.jpg
:height: 450 px
:width: 600 px
:alt: alternate text