diff --git a/pelican/contents.py b/pelican/contents.py index e0bf8adf..185d80c4 100644 --- a/pelican/contents.py +++ b/pelican/contents.py @@ -85,8 +85,7 @@ class Content(object): if not hasattr(self, 'slug') and hasattr(self, 'title'): self.slug = slugify(self.title) - if source_path: - self.source_path = source_path + self.source_path = source_path # manage the date format if not hasattr(self, 'date_format'): @@ -119,6 +118,14 @@ class Content(object): signals.content_object_init.send(self) + def __str__(self): + if self.source_path is None: + return repr(self) + elif six.PY3: + return self.source_path or repr(self) + else: + return str(self.source_path.encode('utf-8', 'replace')) + def check_properties(self): """Test mandatory properties are set.""" for prop in self.mandatory_properties: @@ -130,6 +137,7 @@ class Content(object): """Returns the URL, formatted with the proper values""" metadata = copy.copy(self.metadata) metadata.update({ + 'path': self.metadata.get('path', self.get_relative_source_path()), 'slug': getattr(self, 'slug', ''), 'lang': getattr(self, 'lang', 'en'), 'date': getattr(self, 'date', datetime.now()), @@ -250,6 +258,8 @@ class Content(object): """ if not source_path: source_path = self.source_path + if source_path is None: + return None return os.path.relpath( os.path.abspath(os.path.join(self.settings['PATH'], source_path)), @@ -279,26 +289,88 @@ class Quote(Page): @python_2_unicode_compatible -class StaticContent(object): +@functools.total_ordering +class URLWrapper(object): + def __init__(self, name, settings): + self.name = name + self.slug = slugify(self.name) + self.settings = settings + def as_dict(self): + return self.__dict__ + + def __hash__(self): + return hash(self.name) + + def _key(self): + return self.name + + def _normalize_key(self, key): + return six.text_type(key) + + def __eq__(self, other): + return self._key() == self._normalize_key(other) + + def __ne__(self, other): + return self._key() != self._normalize_key(other) + + def __lt__(self, other): + return self._key() < self._normalize_key(other) + + def __str__(self): + return self.name + + def _from_settings(self, key, get_page_name=False): + """Returns URL information as defined in settings. + + When get_page_name=True returns URL without anything after {slug} e.g. + if in settings: CATEGORY_URL="cat/{slug}.html" this returns + "cat/{slug}" Useful for pagination. + + """ + setting = "%s_%s" % (self.__class__.__name__.upper(), key) + value = self.settings[setting] + if not isinstance(value, six.string_types): + logger.warning('%s is set to %s' % (setting, value)) + return value + else: + if get_page_name: + return os.path.splitext(value)[0].format(**self.as_dict()) + else: + return value.format(**self.as_dict()) + + page_name = property(functools.partial(_from_settings, key='URL', + get_page_name=True)) + url = property(functools.partial(_from_settings, key='URL')) + save_as = property(functools.partial(_from_settings, key='SAVE_AS')) + + +class Category(URLWrapper): + pass + + +class Tag(URLWrapper): + def __init__(self, name, *args, **kwargs): + super(Tag, self).__init__(name.strip(), *args, **kwargs) + + +class Author(URLWrapper): + pass + + +@python_2_unicode_compatible +class Static(Page): @deprecated_attribute(old='filepath', new='source_path', since=(3, 2, 0)) def filepath(): return None - def __init__(self, src, dst=None, settings=None): - if not settings: - settings = copy.deepcopy(_DEFAULT_CONFIG) - self.src = src - self.url = dst or src + @deprecated_attribute(old='src', new='source_path', since=(3, 2, 0)) + def src(): + return None - # On Windows, make sure we end up with Unix-like paths. - if os.name == 'nt': - self.url = self.url.replace('\\', '/') - self.source_path = os.path.join(settings['PATH'], src) - self.save_as = os.path.join(settings['OUTPUT_PATH'], self.url) - - def __str__(self): - return self.source_path + @deprecated_attribute(old='dst', new='save_as', since=(3, 2, 0)) + def dst(): + return None def is_valid_content(content, f): diff --git a/pelican/generators.py b/pelican/generators.py index d09bf676..d39db188 100644 --- a/pelican/generators.py +++ b/pelican/generators.py @@ -20,7 +20,7 @@ from jinja2 import ( ) from pelican.contents import ( - Article, Page, Category, StaticContent, is_valid_content + Article, Page, Category, Static, is_valid_content ) from pelican.readers import read_file from pelican.utils import copy, process_translations, mkdir_p @@ -122,8 +122,7 @@ class Generator(object): return files def add_source_path(self, content): - location = os.path.relpath(os.path.abspath(content.source_path), - os.path.abspath(self.path)) + location = content.get_relative_source_path() self.context['filenames'][location] = content def _update_context(self, items): @@ -523,23 +522,32 @@ class StaticGenerator(Generator): if os.name == 'nt': f_rel = f_rel.replace('\\', '/') # TODO remove this hardcoded 'static' subdirectory - sc = StaticContent(f_rel, os.path.join('static', f_rel), - settings=self.settings) + sc = Static( + content=None, + metadata={'save_as': os.path.join('static', f_rel)}, + settings=self.settings, + source_path=f_rel) self.staticfiles.append(sc) - self.context['filenames'][f_rel] = sc + self.add_source_path(sc) # same thing for FILES_TO_COPY for src, dest in self.settings['FILES_TO_COPY']: - sc = StaticContent(src, dest, settings=self.settings) + sc = Static( + content=None, + metadata={'save_as': dest}, + settings=self.settings, + source_path=src) self.staticfiles.append(sc) - self.context['filenames'][src] = sc + self.add_source_path(sc) def generate_output(self, writer): self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme, 'theme', self.output_path, '.') - # copy all StaticContent files + # copy all Static files for sc in self.staticfiles: - mkdir_p(os.path.dirname(sc.save_as)) - shutil.copy(sc.source_path, sc.save_as) + source_path = os.path.join(self.path, sc.source_path) + save_as = os.path.join(self.output_path, sc.save_as) + mkdir_p(os.path.dirname(save_as)) + shutil.copy(source_path, save_as) logger.info('copying {} to {}'.format(sc.source_path, sc.save_as)) diff --git a/pelican/settings.py b/pelican/settings.py index 914f963b..856c5969 100644 --- a/pelican/settings.py +++ b/pelican/settings.py @@ -52,6 +52,8 @@ _DEFAULT_CONFIG = {'PATH': '.', 'PAGE_SAVE_AS': 'pages/{slug}.html', 'PAGE_LANG_URL': 'pages/{slug}-{lang}.html', 'PAGE_LANG_SAVE_AS': 'pages/{slug}-{lang}.html', + 'STATIC_URL': '{path}', + 'STATIC_SAVE_AS': '{path}', 'CATEGORY_URL': 'category/{slug}.html', 'CATEGORY_SAVE_AS': 'category/{slug}.html', 'TAG_URL': 'tag/{slug}.html',