Example #1
0
def rotate_photo(key, rotate):
    """Rotate a photo 90 degrees to the left or right."""
    photo = get_photo(key)
    if not photo: return

    # Degrees to rotate.
    degrees = None
    if rotate == "left":
        degrees = 90
    elif rotate == "right":
        degrees = -90
    else:
        degrees = 180

    new_names = dict()
    for size in ["large", "thumb", "avatar"]:
        fname = os.path.join(Config.photo.root_private, photo[size])
        logger.info("Rotating image {} by {} degrees.".format(fname, degrees))

        # Give it a new name.
        filetype = fname.split(".")[-1]
        outfile = random_name(filetype)
        new_names[size] = outfile

        img = Image.open(fname)
        img = img.rotate(degrees)
        img.save(os.path.join(Config.photo.root_private, outfile))

        # Delete the old name.
        os.unlink(fname)

    # Save the new image names.
    edit_photo(key, new_names)
Example #2
0
def send_email(to, subject, message, sender=None, reply_to=None):
    """Send an e-mail out."""
    if sender is None:
        sender = Config.mail.sender

    if type(to) != list:
        to = [to]

    logger.info("Send email to {}".format(to))
    if Config.mail.method == "smtp":
        # Send mail with SMTP.
        for email in to:
            msg = MIMEMultipart("alternative")
            msg.set_charset("utf-8")

            msg["Subject"] = subject
            msg["From"] = sender
            msg["To"] = email
            if reply_to is not None:
                msg["Reply-To"] = reply_to

            text = MIMEText(message, "plain", "utf-8")
            msg.attach(text)

            # Send the e-mail.
            try:
                server = smtplib.SMTP(Config.mail.server, Config.mail.port)
                server.sendmail(sender, [email], msg.as_string())
            except:
                pass
Example #3
0
def delete(document):
    """Delete a document from the DB."""
    path = mkpath(document)
    if os.path.isfile(path):
        logger.info("Delete DB document: {}".format(path))
        os.unlink(path)
        del_cache(document)
Example #4
0
def edit_page(name, author, body, note, history=True):
    """Write to a page."""
    name = name.strip("/") # Remove any surrounding slashes.

    # Get the old page first.
    page = get_page(name)
    if not page:
        # Initialize the page.
        page = dict(
            revisions=[],
        )

    # The new revision to be added.
    rev = dict(
        id=hashlib.md5(str(int(time.time())).encode("utf-8")).hexdigest(),
        time=int(time.time()),
        author=author,
        body=body,
        note=note or "Updated the page.",
    )

    # Updating the history?
    if history:
        page["revisions"].insert(0, rev)
    else:
        # Replacing the original item.
        if len(page["revisions"]):
            page["revisions"][0] = rev
        else:
            page["revisions"].append(rev)

    # Write it.
    logger.info("Write to Wiki page {}".format(name))
    JsonDB.commit("wiki/pages/{}".format(name), page)
    return True
Example #5
0
def edit_page(name, author, body, note, history=True):
    """Write to a page."""
    name = name.strip("/")  # Remove any surrounding slashes.

    # Get the old page first.
    page = get_page(name)
    if not page:
        # Initialize the page.
        page = dict(revisions=[], )

    # The new revision to be added.
    rev = dict(
        id=hashlib.md5(str(int(time.time())).encode("utf-8")).hexdigest(),
        time=int(time.time()),
        author=author,
        body=body,
        note=note or "Updated the page.",
    )

    # Updating the history?
    if history:
        page["revisions"].insert(0, rev)
    else:
        # Replacing the original item.
        if len(page["revisions"]):
            page["revisions"][0] = rev
        else:
            page["revisions"].append(rev)

    # Write it.
    logger.info("Write to Wiki page {}".format(name))
    JsonDB.commit("wiki/pages/{}".format(name), page)
    return True
Example #6
0
def rotate_photo(key, rotate):
    """Rotate a photo 90 degrees to the left or right."""
    photo = get_photo(key)
    if not photo: return

    # Degrees to rotate.
    degrees = None
    if rotate == "left":
        degrees = 90
    elif rotate == "right":
        degrees = -90
    else:
        degrees = 180

    new_names = dict()
    for size in ["large", "thumb", "avatar"]:
        fname = os.path.join(Config.photo.root_private, photo[size])
        logger.info("Rotating image {} by {} degrees.".format(fname, degrees))

        # Give it a new name.
        filetype = fname.split(".")[-1]
        outfile = random_name(filetype)
        new_names[size] = outfile

        img = Image.open(fname)
        img = img.rotate(degrees)
        img.save(os.path.join(Config.photo.root_private, outfile))

        # Delete the old name.
        os.unlink(fname)

    # Save the new image names.
    edit_photo(key, new_names)
Example #7
0
def delete_history(name, revision):
    """Delete a revision entry from the history."""
    name = name.strip("/")

    # Get page first.
    page = get_page(name)
    if not page:
        return None

    # Revise history.
    history = list()
    for rev in page["revisions"]:
        if rev["id"] == revision:
            logger.info("Delete history ID {} from Wiki page {}".format(revision, name))
            continue
        history.append(rev)

    # Empty history = delete the page.
    if len(history) == 0:
        logger.info("Deleted last history item; Remove Wiki page {}".format(name))
        return delete_page(name)

    page["revisions"] = history
    JsonDB.commit("wiki/pages/{}".format(name), page)

    return True
Example #8
0
def send_email(to, subject, message, sender=None, reply_to=None):
    """Send an e-mail out."""
    if sender is None:
        sender = Config.mail.sender

    if type(to) != list:
        to = [to]

    logger.info("Send email to {}".format(to))
    if Config.mail.method == "smtp":
        # Send mail with SMTP.
        for email in to:
            msg = MIMEMultipart("alternative")
            msg.set_charset("utf-8")

            msg["Subject"] = subject
            msg["From"] = sender
            msg["To"] = email
            if reply_to is not None:
                msg["Reply-To"] = reply_to

            text = MIMEText(message, "plain", "utf-8")
            msg.attach(text)

            # Send the e-mail.
            try:
                server = smtplib.SMTP(Config.mail.server, Config.mail.port)
                server.sendmail(sender, [email], msg.as_string())
            except:
                pass
Example #9
0
def delete_history(name, revision):
    """Delete a revision entry from the history."""
    name = name.strip("/")

    # Get page first.
    page = get_page(name)
    if not page:
        return None

    # Revise history.
    history = list()
    for rev in page["revisions"]:
        if rev["id"] == revision:
            logger.info("Delete history ID {} from Wiki page {}".format(
                revision, name))
            continue
        history.append(rev)

    # Empty history = delete the page.
    if len(history) == 0:
        logger.info(
            "Deleted last history item; Remove Wiki page {}".format(name))
        return delete_page(name)

    page["revisions"] = history
    JsonDB.commit("wiki/pages/{}".format(name), page)

    return True
Example #10
0
def delete(document):
    """Delete a document from the DB."""
    path = mkpath(document)
    if os.path.isfile(path):
        logger.info("Delete DB document: {}".format(path))
        os.unlink(path)
        del_cache(document)
Example #11
0
def set_album_cover(album, key):
    """Change the album's cover photo."""
    album = sanitize_name(album)
    index = get_index()
    logger.info("Changing album cover for {} to {}".format(album, key))
    if album in index["albums"] and key in index["albums"][album]:
        index["covers"][album] = key
        write_index(index)
        return
    logger.error("Failed to change album index! Album or photo not found.")
Example #12
0
def set_album_cover(album, key):
    """Change the album's cover photo."""
    album = sanitize_name(album)
    index = get_index()
    logger.info("Changing album cover for {} to {}".format(album, key))
    if album in index["albums"] and key in index["albums"][album]:
        index["covers"][album] = key
        write_index(index)
        return
    logger.error("Failed to change album index! Album or photo not found.")
Example #13
0
def delete_page(name):
    """Completely delete a wiki page."""
    name = name.strip("/")
    path = "wiki/pages/{}".format(name)

    if JsonDB.exists(path):
        logger.info("Delete Wiki page {}".format(name))
        JsonDB.delete(path)

    return True
Example #14
0
def delete_page(name):
    """Completely delete a wiki page."""
    name = name.strip("/")
    path = "wiki/pages/{}".format(name)

    if JsonDB.exists(path):
        logger.info("Delete Wiki page {}".format(name))
        JsonDB.delete(path)

    return True
Example #15
0
def add_subscriber(thread, email):
    """Add a subscriber to a thread."""
    if not "@" in email:
        return

    # Sanity check: only subscribe to threads that exist.
    if not JsonDB.exists("comments/threads/{}".format(thread)):
        return

    logger.info("Subscribe e-mail {} to thread {}".format(email, thread))
    subs = get_subscribers(thread)
    subs[email] = int(time.time())
    write_subscribers(thread, subs)
Example #16
0
def edit_photo(key, data):
    """Update a photo's data."""
    index = get_index()
    if not key in index["map"]:
        logger.warning("Tried to delete photo {} but it wasn't found?".format(key))
        return

    album = index["map"][key]

    logger.info("Updating data for the photo {} from album {}".format(key, album))
    index["albums"][album][key].update(data)

    write_index(index)
Example #17
0
def add_subscriber(thread, email):
    """Add a subscriber to a thread."""
    if not "@" in email:
        return

    # Sanity check: only subscribe to threads that exist.
    if not JsonDB.exists("comments/threads/{}".format(thread)):
        return

    logger.info("Subscribe e-mail {} to thread {}".format(email, thread))
    subs = get_subscribers(thread)
    subs[email] = int(time.time())
    write_subscribers(thread, subs)
Example #18
0
def create(username, password, name=None, uid=None, role="user"):
    """Create a new user account.

    Returns the user ID number assigned to this user."""
    # Name defaults to username.
    if name is None:
        name = username

    username = username.lower()

    # Provided with a user ID?
    if uid is not None:
        # See if it's available.
        if exists(uid=uid):
            logger.warning("Wanted to use UID {} for user {} but it wasn't available.".format(uid, username))
            uid = None

    # Need to generate a UID?
    if uid is None:
        uid = get_next_uid()

    uid = int(uid)

    # Username musn't exist.
    if exists(username):
        # The front-end shouldn't let this happen.
        raise Exception("Can't create username {}: already exists!".format(username))

    # Crypt their password.
    hashedpass = hash_password(password)

    logger.info("Create user {} with username {}".format(uid, username))

    # Create the user file.
    JsonDB.commit("users/by-id/{}".format(uid), dict(
        uid=uid,
        username=username,
        name=name,
        picture="",
        role=role,
        password=hashedpass,
        created=time.time(),
    ))

    # And their username to ID map.
    JsonDB.commit("users/by-name/{}".format(username), dict(
        uid=uid,
    ))

    return uid
Example #19
0
def edit_photo(key, data):
    """Update a photo's data."""
    index = get_index()
    if not key in index["map"]:
        logger.warning(
            "Tried to delete photo {} but it wasn't found?".format(key))
        return

    album = index["map"][key]

    logger.info("Updating data for the photo {} from album {}".format(
        key, album))
    index["albums"][album][key].update(data)

    write_index(index)
Example #20
0
def unsubscribe(thread, email):
    """Unsubscribe an e-mail address from a thread.

    If `thread` is `*`, the e-mail is unsubscribed from all threads."""

    # Which threads to unsubscribe from?
    threads = []
    if thread == "*":
        threads = JsonDB.list_docs("comments/subscribers")
    else:
        threads = [thread]

    # Remove them as a subscriber.
    for thread in threads:
        if JsonDB.exists("comments/subscribers/{}".format(thread)):
            logger.info("Unsubscribe e-mail address {} from comment thread {}".format(email, thread))
            db = get_subscribers(thread)
            del db[email]
            write_subscribers(thread, db)
Example #21
0
def delete_photo(key):
    """Delete a photo."""
    index = get_index()
    if not key in index["map"]:
        logger.warning(
            "Tried to delete photo {} but it wasn't found?".format(key))
        return

    album = index["map"][key]

    logger.info("Completely deleting the photo {} from album {}".format(
        key, album))
    photo = index["albums"][album][key]

    # Delete all the images.
    for size in ["large", "thumb", "avatar"]:
        logger.info("Delete: {}".format(photo[size]))
        fname = os.path.join(Config.photo.root_private, photo[size])
        if os.path.isfile(fname):
            os.unlink(fname)

    # Delete it from the sort list.
    index["photo-order"][album].remove(key)
    del index["map"][key]
    del index["albums"][album][key]

    # Was this the album cover?
    if index["covers"][album] == key:
        # Try to pick a new one.
        if len(index["photo-order"][album]) > 0:
            index["covers"][album] = index["photo-order"][album][0]
        else:
            index["covers"][album] = ""

    # If the album is empty now too, delete it as well.
    if len(index["albums"][album].keys()) == 0:
        del index["albums"][album]
        del index["photo-order"][album]
        del index["covers"][album]
        index["album-order"].remove(album)

    write_index(index)
Example #22
0
def unsubscribe(thread, email):
    """Unsubscribe an e-mail address from a thread.

    If `thread` is `*`, the e-mail is unsubscribed from all threads."""

    # Which threads to unsubscribe from?
    threads = []
    if thread == "*":
        threads = JsonDB.list_docs("comments/subscribers")
    else:
        threads = [thread]

    # Remove them as a subscriber.
    for thread in threads:
        if JsonDB.exists("comments/subscribers/{}".format(thread)):
            logger.info(
                "Unsubscribe e-mail address {} from comment thread {}".format(
                    email, thread))
            db = get_subscribers(thread)
            del db[email]
            write_subscribers(thread, db)
Example #23
0
def delete_photo(key):
    """Delete a photo."""
    index = get_index()
    if not key in index["map"]:
        logger.warning("Tried to delete photo {} but it wasn't found?".format(key))
        return

    album = index["map"][key]

    logger.info("Completely deleting the photo {} from album {}".format(key, album))
    photo = index["albums"][album][key]

    # Delete all the images.
    for size in ["large", "thumb", "avatar"]:
        logger.info("Delete: {}".format(photo[size]))
        fname = os.path.join(Config.photo.root_private, photo[size])
        if os.path.isfile(fname):
            os.unlink(fname)

    # Delete it from the sort list.
    index["photo-order"][album].remove(key)
    del index["map"][key]
    del index["albums"][album][key]

    # Was this the album cover?
    if index["covers"][album] == key:
        # Try to pick a new one.
        if len(index["photo-order"][album]) > 0:
            index["covers"][album] = index["photo-order"][album][0]
        else:
            index["covers"][album] = ""

    # If the album is empty now too, delete it as well.
    if len(index["albums"][album].keys()) == 0:
        del index["albums"][album]
        del index["photo-order"][album]
        del index["covers"][album]
        index["album-order"].remove(album)

    write_index(index)
Example #24
0
def load_theme():
    """Pre-load and cache the emoticon theme. This happens on startup."""
    theme = Config.emoticons.theme
    global _cache

    # Cached?
    if _cache:
        return _cache

    # Only if the theme file exists.
    settings = os.path.join(Config.emoticons.root_private, theme,
                            "emoticons.json")
    if not os.path.isfile(settings):
        logger.error("Failed to load smiley theme {}: not found!")

        # Try the default (tango).
        theme = "tango"
        settings = os.path.join(Config.emoticons.root_private, theme,
                                "emoticons.json")
        if os.path.isfile(settings):
            logger.info("Falling back to default theme: tango")
        else:
            # Give up.
            return {}

    # Read it.
    fh = codecs.open(settings, "r", "utf-8")
    text = fh.read()
    fh.close()

    try:
        data = json.loads(text)
    except Exception as e:
        logger.error("Couldn't load JSON from emoticon file: {}".format(e))
        data = {}

    # Cache and return it.
    _cache = data
    return data
Example #25
0
def load_theme():
    """Pre-load and cache the emoticon theme. This happens on startup."""
    theme = Config.emoticons.theme
    global _cache

    # Cached?
    if _cache:
        return _cache

    # Only if the theme file exists.
    settings = os.path.join(Config.emoticons.root_private, theme, "emoticons.json")
    if not os.path.isfile(settings):
        logger.error("Failed to load smiley theme {}: not found!")

        # Try the default (tango).
        theme = "tango"
        settings = os.path.join(Config.emoticons.root_private, theme, "emoticons.json")
        if os.path.isfile(settings):
            logger.info("Falling back to default theme: tango")
        else:
            # Give up.
            return {}

    # Read it.
    fh = codecs.open(settings, "r", "utf-8")
    text = fh.read()
    fh.close()

    try:
        data = json.loads(text)
    except Exception as e:
        logger.error("Couldn't load JSON from emoticon file: {}".format(e))
        data = {}

    # Cache and return it.
    _cache = data
    return data
Example #26
0
def send_email(to, subject, message, header=None, footer=None, sender=None,
               reply_to=None):
    """Send a (markdown-formatted) e-mail out.

    This will deliver an HTML-formatted e-mail (using the ``email.inc.html``
    template) using the rendered Markdown contents of ``message`` and
    ``footer``. It will also send a plain text version using the raw Markdown
    formatting in case the user can't accept HTML.

    Parameters:
        to ([]str): list of addresses to send the message to.
        subject (str): email subject and title.
        message (str): the email body, in Markdown format.
        header (str): the header text for the HTML email (plain text).
        footer (str): optional email footer, in Markdown format. The default
            footer is defined in the ``email.inc.html`` template.
        sender (str): optional sender email address. Defaults to the one
            specified in the site configuration.
        reply_to (str): optional Reply-To address header.
    """
    if sender is None:
        sender = Config.mail.sender

    if type(to) != list:
        to = [to]

    # Render the Markdown bits.
    if footer:
        footer = render_markdown(footer)

    # Default header matches the subject.
    if not header:
        header = subject

    html_message = render_template("email.inc.html",
        title=subject,
        header=header,
        message=render_markdown(message),
        footer=footer,
    )

    logger.info("Send email to {}".format(to))
    if Config.mail.method == "smtp":
        # Send mail with SMTP.
        for email in to:
            msg = MIMEMultipart("alternative")
            msg.set_charset("utf-8")

            msg["Subject"] = subject
            msg["From"] = sender
            msg["To"] = email
            if reply_to is not None:
                msg["Reply-To"] = reply_to

            text = MIMEText(message, "plain", "utf-8")
            msg.attach(text)

            html = MIMEText(html_message, "html", "utf-8")
            msg.attach(html)

            # Send the e-mail.
            try:
                server = smtplib.SMTP(Config.mail.server, Config.mail.port)
                server.sendmail(sender, [email], msg.as_string())
            except:
                pass
Example #27
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")
Example #28
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")