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")
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
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")
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")
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")
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")
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")
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)
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
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")
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)
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")