def init_env(self): """Initialize the environment for generating the Web site to deploy. This function reads the Web site configuration, sets up the template environment and sets object properties. """ self.dir_content = self.get_path('content', required=True) self.config = Config(self.get_path('site.yaml', required=True)) self.dir_templates = self.get_path('templates', required=True) self.template = Template(self) self.dir_bin = self.get_path('bin') # make all settings in site section available to templates for key, val in list(self.config.get_section('site').items()): self.template.add_var(key, val) # optional directory with static files like css, js and images self.dir_static = self.get_path('static') self.dir_dst = self.get_path('deploy') # feeds are only generated if base_url is set in site section of config self.base_url = self.config.get('site', 'base_url') if self.base_url is False: raise Exception('base_url not set in site config.')
def init_env(self): """Initialize the environment for generating the Web site to deploy. This function reads the Web site configuration, sets up the template environment and sets object properties. """ cwd = self.dir_site self.dir_content = path.join(cwd, 'content', required=True) self.config = config.load(path.join(cwd, 'site.yaml', required=True)) self.dir_templates = path.join(cwd, 'templates', required=True) self.template = Template(self) self.dir_bin = path.join(cwd, 'bin') # make all settings in site section available to templates self.template.vars.update(self.config['site']) # Optional directory with static files like css, js and images. self.dir_static = path.join(cwd, 'static') # Directory is created by the generate command. self.dir_deploy = path.join(cwd, 'deploy') self.base_url = self.config['site'].get('base_url') # base_url must be defined in settings if not self.base_url: raise Exception('base_url not set in site config.') # A dictionary of parsed documents indexed by resource paths. self.docs = {} # A dictionary of document collections. self.index = {} # Set default templates once for a Logya instance. self.templates = { 'doc': self.config['content']['doc']['template'], 'index': self.config['content']['index']['template'], 'rss': self.config['content']['rss']['template'] } # Map collection paths to config variables (vars) to make collecion # settings accessible via index URLs. self.collection_index = { v['path']: k for k, v in self.config['collections'].items() }
def init_env(self): """Initialize the environment for generating the Web site to deploy. This function reads the Web site configuration, sets up the template environment and sets object properties. """ cwd = self.dir_site self.dir_content = path.join(cwd, 'content', required=True) self.config = config.load(path.join(cwd, 'site.yaml', required=True)) self.dir_templates = path.join(cwd, 'templates', required=True) self.template = Template(self) self.dir_bin = path.join(cwd, 'bin') # make all settings in site section available to templates self.template.vars.update(self.config['site']) # Optional directory with static files like css, js and images. self.dir_static = path.join(cwd, 'static') # Directory is created by the generate command. self.dir_deploy = path.join(cwd, 'deploy') self.base_url = self.config['site'].get('base_url') # base_url must be defined in settings if not self.base_url: raise Exception('base_url not set in site config.') # A dictionary of parsed documents indexed by resource paths. self.docs = {} # A dictionary of document collections. self.index = {} # Set default templates once for a Logya instance. self.templates = { 'doc': self.config['content']['doc']['template'], 'index': self.config['content']['index']['template'], 'rss': self.config['content']['rss']['template'] } # Set languages if specified. if 'languages' in self.config: self.languages = self.config['languages'] # Map collection paths to config variables (vars) to make collecion # settings accessible via index URLs. self.collection_index = { v['path']: k for k, v in self.config['collections'].items()}
class Logya(object): """Main logic for creating, building and serving a static site.""" index_filename = 'index.html' def __init__(self, **kwargs): """Set required logya object properties.""" self.verbose = kwargs.get('verbose', False) # dir_site is an optional argument of the generate command, which may # instantiate the class with a value of None for dir_site. dir_site = kwargs.get('dir_site') self.dir_site = dir_site if dir_site else os.getcwd() def init_env(self): """Initialize the environment for generating the Web site to deploy. This function reads the Web site configuration, sets up the template environment and sets object properties. """ cwd = self.dir_site self.dir_content = path.join(cwd, 'content', required=True) self.config = config.load(path.join(cwd, 'site.yaml', required=True)) self.dir_templates = path.join(cwd, 'templates', required=True) self.template = Template(self) self.dir_bin = path.join(cwd, 'bin') # make all settings in site section available to templates self.template.vars.update(self.config['site']) # Optional directory with static files like css, js and images. self.dir_static = path.join(cwd, 'static') # Directory is created by the generate command. self.dir_deploy = path.join(cwd, 'deploy') self.base_url = self.config['site'].get('base_url') # base_url must be defined in settings if not self.base_url: raise Exception('base_url not set in site config.') # A dictionary of parsed documents indexed by resource paths. self.docs = {} # A dictionary of document collections. self.index = {} # Set default templates once for a Logya instance. self.templates = { 'doc': self.config['content']['doc']['template'], 'index': self.config['content']['index']['template'], 'rss': self.config['content']['rss']['template'] } # Map collection paths to config variables (vars) to make collecion # settings accessible via index URLs. self.collection_index = { v['path']: k for k, v in self.config['collections'].items()} def info(self, msg): """Print message if in verbose mode.""" if self.verbose: print(msg) def get_doc_template(self, doc): """Get template setting from doc otherwise from configuration.""" return doc.get('template', self.templates['doc']) def get_index_template(self, fullpath): """Get template file name for given fullpath. If a collection is set in site.yaml for fullpath, use the corresponding template if set. Otherwise return the default index template. """ template = self.templates['index'] var = get_collection_var(fullpath, self.collection_index) if var: template = self.config['collections'][var].get('template', template) return template def _update_index(self, doc, url=None): """Add a doc to index determined from given url. For each directory in the URL except for the one containing the content file itself a collection is created, if it doesn't exist, and the document is added to it. """ if url is None: url = doc['url'] dirs = path.parent_dirs(url) for fullpath in path.parent_paths(dirs): if fullpath not in self.index: template = self.get_index_template(fullpath) self.index[fullpath] = Collection(docs=[], template=template, urls=set()) # Don't add documents more than once to a collection. Important to # check and add doc['url'], because url can be an index url like tags. if doc['url'] not in self.index[fullpath].urls: self.index[fullpath].urls.add(doc['url']) self.index[fullpath].docs.append(doc) def _update_doc_index(self, doc, var, basepath): """Add the doc to the index defined for the header variable (var).""" for val in doc[var]: url = '/{}/{}/'.format(basepath, path.slugify(val)) links = var + '_links' doc[links] = doc.get(links, []) + [(url, val)] # Must append file name to url to create subdir. self._update_index(doc, url + self.index_filename) def update_index(self, doc): """Add document to index based on index headers.""" # Don't index documents with noindex set to a true value. if doc.get('noindex'): return self._update_index(doc) # Add to special __index__ for RSS generation. self._update_index(doc, '__index__/index/') for url, collection in self.config['collections'].items(): if url in doc: self._update_doc_index(doc, url, collection['path']) def build_index(self, mode=None): """Build index of documents for content directories to be created. The mode argument hints the Logya command that was executed.""" msg_duplicate = 'The URL {} is already used and will be overwritten.' for doc in DocReader(self.dir_content).parsed: url = doc['url'] # Warn user about duplicate URLs when not in serve mode. if 'serve' != mode and url in self.docs: print(msg_duplicate.format(url)) self.docs[url] = doc self.update_index(doc) # For each collection sort docs by creation dates in descending order. for url, collection in self.index.items(): collection.docs.sort(key=itemgetter('created'), reverse=True) # Make index available to templates. self.template.vars['index'] = self.index def index_title(self, s): """Title for index pages, usually created from directory paths.""" return ' » '.join(s.split('/')).replace('-', ' ').title() def write_rss(self, feed_title, url, docs): """Write RSS 2.0 XML file in target directory""" self.template.vars['url'] = self.base_url self.template.vars['title'] = feed_title self.template.vars['description'] = feed_title self.template.vars['last_build'] = datetime.datetime.now() self.template.vars['docs'] = docs page = self.template.get_page(self.template.vars, self.templates['rss']) content = page.render(self.template.vars) filename = path.target_file( self.dir_deploy, path.join(url, 'rss.xml')) write(filename, content) def write_index(self, url, collection): """Write an auto-generated index.html file.""" check_doc_url = '/{}'.format(path.join(url, self.index_filename)) # make sure there exists no document at the index url if check_doc_url not in self.docs: # Ugly fix for issue #32: delete description var. This is called # for every index, instead of once for all, because write_index is # called in serve mode. Also there may remain other vars causing # future problems. # Emptying the vars dict does not work either, because the index # key is set in build_index and needed. if 'description' in self.template.vars: del self.template.vars['description'] title = self.index_title(url) self.template.vars['docs'] = collection.docs self.template.vars['title'] = title self.template.vars['canonical'] = '{:s}/{:s}/'.format(self.base_url, url) page = self.template.get_page(self.template.vars, collection.template) content = page.render(self.template.vars) filename = path.target_file(self.dir_deploy, url) write(filename, content) # write directory RSS file self.write_rss(title, url, collection.docs) def write_index_files(self): """Write index.html files to deploy directories where non exists. If no template is specified in configuration index won't be written. """ feed_title = self.config['site'].get('feed_title', 'RSS Feed') for url, collection in self.index.items(): if '__index__' != url: self.write_index(url, collection) # write root RSS file if '__index__' in self.index: self.write_rss(feed_title, '', self.index['__index__'].docs)
class Logya(object): """Main logic for creating, building and serving a static site.""" re_url_replace = re.compile(r'[\/\s_]+') def __init__(self, **kwargs): """Set required logya object properties.""" if 'verbose' in kwargs and kwargs['verbose']: self.verbose = True else: self.verbose = False self.dir_current = os.getcwd() self.index_filename = 'index.html' self.feed_limit = 10 # a dictionary of parsed documents indexed by resource paths self.docs_parsed = {} def init_env(self): """Initialize the environment for generating the Web site to deploy. This function reads the Web site configuration, sets up the template environment and sets object properties. """ self.dir_content = self.get_path('content', required=True) self.config = Config(self.get_path('site.yaml', required=True)) self.dir_templates = self.get_path('templates', required=True) self.template = Template(self) self.dir_bin = self.get_path('bin') # make all settings in site section available to templates for key, val in list(self.config.get_section('site').items()): self.template.add_var(key, val) # optional directory with static files like css, js and images self.dir_static = self.get_path('static') self.dir_dst = self.get_path('deploy') # feeds are only generated if base_url is set in site section of config self.base_url = self.config.get('site', 'base_url') if self.base_url is False: raise Exception('base_url not set in site config.') def info(self, msg): """Print message if in verbose mode.""" if self.verbose: print(msg) def set_dir_current(self, dir_current): """Called from tests.""" self.dir_current = dir_current def get_path(self, name, required=False): """Get path relative to current working directory for given name. Raises an exception if resource is required and doesn't exist. """ path = os.path.join(self.dir_current, name) if required and not os.path.exists(path): raise Exception('Resource at path "%s" does not exist.' % path) return path def get_doc_template(self, doc): """Get template setting from doc otherwise from configuration.""" template = self.config.get_item('templates', 'doc', 'content_type', 'template') return doc.get('template', template) def get_dirs_from_path(self, url): """Returns a list of directories from given url. The last directory is omitted as it contains an index.html file containing the content of the appropriate document.""" dirs = [f for f in url.strip('/').split('/') if f] if dirs: dirs = dirs[:-1] return dirs def update_index(self, doc, path): """Add a doc to given path index.""" self.indexes[path] = self.indexes.get(path, []) + [doc] def _update_indexes(self, doc, url=None): """Add a doc to indexes determined from given url.""" if url is None: url = doc['url'] dirs = self.get_dirs_from_path(url) last = 0 for d in dirs: last += 1 self.update_index(doc, '/'.join(dirs[:last])) def update_indexes(self, doc): """Add all indexes for doc determined from headers.""" # don't index documents with noindex set if 'noindex' in doc and doc['noindex']: return self._update_indexes(doc) # add to special __index__ for RSS generation self._update_indexes(doc, '__index__/index/') doc_indexes = self.config.get_section('indexes') for idx in doc_indexes: if idx['var'] in doc: self.update_doc_index(doc, idx['var'], idx['path']) def update_doc_index(self, doc, var, path): """Update doc index.""" for val in doc[var]: var_path = re.sub(self.re_url_replace, '-', val).lower() url = '/%s/%s/' % (path, var_path) links = var + '_links' doc[links] = doc.get(links, []) + [(url, val)] # must append path after tag string to create subdir self._update_indexes(doc, url + self.index_filename) def build_indexes(self, mode=None): """Build indexes of documents for content directories to be created. The mode argument hints the Logya command that was executed.""" # a dictionary of indexes with parsed documents self.indexes = {} docs = DocReader(self.dir_content, DocParser()).get_docs() for doc in docs: # ignore empty documents and those without url if doc is None or 'url' not in doc: continue url = doc['url'] # warn user about duplicate URLs when not in serve mode if 'serve' != mode and url in self.docs_parsed: print(('The URL %s is already used and will be overwritten.' % url)) self.docs_parsed[url] = doc for doc in list(self.docs_parsed.values()): self.update_indexes(doc) # sort indexes by descending docs creation dates for idx in self.indexes: self.indexes[idx] = sorted(self.indexes[idx], key=itemgetter('created'), reverse=True) # make indexes available to templates self.template.add_var('indexes', self.indexes) def index_title(self, s): """Title for index pages, usually created from directory paths.""" if not is3: s = s.encode('utf-8') return ' » '.join(s.split('/')).replace('-', ' ').title() def write_rss(self, feed_title, directory, docs): """Write RSS 2.0 XML file in target directory""" self.template.add_var('url', self.base_url) self.template.add_var('title', feed_title) self.template.add_var('description', directory) self.template.add_var('last_build', datetime.datetime.now()) self.template.add_var('items', docs[0:self.feed_limit]) writer = FileWriter() page = self.template.env.get_template('rss2.xml') fh = writer.getfile(self.dir_dst, os.path.join(directory, 'rss.xml')) writer.write(fh, page.render(self.template.get_vars())) def write_index(self, filewriter, directory, template): """Write an auto-generated index.html file.""" url_path = '/%s' % os.path.join(directory, self.index_filename) # make sure there exists no document at the index path if url_path not in self.docs_parsed: # remove the file name part if it's index.html url = url_path.replace(self.index_filename, '') docs = self.indexes[directory] # Ugly fix for issue #32: delete description var. This is called # for every index, instead of once for all, because write_index is # called in serve mode. Also there may remain other vars causing # future problems. # Emptying the vars dict does not work either, because the indexes # key is set in build_indexes and needed. if 'description' in self.template.vars: del self.template.vars['description'] title = self.index_title(directory) if not is3: title = title.decode('utf-8') self.template.add_var('url', url) self.template.add_var('canonical', self.base_url + url) self.template.add_var('index', docs) self.template.add_var('title', title) self.template.add_var('directory', directory) page = self.template.env.get_template(template) filewriter.write(filewriter.getfile(self.dir_dst, directory), page.render(self.template.get_vars())) # write directory RSS file if self.base_url: self.write_rss(title, directory, docs) def write_indexes(self): """Write index.html files to deploy directories where non exists. If no template file is specified in configuration indexes won't be written. """ template = self.config.get_item('templates', 'index', 'content_type', 'template') if not template: return feed_title = self.config.get('site', 'feed_title') if not feed_title: feed_title = 'RSS Feed' for directory in list(self.indexes.keys()): self.write_index(FileWriter(), directory, template) # write root RSS file if self.base_url and '__index__' in self.indexes: docs = sorted(self.indexes['__index__'], key=itemgetter('created'), reverse=True) self.write_rss(feed_title, '', docs) # TODO remove in 4.0 def get_execs(self, path): """Generator yielding paths to executable files in given directory.""" for p in os.listdir(path): fpath = os.path.join(path, p) if os.path.isfile(fpath) and os.access(fpath, os.X_OK): yield fpath # TODO remove in 4.0 def exec_bin(self): """Execute binary files in bin dir.""" dir_bin = self.get_path('bin') if os.path.exists(dir_bin): args = {'logya': self} for exe in self.get_execs(dir_bin): execfile(exe, args)
class Logya(object): """Main logic for creating, building and serving a static site.""" re_url_replace = re.compile(r'[\/\s_]+') def __init__(self, **kwargs): """Set required logya object properties.""" if 'verbose' in kwargs and kwargs['verbose']: self.verbose = True else: self.verbose = False self.dir_current = os.getcwd() self.index_filename = 'index.html' self.feed_limit = 10 # a dictionary of parsed documents indexed by resource paths self.docs_parsed = {} def init_env(self): """Initialize the environment for generating the Web site to deploy. This function reads the Web site configuration, sets up the template environment and sets object properties. """ self.dir_content = self.get_path('content', required=True) self.config = Config(self.get_path('site.yaml', required=True)) self.dir_templates = self.get_path('templates', required=True) self.template = Template(self) self.dir_bin = self.get_path('bin') # make all settings in site section available to templates for key, val in list(self.config.get_section('site').items()): self.template.add_var(key, val) # optional directory with static files like css, js and images self.dir_static = self.get_path('static') self.dir_dst = self.get_path('deploy') # feeds are only generated if base_url is set in site section of config self.base_url = self.config.get('site', 'base_url') if self.base_url is False: raise Exception('base_url not set in site config.') def info(self, msg): """Print message if in verbose mode.""" if self.verbose: print(msg) def set_dir_current(self, dir_current): """Called from tests.""" self.dir_current = dir_current def get_path(self, name, required=False): """Get path relative to current working directory for given name. Raises an exception if resource is required and doesn't exist. """ path = os.path.join(self.dir_current, name) if required and not os.path.exists(path): raise Exception('Resource at path "%s" does not exist.' % path) return path def get_doc_template(self, doc): """Get template setting from doc otherwise from configuration.""" template = self.config.get_item( 'templates', 'doc', 'content_type', 'template') return doc.get('template', template) def get_dirs_from_path(self, url): """Returns a list of directories from given url. The last directory is omitted as it contains an index.html file containing the content of the appropriate document.""" dirs = [f for f in url.strip('/').split('/') if f] if dirs: dirs = dirs[:-1] return dirs def update_index(self, doc, path): """Add a doc to given path index.""" self.indexes[path] = self.indexes.get(path, []) + [doc] def _update_indexes(self, doc, url=None): """Add a doc to indexes determined from given url.""" if url is None: url = doc['url'] dirs = self.get_dirs_from_path(url) last = 0 for d in dirs: last += 1 self.update_index(doc, '/'.join(dirs[:last])) def update_indexes(self, doc): """Add all indexes for doc determined from headers.""" # don't index documents with noindex set if 'noindex' in doc and doc['noindex']: return self._update_indexes(doc) # add to special __index__ for RSS generation self._update_indexes(doc, '__index__/index/') doc_indexes = self.config.get_section('indexes') for idx in doc_indexes: if idx['var'] in doc: self.update_doc_index(doc, idx['var'], idx['path']) def update_doc_index(self, doc, var, path): """Update doc index.""" for val in doc[var]: var_path = re.sub(self.re_url_replace, '-', val).lower() url = '/%s/%s/' % (path, var_path) links = var + '_links' doc[links] = doc.get(links, []) + [(url, val)] # must append path after tag string to create subdir self._update_indexes(doc, url + self.index_filename) def build_indexes(self, mode=None): """Build indexes of documents for content directories to be created. The mode argument hints the Logya command that was executed.""" # a dictionary of indexes with parsed documents self.indexes = {} docs = DocReader(self.dir_content, DocParser()).get_docs() for doc in docs: # ignore empty documents and those without url if doc is None or 'url' not in doc: continue url = doc['url'] # warn user about duplicate URLs when not in serve mode if 'serve' != mode and url in self.docs_parsed: print(('The URL %s is already used and will be overwritten.' % url)) self.docs_parsed[url] = doc for doc in list(self.docs_parsed.values()): self.update_indexes(doc) # sort indexes by descending docs creation dates for idx in self.indexes: self.indexes[idx] = sorted(self.indexes[idx], key=itemgetter('created'), reverse=True) # make indexes available to templates self.template.add_var('indexes', self.indexes) def index_title(self, s): """Title for index pages, usually created from directory paths.""" if not is3: s = s.encode('utf-8') return ' » '.join(s.split('/')).replace('-', ' ').title() def write_rss(self, feed_title, directory, docs): """Write RSS 2.0 XML file in target directory""" self.template.add_var('url', self.base_url) self.template.add_var('title', feed_title) self.template.add_var('description', directory) self.template.add_var('last_build', datetime.datetime.now()) self.template.add_var('items', docs[0:self.feed_limit]) writer = FileWriter() page = self.template.env.get_template('rss2.xml') fh = writer.getfile(self.dir_dst, os.path.join(directory, 'rss.xml')) writer.write(fh, page.render(self.template.get_vars())) def write_index(self, filewriter, directory, template): """Write an auto-generated index.html file.""" url_path = '/%s' % os.path.join(directory, self.index_filename) # make sure there exists no document at the index path if url_path not in self.docs_parsed: # remove the file name part if it's index.html url = url_path.replace(self.index_filename, '') docs = self.indexes[directory] # Ugly fix for issue #32: delete description var. This is called # for every index, instead of once for all, because write_index is # called in serve mode. Also there may remain other vars causing # future problems. # Emptying the vars dict does not work either, because the indexes # key is set in build_indexes and needed. if 'description' in self.template.vars: del self.template.vars['description'] title = self.index_title(directory) if not is3: title = title.decode('utf-8') self.template.add_var('url', url) self.template.add_var('canonical', self.base_url + url) self.template.add_var('index', docs) self.template.add_var('title', title) self.template.add_var('directory', directory) page = self.template.env.get_template(template) filewriter.write(filewriter.getfile(self.dir_dst, directory), page.render(self.template.get_vars())) # write directory RSS file if self.base_url: self.write_rss(title, directory, docs) def write_indexes(self): """Write index.html files to deploy directories where non exists. If no template file is specified in configuration indexes won't be written. """ template = self.config.get_item( 'templates', 'index', 'content_type', 'template') if not template: return feed_title = self.config.get('site', 'feed_title') if not feed_title: feed_title = 'RSS Feed' for directory in list(self.indexes.keys()): self.write_index(FileWriter(), directory, template) # write root RSS file if self.base_url and '__index__' in self.indexes: docs = sorted(self.indexes['__index__'], key=itemgetter('created'), reverse=True) self.write_rss(feed_title, '', docs) # TODO remove in 4.0 def get_execs(self, path): """Generator yielding paths to executable files in given directory.""" for p in os.listdir(path): fpath = os.path.join(path, p) if os.path.isfile(fpath) and os.access(fpath, os.X_OK): yield fpath # TODO remove in 4.0 def exec_bin(self): """Execute binary files in bin dir.""" dir_bin = self.get_path('bin') if os.path.exists(dir_bin): args = {'logya': self} for exe in self.get_execs(dir_bin): execfile(exe, args)
class Logya(object): """Main logic for creating, building and serving a static site.""" index_filename = 'index.html' def __init__(self, **kwargs): """Set required logya object properties.""" self.verbose = kwargs.get('verbose', False) # dir_site is an optional argument of the generate command, which may # instantiate the class with a value of None for dir_site. dir_site = kwargs.get('dir_site') self.dir_site = dir_site if dir_site else os.getcwd() def init_env(self): """Initialize the environment for generating the Web site to deploy. This function reads the Web site configuration, sets up the template environment and sets object properties. """ cwd = self.dir_site self.dir_content = path.join(cwd, 'content', required=True) self.config = config.load(path.join(cwd, 'site.yaml', required=True)) self.dir_templates = path.join(cwd, 'templates', required=True) self.template = Template(self) self.dir_bin = path.join(cwd, 'bin') # make all settings in site section available to templates self.template.vars.update(self.config['site']) # Optional directory with static files like css, js and images. self.dir_static = path.join(cwd, 'static') # Directory is created by the generate command. self.dir_deploy = path.join(cwd, 'deploy') self.base_url = self.config['site'].get('base_url') # base_url must be defined in settings if not self.base_url: raise Exception('base_url not set in site config.') # A dictionary of parsed documents indexed by resource paths. self.docs = {} # A dictionary of document collections. self.index = {} # Set default templates once for a Logya instance. self.templates = { 'doc': self.config['content']['doc']['template'], 'index': self.config['content']['index']['template'], 'rss': self.config['content']['rss']['template'] } # Map collection paths to config variables (vars) to make collecion # settings accessible via index URLs. self.collection_index = { v['path']: k for k, v in self.config['collections'].items() } def info(self, msg): """Print message if in verbose mode.""" if self.verbose: print(msg) def get_doc_template(self, doc): """Get template setting from doc otherwise from configuration.""" return doc.get('template', self.templates['doc']) def get_index_template(self, fullpath): """Get template file name for given fullpath. If a collection is set in site.yaml for fullpath, use the corresponding template if set. Otherwise return the default index template. """ template = self.templates['index'] var = get_collection_var(fullpath, self.collection_index) if var: template = self.config['collections'][var].get( 'template', template) return template def _update_index(self, doc, url=None): """Add a doc to index determined from given url. For each directory in the URL except for the one containing the content file itself a collection is created, if it doesn't exist, and the document is added to it. """ if url is None: url = doc['url'] dirs = path.parent_dirs(url) for fullpath in path.parent_paths(dirs): if fullpath not in self.index: template = self.get_index_template(fullpath) self.index[fullpath] = Collection(docs=[], template=template, urls=set()) # Don't add documents more than once to a collection. Important to # check and add doc['url'], because url can be an index url like tags. if doc['url'] not in self.index[fullpath].urls: self.index[fullpath].urls.add(doc['url']) self.index[fullpath].docs.append(doc) def _update_doc_index(self, doc, var, basepath): """Add the doc to the index defined for the header variable (var).""" for val in doc[var]: url = '/{}/{}/'.format(basepath, path.slugify(val)) links = var + '_links' doc[links] = doc.get(links, []) + [(url, val)] # Must append file name to url to create subdir. self._update_index(doc, url + self.index_filename) def update_index(self, doc): """Add document to index based on index headers.""" # Don't index documents with noindex set to a true value. if doc.get('noindex'): return self._update_index(doc) # Add to special __index__ for RSS generation. self._update_index(doc, '__index__/index/') for url, collection in self.config['collections'].items(): if url in doc: self._update_doc_index(doc, url, collection['path']) def build_index(self, mode=None): """Build index of documents for content directories to be created. The mode argument hints the Logya command that was executed.""" msg_duplicate = 'The URL {} is already used and will be overwritten.' for doc in DocReader(self.dir_content).parsed: url = doc['url'] # Warn user about duplicate URLs when not in serve mode. if 'serve' != mode and url in self.docs: print(msg_duplicate.format(url)) self.docs[url] = doc self.update_index(doc) # For each collection sort docs by creation dates in descending order. for url, collection in self.index.items(): collection.docs.sort(key=itemgetter('created'), reverse=True) # Make index available to templates. self.template.vars['index'] = self.index def index_title(self, s): """Title for index pages, usually created from directory paths.""" return ' » '.join(s.split('/')).replace('-', ' ').title() def write_rss(self, feed_title, url, docs): """Write RSS 2.0 XML file in target directory""" self.template.vars['url'] = self.base_url self.template.vars['title'] = feed_title self.template.vars['description'] = feed_title self.template.vars['last_build'] = datetime.datetime.now() self.template.vars['docs'] = docs page = self.template.get_page(self.template.vars, self.templates['rss']) content = page.render(self.template.vars) filename = path.target_file(self.dir_deploy, path.join(url, 'rss.xml')) write(filename, content) def write_index(self, url, collection): """Write an auto-generated index.html file.""" check_doc_url = '/{}'.format(path.join(url, self.index_filename)) # make sure there exists no document at the index url if check_doc_url not in self.docs: # Ugly fix for issue #32: delete description var. This is called # for every index, instead of once for all, because write_index is # called in serve mode. Also there may remain other vars causing # future problems. # Emptying the vars dict does not work either, because the index # key is set in build_index and needed. if 'description' in self.template.vars: del self.template.vars['description'] title = self.index_title(url) self.template.vars['docs'] = collection.docs self.template.vars['title'] = title self.template.vars['canonical'] = '{:s}/{:s}/'.format( self.base_url, url) page = self.template.get_page(self.template.vars, collection.template) content = page.render(self.template.vars) filename = path.target_file(self.dir_deploy, url) write(filename, content) # write directory RSS file self.write_rss(title, url, collection.docs) def write_index_files(self): """Write index.html files to deploy directories where non exists. If no template is specified in configuration index won't be written. """ feed_title = self.config['site'].get('feed_title', 'RSS Feed') for url, collection in self.index.items(): if '__index__' != url: self.write_index(url, collection) # write root RSS file if '__index__' in self.index: self.write_rss(feed_title, '', self.index['__index__'].docs)