Added in tagging.

This commit is contained in:
Raoul Snyman 2010-01-22 14:13:16 +02:00
commit f6761bc2a6
21 changed files with 314 additions and 27 deletions

View File

@ -4,3 +4,4 @@ ScribeEngine.e4p
scribeengine.sqlite
posts.sql
*.egg-info
ScrivbeEngine.e4p

1
run-server Executable file
View File

@ -0,0 +1 @@
/home/raoul/VirtualEnv/ScribeEngine/tags/bin/paster serve development.ini --reload

View File

@ -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}')

View File

@ -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:

View File

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

View File

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

View File

@ -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
@ -81,11 +81,11 @@ def teaser(text, url):
def url_for_post(post):
#TODO: this is hard coded.
return url_for(
controller='blog',
controller='blog',
action='view',
year=post.created.strftime('%Y'),
month=post.created.strftime('%m'),
day=post.created.strftime('%d'),
year=post.created.strftime('%Y'),
month=post.created.strftime('%m'),
day=post.created.strftime('%d'),
url=post.url
)

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

View File

@ -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");
});

View File

@ -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

File diff suppressed because one or more lines are too long

View 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);

File diff suppressed because one or more lines are too long

View File

@ -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 {

View File

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

View File

@ -1,5 +1,5 @@
<%inherit file="/base.mako"/>
<%include file="/flash.mako"/>
<%include file="/flash.mako"/>
% for post in c.posts:
<div class="post">
<h2 class="title"><a href="${h.url_for_post(post)}">${post.title}</a></h2>

View 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

View File

@ -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:

View File

@ -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 &amp; Publish"/>

View File

@ -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 &amp; Publish" class="form-button"/>