def _parse_item(self, configuration, f, simple=False): Timer.start() item = Item(f.path) try: matches = re.search(r'\A---\s+^(.+?)$\s+---\s*(.*)\Z', f.content, re.M | re.S) frontmatter, bodymatter = matches.groups() frontmatter = Configuration(frontmatter) except AttributeError: raise ContentException('Invalid frontmatter', 'src: {0}'.format(f.path), 'frontmatter must not be empty') except ConfigurationException: raise ConfigurationException('Invalid frontmatter', 'src: {0}'.format(f.path), 'fontmatter contains invalid YAML') if 'layout' not in frontmatter: raise ContentException('Invalid frontmatter', 'src: {0}'.format(f.path), 'layout must be set') frontmatter.pop('url', None) parser = configuration.get('parser', None) parser = frontmatter.get('parser', parser) parser = self._get_parser(f, parser) text, date = self._parse_filename(f) content = self._writer.from_string(bodymatter, frontmatter) content = parser.parse(content) item['content'] = content item['date'] = date.strftime(self.site['date_format']) item['timestamp'] = timegm(date.utctimetuple()) if simple: url = f.root.path.replace(self.source.path, '') item['url'] = URL.from_path(url, text) else: excerpt = re.search(r'\A.*?(?:<p>(.+?)</p>)?', content, re.M | re.S).group(1) url = URL.from_format(configuration['url'], text, date, frontmatter) item['excerpt'] = excerpt item['tags'] = [] item['url'] = url item.update(frontmatter) logger.debug('.. (%.3fs) %s', Timer.stop(), f.path.replace(self.source.path, '')) return item
def _get_url(self, url='', absolute=False): parts = [self.globals['site']['base_url'], url] domain = self.globals['site']['domain'] if absolute and domain: parts.insert(0, domain) return URL.join(*parts)
def _archive(self, items, archive): for item in items: timestamp = datetime.utcfromtimestamp(item['timestamp']) year, month = timestamp.strftime('%Y %B').split() if year not in archive: months = OrderedDict({month: [item]}) url = URL.from_format(self.configuration['archives_url'], year) archive[year] = {'months': months, 'url': url, 'year': year} elif month not in archive[year]['months']: archive[year]['months'][month] = [item] else: archive[year]['months'][month].append(item)
def serve(self): self.source = Directory(self.options['source']) if not self.source.exists: raise OptionException('Source directory does not exist') logger.info('>> Serving at 127.0.0.1:%s', self.options['port']) logger.info('.. Press ctrl+c to stop') address = ('', self.options['port']) base_url = URL.join(self.options['base_url'], '') cwd = getcwd() chdir(self.source.path) try: self.server = Server(address, base_url, RequestHandler) self.server.serve_forever() except KeyboardInterrupt: self.server.shutdown() chdir(cwd) print('')
def tag(self): tags = [] for item in self.items: item['tags'].sort(key=str.lower) for tag in item['tags']: if tag not in self.tags: self.tags[tag] = [] self.tags[tag].append(item) for name, items in self.tags.items(): url = URL.from_format(self.configuration['tags_url'], name) tags.append(Tag(name, url, len(items), items, OrderedDict())) self._sort(tags, 'name') self._sort(tags, 'count', 'desc') self.tags.clear() for tag in tags: self.tags[tag.name] = tag
def _update_configuration(self): self.configuration = deepcopy(self.defaults) logger.info('>> Searching for configuration file') for configuration in product(('mynt', 'config'), ('.yml', '.yaml')): configuration = ''.join(configuration) configuration = File(normpath(self.source.path, configuration)) if not configuration.exists: continue logger.debug('.. found: %s', configuration.path) if configuration.name == 'config': logger.warn('@@ Deprecated configuration file found') logger.warn('.. rename config.yml to mynt.yml') break else: logger.debug('.. no configuration file found') return try: self.configuration.update(Configuration(configuration.content)) except ConfigurationException as error: raise ConfigurationException( error.message, 'source: {0}'.format(configuration.path)) domain = self.configuration['domain'] if domain and not domain.startswith(('https://', 'http://', '//')): logger.warn('@@ Configuration setting `domain` missing protocol') logger.warn('.. defaulting to `https`') self.configuration['domain'] = 'https://{0}'.format(domain) self.configuration['base_url'] = self.options.get('base_url') self.configuration['locale'] = self.options.get('locale') options = ('archives_url', 'assets_url', 'base_url', 'posts_url', 'tags_url') for option in options: url = URL.join(self.configuration[option], '') if re.search(r'(?:^\.{2}/|/\.{2}$|/\.{2}/)', url): raise ConfigurationException( 'Invalid configuration option', 'option: {0}'.format(self.configuration[option]), 'path traversal is not allowed') containers_source = normpath(self.source.path, '_containers') for name, options in self.configuration['containers'].items(): prefix = op.commonprefix( (containers_source, normpath(containers_source, name))) if prefix != containers_source: raise ConfigurationException( 'Invalid configuration option', 'setting: containers:{0}'.format(name), 'container name contains illegal characters') try: url = URL.join(options['url']) except KeyError: raise ConfigurationException( 'Invalid configuration option', 'setting: containers:{0}'.format(name), 'url must be set for all containers') if re.search(r'(?:^\.{2}/|/\.{2}$|/\.{2}/)', url): raise ConfigurationException( 'Invalid configuration option', 'setting: containers:{0}:url'.format(name), 'path traversal is not allowed') for name, value in self.container_defaults.items(): if name not in options: options[name] = value options['url'] = url for pattern in self.configuration['include']: prefix = op.commonprefix( (self.source.path, normpath(self.source.path, pattern))) if prefix != self.source.path: raise ConfigurationException('Invalid include path', 'path: {0}'.format(pattern), 'path traversal is not allowed')
def _get_asset(self, asset): assets_url = self.globals['site']['assets_url'] base_url = self.globals['site']['base_url'] return URL.join(base_url, assets_url, asset)