Example #1
0
def edit():
    """Wiki page editor."""
    title = request.args.get("name", "")
    body = ""
    history = True  # Update History box is always checked by default
    note = request.args.get("note", "")

    # Editing an existing page?
    page = Wiki.get_page(title)
    if page:
        head = page["revisions"][0]
        body = head["body"]

    if request.method == "POST":
        # Submitting the form.
        action = request.form.get("action", "preview")
        title = request.form.get("name", "")
        body = request.form.get("body", "")
        history = request.form.get("history", "false") == "true"
        note = request.form.get("note", "")

        if action == "preview":
            # Just previewing it.
            g.info["preview"] = True

            # Render markdown
            g.info["rendered_body"] = Wiki.render_page(body)

            # Render emoticons.
            g.info["rendered_body"] = Emoticons.render(g.info["rendered_body"])
        elif action == "publish":
            # Publishing! Validate inputs.
            invalid = False

            if len(title) == 0:
                invalid = True
                flash("You must have a page title.")
            if len(body) == 0:
                invalid = True
                flash("You must have a page body.")

            if not invalid:
                # Update the page.
                Wiki.edit_page(
                    author=g.info["session"]["uid"],
                    name=title,
                    body=body,
                    note=note,
                    history=history,
                )
                return redirect(
                    url_for("wiki.view_page", name=Wiki.name_to_url(title)))

    g.info["title"] = title
    g.info["body"] = body
    g.info["note"] = note
    g.info["history"] = history
    return template("wiki/edit.html")
Example #2
0
def format_message(message):
    """HTML sanitize the message and format it for display."""

    # Comments use Markdown formatting, and HTML tags are escaped by default.
    message = render_markdown(message)

    # Process emoticons.
    message = Emoticons.render(message)
    return message
Example #3
0
def format_message(message):
    """HTML sanitize the message and format it for display."""

    # Comments use Markdown formatting, and HTML tags are escaped by default.
    message = render_markdown(message)

    # Process emoticons.
    message = Emoticons.render(message)
    return message
Example #4
0
def entry(fid):
    """Endpoint to view a specific blog entry."""

    # Resolve the friendly ID to a real ID.
    post_id = Blog.resolve_id(fid)
    if not post_id:
        flash("That blog post wasn't found.")
        return redirect(url_for(".index"))

    # Look up the post.
    post = Blog.get_entry(post_id)
    post["post_id"] = post_id

    # Body has a snipped section?
    if "<snip>" in post["body"]:
        post["body"] = re.sub(r'\s*<snip>\s*', '\n\n', post["body"])

    # Render the body.
    if post["format"] == "markdown":
        post["rendered_body"] = render_markdown(post["body"])
    else:
        post["rendered_body"] = post["body"]

    # Render emoticons.
    if post["emoticons"]:
        post["rendered_body"] = Emoticons.render(post["rendered_body"])

    # Get the author's information.
    post["profile"] = User.get_user(uid=post["author"])
    post["photo"]   = User.get_picture(uid=post["author"])
    post["photo_url"] = Config.photo.root_public

    # Pretty-print the time.
    post["pretty_time"] = pretty_time(Config.blog.time_format, post["time"])

    # Count the comments for this post
    post["comment_count"] = Comment.count_comments("blog-{}".format(post_id))

    # Inject information about this post's siblings.
    index = Blog.get_index()
    siblings = [None, None] # previous, next
    sorted_ids = list(map(lambda y: int(y), sorted(index.keys(), key=lambda x: index[x]["time"], reverse=True)))
    for i in range(0, len(sorted_ids)):
        if sorted_ids[i] == post_id:
            # Found us!
            if i > 0:
                # We have an older post.
                siblings[0] = index[ str(sorted_ids[i-1]) ]
            if i < len(sorted_ids) - 1:
                # We have a newer post.
                siblings[1] = index[ str(sorted_ids[i+1]) ]
    post["siblings"] = siblings

    g.info["post"] = post
    return template("blog/entry.html")
Example #5
0
def edit():
    """Wiki page editor."""
    title = request.args.get("name", "")
    body = ""
    history = True  # Update History box is always checked by default
    note = request.args.get("note", "")

    # Editing an existing page?
    page = Wiki.get_page(title)
    if page:
        head = page["revisions"][0]
        body = head["body"]

    if request.method == "POST":
        # Submitting the form.
        action = request.form.get("action", "preview")
        title = request.form.get("name", "")
        body = request.form.get("body", "")
        history = request.form.get("history", "false") == "true"
        note = request.form.get("note", "")

        if action == "preview":
            # Just previewing it.
            g.info["preview"] = True

            # Render markdown
            g.info["rendered_body"] = Wiki.render_page(body)

            # Render emoticons.
            g.info["rendered_body"] = Emoticons.render(g.info["rendered_body"])
        elif action == "publish":
            # Publishing! Validate inputs.
            invalid = False

            if len(title) == 0:
                invalid = True
                flash("You must have a page title.")
            if len(body) == 0:
                invalid = True
                flash("You must have a page body.")

            if not invalid:
                # Update the page.
                Wiki.edit_page(author=g.info["session"]["uid"], name=title, body=body, note=note, history=history)
                return redirect(url_for("wiki.view_page", name=Wiki.name_to_url(title)))

    g.info["title"] = title
    g.info["body"] = body
    g.info["note"] = note
    g.info["history"] = history
    return template("wiki/edit.html")
Example #6
0
def view_page(name):
    """Show a specific wiki page."""
    link = name
    name = Wiki.url_to_name(name)

    g.info["link"] = link
    g.info["title"] = name

    # Look up the page.
    page = Wiki.get_page(name)
    if not page:
        # Page doesn't exist... yet!
        g.info["title"] = Wiki.url_to_name(name)
        return template("wiki/missing.html"), 404

    # Which revision to show?
    version = request.args.get("revision", None)
    if version:
        # Find this one.
        rev = None
        for item in page["revisions"]:
            if item["id"] == version:
                rev = item
                break

        if rev is None:
            flash("That revision was not found for this page.")
            rev = page["revisions"][0]
    else:
        # Show the latest one.
        rev = page["revisions"][0]

    # Getting the plain text source?
    if request.args.get("source", None):
        g.info["markdown"] = render_markdown(
            "\n".join(["# Source: {}".format(name), "", "```markdown", rev["body"], "```"])
        )
        return template("markdown.inc.html")

    # Render it!
    g.info["rendered_body"] = Wiki.render_page(rev["body"])
    g.info["rendered_body"] = Emoticons.render(g.info["rendered_body"])
    g.info["pretty_time"] = pretty_time(Config.wiki.time_format, rev["time"])

    # Author info
    g.info["author"] = User.get_user(uid=rev["author"])

    return template("wiki/page.html")
Example #7
0
def view_page(name):
    """Show a specific wiki page."""
    link = name
    name = Wiki.url_to_name(name)

    g.info["link"] = link
    g.info["title"] = name

    # Look up the page.
    page = Wiki.get_page(name)
    if not page:
        # Page doesn't exist... yet!
        g.info["title"] = Wiki.url_to_name(name)
        return template("wiki/missing.html"), 404

    # Which revision to show?
    version = request.args.get("revision", None)
    if version:
        # Find this one.
        rev = None
        for item in page["revisions"]:
            if item["id"] == version:
                rev = item
                break

        if rev is None:
            flash("That revision was not found for this page.")
            rev = page["revisions"][0]
    else:
        # Show the latest one.
        rev = page["revisions"][0]

    # Getting the plain text source?
    if request.args.get("source", None):
        g.info["markdown"] = render_markdown("\n".join([
            "# Source: {}".format(name), "", "```markdown", rev["body"], "```"
        ]))
        return template("markdown.inc.html")

    # Render it!
    g.info["rendered_body"] = Wiki.render_page(rev["body"])
    g.info["rendered_body"] = Emoticons.render(g.info["rendered_body"])
    g.info["pretty_time"] = pretty_time(Config.wiki.time_format, rev["time"])

    # Author info
    g.info["author"] = User.get_user(uid=rev["author"])

    return template("wiki/page.html")
Example #8
0
def entry(fid):
    """Endpoint to view a specific blog entry."""

    # Resolve the friendly ID to a real ID.
    post_id = Blog.resolve_id(fid, drafts=True)
    if not post_id:
        # See if the friendly ID contains any extraneous dashes at the front
        # or end, and remove them and see if we have a match. This allows for
        # fixing blog fid's that allowed leading/trailing dashes and having the
        # old URL just redirect to the new one.
        fid = fid.strip("-")
        post_id = Blog.resolve_id(fid, drafts=True)

        # If still nothing, try consolidating extra dashes into one.
        if not post_id:
            fid = re.sub(r'-+', '-', fid)
            post_id = Blog.resolve_id(fid, drafts=True)

        # Did we find one now?
        if post_id:
            return redirect(url_for(".entry", fid=fid))

        flash("That blog post wasn't found.")
        return redirect(url_for(".index"))

    # Look up the post.
    post = Blog.get_entry(post_id)
    post["post_id"] = post_id

    # Body has a snipped section?
    if "<snip>" in post["body"]:
        post["body"] = re.sub(r'\s*<snip>\s*', '\n\n', post["body"])

    # Render the body.
    if post["format"] == "markdown":
        post["rendered_body"] = render_markdown(post["body"])
    else:
        post["rendered_body"] = post["body"]

    # Render emoticons.
    if post["emoticons"]:
        post["rendered_body"] = Emoticons.render(post["rendered_body"])

    # Get the author's information.
    post["profile"] = User.get_user(uid=post["author"])
    post["photo"]   = User.get_picture(uid=post["author"])
    post["photo_url"] = Config.photo.root_public

    # Pretty-print the time.
    post["pretty_time"] = pretty_time(Config.blog.time_format, post["time"])

    # Count the comments for this post
    post["comment_count"] = Comment.count_comments("blog-{}".format(post_id))

    # Inject information about this post's siblings.
    index = Blog.get_index()
    siblings = [None, None] # previous, next
    sorted_ids = list(map(lambda y: int(y), sorted(index.keys(), key=lambda x: index[x]["time"], reverse=True)))
    for i in range(0, len(sorted_ids)):
        if sorted_ids[i] == post_id:
            # Found us!
            if i > 0:
                # We have an older post.
                siblings[0] = index[ str(sorted_ids[i-1]) ]
            if i < len(sorted_ids) - 1:
                # We have a newer post.
                siblings[1] = index[ str(sorted_ids[i+1]) ]
    post["siblings"] = siblings

    g.info["post"] = post
    return template("blog/entry.html")
Example #9
0
def partial_index(template_name="blog/index.inc.html", mode="normal"):
    """Partial template for including the index view of the blog.

    Args:
        template_name (str): The name of the template to be rendered.
        mode (str): The view mode of the posts, one of:
            - normal: Only list public entries, or private posts for users
                who are logged in.
            - drafts: Only list draft entries for logged-in users.
    """

    # Get the blog index.
    if mode == "normal":
        index = Blog.get_index()
    elif mode == "drafts":
        index = Blog.get_drafts()
    elif mode == "private":
        index = Blog.get_private()
    else:
        return "Invalid partial_index mode."

    # Let the pages know what mode they're in.
    g.info["mode"] = mode

    pool  = {} # The set of blog posts to show.

    category = g.info.get("url_category", None)
    if category == Config.blog.default_category:
        category = ""

    # Are we narrowing by category?
    if category is not None:
        # Narrow down the index to just those that match the category.
        for post_id, data in index.items():
            if not category in data["categories"]:
                continue
            pool[post_id] = data

        # No such category?
        if len(pool) == 0:
            flash("There are no posts with that category.")
            return redirect(url_for(".index"))
    else:
        pool = index

    # Get the posts we want.
    posts = get_index_posts(pool)

    # Handle pagination.
    offset = request.args.get("skip", 0)
    try:    offset = int(offset)
    except: offset = 0

    # Handle the offsets, and get those for the "older" and "earlier" posts.
    # "earlier" posts count down (towards index 0), "older" counts up.
    g.info["offset"]  = offset
    g.info["earlier"] = offset - int(Config.blog.entries_per_page) if offset > 0 else 0
    g.info["older"]   = offset + int(Config.blog.entries_per_page)
    if g.info["earlier"] < 0:
        g.info["earlier"] = 0
    if g.info["older"] < 0 or g.info["older"] > len(posts) - 1:
        g.info["older"] = 0
    g.info["count"] = 0

    # Can we go to other pages?
    g.info["can_earlier"] = True if offset > 0 else False
    g.info["can_older"]   = False if g.info["older"] == 0 else True

    # Load the selected posts.
    selected = []
    stop = offset + int(Config.blog.entries_per_page)
    if stop > len(posts): stop = len(posts)
    index = 1 # Let each post know its position on-page.
    for i in range(offset, stop):
        post_id = posts[i]
        post    = Blog.get_entry(post_id)

        post["post_id"] = post_id

        # Body has a snipped section?
        if "<snip>" in post["body"]:
            post["body"] = post["body"].split("<snip>")[0]
            post["snipped"] = True

        # Render the body.
        if post["format"] == "markdown":
            post["rendered_body"] = render_markdown(post["body"])
        else:
            post["rendered_body"] = post["body"]

        # Render emoticons.
        if post["emoticons"]:
            post["rendered_body"] = Emoticons.render(post["rendered_body"])

        # Get the author's information.
        post["profile"] = User.get_user(uid=post["author"])
        post["photo"]   = User.get_picture(uid=post["author"])
        post["photo_url"] = Config.photo.root_public

        post["pretty_time"] = pretty_time(Config.blog.time_format, post["time"])

        # Count the comments for this post
        post["comment_count"] = Comment.count_comments("blog-{}".format(post_id))
        post["position_index"] = index
        index += 1

        selected.append(post)
        g.info["count"] += 1

    g.info["category"] = category
    g.info["posts"] = selected

    return template(template_name)
Example #10
0
def rss():
    """RSS feed for the blog."""
    doc = Document()

    rss = doc.createElement("rss")
    rss.setAttribute("version", "2.0")
    rss.setAttribute("xmlns:blogChannel", "http://backend.userland.com/blogChannelModule")
    doc.appendChild(rss)

    channel = doc.createElement("channel")
    rss.appendChild(channel)

    rss_time = "%a, %d %b %Y %H:%M:%S GMT"

    ######
    ## Channel Information
    ######

    today = time.strftime(rss_time, time.gmtime())

    xml_add_text_tags(doc, channel, [
        ["title", Config.blog.title],
        ["link", Config.blog.link],
        ["description", Config.blog.description],
        ["language", Config.blog.language],
        ["copyright", Config.blog.copyright],
        ["pubDate", today],
        ["lastBuildDate", today],
        ["webmaster", Config.blog.webmaster],
    ])

    ######
    ## Image Information
    ######

    image = doc.createElement("image")
    channel.appendChild(image)
    xml_add_text_tags(doc, image, [
        ["title", Config.blog.image_title],
        ["url", Config.blog.image_url],
        ["link", Config.blog.link],
        ["width", Config.blog.image_width],
        ["height", Config.blog.image_height],
        ["description", Config.blog.image_description],
    ])

    ######
    ## Add the blog posts
    ######

    index = Blog.get_index()
    posts = get_index_posts(index)
    for post_id in posts[:int(Config.blog.entries_per_feed)]:
        post = Blog.get_entry(post_id)
        item = doc.createElement("item")
        channel.appendChild(item)

        # Render the body.
        if post["format"] == "markdown":
            post["rendered_body"] = render_markdown(post["body"])
        else:
            post["rendered_body"] = post["body"]

        # Render emoticons.
        if post["emoticons"]:
            post["rendered_body"] = Emoticons.render(post["rendered_body"])

        xml_add_text_tags(doc, item, [
            ["title", post["subject"]],
            ["link", url_for("blog.entry", fid=post["fid"], _external=True)],
            ["description", post["rendered_body"]],
            ["pubDate", time.strftime(rss_time, time.gmtime(post["time"]))],
        ])

    resp = make_response(doc.toprettyxml(encoding="utf-8"))
    resp.headers["Content-Type"] = "application/rss+xml; charset=utf-8"
    return resp
Example #11
0
def update():
    """Post/edit a blog entry."""

    # Get our available avatars.
    g.info["avatars"] = Blog.list_avatars()
    g.info["userpic"] = User.get_picture(uid=g.info["session"]["uid"])

    # Default vars.
    g.info.update(dict(
        post_id="",
        fid="",
        author=g.info["session"]["uid"],
        subject="",
        body="",
        format="markdown",
        avatar="",
        categories="",
        privacy=Config.blog.default_privacy,
        sticky=False,
        emoticons=True,
        comments=Config.blog.allow_comments,
        preview=False,
    ))

    # Editing an existing post?
    post_id = request.args.get("id", request.form.get("id", None))
    if post_id:
        post_id = Blog.resolve_id(post_id, drafts=True)
        if post_id:
            logger.info("Editing existing blog post {}".format(post_id))
            post = Blog.get_entry(post_id)
            g.info["post_id"] = post_id
            g.info["post"] = post

            # Copy fields.
            for field in ["author", "fid", "subject", "time", "format",
                          "body", "avatar", "categories", "privacy",
                          "sticky", "emoticons", "comments"]:
                g.info[field] = post[field]

    # Are we SUBMITTING the form?
    if request.method == "POST":
        action = request.form.get("action")

        # Get all the fields from the posted params.
        g.info["post_id"] = request.form.get("id")
        for field in ["fid", "subject", "format", "body", "avatar", "categories", "privacy"]:
            g.info[field] = request.form.get(field)
        for boolean in ["sticky", "emoticons", "comments"]:
            g.info[boolean] = True if request.form.get(boolean, None) == "true" else False
        g.info["author"] = int(g.info["author"])

        # What action are they doing?
        if action == "preview":
            g.info["preview"] = True

            # Render markdown?
            if g.info["format"] == "markdown":
                g.info["rendered_body"] = render_markdown(g.info["body"])
            else:
                g.info["rendered_body"] = g.info["body"]

            # Render emoticons.
            if g.info["emoticons"]:
                g.info["rendered_body"] = Emoticons.render(g.info["rendered_body"])

        elif action == "publish":
            # Publishing! Validate inputs first.
            invalid = False
            if len(g.info["body"]) == 0:
                invalid = True
                flash("You must enter a body for your blog post.")
            if len(g.info["subject"]) == 0:
                invalid = True
                flash("You must enter a subject for your blog post.")

            # Resetting the post's time stamp?
            if not request.form.get("id") or request.form.get("reset-time"):
                g.info["time"] = float(time.time())
            else:
                g.info["time"] = float(request.form.get("time", time.time()))

            # Format the categories.
            tags = []
            for tag in g.info["categories"].split(","):
                tags.append(tag.strip())

            # Okay to update?
            if invalid is False:
                new_id, new_fid = Blog.post_entry(
                    post_id    = g.info["post_id"],
                    epoch      = g.info["time"],
                    author     = g.info["author"],
                    subject    = g.info["subject"],
                    fid        = g.info["fid"],
                    avatar     = g.info["avatar"],
                    categories = tags,
                    privacy    = g.info["privacy"],
                    ip         = remote_addr(),
                    emoticons  = g.info["emoticons"],
                    sticky     = g.info["sticky"],
                    comments   = g.info["comments"],
                    format     = g.info["format"],
                    body       = g.info["body"],
                )

                return redirect(url_for(".entry", fid=new_fid))


    if type(g.info["categories"]) is list:
        g.info["categories"] = ", ".join(g.info["categories"])

    return template("blog/update.html")
Example #12
0
def partial_index(template_name="blog/index.inc.html"):
    """Partial template for including the index view of the blog."""

    # Get the blog index.
    index = Blog.get_index()
    pool  = {} # The set of blog posts to show.

    category = g.info.get("url_category", None)
    if category == Config.blog.default_category:
        category = ""

    # Are we narrowing by category?
    if category is not None:
        # Narrow down the index to just those that match the category.
        for post_id, data in index.items():
            if not category in data["categories"]:
                continue
            pool[post_id] = data

        # No such category?
        if len(pool) == 0:
            flash("There are no posts with that category.")
            return redirect(url_for(".index"))
    else:
        pool = index

    # Get the posts we want.
    posts = get_index_posts(pool)

    # Handle pagination.
    offset = request.args.get("skip", 0)
    try:    offset = int(offset)
    except: offset = 0

    # Handle the offsets, and get those for the "older" and "earlier" posts.
    # "earlier" posts count down (towards index 0), "older" counts up.
    g.info["offset"]  = offset
    g.info["earlier"] = offset - int(Config.blog.entries_per_page) if offset > 0 else 0
    g.info["older"]   = offset + int(Config.blog.entries_per_page)
    if g.info["earlier"] < 0:
        g.info["earlier"] = 0
    if g.info["older"] < 0 or g.info["older"] > len(posts):
        g.info["older"] = 0
    g.info["count"] = 0

    # Can we go to other pages?
    g.info["can_earlier"] = True if offset > 0 else False
    g.info["can_older"]   = False if g.info["older"] == 0 else True

    # Load the selected posts.
    selected = []
    stop = offset + int(Config.blog.entries_per_page)
    if stop > len(posts): stop = len(posts)
    index = 1 # Let each post know its position on-page.
    for i in range(offset, stop):
        post_id = posts[i]
        post    = Blog.get_entry(post_id)

        post["post_id"] = post_id

        # Body has a snipped section?
        if "<snip>" in post["body"]:
            post["body"] = post["body"].split("<snip>")[0]
            post["snipped"] = True

        # Render the body.
        if post["format"] == "markdown":
            post["rendered_body"] = render_markdown(post["body"])
        else:
            post["rendered_body"] = post["body"]

        # Render emoticons.
        if post["emoticons"]:
            post["rendered_body"] = Emoticons.render(post["rendered_body"])

        # Get the author's information.
        post["profile"] = User.get_user(uid=post["author"])
        post["photo"]   = User.get_picture(uid=post["author"])
        post["photo_url"] = Config.photo.root_public

        post["pretty_time"] = pretty_time(Config.blog.time_format, post["time"])

        # Count the comments for this post
        post["comment_count"] = Comment.count_comments("blog-{}".format(post_id))
        post["position_index"] = index
        index += 1

        selected.append(post)
        g.info["count"] += 1

    g.info["category"] = category
    g.info["posts"] = selected

    return template(template_name)
Example #13
0
def rss():
    """RSS feed for the blog."""
    doc = Document()

    rss = doc.createElement("rss")
    rss.setAttribute("version", "2.0")
    rss.setAttribute("xmlns:blogChannel", "http://backend.userland.com/blogChannelModule")
    doc.appendChild(rss)

    channel = doc.createElement("channel")
    rss.appendChild(channel)

    rss_time = "%a, %d %b %Y %H:%M:%S GMT"

    ######
    ## Channel Information
    ######

    today = time.strftime(rss_time, time.gmtime())

    xml_add_text_tags(doc, channel, [
        ["title", Config.blog.title],
        ["link", Config.blog.link],
        ["description", Config.blog.description],
        ["language", Config.blog.language],
        ["copyright", Config.blog.copyright],
        ["pubDate", today],
        ["lastBuildDate", today],
        ["webmaster", Config.blog.webmaster],
    ])

    ######
    ## Image Information
    ######

    image = doc.createElement("image")
    channel.appendChild(image)
    xml_add_text_tags(doc, image, [
        ["title", Config.blog.image_title],
        ["url", Config.blog.image_url],
        ["link", Config.blog.link],
        ["width", Config.blog.image_width],
        ["height", Config.blog.image_height],
        ["description", Config.blog.image_description],
    ])

    ######
    ## Add the blog posts
    ######

    index = Blog.get_index()
    posts = get_index_posts(index)
    for post_id in posts[:int(Config.blog.entries_per_feed)]:
        post = Blog.get_entry(post_id)
        item = doc.createElement("item")
        channel.appendChild(item)

        # Render the body.
        if post["format"] == "markdown":
            post["rendered_body"] = render_markdown(post["body"])
        else:
            post["rendered_body"] = post["body"]

        # Render emoticons.
        if post["emoticons"]:
            post["rendered_body"] = Emoticons.render(post["rendered_body"])

        xml_add_text_tags(doc, item, [
            ["title", post["subject"]],
            ["link", url_for("blog.entry", fid=post["fid"], _external=True)],
            ["description", post["rendered_body"]],
            ["pubDate", time.strftime(rss_time, time.gmtime(post["time"]))],
        ])

    resp = make_response(doc.toprettyxml(encoding="utf-8"))
    resp.headers["Content-Type"] = "application/rss+xml; charset=utf-8"
    return resp
Example #14
0
def update():
    """Post/edit a blog entry."""

    # Get our available avatars.
    g.info["avatars"] = Blog.list_avatars()
    g.info["userpic"] = User.get_picture(uid=g.info["session"]["uid"])

    # Default vars.
    g.info.update(dict(
        post_id="",
        fid="",
        author=g.info["session"]["uid"],
        subject="",
        body="",
        format="markdown",
        avatar="",
        categories="",
        privacy=Config.blog.default_privacy,
        emoticons=True,
        comments=Config.blog.allow_comments,
        month="",
        day="",
        year="",
        hour="",
        min="",
        sec="",
        preview=False,
    ))

    # Editing an existing post?
    post_id = request.args.get("id", None)
    if post_id:
        post_id = Blog.resolve_id(post_id)
        if post_id:
            logger.info("Editing existing blog post {}".format(post_id))
            post = Blog.get_entry(post_id)
            g.info["post_id"] = post_id
            g.info["post"] = post

            # Copy fields.
            for field in ["author", "fid", "subject", "format", "format",
                          "body", "avatar", "categories", "privacy",
                          "emoticons", "comments"]:
                g.info[field] = post[field]

            # Dissect the time.
            date = datetime.datetime.fromtimestamp(post["time"])
            g.info.update(dict(
                month="{:02d}".format(date.month),
                day="{:02d}".format(date.day),
                year=date.year,
                hour="{:02d}".format(date.hour),
                min="{:02d}".format(date.minute),
                sec="{:02d}".format(date.second),
            ))

    # Are we SUBMITTING the form?
    if request.method == "POST":
        action = request.form.get("action")

        # Get all the fields from the posted params.
        g.info["post_id"] = request.form.get("id")
        for field in ["fid", "subject", "format", "body", "avatar", "categories", "privacy"]:
            g.info[field] = request.form.get(field)
        for boolean in ["emoticons", "comments"]:
            g.info[boolean] = True if request.form.get(boolean, None) == "true" else False
        for number in ["author", "month", "day", "year", "hour", "min", "sec"]:
            g.info[number] = int(request.form.get(number, 0))

        # What action are they doing?
        if action == "preview":
            g.info["preview"] = True

            # Render markdown?
            if g.info["format"] == "markdown":
                g.info["rendered_body"] = render_markdown(g.info["body"])
            else:
                g.info["rendered_body"] = g.info["body"]

            # Render emoticons.
            if g.info["emoticons"]:
                g.info["rendered_body"] = Emoticons.render(g.info["rendered_body"])

        elif action == "publish":
            # Publishing! Validate inputs first.
            invalid = False
            if len(g.info["body"]) == 0:
                invalid = True
                flash("You must enter a body for your blog post.")
            if len(g.info["subject"]) == 0:
                invalid = True
                flash("You must enter a subject for your blog post.")

            # Make sure the times are valid.
            date = None
            try:
                date = datetime.datetime(
                    g.info["year"],
                    g.info["month"],
                    g.info["day"],
                    g.info["hour"],
                    g.info["min"],
                    g.info["sec"],
                )
            except ValueError as e:
                invalid = True
                flash("Invalid date/time: " + str(e))

            # Format the categories.
            tags = []
            for tag in g.info["categories"].split(","):
                tags.append(tag.strip())

            # Okay to update?
            if invalid is False:
                # Convert the date into a Unix time stamp.
                epoch = float(date.strftime("%s"))

                new_id, new_fid = Blog.post_entry(
                    post_id    = g.info["post_id"],
                    epoch      = epoch,
                    author     = g.info["author"],
                    subject    = g.info["subject"],
                    fid        = g.info["fid"],
                    avatar     = g.info["avatar"],
                    categories = tags,
                    privacy    = g.info["privacy"],
                    ip         = remote_addr(),
                    emoticons  = g.info["emoticons"],
                    comments   = g.info["comments"],
                    format     = g.info["format"],
                    body       = g.info["body"],
                )

                return redirect(url_for(".entry", fid=new_fid))


    if type(g.info["categories"]) is list:
        g.info["categories"] = ", ".join(g.info["categories"])

    return template("blog/update.html")