Beispiel #1
0
def readfile(filename, request):
    entry_data = {}
    lines = open(filename).readlines()

    if len(lines) == 0:
        return {"title": "", "body": ""}

    title = lines.pop(0).strip()

    # absorb meta data
    while lines and lines[0].startswith("#"):
        meta = lines.pop(0)
        # remove the hash
        meta = meta[1:].strip()
        meta = meta.split(" ", 1)
        # if there's no value, we append a 1
        if len(meta) == 1:
            meta.append("1")
        entry_data[meta[0].strip()] = meta[1].strip()

    body = parse(''.join(lines), request)
    entry_data["title"] = title
    entry_data["body"] = body

    # Call the postformat callbacks
    tools.run_callback('postformat', {'request': request,
                                      'entry_data': entry_data})
    return entry_data
Beispiel #2
0
    def run_callback(self, callback="help"):
        """This method executes the start callback (initializing
        plugins), executes the requested callback, and then executes
        the end callback.

        This is useful for scripts outside of Douglas that need to
        do things inside of the Douglas framework.

        If you want to run a callback from a plugin, use
        ``tools.run_callback`` instead.

        :param callback: the name of the callback to execute.

        :returns: the results of the callback.
        """
        self.initialize()

        # run the start callback
        tools.run_callback("start", {'request': self._request})

        # invoke all callbacks for the 'callback'
        handled = tools.run_callback(callback,
                                     {'request': self._request},
                                     mappingfunc=lambda x,y:x,
                                     donefunc=lambda x:x)

        # do end callback
        tools.run_callback("end", {'request': self._request})

        return handled
Beispiel #3
0
def cmd_buildtags(command, argv):
    """Command for building the tags index."""
    cfg = import_config()
    datadir = cfg['datadir']
    sep = cfg.get('tags_separator', ',')
    tagsfile = get_tagsfile(cfg)

    from douglas import tools
    from douglas.app import Douglas, initialize
    from douglas.entries import fileentry

    # Build a douglas object, initialize it, and run the start
    # callback.  This gives entry parsing related plugins a chance to
    # get their stuff together so that they work correctly.
    initialize(cfg)
    p = Douglas(cfg, {})
    p.initialize()
    req = p.get_request()
    tools.run_callback("start", {"request": req})

    # Grab all the entries in the datadir
    entrylist = [fileentry.FileEntry(req, e, datadir)
                 for e in tools.get_entries(cfg, datadir)]

    tags_to_files = {}
    for mem in entrylist:
        tagsline = mem["tags"]
        if not tagsline:
            continue
        tagsline = [t.strip() for t in tagsline.split(sep)]
        for t in tagsline:
            tags_to_files.setdefault(t, []).append(mem["filename"])

    savefile(tagsfile, tags_to_files)
    return 0
Beispiel #4
0
def blosxom_entry_parser(filename, request):
    """Open up a ``.txt`` file and read its contents.  The first line
    becomes the title of the entry.  The other lines are the body of
    the entry.

    :param filename: a filename to extract data and metadata from
    :param request: a standard request object

    :returns: dict containing parsed data and meta data with the
              particular file (and plugin)
    """
    config = request.get_configuration()

    entry_data = {}

    f = open(filename, "r")
    lines = f.readlines()
    f.close()

    # the file has nothing in it...  so we're going to return a blank
    # entry data object.
    if len(lines) == 0:
        return {"title": "", "body": ""}

    # the first line is the title
    entry_data["title"] = lines.pop(0).strip()

    # absorb meta data lines which begin with a #
    while lines and lines[0].startswith("#"):
        meta = lines.pop(0)
        # remove the hash
        meta = meta[1:].strip()
        meta = meta.split(" ", 1)
        # if there's no value, we append a 1
        if len(meta) == 1:
            meta.append("1")
        entry_data[meta[0].strip()] = meta[1].strip()

    # call the preformat function
    args = {'parser': entry_data.get('parser', config.get('parser', 'plain')),
            'story': lines,
            'request': request}
    entry_data["body"] = tools.run_callback(
        'preformat',
        args,
        donefunc=lambda x: x != None,
        defaultfunc=lambda x: ''.join(x['story']))

    # call the postformat callbacks
    tools.run_callback('postformat',
                      {'request': request,
                       'entry_data': entry_data})

    return entry_data
Beispiel #5
0
def blosxom_file_list_handler(args):
    """This is the default handler for getting entries.  It takes the
    request object in and figures out which entries based on the
    default behavior that we want to show and generates a list of
    EntryBase subclass objects which it returns.

    :param args: dict containing the incoming Request object

    :returns: the content we want to render
    """
    request = args["request"]

    data = request.get_data()
    config = request.get_configuration()

    if data['bl_type'] == 'dir':
        filelist = tools.walk(request,
                              data['root_datadir'],
                              int(config.get("depth", "0")))
    elif data['bl_type'] == 'file':
        filelist = [data['root_datadir']]
    else:
        filelist = []

    entrylist = [FileEntry(request, e, data["root_datadir"]) for e in filelist]

    # if we're looking at a set of archives, remove all the entries
    # that aren't in the archive
    if data.get("pi_yr", ""):
        tmp_pi_mo = data.get("pi_mo", "")
        datestr = "%s%s%s" % (data.get("pi_yr", ""),
                              tools.month2num.get(tmp_pi_mo, tmp_pi_mo),
                              data.get("pi_da", ""))
        entrylist = [x for x in entrylist
                     if time.strftime("%Y%m%d%H%M%S", x["timetuple"]).startswith(datestr)]


    args = {"request": request, "entry_list": entrylist}
    entrylist = tools.run_callback("sortlist",
                                   args,
                                   donefunc=lambda x: x != None,
                                   defaultfunc=blosxom_sort_list_handler)

    args = {"request": request, "entry_list": entrylist}    
    entrylist = tools.run_callback("truncatelist",
                                   args,
                                   donefunc=lambda x: x != None,
                                   defaultfunc=blosxom_truncate_list_handler)

    return entrylist
Beispiel #6
0
def category_to_tags(command, argv):
    """Goes through all entries and converts the category to tags
    metadata.

    It adds the tags line as the second line.

    It maintains the mtime for the file.
    """
    import config

    datadir = config.py.get("datadir")
    if not datadir:
        raise ValueError("config.py has no datadir property.")

    sep = config.py.get("tags_separator", ",")

    from douglas import tools
    from douglas.app import blosxom_entry_parser, Request

    data = {}

    # register entryparsers so that we parse all possible file types.
    data["extensions"] = tools.run_callback("entryparser",
                                            {"txt": blosxom_entry_parser},
                                            mappingfunc=lambda x, y: y,
                                            defaultfunc=lambda x: x)

    req = Request(config.py, {}, data)

    # grab all the entries in the datadir
    filelist = tools.walk(req, datadir)

    if not datadir.endswith(os.sep):
        datadir = datadir + os.sep

    for mem in filelist:
        print "working on %s..." % mem

        category = os.path.dirname(mem)[len(datadir):]
        tags = category.split(os.sep)
        print "   adding tags %s" % tags
        tags = "#tags %s\n" % (sep.join(tags))

        atime, mtime = os.stat(mem)[7:9]

        fp = open(mem, "r")
        data = fp.readlines()
        fp.close()

        data.insert(1, tags)

        fp = open(mem, "w")
        fp.write("".join(data))
        fp.close()

        os.utime(mem, (atime, mtime))

    return 0
Beispiel #7
0
    def initialize(self):
        """The initialize step further initializes the Request by
        setting additional information in the ``data`` dict,
        registering plugins, and entryparsers.
        """
        data = self._request.get_data()
        pyhttp = self._request.get_http()
        config = self._request.get_configuration()

        # initialize the locale, if wanted (will silently fail if locale
        # is not available)
        if config.get('locale', None):
            try:
                locale.setlocale(locale.LC_ALL, config['locale'])
            except locale.Error:
                # invalid locale
                pass

        # initialize the tools module
        tools.initialize()

        data["douglas_version"] = __version__
        data['pi_bl'] = ''

        # if the user specifies base_url in config, we use that.
        # otherwise we compose it from SCRIPT_NAME in the environment
        # or we leave it blank.
        if not "base_url" in config:
            if pyhttp.has_key('SCRIPT_NAME'):
                # allow http and https
                config['base_url'] = '%s://%s%s' % \
                                     (pyhttp['wsgi.url_scheme'],
                                      pyhttp['HTTP_HOST'],
                                      pyhttp['SCRIPT_NAME'])
            else:
                config["base_url"] = ""

        # take off the trailing slash for base_url
        if config['base_url'].endswith("/"):
            config['base_url'] = config['base_url'][:-1]

        datadir = config["datadir"]
        if datadir.endswith("/") or datadir.endswith("\\"):
            datadir = datadir[:-1]
            config['datadir'] = datadir

        # import and initialize plugins
        plugin_utils.initialize_plugins(config.get("plugin_dirs", []),
                                        config.get("load_plugins", None))

        # entryparser callback is run here first to allow other
        # plugins register what file extensions can be used
        data['extensions'] = tools.run_callback("entryparser",
                                        {'txt': blosxom_entry_parser},
                                        mappingfunc=lambda x,y:y,
                                        defaultfunc=lambda x:x)
Beispiel #8
0
    def render(self, render_headers=True):
        """
        Do final rendering.

        :arg render_headers: whether (True) or not (False) to show the
            headers
        """
        # if we've already rendered, then we don't want to do so again
        if self.rendered:
            return

        config = self._request.get_configuration()
        data = self._request.get_data()

        themedir = config['themedir']
        theme = data.get("theme") or "html"

        data['content-type'] = self.get_content_type(themedir, theme)

        if render_headers:
            self.add_header('Content-type', data['content-type'])
            self.show_headers()

        if self._content:
            content = self._content
            if not isinstance(content, list):
                content = [content]

            context = self.build_context()
            context['content'] = content

            # Allow plugins to alter the context adding additional
            # bits
            args = {
                'context': context,
                'request': self._request,
            }
            args = run_callback(
                "context_processor",
                args,
                mappingfunc=lambda x,y:y,
                defaultfunc=lambda x:x)

            context = args['context']
            env = build_environment(themedir, theme)

            template_name = context.get('bl_type', 'entry') + '.' + theme
            template = env.get_template(template_name)
            output = template.render(context)
            self.write(output.encode(config['blog_encoding']))

        self.rendered = 1
Beispiel #9
0
def cmd_persistdate(command, argv):
    from douglas.cmdline import import_config
    config = import_config()

    datadir = config.py.get('datadir')

    if not datadir:
        raise ValueError('config.py has no datadir property.')

    from douglas import tools
    from douglas.app import Douglas

    p = Douglas(config.py, {})
    p.initialize()
    req = p.get_request()
    tools.run_callback('start', {'request': req})

    filelist = tools.get_entries(config, datadir)
    print '%d files' % len(filelist)
    for fn in filelist:
        lines = open(fn, 'r').readlines()
        try:
            metadata = get_metadata(lines)
        except IndexError as exc:
            print '%s errored out: %s' % (fn, exc)
            continue

        if 'published' in metadata:
            print '%s already has metadata...' % fn
            continue

        print 'working on %s...' % fn
        timetuple = tools.filestat(req, fn)
        published = time.strftime('%Y-%m-%d %H:%M:%S', timetuple)
        lines.insert(1, '#published %s\n' % published)
        fp = open(fn, 'w')
        fp.write(''.join(lines))
        fp.close()
Beispiel #10
0
def buildtags(command, argv):
    """Command for building the tags index."""
    import config

    datadir = config.py.get("datadir")
    if not datadir:
        raise ValueError("config.py has no datadir property.")

    sep = config.py.get("tags_separator", ",")
    tagsfile = get_tagsfile(config.py)

    from douglas import tools
    from douglas.app import Douglas
    from douglas.entries import fileentry

    # build a douglas object, initialize it, and run the start
    # callback.  this gives entry parsing related plugins a chance to
    # get their stuff together so that they work correctly.
    p = Douglas(config.py, {})
    p.initialize()
    req = p.get_request()
    tools.run_callback("start", {"request": req})

    # grab all the entries in the datadir
    filelist = tools.walk(req, datadir)
    entrylist = [fileentry.FileEntry(req, e, datadir) for e in filelist]

    tags_to_files = {}
    for mem in entrylist:
        tagsline = mem["tags"]
        if not tagsline:
            continue
        tagsline = [t.strip() for t in tagsline.split(sep)]
        for t in tagsline:
            tags_to_files.setdefault(t, []).append(mem["filename"])

    savefile(tagsfile, tags_to_files)
    return 0
Beispiel #11
0
    def run(self, static=False):
        """This is the main loop for Douglas.  This method will run
        the handle callback to allow registered handlers to handle the
        request.  If nothing handles the request, then we use the
        ``default_blosxom_handler``.

        :param static: True if Douglas should execute in "static rendering
                       mode" and False otherwise.
        """
        self.initialize()

        # buffer the input stream in a StringIO instance if dynamic
        # rendering is used.  This is done to have a known/consistent
        # way of accessing incomming data.
        if static == False:
            self.get_request().buffer_input_stream()

        # run the start callback
        tools.run_callback("start", {'request': self._request})

        # allow anyone else to handle the request at this point
        handled = tools.run_callback("handle",
                        {'request': self._request},
                        mappingfunc=lambda x,y:x,
                        donefunc=lambda x:x)

        if not handled == 1:
            blosxom_handler(self._request)

        # do end callback
        tools.run_callback("end", {'request': self._request})

        # we're done, clean up.
        # only call this if we're not in static rendering mode.
        if static == False:
            self.cleanup()
Beispiel #12
0
def cb_filelist(args):
    from douglas import tools
    from douglas.app import blosxom_truncate_list_handler

    # handles /trigger/tag to show all the entries tagged that
    # way
    req = args["request"]

    pyhttp = req.get_http()
    data = req.get_data()
    config = req.get_configuration()

    trigger = "/" + config.get("tags_trigger", "tag")
    if not pyhttp["PATH_INFO"].startswith(trigger):
        return

    datadir = config["datadir"]
    tagsfile = get_tagsfile(config)
    tagsdata = loadfile(tagsfile)

    tag = pyhttp["PATH_INFO"][len(trigger) + 1:]
    filelist = tagsdata.get(tag, [])
    if not filelist:
        tag, ext = os.path.splitext(tag)
        filelist = tagsdata.get(tag, [])
        if filelist:
            data["theme"] = ext[1:]

    from douglas.entries import fileentry
    entrylist = [fileentry.FileEntry(req, e, datadir) for e in filelist]

    # sort the list by mtime
    entrylist = [(e._mtime, e) for e in entrylist]
    entrylist.sort()
    entrylist.reverse()
    entrylist = [e[1] for e in entrylist]

    data["truncate"] = config.get("truncate_tags", True)

    args = {"request": req, "entry_list": entrylist}
    entrylist = tools.run_callback("truncatelist",
                                   args,
                                   donefunc=lambda x: x != None,
                                   defaultfunc=blosxom_truncate_list_handler)

    return entrylist
Beispiel #13
0
def cb_filelist(args):
    from douglas import tools
    from douglas.app import blosxom_truncate_list_handler

    # Handles /trigger/tag to show all the entries tagged that way
    req = args['request']

    pyhttp = req.get_http()
    data = req.get_data()
    config = req.get_configuration()

    trigger = '/' + config.get('tags_trigger', 'tag')
    if not pyhttp['PATH_INFO'].startswith(trigger):
        return

    datadir = config['datadir']
    tagsfile = get_tagsfile(config)
    tagsdata = loadfile(tagsfile)

    tag = pyhttp['PATH_INFO'][len(trigger)+1:]
    filelist = tagsdata.get(tag, [])
    if not filelist:
        tag, ext = os.path.splitext(tag)
        filelist = tagsdata.get(tag, [])
        if filelist:
            data['theme'] = ext[1:]

    from douglas.entries import fileentry
    entrylist = [fileentry.FileEntry(req, e, datadir) for e in filelist]

    # sort the list by mtime
    entrylist.sort(key=lambda entry: entry._mtime, reverse=True)

    data['truncate'] = config.get('truncate_tags', True)

    args = {'request': req, 'entry_list': entrylist}
    entrylist = tools.run_callback('truncatelist',
                                   args,
                                   donefunc=lambda x: x != None,
                                   defaultfunc=blosxom_truncate_list_handler)

    return entrylist
Beispiel #14
0
def initialize(cfg):
    # import and initialize plugins
    plugin_utils.initialize_plugins(cfg['plugin_dirs'], cfg['load_plugins'])

    # entryparser callback is run here first to allow other
    # plugins register what file extensions can be used
    extensions = tools.run_callback(
        "entryparser",
        {'txt': blosxom_entry_parser},
        mappingfunc=lambda x, y: y,
        defaultfunc=lambda x: x)

    # go through the config.py and override entryparser extensions
    for ext, parser_module in cfg['entryparsers'].items():
        module, callable_name = parser_module.rsplit(':', 1)
        module = tools.importname(None, module)
        extensions[ext] = getattr(module, callable_name)

    # FIXME - this is a lousy place to store this
    cfg['extensions'] = extensions
Beispiel #15
0
def get_handlers():
    try:
        from config import py as cfg
        plugin_utils.initialize_plugins(cfg.get("plugin_dirs", []),
                                        cfg.get("load_plugins", None))
    except ImportError:
        pass

    handlers_dict = dict([(v[0], (v[1], v[2])) for v in DEFAULT_HANDLERS])
    handlers_dict = run_callback("commandline", handlers_dict,
                                 mappingfunc=lambda x, y: y,
                                 defaultfunc=lambda x: x)

    # test the handlers, drop any that aren't the right return type,
    # and print a warning.
    handlers = []
    for k, v in handlers_dict.items():
        if not len(v) == 2 or not callable(v[0]) or not isinstance(v[1], str):
            print "Plugin returned '%s' for commandline." % ((k, v),)
            continue
        handlers.append((k, v[0], v[1]))

    return handlers
Beispiel #16
0
    def run_compile(self, incremental=False):
        """Compiles the blog into an HTML site.

        This will go through all possible things in the blog and
        compile the blog to the ``compiledir`` specified in the config
        file.

        This figures out all the possible ``path_info`` settings and
        calls ``self.run()`` a bazillion times saving each file.

        :param incremental: Whether (True) or not (False) to compile
            incrementally. If we're incrementally compiling, then only
            the urls that are likely to have changed get re-compiled.

        """
        self.initialize()

        cfg = self._request.get_configuration()
        compiledir = cfg['compiledir']
        datadir = cfg['datadir']

        if not compiledir:
            print 'Error: You must set compiledir in your config file.'
            return 0

        print 'Compiling to "{0}".'.format(compiledir)
        if incremental:
            print 'Incremental is set.'
        print ''

        themes = cfg['compile_themes']
        index_themes = cfg['compile_index_themes']

        dayindexes = cfg['day_indexes']
        monthindexes = cfg['month_indexes']
        yearindexes = cfg['year_indexes']

        renderme = []
        dates = {}
        categories = {}

        # first we handle entries and categories
        listing = tools.get_entries(cfg, datadir)

        for mem in listing:
            # Skip files that have extensions we don't know what to do
            # with.
            ext = os.path.splitext(mem)[1].lstrip('.')
            if not ext in cfg['extensions'].keys():
                continue

            # Get the mtime of the entry.
            mtime = time.mktime(tools.filestat(self._request, mem))

            # remove the datadir from the front and the bit at the end
            mem = mem[len(datadir):mem.rfind('.')]

            # This is the compiled file filename.
            fn = os.path.normpath(compiledir + mem)

            if incremental:
                # If we're incrementally rendering, we check the mtime
                # for the compiled file for one of the themes. If the entry
                # is more recent than the compiled version, we recompile.
                # Otherwise we skip it.
                try:
                    smtime = os.stat(fn + '.' + themes[0])[8]
                    if smtime < mtime or not incremental:
                        continue

                except (IOError, OSError):
                    pass

            # Figure out category indexes to re-render.
            temp = os.path.dirname(mem).split(os.sep)
            for i in range(len(temp)+1):
                p = os.sep.join(temp[0:i])
                categories[p] = 0

            # Figure out year/month/day indexes to re-render.
            mtime = time.localtime(mtime)
            year = time.strftime('%Y', mtime)
            month = time.strftime('%m', mtime)
            day = time.strftime('%d', mtime)

            if yearindexes:
                dates[year] = 1

            if monthindexes:
                dates[year + '/' + month] = 1

            if dayindexes:
                dates[year + '/' + month + '/' + day] = 1

            # Toss each theme for this entry in the render queue.
            for f in themes:
                renderme.append((mem + '.' + f, ''))

        print '- Found {0} entry(es) ...'.format(len(renderme))

        if categories:
            categories = sorted(categories.keys())

            # if they have stuff in their root category, it'll add a "/"
            # to the category list and we want to remove that because it's
            # a duplicate of "".
            if '/' in categories:
                categories.remove('/')

            print '- Found {0} category index(es) ...'.format(len(categories))

            for mem in categories:
                mem = os.path.normpath(mem + '/index.')
                for f in index_themes:
                    renderme.append((mem + f, ''))

        if dates:
            dates = ['/' + d for d in sorted(dates.keys())]

            print '- Found {0} date index(es) ...'.format(len(dates))

            for mem in dates:
                mem = os.path.normpath(mem + '/index.')
                for f in index_themes:
                    renderme.append((mem + f, ''))

        additional_stuff = cfg['compile_urls']
        if additional_stuff:
            print '- Found {0} arbitrary url(s) ...'.format(
                len(additional_stuff))

            for mem in additional_stuff:
                if mem.find('?') != -1:
                    url = mem[:mem.find('?')]
                    query = mem[mem.find('?')+1:]
                else:
                    url = mem
                    query = ''

                renderme.append((url, query))

        # Pass the complete render list to all the plugins via
        # cb_compile_filelist and they can add to the filelist any
        # (url, query) tuples they want rendered.
        total = len(renderme)
        tools.run_callback('compile_filelist',
                           {'request': self._request,
                            'filelist': renderme,
                            'themes': themes,
                            'incremental': incremental})

        renderme = sorted(set(renderme))
        print '- Found {0} url(s) specified by plugins ...'.format(
            len(renderme) - total)

        print ''
        print 'Compiling {0} url(s) total.'.format(len(renderme))
        print ''

        print 'Rendering files ...'
        for url, q in renderme:
            url = url.replace(os.sep, '/')
            print '   Rendering {0} ...'.format(url)
            tools.render_url_statically(dict(cfg), url, q)

        # We're done, clean up
        self.cleanup()
Beispiel #17
0
def blosxom_handler(request):
    """This is the default blosxom handler.

    It calls the renderer callback to get a renderer.  If there is no
    renderer, it uses the blosxom renderer.

    It calls the pathinfo callback to process the path_info http
    variable.

    It calls the filelist callback to build a list of entries to
    display.

    It calls the prepare callback to do any additional preparation
    before rendering the entries.

    Then it tells the renderer to render the entries.

    :param request: the request object.
    """
    config = request.get_configuration()
    data = request.get_data()

    # go through the renderer callback to see if anyone else wants to
    # render.  this renderer gets stored in the data dict for
    # downstream processing.
    rend = tools.run_callback('renderer',
                              {'request': request},
                              donefunc = lambda x: x != None,
                              defaultfunc = lambda x: None)

    if not rend:
        # get the renderer we want to use
        rend = config.get("renderer", "jinjarenderer")

        # import the renderer
        rend = tools.importname("douglas.renderers", rend)

        # get the renderer object
        rend = rend.Renderer(request, config.get("stdoutput", sys.stdout))

    data['renderer'] = rend

    # generate the timezone variable
    data["timezone"] = time.tzname[time.localtime()[8]]

    # process the path info to determine what kind of blog entry(ies)
    # this is
    tools.run_callback("pathinfo",
                       {"request": request},
                       donefunc=lambda x:x != None,
                       defaultfunc=blosxom_process_path_info)

    # call the filelist callback to generate a list of entries
    data["entry_list"] = tools.run_callback(
        "filelist",
        {"request": request},
        donefunc=lambda x:x != None,
        defaultfunc=blosxom_file_list_handler)

    # figure out the blog-level mtime which is the mtime of the head
    # of the entry_list
    entry_list = data["entry_list"]
    if isinstance(entry_list, list) and len(entry_list) > 0:
        mtime = entry_list[0].get("mtime", time.time())
    else:
        mtime = time.time()
    mtime_tuple = time.localtime(mtime)
    mtime_gmtuple = time.gmtime(mtime)

    data["latest_date"] = time.strftime('%a, %d %b %Y', mtime_tuple)

    # Make sure we get proper 'English' dates when using standards
    loc = locale.getlocale(locale.LC_ALL)
    locale.setlocale(locale.LC_ALL, 'C')

    data["latest_w3cdate"] = time.strftime('%Y-%m-%dT%H:%M:%SZ',
                                           mtime_gmtuple)
    data['latest_rfc822date'] = time.strftime('%a, %d %b %Y %H:%M GMT',
                                              mtime_gmtuple)

    # set the locale back
    locale.setlocale(locale.LC_ALL, loc)

    # we pass the request with the entry_list through the prepare
    # callback giving everyone a chance to transform the data.  the
    # request is modified in place.
    tools.run_callback("prepare", {"request": request})

    # now we pass the entry_list through the renderer
    entry_list = data["entry_list"]
    renderer = data['renderer']

    if renderer and not renderer.rendered:
        if entry_list:
            renderer.set_content(entry_list)
        else:
            renderer.add_header('Status', '404 Not Found')
            renderer.set_content(
                {'title': 'The page you are looking for is not available',
                 'body': 'Somehow I cannot find the page you want. ' +
                 'Go Back to <a href="%s">%s</a>?'
                 % (config["base_url"], config["blog_title"])})
        renderer.render()

    elif not renderer:
        output = config.get('stdoutput', sys.stdout)
        output.write("Content-Type: text/plain\n\n" +
                     "There is something wrong with your setup.\n" +
                     "Check your config files and verify that your " +
                     "configuration is correct.\n")
Beispiel #18
0
    def run_static_renderer(self, incremental=False):
        """This will go through all possible things in the blog and
        statically render everything to the ``static_dir`` specified
        in the config file.

        This figures out all the possible ``path_info`` settings and
        calls ``self.run()`` a bazillion times saving each file.

        :param incremental: Whether (True) or not (False) to
                            incrementally render the pages.  If we're
                            incrementally rendering pages, then we
                            render only the ones that have changed.
        """
        self.initialize()

        config = self._request.get_configuration()
        data = self._request.get_data()
        print "Performing static rendering."
        if incremental:
            print "Incremental is set."

        staticdir = config.get("static_dir", "")
        datadir = config["datadir"]

        if not staticdir:
            print "Error: You must set static_dir in your config file."
            return 0

        themes = config.get("static_themes", ["html"])
        index_themes = config.get("static_index_themes", ["html"])

        renderme = []

        monthnames = config.get("static_monthnames", True)
        monthnumbers = config.get("static_monthnumbers", False)
        yearindexes = config.get("static_yearindexes", True)

        dates = {}
        categories = {}

        # first we handle entries and categories
        listing = tools.walk(self._request, datadir)

        for mem in listing:
            # skip the ones that have bad extensions
            ext = mem[mem.rfind(".")+1:]
            if not ext in data["extensions"].keys():
                continue

            # grab the mtime of the entry file
            mtime = time.mktime(tools.filestat(self._request, mem))

            # remove the datadir from the front and the bit at the end
            mem = mem[len(datadir):mem.rfind(".")]

            # this is the static filename
            fn = os.path.normpath(staticdir + mem)

            # grab the mtime of one of the statically rendered file
            try:
                smtime = os.stat(fn + "." + themes[0])[8]
            except:
                smtime = 0

            # if the entry is more recent than the static, we want to
            # re-render
            if smtime < mtime or not incremental:

                # grab the categories
                temp = os.path.dirname(mem).split(os.sep)
                for i in range(len(temp)+1):
                    p = os.sep.join(temp[0:i])
                    categories[p] = 0

                # grab the date
                mtime = time.localtime(mtime)
                year = time.strftime("%Y", mtime)
                month = time.strftime("%m", mtime)
                day = time.strftime("%d", mtime)

                if yearindexes:
                    dates[year] = 1

                if monthnumbers:
                    dates[year + "/" + month] = 1
                    dates[year + "/" + month + "/" + day] = 1

                if monthnames:
                    monthname = tools.num2month[month]
                    dates[year + "/" + monthname] = 1
                    dates[year + "/" + monthname + "/" + day] = 1

                # toss in the render queue
                for f in themes:
                    renderme.append((mem + "." + f, ""))

        print "rendering %d entries." % len(renderme)

        # handle categories
        categories = categories.keys()
        categories.sort()

        # if they have stuff in their root category, it'll add a "/"
        # to the category list and we want to remove that because it's
        # a duplicate of "".
        if "/" in categories:
            categories.remove("/")

        print "rendering %d category indexes." % len(categories)

        for mem in categories:
            mem = os.path.normpath(mem + "/index.")
            for f in index_themes:
                renderme.append((mem + f, ""))

        # now we handle dates
        dates = dates.keys()
        dates.sort()

        dates = ["/" + d for d in dates]

        print "rendering %d date indexes." % len(dates)

        for mem in dates:
            mem = os.path.normpath(mem + "/index.")
            for f in index_themes:
                renderme.append((mem + f, ""))

        # now we handle arbitrary urls
        additional_stuff = config.get("static_urls", [])
        print "rendering %d arbitrary urls." % len(additional_stuff)

        for mem in additional_stuff:
            if mem.find("?") != -1:
                url = mem[:mem.find("?")]
                query = mem[mem.find("?")+1:]
            else:
                url = mem
                query = ""

            renderme.append((url, query))

        # now we pass the complete render list to all the plugins via
        # cb_staticrender_filelist and they can add to the filelist
        # any (url, query) tuples they want rendered.
        print "(before) building %s files." % len(renderme)
        tools.run_callback("staticrender_filelist",
                           {'request': self._request,
                            'filelist': renderme,
                            'themes': themes,
                            'incremental': incremental})

        renderme = sorted(set(renderme))

        print "building %s files." % len(renderme)

        for url, q in renderme:
            url = url.replace(os.sep, "/")
            print "rendering '%s' ..." % url

            tools.render_url_statically(dict(config), url, q)

        # we're done, clean up
        self.cleanup()