$(".planet-search-autocomplete").typeahead({
source: function(query, process) {
- $.get("http://planet.ipfire.org/api/search/autocomplete", { q: query }, function(data) {
+ $.get("/api/planet/search/autocomplete", { q: query }, function(data) {
if (data.query == query) {
process(data.results);
}
{% extends "admin-base.html" %}
-{% block bodyA %}
- <div class="post">
- <h3>{{ _("Compose new entry") }}</h3>
- <form name="entry" method="post">
- {% raw xsrf_form_html() %}
- {% if entry.id %}
- <input type="hidden" name="id" value="{{ entry.id }}">
- {% end %}
-
- <table>
- <tr>
- <td>{{ _("Title") }}</td>
- <td><input type="text" name="title" value="{{ entry.title }}"
- size="50" /></td>
- </tr>
- <tr>
- <td> </td>
- <td>
- <textarea id="markdown" name="markdown" rows="20" cols="80">{{ entry.markdown }}</textarea>
- </td>
- </tr>
- <tr>
- <td colspan="2">
- <input type="button" onClick="preview()" value="{{ _("Preview") }}" />
- <input type="submit" value="{{ _("Save") }}" />
- </td>
- </tr>
- </table>
- </form>
+{% block body %}
+ <div class="page-header">
+ <h2>{{ _("Compose new entry") }}</h2>
</div>
+ <form class="form-horizontal" action="" method="POST" name="entry">
+ {% raw xsrf_form_html() %}
+
+ {% if entry.id %}
+ <input type="hidden" name="id" value="{{ entry.id }}">
+ {% end %}
+
+ <div class="control-group">
+ <label class="control-label" for="inputTitle">{{ _("Title") }}</label>
+ <div class="controls">
+ <input class="input-block-level" type="text" name="title" id="inputTitle"
+ value="{{ entry.title }}" placeholder="{{ _("Title") }}">
+ </div>
+ </div>
+
+ <div class="control-group">
+ <div class="controls">
+ <textarea class="input-block-level" name="markdown" rows="12" id="inputMarkdown"
+ placeholder="{{ _("Content") }}">{{ entry.markdown }}</textarea>
+ </div>
+ </div>
+
+ <hr>
+
+ <div class="control-group">
+ <label class="control-label" for="inputTags">{{ _("Tags") }}</label>
+ <div class="controls">
+ <input class="input-block-level planet-search-autocomplete" type="text"
+ name="tags" id="inputTags" value="{{ " ".join(entry.tags) }}"
+ placeholder="{{ _("Tags") }}" autocomplete="off">
+ </div>
+ </div>
+ <div class="form-actions">
+ <button type="submit" class="btn btn-primary">{{ _("Save") }}</button>
+ <a class="btn" href="javascript:preview();">{{ _("Preview") }}</a>
+ <a class="btn" href="/planet">{{ _("Cancel") }}</a>
+ </div>
+ </form>
+
<div id="preview"></div>
<script type="text/javascript">
preview = function() {
- $.postJSON("/api/planet/render", { text : $("#markdown").val() },
+ $.postJSON("/api/planet/render", { text : $("#inputMarkdown").val() },
function(data) {
$("#preview").html(data.html);
}
</div>
<p class="pull-right" style="clear: both;">
+ {% if entry.tags %}
+ {{ _("Tags") }}:
+ {% for tag in entry.tags %}
+ <a href="/search?q={{ tag }}">{{ tag }}</a>
+ {% end %} •
+ {% end %}
{{ _("Posted by") }} <a href="/user/{{ entry.author.uid }}">{{ entry.author.cn }}</a>
{{ _("on") }} {{ locale.format_date(entry.published, shorter=True) }}
</p>
(r"/year/(\d+)", PlanetYearHandler),
# API
- (r"/api/search/autocomplete", PlanetAPISearchAutocomplete),
+ (r"/api/planet/search/autocomplete", PlanetAPISearchAutocomplete),
# RSS
(r"/rss", RSSPlanetAllHandler),
(r"/downloads", AdminDownloadsHandler),
(r"/downloads/mirrors", AdminDownloadsMirrorsHandler),
# API
+ (r"/api/planet/search/autocomplete", PlanetAPISearchAutocomplete),
(r"/api/planet/render", AdminApiPlanetRenderMarkupHandler)
] + static_handlers)
from misc import Singleton
class PlanetEntry(object):
- def __init__(self, entry=None):
+ def __init__(self, db, entry=None):
+ self.db = db
+
if entry:
self.__entry = entry
else:
"id" : None,
"title" : "",
"markdown" : "",
+ "tags" : [],
})
def set(self, key, val):
def author(self):
return Accounts().search(self.__entry.author_id)
+ # Tags
+
+ def get_tags(self):
+ if not hasattr(self, "__tags"):
+ res = self.db.query("SELECT tag FROM planet_tags \
+ WHERE post_id = %s ORDER BY tag", self.id)
+ self.__tags = []
+ for row in res:
+ self.__tags.append(row.tag)
+
+ return self.__tags
+
+ def set_tags(self, tags):
+ # Delete all existing tags.
+ self.db.execute("DELETE FROM planet_tags WHERE post_id = %s", self.id)
+
+ self.db.executemany("INSERT INTO planet_tags(post_id, tag) VALUES(%s, %s)",
+ ((self.id, tag) for tag in tags))
+
+ # Update cache.
+ self.__tags = tags
+ self.__tags.sort()
+
+ tags = property(get_tags, set_tags)
+
class Planet(object):
__metaclass__ = Singleton
def get_entry_by_slug(self, slug):
entry = self.db.get("SELECT * FROM planet WHERE slug = %s", slug)
if entry:
- return PlanetEntry(entry)
+ return PlanetEntry(self.db, entry)
def get_entry_by_id(self, id):
entry = self.db.get("SELECT * FROM planet WHERE id = %s", id)
if entry:
- return PlanetEntry(entry)
+ return PlanetEntry(self.db, entry)
def _limit_and_offset_query(self, limit=None, offset=None):
query = " "
entries = []
for entry in self.db.query(query):
- entries.append(PlanetEntry(entry))
+ entries.append(PlanetEntry(self.db, entry))
return entries
entries = self.db.query(query)
- return [PlanetEntry(e) for e in entries]
+ return [PlanetEntry(self.db, e) for e in entries]
def get_entries_by_year(self, year):
entries = self.db.query("SELECT * FROM planet \
WHERE YEAR(published) = %s ORDER BY published DESC", year)
- return [PlanetEntry(e) for e in entries]
+ return [PlanetEntry(self.db, e) for e in entries]
def render(self, text, limit=0):
if limit and len(text) >= limit:
# Split tags.
tags = what.split()
- query = "SELECT * FROM planet WHERE id IN ( \
- SELECT DISTINCT post_id FROM planet_tags"
+ query = "SELECT planet.* FROM planet INNER JOIN ( \
+ SELECT post_id FROM planet_tags \
+ INNER JOIN planet ON planet_tags.post_id = planet.id \
+ WHERE %s GROUP BY post_id HAVING COUNT(post_id) = %%s \
+ ) pt ON planet.id = pt.post_id ORDER BY published DESC"
- clauses = []
- args = []
+ args = (tags, len(tags))
+ clauses, args = [], tags
for tag in tags:
- clauses.append("tag = %s")
- args.append(tag)
-
- query += " WHERE %s" % " OR ".join(clauses)
- query += " ORDER BY COUNT(*) DESC)"
-
- entries = self.db.query(query, *args)
+ clauses.append("planet_tags.tag = %s")
+ args.append(len(tags))
- return [PlanetEntry(e) for e in entries]
+ entries = self.db.query(query % " OR ".join(clauses), *args)
+ return [PlanetEntry(self.db, e) for e in entries]
def search_autocomplete(self, what):
tags = what.split()
class AdminPlanetComposeHandler(AdminBaseHandler):
@tornado.web.authenticated
def get(self, id=None):
- entry = backend.PlanetEntry()
+ entry = backend.PlanetEntry(self.planet.db)
if id:
entry = self.planet.get_entry_by_id(id)
def post(self, id=None):
id = self.get_argument("id", id)
- entry = backend.PlanetEntry()
+ entry = backend.PlanetEntry(self.planet.db)
if id:
entry = self.planet.get_entry_by_id(id)
entry.set("markdown", self.get_argument("markdown"))
entry.set("author_id", self.current_user)
+ tags = self.get_argument("tags", "")
+ entry.tags = tags.split()
+
if id:
self.planet.update_entry(entry)
else: