Added in tagging.
This commit is contained in:
commit
f6761bc2a6
@ -4,3 +4,4 @@ ScribeEngine.e4p
|
||||
scribeengine.sqlite
|
||||
posts.sql
|
||||
*.egg-info
|
||||
ScrivbeEngine.e4p
|
||||
|
1
run-server
Executable file
1
run-server
Executable file
@ -0,0 +1 @@
|
||||
/home/raoul/VirtualEnv/ScribeEngine/tags/bin/paster serve development.ini --reload
|
@ -48,6 +48,8 @@ def make_map():
|
||||
map.connect('/archive/{year}/{month}/{day}', controller='blog', action='archive')
|
||||
map.connect('/archive/{year}/{month}/{day}/{url}', controller='blog', action='view')
|
||||
|
||||
map.connect('/tag/{id}', controller='blog', action='tag')
|
||||
|
||||
map.connect('/{controller}/{action}')
|
||||
map.connect('/{controller}/{action}/{id}')
|
||||
|
||||
|
@ -25,7 +25,7 @@ from datetime import datetime
|
||||
|
||||
from scribeengine.lib.base import *
|
||||
from scribeengine.lib import utils
|
||||
from scribeengine.model import Post, Comment
|
||||
from scribeengine.model import Post, Comment, Tag
|
||||
from scribeengine.model.meta import Session
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -71,6 +71,15 @@ class BlogController(BaseController):
|
||||
c.page_title = c.post.title
|
||||
return render(u'/blog/view.mako')
|
||||
|
||||
def tag(self, id=None):
|
||||
if not id:
|
||||
h.redirect_to('/')
|
||||
c.tag = Session.query(Tag).filter_by(url=id).first()
|
||||
if not c.tag:
|
||||
h.redirect_to('/')
|
||||
c.page_title = u'Blog posts with tag: %s' % c.tag.name
|
||||
return render('/blog/tag.mako')
|
||||
|
||||
@authenticate()
|
||||
def comment_POST(self, id):
|
||||
if not id:
|
||||
|
@ -25,13 +25,18 @@ from datetime import datetime
|
||||
|
||||
from scribeengine.lib.base import *
|
||||
from scribeengine.lib import utils
|
||||
from scribeengine.model import Post
|
||||
from scribeengine.model import Post, Tag
|
||||
from scribeengine.model.meta import Session
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class PostController(BaseController):
|
||||
|
||||
def __before__(self):
|
||||
BaseController.__before__(self)
|
||||
self._add_javascript(u'jquery.tag.editor.js')
|
||||
self._add_javascript(u'ScribeEngine.Post.js')
|
||||
|
||||
def index(self):
|
||||
h.redirect_to('/')
|
||||
|
||||
@ -61,6 +66,13 @@ class PostController(BaseController):
|
||||
post.body = c.form_values[u'body']
|
||||
post.status = u'published'
|
||||
post.url = url
|
||||
tags = c.form_values[u'tags']
|
||||
tag_list = [tag_name.strip() for tag_name in tags.split(u',')]
|
||||
for tag in post.tags:
|
||||
if tag.name in tag_list:
|
||||
del tag_list[tag.name]
|
||||
for tag in tag_list:
|
||||
post.tags.append(Tag(name=tag, url=utils.generate_url(tag)))
|
||||
Session.add(post)
|
||||
Session.commit()
|
||||
h.redirect_to(str('/archive/%s/%s' % (post.created.strftime('%Y/%m/%d'), post.url)))
|
||||
|
@ -63,8 +63,8 @@ class BaseController(WSGIController):
|
||||
self._add_javascript(u'jquery.js')
|
||||
if c.jsvalidation:
|
||||
self._add_javascript(u'jquery.validate.js')
|
||||
self._add_javascript(u'scribeengine.js')
|
||||
self._add_jsinit(u'init.js')
|
||||
self._add_javascript(u'ScribeEngine.js')
|
||||
self._add_jsinit(u'ScribeEngine.Init.js')
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
"""Invoke the Controller"""
|
||||
@ -178,14 +178,14 @@ class BaseController(WSGIController):
|
||||
return False, {u'values': params, u'errors': error_dict}
|
||||
return True, {u'values': values, u'errors': {}}
|
||||
|
||||
def _add_javascript(self, filename, subdir=u'global'):
|
||||
def _add_javascript(self, filename):
|
||||
"""
|
||||
This method dynamically adds javascript files to the <head> section
|
||||
of the template.
|
||||
"""
|
||||
if not getattr(c, u'scripts', None):
|
||||
c.scripts = []
|
||||
c.scripts.append((filename, subdir))
|
||||
c.scripts.append(filename)
|
||||
|
||||
def _add_jsinit(self, filename):
|
||||
"""
|
||||
|
@ -64,7 +64,7 @@ class Flash(object):
|
||||
def teaser(text, url):
|
||||
position = text.find(u'</p>')
|
||||
if position > 0:
|
||||
return text[:position]
|
||||
return text[:position + 4]
|
||||
elif len(text) > 300:
|
||||
text = text[:297]
|
||||
position = len(text) - 1
|
||||
|
@ -67,7 +67,7 @@ def generate_url(title):
|
||||
``title``
|
||||
The title of the blog post.
|
||||
"""
|
||||
return re.sub(r'[^a-zA-Z0-9]+', u'-', title.lower())
|
||||
return re.sub(r'[^a-zA-Z0-9]+', u'-', title.lower()).strip()
|
||||
|
||||
def hash_password(password):
|
||||
"""
|
||||
|
BIN
scribeengine/public/images/minus_small.png
Normal file
BIN
scribeengine/public/images/minus_small.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 560 B |
@ -16,3 +16,11 @@
|
||||
* with this program; if not, write to the Free Software Foundation, Inc., *
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
|
||||
*****************************************************************************/
|
||||
|
||||
ScribeEngine.Namespace.create("ScribeEngine.Post", {
|
||||
|
||||
});
|
||||
|
||||
ScribeEngine.Events.load(function () {
|
||||
ScribeEngine.Widgets.tagEditor("#post-tags");
|
||||
});
|
||||
|
@ -90,7 +90,7 @@ ScribeEngine.Namespace.create("ScribeEngine.Events", {
|
||||
keypress: function (selector, func) {
|
||||
$(selector).bind("keypress", func);
|
||||
},
|
||||
get_element: function(event) {
|
||||
getElement: function(event) {
|
||||
var targ;
|
||||
if (!event) {
|
||||
var event = window.event;
|
||||
@ -122,6 +122,10 @@ ScribeEngine.Namespace.create("ScribeEngine.Widgets", {
|
||||
datepicker: function (selector)
|
||||
{
|
||||
$(selector).datepicker({showButtonPanel: true, dateFormat: "dd/mm/yy"});
|
||||
},
|
||||
tagEditor: function (selector)
|
||||
{
|
||||
$(selector).tagEditor({completeOnBlur: true, initialParse: true});
|
||||
}
|
||||
});
|
||||
|
||||
@ -226,12 +230,4 @@ ScribeEngine.Namespace.create("ScribeEngine.General", {
|
||||
* This function below will be executed on all page views.
|
||||
*/
|
||||
ScribeEngine.Events.load(function () {
|
||||
// Hide hidden elements
|
||||
ScribeEngine.General.hide_elements();
|
||||
// Initialise collapsible fieldsets
|
||||
ScribeEngine.General.init_fieldsets();
|
||||
// Initialise elastic textareas
|
||||
ScribeEngine.General.init_textareas();
|
||||
// Show any flash messages
|
||||
ScribeEngine.General.show_message();
|
||||
});
|
||||
|
19
scribeengine/public/scripts/jquery.js
vendored
Executable file
19
scribeengine/public/scripts/jquery.js
vendored
Executable file
File diff suppressed because one or more lines are too long
114
scribeengine/public/scripts/jquery.tag.editor.js
Normal file
114
scribeengine/public/scripts/jquery.tag.editor.js
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
@author: Karl-Johan Sjögren / http://blog.crazybeavers.se/
|
||||
@url: http://blog.crazybeavers.se/wp-content/demos/jquery.tag.editor/
|
||||
@license: Creative Commons License - ShareAlike http://creativecommons.org/licenses/by-sa/3.0/
|
||||
@version: 1.1
|
||||
*/
|
||||
(function($) {
|
||||
$.fn.extend({
|
||||
tagEditor: function(options) {
|
||||
var defaults = {
|
||||
separator: ',',
|
||||
items: [],
|
||||
className: 'tagEditor',
|
||||
confirmRemoval: false,
|
||||
completeOnSeparator: false,
|
||||
completeOnBlur: false
|
||||
}
|
||||
|
||||
var options = $.extend(defaults, options);
|
||||
var listBase, textBase = this, hiddenText;
|
||||
var itemBase = [];
|
||||
|
||||
return this.each(function() {
|
||||
textBase.id = options.formField;
|
||||
|
||||
hiddenText = $(document.createElement('input'));
|
||||
hiddenText.attr('type', 'hidden');
|
||||
textBase.after(hiddenText);
|
||||
|
||||
listBase = $(document.createElement('ul'));
|
||||
listBase.attr('class', options.className);
|
||||
$(this).after(listBase);
|
||||
|
||||
for (var i = 0; i < options.items.length; i++) {
|
||||
addTag(jQuery.trim(options.items[i]));
|
||||
}
|
||||
|
||||
buildArray();
|
||||
$(this).keypress(handleKeys);
|
||||
$(this).blur(parse);
|
||||
|
||||
var form = $(this).parents("form");
|
||||
form.submit(function() {
|
||||
parse();
|
||||
hiddenText.val(itemBase.join(options.separator));
|
||||
hiddenText.attr("id", textBase.attr("id"));
|
||||
hiddenText.attr("name", textBase.attr("name"));
|
||||
textBase.attr("id", textBase.attr("id") + '_old');
|
||||
textBase.attr("name", textBase.attr("name") + '_old');
|
||||
});
|
||||
|
||||
function addTag(tag) {
|
||||
tag = jQuery.trim(tag);
|
||||
for (var i = 0; i < itemBase.length; i++) {
|
||||
if (itemBase[i].toLowerCase() == tag.toLowerCase())
|
||||
return;
|
||||
}
|
||||
|
||||
var item = $(document.createElement('li'));
|
||||
item.text(tag);
|
||||
item.attr('title', 'Remove tag');
|
||||
item.click(function() {
|
||||
if (options.confirmRemoval)
|
||||
if (!confirm("Do you really want to remove the tag?"))
|
||||
return;
|
||||
|
||||
item.remove();
|
||||
parse();
|
||||
});
|
||||
|
||||
listBase.append(item);
|
||||
}
|
||||
|
||||
function buildArray() {
|
||||
itemBase = [];
|
||||
var items = $("li", listBase);
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
itemBase.push(jQuery.trim($(items[i]).text()));
|
||||
}
|
||||
}
|
||||
|
||||
function parse() {
|
||||
var items = textBase.val().split(options.separator);
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var trimmedItem = jQuery.trim(items[i]);
|
||||
if (trimmedItem.length > 0)
|
||||
addTag(trimmedItem);
|
||||
}
|
||||
textBase.val("");
|
||||
buildArray();
|
||||
}
|
||||
|
||||
function handleKeys(ev) {
|
||||
if (options.completeOnSeparator) {
|
||||
if (String.fromCharCode(ev.keyCode) == options.separator) {
|
||||
parse();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
switch (ev.keyCode) {
|
||||
case 13:
|
||||
{
|
||||
parse();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
})(jQuery);
|
16
scribeengine/public/scripts/jquery.validate.js
vendored
Normal file
16
scribeengine/public/scripts/jquery.validate.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -16,9 +16,6 @@ body {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 3em;
|
||||
}
|
||||
@ -29,7 +26,7 @@ h2 {
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 1em;
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
p, ul, ol, pre {
|
||||
@ -141,6 +138,11 @@ hr {
|
||||
width: 605px;
|
||||
}
|
||||
|
||||
.title {
|
||||
border-bottom: 1px solid #454545;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.post {
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
@ -198,7 +200,6 @@ hr {
|
||||
|
||||
#comments,
|
||||
#respond {
|
||||
font-size: 1.5em;
|
||||
margin-top: 1.8em;
|
||||
}
|
||||
|
||||
@ -224,6 +225,36 @@ hr {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
/* Tags */
|
||||
|
||||
.tags {
|
||||
margin-top: 1.8em;
|
||||
}
|
||||
|
||||
.tags p {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tags ul {
|
||||
margin: 4px 0 4px 10px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tags ul li {
|
||||
background-color: #454545;
|
||||
cursor: pointer;
|
||||
display: inline;
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 2px 6px;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
|
||||
.tags ul li a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Sidebar */
|
||||
|
||||
#sidebar {
|
||||
@ -404,6 +435,35 @@ fieldset {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Tag Editor */
|
||||
|
||||
.tagEditor
|
||||
{
|
||||
margin: 4px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.tagEditor li
|
||||
{
|
||||
display: inline;
|
||||
background-image: url(/images/minus_small.png);
|
||||
background-color: #454545;
|
||||
background-position: right center;
|
||||
background-repeat: no-repeat;
|
||||
list-style-type: none;
|
||||
padding: 2px 18px 2px 6px;
|
||||
margin: 0 4px;
|
||||
cursor: pointer;
|
||||
-moz-border-radius: 5px;
|
||||
-webkit-border-radius: 5px;
|
||||
}
|
||||
|
||||
.tagEditor li:hover
|
||||
{
|
||||
background-color: #eee;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
/* Miscellaneous Styles */
|
||||
|
||||
.hidden {
|
||||
|
@ -7,6 +7,15 @@
|
||||
<meta name="keywords" content="" />
|
||||
<meta name="description" content="" />
|
||||
<link href="${h.url_for('/styles/style.css')}" rel="stylesheet" type="text/css" media="screen" />
|
||||
% for script in c.scripts:
|
||||
<script src="/scripts/${script}" type="text/javascript"></script>
|
||||
% endfor
|
||||
% if c.jsvalidation:
|
||||
<script src="/scripts/${c.jsvalidation}" type="text/javascript"></script>
|
||||
% endif
|
||||
% if c.jsinit:
|
||||
<script src="/scripts/${c.jsinit}" type="text/javascript"></script>
|
||||
% endif
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
|
22
scribeengine/templates/blog/tag.mako
Normal file
22
scribeengine/templates/blog/tag.mako
Normal file
@ -0,0 +1,22 @@
|
||||
<%inherit file="/base.mako"/>
|
||||
<%include file="/flash.mako"/>
|
||||
<h2 class="title">Blog posts with tag: ${c.tag.name}</h2>
|
||||
% for post in c.tag.posts:
|
||||
<div class="post">
|
||||
<h3 class="title"><a href="${h.url_for_post(post)}">${post.title}</a></h3>
|
||||
<div class="entry">
|
||||
${h.literal(h.teaser(post.body, h.url_for_post(post)))}
|
||||
</div>
|
||||
<p class="meta">
|
||||
<span class="byline">Posted by ${post.user.nick} on ${post.created.strftime('%B %d, %Y')}</span>
|
||||
<a href="${h.url_for_post(post)}" class="read-more">Read more</a>
|
||||
% if len(post.comments) == 0:
|
||||
<a href="${h.url_for_post(post)}#comments" class="comments">No comments</a>
|
||||
% elif len(post.comments) == 1:
|
||||
<a href="${h.url_for_post(post)}#comments" class="comments">1 comment</a>
|
||||
% else:
|
||||
<a href="${h.url_for_post(post)}#comments" class="comments">${len(post.comments)} comments</a>
|
||||
% endif
|
||||
</p>
|
||||
</div>
|
||||
% endfor
|
@ -6,6 +6,16 @@
|
||||
<div class="entry">
|
||||
${h.literal(c.post.body)}
|
||||
</div>
|
||||
% if c.post.tags:
|
||||
<div class="tags">
|
||||
<p>Filed under:</p>
|
||||
<ul>
|
||||
% for tag in c.post.tags:
|
||||
<li><a href="/tag/${tag.url}">${tag.name}</a></li>
|
||||
% endfor
|
||||
</ul>
|
||||
</div>
|
||||
% endif
|
||||
% if len(c.post.comments) == 0:
|
||||
<h3 id="comments">No Responses</h3>
|
||||
% elif len(c.post.comments) == 1:
|
||||
|
@ -13,6 +13,10 @@
|
||||
<!-- <label for="post-body">Body:</label> -->
|
||||
<textarea name="body" id="post-body" class="form-textarea">${c.post.body}</textarea>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="post-tags">Tags:</label>
|
||||
<input type="text" name="tags" id="post-tags" class="form-text" value="${c.post.tags_list}" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<input type="submit" name="action" value="Save Draft"/>
|
||||
<input type="submit" name="action" value="Save & Publish"/>
|
||||
|
@ -13,6 +13,10 @@
|
||||
<!-- <label for="post-body">Body:</label> -->
|
||||
<textarea name="body" id="post-body" class="form-textarea"></textarea>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<label for="post-tags">Tags:</label>
|
||||
<input type="text" name="tags" id="post-tags" class="form-text" />
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<input type="submit" name="action" value="Save Draft" class="form-button"/>
|
||||
<input type="submit" name="action" value="Save & Publish" class="form-button"/>
|
||||
|
Reference in New Issue
Block a user