summaryrefslogtreecommitdiff
path: root/gstudio/templatetags
diff options
context:
space:
mode:
authorgnowgi <nagarjun@gnowledge.org>2012-03-15 16:19:20 +0530
committergnowgi <nagarjun@gnowledge.org>2012-03-15 16:19:20 +0530
commit7a4f561e851fdc7246d804c3abb6748b8a4199a6 (patch)
treed2afc3463fd49625a9be482012f5c3bfcf7c42b9 /gstudio/templatetags
downloadgnowsys-7a4f561e851fdc7246d804c3abb6748b8a4199a6.tar.gz
master trunk of gnowsys-studio
Diffstat (limited to 'gstudio/templatetags')
-rw-r--r--gstudio/templatetags/__init__.py0
-rw-r--r--gstudio/templatetags/gstudio_admin_tags.py59
-rw-r--r--gstudio/templatetags/gstudio_tags.py352
-rw-r--r--gstudio/templatetags/zbreadcrumbs.py167
-rw-r--r--gstudio/templatetags/zcalendar.py128
5 files changed, 706 insertions, 0 deletions
diff --git a/gstudio/templatetags/__init__.py b/gstudio/templatetags/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gstudio/templatetags/__init__.py
diff --git a/gstudio/templatetags/gstudio_admin_tags.py b/gstudio/templatetags/gstudio_admin_tags.py
new file mode 100644
index 0000000..a341b3f
--- /dev/null
+++ b/gstudio/templatetags/gstudio_admin_tags.py
@@ -0,0 +1,59 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Template tags and filters for Gstudio's admin"""
+from django.template import Library
+from django.contrib import comments
+from django.contrib.contenttypes.models import ContentType
+
+from gstudio.models import Nodetype
+from gstudio.models import Author
+from gstudio.models import Metatype
+from gstudio.managers import DRAFT
+from gstudio.managers import tags_published
+
+register = Library()
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_draft_nodetypes(
+ number=5, template='admin/gstudio/widgets/_draft_nodetypes.html'):
+ """Return the latest draft nodetypes"""
+ return {'template': template,
+ 'nodetypes': Nodetype.objects.filter(status=DRAFT)[:number]}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_content_stats(
+ template='admin/gstudio/widgets/_content_stats.html'):
+ """Return statistics of the contents"""
+ content_type = ContentType.objects.get_for_model(Nodetype)
+
+ discussions = comments.get_model().objects.filter(
+ is_public=True, content_type=content_type)
+
+ return {'template': template,
+ 'nodetypes': Nodetype.published.count(),
+ 'metatypes': Metatype.objects.count(),
+ 'tags': tags_published().count(),
+ 'authors': Author.published.count(),
+ 'comments': discussions.filter(flags=None).count(),
+ 'pingbacks': discussions.filter(flags__flag='pingback').count(),
+ 'trackbacks': discussions.filter(flags__flag='trackback').count(),
+ 'rejects': comments.get_model().objects.filter(
+ is_public=False, content_type=content_type).count(),
+ }
diff --git a/gstudio/templatetags/gstudio_tags.py b/gstudio/templatetags/gstudio_tags.py
new file mode 100644
index 0000000..574da8c
--- /dev/null
+++ b/gstudio/templatetags/gstudio_tags.py
@@ -0,0 +1,352 @@
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+"""Template tags and filters for Gstudio"""
+from hashlib import md5
+from random import sample
+from urllib import urlencode
+from datetime import datetime
+
+from django.db.models import Q
+from django.db import connection
+from django.template import Node
+from django.template import Library
+from django.template import TemplateSyntaxError
+from django.contrib.comments.models import CommentFlag
+from django.contrib.contenttypes.models import ContentType
+from django.utils.encoding import smart_unicode
+from django.contrib.comments import get_model as get_comment_model
+
+from tagging.models import Tag
+from tagging.utils import calculate_cloud
+
+from gstudio.models import Nodetype
+from gstudio.models import Author
+from gstudio.models import Metatype
+
+from gstudio.gnowql import get_node
+
+from gstudio.managers import tags_published
+from gstudio.comparison import VectorBuilder
+from gstudio.comparison import pearson_score
+from gstudio.templatetags.zcalendar import GstudioCalendar
+from gstudio.templatetags.zbreadcrumbs import retrieve_breadcrumbs
+
+register = Library()
+
+VECTORS = None
+VECTORS_FACTORY = lambda: VectorBuilder(Nodetype.published.all(),
+ ['title', 'excerpt', 'content'])
+CACHE_NODETYPES_RELATED = {}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_metatypes(template='gstudio/tags/metatypes.html'):
+ """Return the metatypes"""
+ return {'template': template,
+ 'metatypes': Metatype.tree.all()}
+
+#@register.inclusion_tag('gstudio/tags/dummy.html')
+#def get_subtypes(template='gstudio/tags/nodetypes.html'):
+# """Return the subtypes"""
+# return {'template': template,
+# 'subtypes': Nodetype.tree.all()}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_authors(template='gstudio/tags/authors.html'):
+ """Return the published authors"""
+ return {'template': template,
+ 'authors': Author.published.all()}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_recent_nodetypes(number=5, template='gstudio/tags/recent_nodetypes.html'):
+ """Return the most recent nodetypes"""
+ return {'template': template,
+ 'nodetypes': Nodetype.published.all()[:number]}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_featured_nodetypes(number=5,
+ template='gstudio/tags/featured_nodetypes.html'):
+ """Return the featured nodetypes"""
+ return {'template': template,
+ 'nodetypes': Nodetype.published.filter(featured=True)[:number]}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_random_nodetypes(number=5, template='gstudio/tags/random_nodetypes.html'):
+ """Return random nodetypes"""
+ nodetypes = Nodetype.published.all()
+ if number > len(nodetypes):
+ number = len(nodetypes)
+ return {'template': template,
+ 'nodetypes': sample(nodetypes, number)}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_popular_nodetypes(number=5, template='gstudio/tags/popular_nodetypes.html'):
+ """Return popular nodetypes"""
+ ctype = ContentType.objects.get_for_model(Nodetype)
+ query = """SELECT object_pk, COUNT(*) AS score
+ FROM %s
+ WHERE content_type_id = %%s
+ AND is_public = '1'
+ GROUP BY object_pk
+ ORDER BY score DESC""" % get_comment_model()._meta.db_table
+
+ cursor = connection.cursor()
+ cursor.execute(query, [ctype.id])
+ object_ids = [int(row[0]) for row in cursor.fetchall()]
+
+ # Use ``in_bulk`` here instead of an ``id__in`` filter, because ``id__in``
+ # would clobber the ordering.
+ object_dict = Nodetype.published.in_bulk(object_ids)
+
+ return {'template': template,
+ 'nodetypes': [object_dict[object_id]
+ for object_id in object_ids
+ if object_id in object_dict][:number]}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html', takes_context=True)
+def get_similar_nodetypes(context, number=5,
+ template='gstudio/tags/similar_nodetypes.html',
+ flush=False):
+ """Return similar nodetypes"""
+ global VECTORS
+ global CACHE_NODETYPES_RELATED
+
+ if VECTORS is None or flush:
+ VECTORS = VECTORS_FACTORY()
+ CACHE_NODETYPES_RELATED = {}
+
+ def compute_related(object_id, dataset):
+ """Compute related nodetypes to a nodetype with a dataset"""
+ object_vector = None
+ for nodetype, e_vector in dataset.items():
+ if nodetype.pk == object_id:
+ object_vector = e_vector
+
+ if not object_vector:
+ return []
+
+ nodetype_related = {}
+ for nodetype, e_vector in dataset.items():
+ if nodetype.pk != object_id:
+ score = pearson_score(object_vector, e_vector)
+ if score:
+ nodetype_related[nodetype] = score
+
+ related = sorted(nodetype_related.items(), key=lambda(k, v): (v, k))
+ return [rel[0] for rel in related]
+
+ object_id = context['object'].pk
+ columns, dataset = VECTORS()
+ key = '%s-%s' % (object_id, VECTORS.key)
+ if not key in CACHE_NODETYPES_RELATED.keys():
+ CACHE_NODETYPES_RELATED[key] = compute_related(object_id, dataset)
+
+ nodetypes = CACHE_NODETYPES_RELATED[key][:number]
+ return {'template': template,
+ 'nodetypes': nodetypes}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_archives_nodetypes(template='gstudio/tags/archives_nodetypes.html'):
+ """Return archives nodetypes"""
+ return {'template': template,
+ 'archives': Nodetype.published.dates('creation_date', 'month',
+ order='DESC')}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_archives_nodetypes_tree(
+ template='gstudio/tags/archives_nodetypes_tree.html'):
+ """Return archives nodetypes as a Tree"""
+ return {'template': template,
+ 'archives': Nodetype.published.dates('creation_date', 'day',
+ order='ASC')}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html', takes_context=True)
+def get_calendar_nodetypes(context, year=None, month=None,
+ template='gstudio/tags/calendar.html'):
+ """Return an HTML calendar of nodetypes"""
+ if not year or not month:
+ date_month = context.get('month') or context.get('day') or \
+ getattr(context.get('object'), 'creation_date', None) or \
+ datetime.today()
+ year, month = date_month.timetuple()[:2]
+
+ calendar = GstudioCalendar()
+ current_month = datetime(year, month, 1)
+
+ dates = list(Nodetype.published.dates('creation_date', 'month'))
+
+ if not current_month in dates:
+ dates.append(current_month)
+ dates.sort()
+ index = dates.index(current_month)
+
+ previous_month = index > 0 and dates[index - 1] or None
+ next_month = index != len(dates) - 1 and dates[index + 1] or None
+
+ return {'template': template,
+ 'next_month': next_month,
+ 'previous_month': previous_month,
+ 'calendar': calendar.formatmonth(year, month)}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_recent_comments(number=5, template='gstudio/tags/recent_comments.html'):
+ """Return the most recent comments"""
+ # Using map(smart_unicode... fix bug related to issue #8554
+ nodetype_published_pks = map(smart_unicode,
+ Nodetype.published.values_list('id', flat=True))
+ content_type = ContentType.objects.get_for_model(Nodetype)
+
+ comments = get_comment_model().objects.filter(
+ Q(flags=None) | Q(flags__flag=CommentFlag.MODERATOR_APPROVAL),
+ content_type=content_type, object_pk__in=nodetype_published_pks,
+ is_public=True).order_by('-submit_date')[:number]
+
+ return {'template': template,
+ 'comments': comments}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_recent_linkbacks(number=5,
+ template='gstudio/tags/recent_linkbacks.html'):
+ """Return the most recent linkbacks"""
+ nodetype_published_pks = map(smart_unicode,
+ Nodetype.published.values_list('id', flat=True))
+ content_type = ContentType.objects.get_for_model(Nodetype)
+
+ linkbacks = get_comment_model().objects.filter(
+ content_type=content_type,
+ object_pk__in=nodetype_published_pks,
+ flags__flag__in=['pingback', 'trackback'],
+ is_public=True).order_by(
+ '-submit_date')[:number]
+
+ return {'template': template,
+ 'linkbacks': linkbacks}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html', takes_context=True)
+def gstudio_pagination(context, page, begin_pages=3, end_pages=3,
+ before_pages=2, after_pages=2,
+ template='gstudio/tags/pagination.html'):
+ """Return a Digg-like pagination, by splitting long list of page
+ into 3 blocks of pages"""
+ GET_string = ''
+ for key, value in context['request'].GET.items():
+ if key != 'page':
+ GET_string += '&%s=%s' % (key, value)
+
+ begin = page.paginator.page_range[:begin_pages]
+ end = page.paginator.page_range[-end_pages:]
+ middle = page.paginator.page_range[max(page.number - before_pages - 1, 0):
+ page.number + after_pages]
+
+ if set(begin) & set(end): # [1, 2, 3], [...], [2, 3, 4]
+ begin = sorted(set(begin + end)) # [1, 2, 3, 4]
+ middle, end = [], []
+ elif begin[-1] + 1 == end[0]: # [1, 2, 3], [...], [4, 5, 6]
+ begin += end # [1, 2, 3, 4, 5, 6]
+ middle, end = [], []
+ elif set(begin) & set(middle): # [1, 2, 3], [2, 3, 4], [...]
+ begin = sorted(set(begin + middle)) # [1, 2, 3, 4]
+ middle = []
+ elif begin[-1] + 1 == middle[0]: # [1, 2, 3], [4, 5, 6], [...]
+ begin += middle # [1, 2, 3, 4, 5, 6]
+ middle = []
+ elif middle[-1] + 1 == end[0]: # [...], [15, 16, 17], [18, 19, 20]
+ end = middle + end # [15, 16, 17, 18, 19, 20]
+ middle = []
+ elif set(middle) & set(end): # [...], [17, 18, 19], [18, 19, 20]
+ end = sorted(set(middle + end)) # [17, 18, 19, 20]
+ middle = []
+
+ return {'template': template, 'page': page, 'GET_string': GET_string,
+ 'begin': begin, 'middle': middle, 'end': end}
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html', takes_context=True)
+def gstudio_breadcrumbs(context, separator='/', root_name='gstudio',
+ template='gstudio/tags/breadcrumbs.html',):
+ """Return a breadcrumb for the application"""
+ path = context['request'].path
+ page_object = context.get('object') or context.get('metatype') or \
+ context.get('tag') or context.get('author')
+ breadcrumbs = retrieve_breadcrumbs(path, page_object, root_name)
+
+ return {'template': template,
+ 'separator': separator,
+ 'breadcrumbs': breadcrumbs}
+
+@register.simple_tag
+def get_gravatar(email, size=80, rating='g', default=None):
+ """Return url for a Gravatar"""
+ url = 'http://www.gravatar.com/avatar/%s.jpg' % \
+ md5(email.strip().lower()).hexdigest()
+ options = {'s': size, 'r': rating}
+ if default:
+ options['d'] = default
+
+ url = '%s?%s' % (url, urlencode(options))
+ return url.replace('&', '&amp;')
+
+@register.simple_tag
+def get_type(name):
+
+ """Return the type of node"""
+ return get_node(name)
+
+
+class TagsNode(Node):
+ def __init__(self, context_var):
+ self.context_var = context_var
+
+ def render(self, context):
+ context[self.context_var] = tags_published()
+ return ''
+
+
+@register.tag
+def get_tags(parser, token):
+ """{% get_tags as var %}"""
+ bits = token.split_contents()
+
+ if len(bits) != 3:
+ raise TemplateSyntaxError(
+ 'get_tags tag takes exactly two arguments')
+ if bits[1] != 'as':
+ raise TemplateSyntaxError(
+ "first argument to get_tags tag must be 'as'")
+ return TagsNode(bits[2])
+
+
+@register.inclusion_tag('gstudio/tags/dummy.html')
+def get_tag_cloud(steps=6, template='gstudio/tags/tag_cloud.html'):
+ """Return a cloud of published tags"""
+ tags = Tag.objects.usage_for_queryset(
+ Nodetype.published.all(), counts=True)
+ return {'template': template,
+ 'tags': calculate_cloud(tags, steps)}
diff --git a/gstudio/templatetags/zbreadcrumbs.py b/gstudio/templatetags/zbreadcrumbs.py
new file mode 100644
index 0000000..27f06c4
--- /dev/null
+++ b/gstudio/templatetags/zbreadcrumbs.py
@@ -0,0 +1,167 @@
+
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+"""Breadcrumb module for Gstudio templatetags"""
+import re
+from datetime import datetime
+
+from django.core.urlresolvers import reverse
+from django.utils.translation import ugettext as _
+
+
+class Crumb(object):
+ """Part of the Breadcrumbs"""
+ def __init__(self, name, url=None):
+ self.name = name
+ self.url = url
+
+
+def year_crumb(creation_date):
+ """Crumb for a year"""
+ year = creation_date.strftime('%Y')
+ return Crumb(year, reverse('gstudio_nodetype_archive_year',
+ args=[year]))
+
+
+def month_crumb(creation_date):
+ """Crumb for a month"""
+ year = creation_date.strftime('%Y')
+ month = creation_date.strftime('%m')
+ month_text = creation_date.strftime('%b').capitalize()
+ return Crumb(month_text, reverse('gstudio_nodetype_archive_month',
+ args=[year, month]))
+
+
+def day_crumb(creation_date):
+ """Crumb for a day"""
+ year = creation_date.strftime('%Y')
+ month = creation_date.strftime('%m')
+ day = creation_date.strftime('%d')
+ return Crumb(day, reverse('gstudio_nodetype_archive_day',
+ args=[year, month, day]))
+
+
+GSTUDIO_ROOT_URL = lambda: reverse('gstudio_nodetype_archive_index')
+
+MODEL_BREADCRUMBS = {'Tag': lambda x: [Crumb(_('Tags'),
+ reverse('gstudio_tag_list')),
+ Crumb(x.name)],
+ 'Author': lambda x: [Crumb(_('Authors'),
+ reverse('gstudio_author_list')),
+ Crumb(x.username)],
+ 'Metatype': lambda x: [Crumb(
+ _('Metatypes'), reverse('gstudio_metatype_list'))] + \
+ [Crumb(anc.title, anc.get_absolute_url())
+ for anc in x.get_ancestors()] + [Crumb(x.title)],
+ 'Nodetype': lambda x: [year_crumb(x.creation_date),
+ month_crumb(x.creation_date),
+ day_crumb(x.creation_date),
+ Crumb(x.title)]}
+
+DATE_REGEXP = re.compile(
+ r'.*(?P<year>\d{4})/(?P<month>\d{2})?/(?P<day>\d{2})?.*')
+
+
+def retrieve_breadcrumbs(path, model_instance, root_name='gstudio'):
+ """Build a semi-hardcoded breadcrumbs
+ based of the model's url handled by Gstudio"""
+ breadcrumbs = []
+
+ if root_name:
+ breadcrumbs.append(Crumb(root_name, GSTUDIO_ROOT_URL()))
+
+ if model_instance is not None:
+ key = model_instance.__class__.__name__
+ if key in MODEL_BREADCRUMBS:
+ breadcrumbs.extend(MODEL_BREADCRUMBS[key](model_instance))
+ return breadcrumbs
+
+ date_match = DATE_REGEXP.match(path)
+ if date_match:
+ date_dict = date_match.groupdict()
+ path_date = datetime(
+ int(date_dict['year']),
+ date_dict.get('month') is not None and \
+ int(date_dict.get('month')) or 1,
+ date_dict.get('day') is not None and \
+ int(date_dict.get('day')) or 1)
+
+ date_breadcrumbs = [year_crumb(path_date)]
+ if date_dict['month']:
+ date_breadcrumbs.append(month_crumb(path_date))
+ if date_dict['day']:
+ date_breadcrumbs.append(day_crumb(path_date))
+ breadcrumbs.extend(date_breadcrumbs)
+
+ return breadcrumbs
+
+ url_components = [comp for comp in
+ path.replace(GSTUDIO_ROOT_URL(), '').split('/') if comp]
+ if len(url_components):
+ breadcrumbs.append(Crumb(_(url_components[-1].capitalize())))
+
+ return breadcrumbs
diff --git a/gstudio/templatetags/zcalendar.py b/gstudio/templatetags/zcalendar.py
new file mode 100644
index 0000000..8ff1b77
--- /dev/null
+++ b/gstudio/templatetags/zcalendar.py
@@ -0,0 +1,128 @@
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+# This project incorporates work covered by the following copyright and permission notice:
+
+# Copyright (c) 2009, Julien Fache
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of the author nor the names of other
+# contributors may be used to endorse or promote products derived
+# from this software without specific prior written permission.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# Copyright (c) 2011, 2012 Free Software Foundation
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+
+
+"""Calendar module for Gstudio templatetags"""
+from datetime import date
+from calendar import HTMLCalendar
+
+from django.utils.dates import MONTHS
+from django.utils.dates import WEEKDAYS_ABBR
+from django.utils.formats import get_format
+from django.core.urlresolvers import reverse
+
+from gstudio.models import Nodetype
+
+AMERICAN_TO_EUROPEAN_WEEK_DAYS = [6, 0, 1, 2, 3, 4, 5]
+
+
+class GstudioCalendar(HTMLCalendar):
+ """Override of HTMLCalendar"""
+
+ def __init__(self):
+ """Retrieve and convert the localized first week day
+ at initialization"""
+ HTMLCalendar.__init__(self, AMERICAN_TO_EUROPEAN_WEEK_DAYS[
+ get_format('FIRST_DAY_OF_WEEK')])
+
+ def formatday(self, day, weekday):
+ """Return a day as a table cell with a link
+ if nodetypes are published this day"""
+ if day and day in self.day_nodetypes:
+ day_date = date(self.current_year, self.current_month, day)
+ archive_day_url = reverse('gstudio_nodetype_archive_day',
+ args=[day_date.strftime('%Y'),
+ day_date.strftime('%m'),
+ day_date.strftime('%d')])
+ return '<td class="%s nodetype"><a href="%s" '\
+ 'rel="archives">%d</a></td>' % (
+ self.cssclasses[weekday], archive_day_url, day)
+
+ return super(GstudioCalendar, self).formatday(day, weekday)
+
+ def formatmonth(self, theyear, themonth, withyear=True):
+ """Return a formatted month as a table with
+ new attributes computed for formatting a day"""
+ self.current_year = theyear
+ self.current_month = themonth
+ self.day_nodetypes = [nodetypes.creation_date.day for nodetypes in
+ Nodetype.published.filter(
+ creation_date__year=theyear,
+ creation_date__month=themonth)]
+
+ return super(GstudioCalendar, self).formatmonth(
+ theyear, themonth, withyear)
+
+ def formatweekday(self, day):
+ """Return a weekday name translated
+ as a table header."""
+ return '<th class="%s">%s</th>' % (self.cssclasses[day],
+ WEEKDAYS_ABBR[day].title())
+
+ def formatmonthname(self, theyear, themonth, withyear=True):
+ """Return a month name translated
+ as a table row."""
+ monthname = '%s %s' % (MONTHS[themonth].title(), theyear)
+ return '<tr><th colspan="7" class="month">%s</th></tr>' % monthname