def _parse_item(self, config, f, simple = False): Timer.start() item = Item(f.path) try: frontmatter, bodymatter = re.search(r'\A---\s+^(.+?)$\s+---\s*(.*)\Z', f.content, re.M | re.S).groups() frontmatter = Config(frontmatter) except AttributeError: raise ContentException('Invalid frontmatter.', 'src: {0}'.format(f.path), 'frontmatter must not be empty') except ConfigException: raise ConfigException('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') parser = self._get_parser(f, frontmatter.get('parser', config.get('parser', None))) text, date = self._parse_filename(f) frontmatter.pop('url', None) frontmatter['slug'] = text result = parser.parse(self._writer.from_string(bodymatter, frontmatter)) content, toc = result if isinstance(result, tuple) else (result, None) item['content'] = content item['date'] = date.strftime(self.site['date_format']).decode('utf-8') item['timestamp'] = timegm(date.utctimetuple()) if toc is not None: item['toc'] = toc if simple: item['url'] = Url.from_path(f.root.path.replace(self.src.path, ''), text) else: item['excerpt'] = re.search(r'\A.*?(?:<p>(.+?)</p>)?', content, re.M | re.S).group(1) item['tags'] = [] item['url'] = Url.from_format(config['url'], text, date, frontmatter) item.update(frontmatter) logger.debug('.. (%.3fs) %s', Timer.stop(), f.path.replace(self.src.path, '')) return item
def tag(self): tags = [] for item in self.items: item['tags'].sort(key = unicode.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.iteritems(): tags.append(Tag( name, Url.from_format(self.config['tags_url'], name), 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 _parse_item(self, config, f, simple = False): Timer.start() item = Item(f.path) try: frontmatter, bodymatter = re.search(r'\A---\s+^(.+?)$\s+---\s*(.*)\Z', f.content, re.M | re.S).groups() frontmatter = Config(frontmatter) except AttributeError: raise ContentException('Invalid frontmatter.', 'src: {0}'.format(f.path), 'frontmatter must not be empty') except ConfigException: raise ConfigException('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 = self._get_parser(f, frontmatter.get('parser', config.get('parser', None))) text, date = self._parse_filename(f) content = parser.parse(self._writer.from_string(bodymatter, frontmatter)) item['content'] = content item['date'] = date.strftime(self.site['date_format']).decode('utf-8') item['timestamp'] = timegm(date.utctimetuple()) if simple: item['url'] = Url.from_path(f.root.path.replace(self.src.path, ''), text) else: item['excerpt'] = re.search(r'\A.*?(?:<p>(.+?)</p>)?', content, re.M | re.S).group(1) item['tags'] = [] item['url'] = Url.from_format(config['url'], text, date, frontmatter) item.update(frontmatter) logger.debug('.. (%.3fs) %s', Timer.stop(), f.path.replace(self.src.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: if not domain.startswith(('http://', 'https://')): domain = 'http://' + domain parts.insert(0, domain) return Url.join(*parts)
def _get_url(self, url = '', absolute = False): parts = [self.globals['site']['base_url'], url] domain = self.globals['site']['domain'] if absolute and domain: if not domain.startswith(('http://', 'https://')): domain = 'http://' + domain parts.insert(0, domain) return Url.join(*parts)
def _archive(self, items, archive): for item in items: year, month = datetime.utcfromtimestamp(item['timestamp']).strftime('%Y %B').decode('utf-8').split() if year not in archive: archive[year] = { 'months': OrderedDict({month: [item]}), 'url': Url.from_format(self.config['archives_url'], year), '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.src = Directory(self.opts['src']) base_url = Url.join(self.opts['base_url'], '') if not self.src.exists: raise OptionException('Source must exist.') logger.info('>> Serving at 127.0.0.1:%s', self.opts['port']) logger.info('Press ctrl+c to stop.') cwd = getcwd() self.server = Server(('', self.opts['port']), base_url, RequestHandler) chdir(self.src.path) try: self.server.serve_forever() except KeyboardInterrupt: self.server.shutdown() chdir(cwd) print('')
def _update_config(self): self.config = deepcopy(self.defaults) logger.debug('>> Searching for config') for ext in ('.yml', '.yaml'): f = File(normpath(self.src.path, 'config' + ext)) if f.exists: logger.debug('.. found: %s', f.path) try: self.config.update(Config(f.content)) except ConfigException as e: raise ConfigException(e.message, 'src: {0}'.format(f.path)) self.config['locale'] = self.opts.get('locale', self.config['locale']) self.config['assets_url'] = Url.join(self.config['assets_url'], '') self.config['base_url'] = Url.join( self.opts.get('base_url', self.config['base_url']), '') for setting in ('archives_url', 'posts_url', 'tags_url'): self.config[setting] = Url.join(self.config[setting]) for setting in ('archives_url', 'assets_url', 'base_url', 'posts_url', 'tags_url'): if re.search(r'(?:^\.{2}/|/\.{2}$|/\.{2}/)', self.config[setting]): raise ConfigException('Invalid config setting.', 'setting: {0}'.format(setting), 'path traversal is not allowed') containers_src = normpath(self.src.path, '_containers') for name, config in self.config['containers'].iteritems(): if op.commonprefix( (containers_src, normpath(containers_src, name))) != containers_src: raise ConfigException( 'Invalid config setting.', 'setting: containers:{0}'.format(name), 'container name contains illegal characters') try: url = Url.join(config['url']) except KeyError: raise ConfigException( 'Invalid config setting.', 'setting: containers:{0}'.format(name), 'url must be set for all containers') if re.search(r'(?:^\.{2}/|/\.{2}$|/\.{2}/)', url): raise ConfigException( 'Invalid config setting.', 'setting: containers:{0}:url'.format(name), 'path traversal is not allowed') config.update( (k, v) for k, v in self.container_defaults.iteritems() if k not in config) config['url'] = url for pattern in self.config['include']: if op.commonprefix( (self.src.path, normpath(self.src.path, pattern))) != self.src.path: raise ConfigException('Invalid include path.', 'path: {0}'.format(pattern), 'path traversal is not allowed') break else: logger.debug('.. no config file found')
def _update_config(self): self.config = deepcopy(self.defaults) logger.debug('>> Searching for config') for ext in ('.yml', '.yaml'): f = File(normpath(self.src.path, 'config' + ext)) if f.exists: logger.debug('.. found: %s', f.path) try: self.config.update(Config(f.content)) except ConfigException as e: raise ConfigException(e.message, 'src: {0}'.format(f.path)) self.config['locale'] = self.opts.get('locale', self.config['locale']) self.config['assets_url'] = Url.join(self.config['assets_url'], '') self.config['base_url'] = Url.join(self.opts.get('base_url', self.config['base_url']), '') for setting in ('archives_url', 'posts_url', 'tags_url'): self.config[setting] = Url.join(self.config[setting]) for setting in ('archives_url', 'assets_url', 'base_url', 'posts_url', 'tags_url'): if re.search(r'(?:^\.{2}/|/\.{2}$|/\.{2}/)', self.config[setting]): raise ConfigException('Invalid config setting.', 'setting: {0}'.format(setting), 'path traversal is not allowed') containers_src = normpath(self.src.path, '_containers') for name, config in self.config['containers'].iteritems(): if op.commonprefix((containers_src, normpath(containers_src, name))) != containers_src: raise ConfigException('Invalid config setting.', 'setting: containers:{0}'.format(name), 'container name contains illegal characters') try: url = Url.join(config['url']) except KeyError: raise ConfigException('Invalid config setting.', 'setting: containers:{0}'.format(name), 'url must be set for all containers') if re.search(r'(?:^\.{2}/|/\.{2}$|/\.{2}/)', url): raise ConfigException('Invalid config setting.', 'setting: containers:{0}:url'.format(name), 'path traversal is not allowed') config.update((k, v) for k, v in self.container_defaults.iteritems() if k not in config) config['url'] = url for pattern in self.config['include']: if op.commonprefix((self.src.path, normpath(self.src.path, pattern))) != self.src.path: raise ConfigException('Invalid include path.', 'path: {0}'.format(pattern), 'path traversal is not allowed') break else: logger.debug('.. no config file found')
def _get_asset(self, asset): return Url.join(self.globals['site']['base_url'], self.globals['site']['assets_url'], asset)
def _get_figure(self, slug, figure): return Url.join(self.globals['site']['base_url'], self.globals['site']['figures_url'], slug, figure)