Beispiel #1
0
def history(name):
    """Page history."""
    name = Wiki.url_to_name(name)

    # Look up the page.
    page = Wiki.get_page(name)
    if not page:
        flash("Wiki page not found.")
        return redirect(url_for(".index"))

    authors = dict()
    history = list()
    for rev in page["revisions"]:
        uid = rev["author"]
        if not uid in authors:
            authors[uid] = User.get_user(uid=uid)

        history.append(
            dict(
                id=rev["id"],
                author=authors[uid],
                note=rev["note"],
                pretty_time=pretty_time(Config.wiki.time_format, rev["time"]),
            ))

    g.info["link"] = Wiki.name_to_url(name)
    g.info["title"] = name
    g.info["history"] = history
    return template("wiki/history.html")
Beispiel #2
0
def before_request():
    """Called before all requests. Initialize global template variables."""

    # Default template vars.
    g.info = rophako.utils.default_vars()

    # Default session vars.
    if not "login" in session:
        session.update(g.info["session"])

    # CSRF protection.
    if request.method == "POST":
        token = session.pop("_csrf", None)
        if not token or str(token) != str(request.form.get("token")):
            abort(403)

    # Refresh their login status from the DB.
    if session["login"]:
        import rophako.model.user as User
        if not User.exists(uid=session["uid"]):
            # Weird! Log them out.
            from rophako.modules.account import logout
            logout()
            return

        db = User.get_user(uid=session["uid"])
        session["username"] = db["username"]
        session["name"] = db["name"]
        session["role"] = db["role"]

    # Copy session params into g.info. The only people who should touch the
    # session are the login/out pages.
    for key in session:
        g.info["session"][key] = session[key]
Beispiel #3
0
def history(name):
    """Page history."""
    name = Wiki.url_to_name(name)

    # Look up the page.
    page = Wiki.get_page(name)
    if not page:
        flash("Wiki page not found.")
        return redirect(url_for(".index"))

    authors = dict()
    history = list()
    for rev in page["revisions"]:
        uid = rev["author"]
        if not uid in authors:
            authors[uid] = User.get_user(uid=uid)

        history.append(
            dict(
                id=rev["id"],
                author=authors[uid],
                note=rev["note"],
                pretty_time=pretty_time(Config.wiki.time_format, rev["time"]),
            )
        )

    g.info["link"] = Wiki.name_to_url(name)
    g.info["title"] = name
    g.info["history"] = history
    return template("wiki/history.html")
Beispiel #4
0
def login():
    """Log into an account."""

    if request.method == "POST":
        username = request.form.get("username", "")
        password = request.form.get("password", "")

        # Lowercase the username.
        username = username.lower()

        if User.check_auth(username, password):
            # OK!
            db = User.get_user(username=username)
            session["login"] = True
            session["username"] = username
            session["uid"]  = db["uid"]
            session["name"] = db["name"]
            session["role"] = db["role"]

            # Redirect them to a local page?
            url = request.form.get("url", "")
            if url.startswith("/"):
                return redirect(url)

            return redirect(url_for("index"))
        else:
            flash("Authentication failed.")
            return redirect(url_for(".login"))

    return template("account/login.html")
Beispiel #5
0
def impersonate(uid):
    """Impersonate a user."""
    # Check that they exist.
    if not User.exists(uid=uid):
        flash("That user ID wasn't found.")
        return redirect(url_for(".users"))

    db = User.get_user(uid=uid)
    if db["role"] == "deleted":
        flash("That user was deleted!")
        return redirect(url_for(".users"))

    # Log them in!
    orig_uid = session["uid"]
    session.update(
        login=True,
        uid=uid,
        username=db["username"],
        name=db["name"],
        role=db["role"],
        impersonator=orig_uid,
    )

    flash("Now logged in as {}".format(db["name"]))
    return redirect(url_for("index"))
Beispiel #6
0
def login():
    """Log into an account."""

    if request.method == "POST":
        username = request.form.get("username", "")
        password = request.form.get("password", "")

        # Lowercase the username.
        username = username.lower()

        if User.check_auth(username, password):
            # OK!
            db = User.get_user(username=username)
            session["login"] = True
            session["username"] = username
            session["uid"] = db["uid"]
            session["name"] = db["name"]
            session["role"] = db["role"]

            # Redirect them to a local page?
            url = request.form.get("url", "")
            if url.startswith("/"):
                return redirect(url)

            return redirect(url_for("index"))
        else:
            flash("Authentication failed.")
            return redirect(url_for(".login"))

    return template("account/login.html")
Beispiel #7
0
def edit_user(uid):
    uid = int(uid)
    user = User.get_user(uid=uid)

    # Submitting?
    if request.method == "POST":
        action = request.form.get("action", "")
        username = request.form.get("username", "")
        name = request.form.get("name", "")
        pw1 = request.form.get("password1", "")
        pw2 = request.form.get("password2", "")
        role = request.form.get("role", "")

        username = username.lower()

        if action == "save":
            # Validate...
            errors = None

            # Don't allow them to change the username to one that exists.
            if username != user["username"]:
                if User.exists(username=username):
                    flash("That username already exists.")
                    return redirect(url_for(".edit_user", uid=uid))

            # Password provided?
            if len(pw1) > 0:
                errors = validate_create_form(username, pw1, pw2)
            elif username != user["username"]:
                # Just validate the username, then.
                errors = validate_create_form(username, skip_passwd=True)

            if errors:
                for error in errors:
                    flash(error)
                return redirect(url_for(".edit_user", uid=uid))

            # Update the user.
            user["username"] = username
            user["name"] = name or username
            user["role"] = role
            if len(pw1) > 0:
                user["password"] = User.hash_password(pw1)
            User.update_user(uid, user)

            flash("User account updated!")
            return redirect(url_for(".users"))

        elif action == "delete":
            # Don't let them delete themself!
            if uid == g.info["session"]["uid"]:
                flash("You shouldn't delete yourself!")
                return redirect(url_for(".edit_user", uid=uid))

            User.delete_user(uid)
            flash("User deleted!")
            return redirect(url_for(".users"))

    return template("admin/edit_user.html", info=user)
Beispiel #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)
    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")
Beispiel #9
0
def unimpersonate():
    """Unimpersonate a user."""

    # Must be impersonating, first!
    if not "impersonator" in session:
        flash("Stop messing around.")
        return redirect(url_for("index"))

    uid = session.pop("impersonator")
    db = User.get_user(uid=uid)
    session.update(login=True, uid=uid, username=db["username"], name=db["name"], role=db["role"])

    flash("No longer impersonating.")
    return redirect(url_for("index"))
Beispiel #10
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")
Beispiel #11
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")
Beispiel #12
0
def partial_index(thread, subject, header=True, addable=True):
    """Partial template for including the index view of a comment thread.

    Parameters:
        thread (str): the unique name for the comment thread.
        subject (str): subject name for the comment thread.
        header (bool): show the 'Comments' H1 header.
        addable (bool): can new comments be added to the thread?
    """

    # Get all the comments on this thread.
    comments = Comment.get_comments(thread)

    # Sort the comments by most recent on bottom.
    sorted_cids = [ x for x in sorted(comments, key=lambda y: comments[y]["time"]) ]
    sorted_comments = []
    for cid in sorted_cids:
        comment = comments[cid]
        comment["id"] = cid

        # Was the commenter logged in?
        if comment["uid"] > 0:
            user = User.get_user(uid=comment["uid"])
            avatar = User.get_picture(uid=comment["uid"])
            comment["name"] = user["name"]
            comment["username"] = user["username"]
            comment["image"] = avatar

        # Add the pretty time.
        comment["pretty_time"] = pretty_time(Config.comment.time_format, comment["time"])

        # Format the message for display.
        comment["formatted_message"] = Comment.format_message(comment["message"])

        # Was this comment posted by the current user viewing it?
        comment["editable"] = Comment.is_editable(thread, cid, comment)

        sorted_comments.append(comment)

    g.info["header"] = header
    g.info["thread"] = thread
    g.info["subject"] = subject
    g.info["commenting_disabled"] = not addable
    g.info["url"] = request.url
    g.info["comments"] = sorted_comments
    g.info["photo_url"] = Config.photo.root_public
    return template("comment/index.inc.html")
Beispiel #13
0
def view_photo(key):
    """View a specific photo."""
    photo = Photo.get_photo(key)
    if photo is None:
        flash("That photo wasn't found!")
        return redirect(url_for(".albums"))

    # Get the author info.
    author = User.get_user(uid=photo["author"])
    if author:
        g.info["author"] = author

    g.info["photo"] = photo
    g.info["photo"]["key"] = key
    g.info["photo"]["pretty_time"] = pretty_time(Config.photo.time_format, photo["uploaded"])
    g.info["photo"]["markdown"] = render_markdown(photo.get("description", ""))
    return template("photos/view.html")
Beispiel #14
0
def partial_index(thread, subject, header=True, addable=True):
    """Partial template for including the index view of a comment thread.

    * thread: unique name for the comment thread
    * subject: subject name for the comment thread
    * header: show the Comments h1 header
    * addable: boolean, can new comments be added to the thread"""

    comments = Comment.get_comments(thread)

    # Sort the comments by most recent on bottom.
    sorted_cids = [
        x for x in sorted(comments, key=lambda y: comments[y]["time"])
    ]
    sorted_comments = []
    for cid in sorted_cids:
        comment = comments[cid]
        comment["id"] = cid

        # Was the commenter logged in?
        if comment["uid"] > 0:
            user = User.get_user(uid=comment["uid"])
            avatar = User.get_picture(uid=comment["uid"])
            comment["name"] = user["name"]
            comment["username"] = user["username"]
            comment["image"] = avatar

        # Add the pretty time.
        comment["pretty_time"] = pretty_time(Config.comment.time_format,
                                             comment["time"])

        # Format the message for display.
        comment["formatted_message"] = Comment.format_message(
            comment["message"])

        sorted_comments.append(comment)

    g.info["header"] = header
    g.info["thread"] = thread
    g.info["subject"] = subject
    g.info["commenting_disabled"] = not addable
    g.info["url"] = request.url
    g.info["comments"] = sorted_comments
    g.info["photo_url"] = Config.photo.root_public
    return template("comment/index.inc.html")
Beispiel #15
0
def unimpersonate():
    """Unimpersonate a user."""

    # Must be impersonating, first!
    if not "impersonator" in session:
        flash("Stop messing around.")
        return redirect(url_for("index"))

    uid = session.pop("impersonator")
    db = User.get_user(uid=uid)
    session.update(
        login=True,
        uid=uid,
        username=db["username"],
        name=db["name"],
        role=db["role"],
    )

    flash("No longer impersonating.")
    return redirect(url_for("index"))
Beispiel #16
0
def impersonate(uid):
    """Impersonate a user."""
    # Check that they exist.
    if not User.exists(uid=uid):
        flash("That user ID wasn't found.")
        return redirect(url_for(".users"))

    db = User.get_user(uid=uid)
    if db["role"] == "deleted":
        flash("That user was deleted!")
        return redirect(url_for(".users"))

    # Log them in!
    orig_uid = session["uid"]
    session.update(
        login=True, uid=uid, username=db["username"], name=db["name"], role=db["role"], impersonator=orig_uid
    )

    flash("Now logged in as {}".format(db["name"]))
    return redirect(url_for("index"))
Beispiel #17
0
def partial_index(thread, subject, header=True, addable=True):
    """Partial template for including the index view of a comment thread.

    * thread: unique name for the comment thread
    * subject: subject name for the comment thread
    * header: show the Comments h1 header
    * addable: boolean, can new comments be added to the thread"""

    comments = Comment.get_comments(thread)

    # Sort the comments by most recent on bottom.
    sorted_cids = [ x for x in sorted(comments, key=lambda y: comments[y]["time"]) ]
    sorted_comments = []
    for cid in sorted_cids:
        comment = comments[cid]
        comment["id"] = cid

        # Was the commenter logged in?
        if comment["uid"] > 0:
            user = User.get_user(uid=comment["uid"])
            avatar = User.get_picture(uid=comment["uid"])
            comment["name"] = user["name"]
            comment["username"] = user["username"]
            comment["image"] = avatar

        # Add the pretty time.
        comment["pretty_time"] = pretty_time(Config.comment.time_format, comment["time"])

        # Format the message for display.
        comment["formatted_message"] = Comment.format_message(comment["message"])

        sorted_comments.append(comment)

    g.info["header"] = header
    g.info["thread"] = thread
    g.info["subject"] = subject
    g.info["commenting_disabled"] = not addable
    g.info["url"] = request.url
    g.info["comments"] = sorted_comments
    g.info["photo_url"] = Config.photo.root_public
    return template("comment/index.inc.html")
Beispiel #18
0
def archive():
    """List all blog posts over time on one page."""
    index = Blog.get_index()

    # Group by calendar month, and keep track of friendly versions of months.
    groups = dict()
    friendly_months = dict()
    for post_id, data in index.items():
        ts = datetime.datetime.fromtimestamp(data["time"])
        date = ts.strftime("%Y-%m")
        if not date in groups:
            groups[date] = dict()
            friendly = ts.strftime("%B %Y")
            friendly_months[date] = friendly

        # Get author's profile && Pretty-print the time.
        data["profile"] = User.get_user(uid=data["author"])
        data["pretty_time"] = pretty_time(Config.blog.time_format, data["time"])
        groups[date][post_id] = data

    # Sort by calendar month.
    sort_months = sorted(groups.keys(), reverse=True)

    # Prepare the results.
    result = list()
    for month in sort_months:
        data = dict(
            month=month,
            month_friendly=friendly_months[month],
            posts=list()
        )

        # Sort the posts by time created, descending.
        for post_id in sorted(groups[month].keys(), key=lambda x: groups[month][x]["time"], reverse=True):
            data["posts"].append(groups[month][post_id])

        result.append(data)

    g.info["archive"] = result
    return template("blog/archive.html")
Beispiel #19
0
def archive():
    """List all blog posts over time on one page."""
    index = Blog.get_index()

    # Group by calendar month, and keep track of friendly versions of months.
    groups = dict()
    friendly_months = dict()
    for post_id, data in index.items():
        ts = datetime.datetime.fromtimestamp(data["time"])
        date = ts.strftime("%Y-%m")
        if not date in groups:
            groups[date] = dict()
            friendly = ts.strftime("%B %Y")
            friendly_months[date] = friendly

        # Get author's profile && Pretty-print the time.
        data["profile"] = User.get_user(uid=data["author"])
        data["pretty_time"] = pretty_time(Config.blog.time_format, data["time"])
        groups[date][post_id] = data

    # Sort by calendar month.
    sort_months = sorted(groups.keys(), reverse=True)

    # Prepare the results.
    result = list()
    for month in sort_months:
        data = dict(
            month=month,
            month_friendly=friendly_months[month],
            posts=list()
        )

        # Sort the posts by time created, descending.
        for post_id in sorted(groups[month].keys(), key=lambda x: groups[month][x]["time"], reverse=True):
            data["posts"].append(groups[month][post_id])

        result.append(data)

    g.info["archive"] = result
    return template("blog/archive.html")
Beispiel #20
0
def before_request():
    """Called before all requests. Initialize global template variables."""

    # Session lifetime.
    app.permanent_session_lifetime = datetime.timedelta(days=Config.security.session_lifetime)
    session.permanent = True

    # Default template vars.
    g.info = rophako.utils.default_vars()

    # Default session vars.
    if not "login" in session:
        session.update(g.info["session"])

    # CSRF protection.
    if request.method == "POST":
        token = session.pop("_csrf", None)
        if not token or str(token) != str(request.form.get("token")):
            abort(403)

    # Refresh their login status from the DB.
    if session["login"]:
        import rophako.model.user as User
        if not User.exists(uid=session["uid"]):
            # Weird! Log them out.
            from rophako.modules.account import logout
            logout()
            return

        db = User.get_user(uid=session["uid"])
        session["username"] = db["username"]
        session["name"]     = db["name"]
        session["role"]     = db["role"]

    # Copy session params into g.info. The only people who should touch the
    # session are the login/out pages.
    for key in session:
        g.info["session"][key] = session[key]
Beispiel #21
0
def edit_user(uid):
    uid = int(uid)
    user = User.get_user(uid=uid)

    # Submitting?
    if request.method == "POST":
        action = request.form.get("action", "")
        username = request.form.get("username", "")
        name = request.form.get("name", "")
        pw1 = request.form.get("password1", "")
        pw2 = request.form.get("password2", "")
        role = request.form.get("role", "")

        username = username.lower()

        if action == "save":
            # Validate...
            errors = None

            # Don't allow them to change the username to one that exists.
            if username != user["username"]:
                if User.exists(username=username):
                    flash("That username already exists.")
                    return redirect(url_for(".edit_user", uid=uid))

            # Password provided?
            if len(pw1) > 0:
                errors = validate_create_form(username, pw1, pw2)
            elif username != user["username"]:
                # Just validate the username, then.
                errors = validate_create_form(username, skip_passwd=True)

            if errors:
                for error in errors:
                    flash(error)
                return redirect(url_for(".edit_user", uid=uid))

            # Update the user.
            user["username"] = username
            user["name"] = name or username
            user["role"] = role
            if len(pw1) > 0:
                user["password"] = User.hash_password(pw1)
            User.update_user(uid, user)

            flash("User account updated!")
            return redirect(url_for(".users"))

        elif action == "delete":
            # Don't let them delete themself!
            if uid == g.info["session"]["uid"]:
                flash("You shouldn't delete yourself!")
                return redirect(url_for(".edit_user", uid=uid))

            User.delete_user(uid)
            flash("User deleted!")
            return redirect(url_for(".users"))

    return template(
        "admin/edit_user.html",
        info=user,
    )
Beispiel #22
0
def add_comment(thread, uid, name, subject, message, url, time, ip,
    token=None, image=None):
    """Add a comment to a comment thread.

    Parameters:
        thread (str): the unique comment thread name.
        uid (int): 0 for guest posts, otherwise the UID of the logged-in user.
        name (str): the commenter's name (if a guest)
        subject (str)
        message (str)
        url (str): the URL where the comment can be read (i.e. the blog post)
        time (int): epoch time of the comment.
        ip (str): the user's IP address.
        token (str): the user's session's comment deletion token.
        image (str): the URL to a Gravatar image, if any.
    """

    # Get the comments for this thread.
    comments = get_comments(thread)

    # Make up a unique ID for the comment.
    cid = random_hash()
    while cid in comments:
        cid = random_hash()

    # Add the comment.
    comments[cid] = dict(
        uid=uid,
        name=name or "Anonymous",
        image=image or "",
        message=message,
        time=time or int(time.time()),
        ip=ip,
        token=token,
    )
    write_comments(thread, comments)

    # Get info about the commenter.
    if uid > 0:
        user = User.get_user(uid=uid)
        if user:
            name = user["name"]

    # Send the e-mail to the site admins.
    send_email(
        to=Config.site.notify_address,
        subject="Comment Added: {}".format(subject),
        message="""{name} has left a comment on: {subject}

{message}

-----

To view this comment, please go to <{url}>

Was this comment spam? [Delete it]({deletion_link}).""".format(
            name=name,
            subject=subject,
            message=message,
            url=url,
            deletion_link=url_for("comment.quick_delete",
                token=make_quick_delete_token(thread, cid),
                url=url,
                _external=True,
            )
        ),
    )

    # Notify any subscribers.
    subs = get_subscribers(thread)
    for sub in subs.keys():
        # Make the unsubscribe link.
        unsub = url_for("comment.unsubscribe", thread=thread, who=sub, _external=True)

        send_email(
            to=sub,
            subject="New Comment: {}".format(subject),
            message="""Hello,

{name} has left a comment on: {subject}

{message}

-----

To view this comment, please go to <{url}>""".format(
                thread=thread,
                name=name,
                subject=subject,
                message=message,
                url=url,
                unsub=unsub,
            ),
            footer="You received this e-mail because you subscribed to the "
                "comment thread that this comment was added to. You may "
                "[**unsubscribe**]({unsub}) if you like.".format(
                unsub=unsub,
            ),
        )
Beispiel #23
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)
Beispiel #24
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)
Beispiel #25
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")
Beispiel #26
0
def add_comment(thread, uid, name, subject, message, url, time, ip, image=None):
    """Add a comment to a comment thread.

    * uid is 0 if it's a guest post, otherwise the UID of the user.
    * name is the commenter's name (if a guest)
    * subject is for the e-mails that are sent out
    * message is self explanatory.
    * url is the URL where the comment can be read.
    * time, epoch time of comment.
    * ip is the IP address of the commenter.
    * image is a Gravatar image URL etc.
    """

    # Get the comments for this thread.
    comments = get_comments(thread)

    # Make up a unique ID for the comment.
    cid = random_hash()
    while cid in comments:
        cid = random_hash()

    # Add the comment.
    comments[cid] = dict(
        uid=uid,
        name=name or "Anonymous",
        image=image or "",
        message=message,
        time=time or int(time.time()),
        ip=ip,
    )
    write_comments(thread, comments)

    # Get info about the commenter.
    if uid > 0:
        user = User.get_user(uid=uid)
        if user:
            name = user["name"]

    # Send the e-mail to the site admins.
    send_email(
        to=Config.site.notify_address,
        subject="New comment: {}".format(subject),
        message="""{name} has left a comment on: {subject}

{message}

To view this comment, please go to {url}

=====================

This e-mail was automatically generated. Do not reply to it.""".format(
            name=name,
            subject=subject,
            message=message,
            url=url,
        ),
    )

    # Notify any subscribers.
    subs = get_subscribers(thread)
    for sub in subs.keys():
        # Make the unsubscribe link.
        unsub = url_for("comment.unsubscribe", thread=thread, who=sub, _external=True)

        send_email(
            to=sub,
            subject="New Comment: {}".format(subject),
            message="""Hello,

You are currently subscribed to the comment thread '{thread}', and somebody has
just added a new comment!

{name} has left a comment on: {subject}

{message}

To view this comment, please go to {url}

=====================

This e-mail was automatically generated. Do not reply to it.

If you wish to unsubscribe from this comment thread, please visit the following
URL: {unsub}""".format(
                thread=thread,
                name=name,
                subject=subject,
                message=message,
                url=url,
                unsub=unsub,
            )
        )
Beispiel #27
0
def add_comment(thread,
                uid,
                name,
                subject,
                message,
                url,
                time,
                ip,
                image=None):
    """Add a comment to a comment thread.

    * uid is 0 if it's a guest post, otherwise the UID of the user.
    * name is the commenter's name (if a guest)
    * subject is for the e-mails that are sent out
    * message is self explanatory.
    * url is the URL where the comment can be read.
    * time, epoch time of comment.
    * ip is the IP address of the commenter.
    * image is a Gravatar image URL etc.
    """

    # Get the comments for this thread.
    comments = get_comments(thread)

    # Make up a unique ID for the comment.
    cid = random_hash()
    while cid in comments:
        cid = random_hash()

    # Add the comment.
    comments[cid] = dict(
        uid=uid,
        name=name or "Anonymous",
        image=image or "",
        message=message,
        time=time or int(time.time()),
        ip=ip,
    )
    write_comments(thread, comments)

    # Get info about the commenter.
    if uid > 0:
        user = User.get_user(uid=uid)
        if user:
            name = user["name"]

    # Send the e-mail to the site admins.
    send_email(
        to=Config.site.notify_address,
        subject="New comment: {}".format(subject),
        message="""{name} has left a comment on: {subject}

{message}

To view this comment, please go to {url}

=====================

This e-mail was automatically generated. Do not reply to it.""".format(
            name=name,
            subject=subject,
            message=message,
            url=url,
        ),
    )

    # Notify any subscribers.
    subs = get_subscribers(thread)
    for sub in subs.keys():
        # Make the unsubscribe link.
        unsub = url_for("comment.unsubscribe",
                        thread=thread,
                        who=sub,
                        _external=True)

        send_email(to=sub,
                   subject="New Comment: {}".format(subject),
                   message="""Hello,

You are currently subscribed to the comment thread '{thread}', and somebody has
just added a new comment!

{name} has left a comment on: {subject}

{message}

To view this comment, please go to {url}

=====================

This e-mail was automatically generated. Do not reply to it.

If you wish to unsubscribe from this comment thread, please visit the following
URL: {unsub}""".format(
                       thread=thread,
                       name=name,
                       subject=subject,
                       message=message,
                       url=url,
                       unsub=unsub,
                   ))