Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

first commit

  • Loading branch information...
commit 47c69970ed9bd20f59c743abe5e2a221e3b0e4c6 0 parents
@ashok-raavi authored
Showing with 2,763 additions and 0 deletions.
  1. 0  example/__init__.py
  2. +28 −0 example/blogango/__init__.py
  3. +16 −0 example/blogango/admin.py
  4. +372 −0 example/blogango/akismet.py
  5. 0  example/blogango/conf/__init__.py
  6. +5 −0 example/blogango/conf/settings.py
  7. +27 −0 example/blogango/context_processors.py
  8. +43 −0 example/blogango/feeds.py
  9. +1 −0  example/blogango/fixtures/initial_data.json
  10. +46 −0 example/blogango/forms.py
  11. +151 −0 example/blogango/models.py
  12. +32 −0 example/blogango/search.py
  13. +9 −0 example/blogango/templates/b-404.html
  14. +9 −0 example/blogango/templates/b-500.html
  15. +120 −0 example/blogango/templates/base.html
  16. +23 −0 example/blogango/templates/blogango/archive_view.html
  17. +20 −0 example/blogango/templates/blogango/author.html
  18. +22 −0 example/blogango/templates/blogango/blogroll.html
  19. +8 −0 example/blogango/templates/blogango/comment.html
  20. +5 −0 example/blogango/templates/blogango/commentcontrols.html
  21. +24 −0 example/blogango/templates/blogango/create.html
  22. +29 −0 example/blogango/templates/blogango/details.html
  23. +20 −0 example/blogango/templates/blogango/edit_preferences.html
  24. +1 −0  example/blogango/templates/blogango/eggs.html
  25. +6 −0 example/blogango/templates/blogango/entry_controls.html
  26. +26 −0 example/blogango/templates/blogango/install.html
  27. +29 −0 example/blogango/templates/blogango/mainpage.html
  28. +27 −0 example/blogango/templates/blogango/manage.html
  29. +32 −0 example/blogango/templates/blogango/manage_entries.html
  30. +34 −0 example/blogango/templates/blogango/mod_comment.html
  31. +20 −0 example/blogango/templates/blogango/tag_details.html
  32. +7 −0 example/blogango/templates/blogango/topbar.html
  33. +9 −0 example/blogango/templates/entry_footer.html
  34. +29 −0 example/blogango/templates/registration/login.html
  35. +17 −0 example/blogango/templates/registration/logout.html
  36. +25 −0 example/blogango/templates/registration/password_change.html
  37. +18 −0 example/blogango/templates/search.html
  38. 0  example/blogango/templatetags/__init__.py
  39. +12 −0 example/blogango/templatetags/filters.py
  40. +44 −0 example/blogango/templatetags/gravatar.py
  41. +59 −0 example/blogango/urls.py
  42. +359 −0 example/blogango/views.py
  43. +12 −0 example/doc.txt
  44. +11 −0 example/manage.py
  45. +101 −0 example/settings.py
  46. +114 −0 example/site_media/blogango/css/agiliqblog.css
  47. +1 −0  example/site_media/blogango/css/prettify.css
  48. BIN  example/site_media/blogango/images/bg.png
  49. BIN  example/site_media/blogango/images/blog.png
  50. BIN  example/site_media/blogango/images/date_icon.png
  51. BIN  example/site_media/blogango/images/icon_smile.gif
  52. BIN  example/site_media/blogango/images/linesbg.png
  53. BIN  example/site_media/blogango/images/linesbg2.png
  54. +23 −0 example/site_media/blogango/js/prettify.js
  55. +1 −0  example/subdomain_admin
  56. 0  example/subdomains/__init__.py
  57. 0  example/subdomains/conf/__init__.py
  58. +5 −0 example/subdomains/conf/settings.py
  59. +6 −0 example/subdomains/context_processors.py
  60. +57 −0 example/subdomains/decorators.py
  61. +31 −0 example/subdomains/forms.py
  62. +85 −0 example/subdomains/middleware.py
  63. +82 −0 example/subdomains/models.py
  64. +48 −0 example/subdomains/requestfactory.py
  65. +10 −0 example/subdomains/templates/subdomains/base.html
  66. +14 −0 example/subdomains/templates/subdomains/create_subdomain.html
  67. +147 −0 example/subdomains/tests.py
  68. +7 −0 example/subdomains/urls.py
  69. +35 −0 example/subdomains/views.py
  70. BIN  example/tmp/subdomain_admin.db
  71. +27 −0 example/urls.py
  72. 0  subdomain_admin/__init__.py
  73. +36 −0 subdomain_admin/admin.py
  74. +66 −0 subdomain_admin/forms.py
  75. +24 −0 subdomain_admin/models.py
  76. +12 −0 subdomain_admin/templates/subdomain_admin/createsite.html
  77. +23 −0 subdomain_admin/tests.py
  78. +21 −0 subdomain_admin/views.py
0  example/__init__.py
No changes.
28 example/blogango/__init__.py
@@ -0,0 +1,28 @@
+
+from datetime import date, datetime, time
+from time import strptime
+
+from django.core.urlresolvers import reverse
+from django.db.models import signals
+
+from pingback import create_ping_func
+from pingback.client import ping_external_links, ping_directories
+from django_xmlrpc import xmlrpcdispatcher
+
+from blogango.models import BlogEntry
+
+def pingback_blog_handler(year, month, slug, **kwargs):
+ return BlogEntry.objects.get(created_on__year=year,
+ created_on__month=month,
+ slug=slug,
+ is_published=True)
+
+
+ping_details = {'blogango_details': pingback_blog_handler}
+
+ping_func = create_ping_func(**ping_details)
+
+xmlrpcdispatcher.register_function(ping_func, 'pingback.ping')
+
+signals.post_save.connect(ping_external_links(content_attr='text', url_attr='get_absolute_url'), sender=BlogEntry, weak=False)
+
16 example/blogango/admin.py
@@ -0,0 +1,16 @@
+from django.contrib import admin
+
+from blogango.models import Blog, BlogEntry, Comment, BlogRoll
+
+from subdomain_admin.admin import SubdomainAdmin
+
+class BlogEntryAdmin(SubdomainAdmin):
+ prepopulated_fields = {'slug': ('title',)}
+
+class BlogRollAdmin(SubdomainAdmin):
+ pass
+
+admin.site.register(Blog)
+admin.site.register(BlogEntry, BlogEntryAdmin)
+admin.site.register(Comment)
+admin.site.register(BlogRoll, BlogRollAdmin)
372 example/blogango/akismet.py
@@ -0,0 +1,372 @@
+# Version 0.2.0
+# 2009/06/18
+
+# Copyright Michael Foord 2005-2009
+# akismet.py
+# Python interface to the akismet API
+# E-mail fuzzyman@voidspace.org.uk
+
+# http://www.voidspace.org.uk/python/modules.shtml
+# http://akismet.com
+
+# Released subject to the BSD License
+# See http://www.voidspace.org.uk/python/license.shtml
+
+
+"""
+A python interface to the `Akismet <http://akismet.com>`_ API.
+This is a web service for blocking SPAM comments to blogs - or other online
+services.
+
+You will need a Wordpress API key, from `wordpress.com <http://wordpress.com>`_.
+
+You should pass in the keyword argument 'agent' to the name of your program,
+when you create an Akismet instance. This sets the ``user-agent`` to a useful
+value.
+
+The default is : ::
+
+ Python Interface by Fuzzyman | akismet.py/0.2.0
+
+Whatever you pass in, will replace the *Python Interface by Fuzzyman* part.
+**0.2.0** will change with the version of this interface.
+
+Usage example::
+
+ from akismet import Akismet
+
+ api = Akismet(agent='Test Script')
+ # if apikey.txt is in place,
+ # the key will automatically be set
+ # or you can call api.setAPIKey()
+ #
+ if api.key is None:
+ print "No 'apikey.txt' file."
+ elif not api.verify_key():
+ print "The API key is invalid."
+ else:
+ # data should be a dictionary of values
+ # They can all be filled in with defaults
+ # from a CGI environment
+ if api.comment_check(comment, data):
+ print 'This comment is spam.'
+ else:
+ print 'This comment is ham.'
+"""
+
+
+import os, sys
+from urllib import urlencode
+
+import socket
+if hasattr(socket, 'setdefaulttimeout'):
+ # Set the default timeout on sockets to 5 seconds
+ socket.setdefaulttimeout(5)
+
+__version__ = '0.2.0'
+
+__all__ = (
+ '__version__',
+ 'Akismet',
+ 'AkismetError',
+ 'APIKeyError',
+ )
+
+__author__ = 'Michael Foord <fuzzyman AT voidspace DOT org DOT uk>'
+
+__docformat__ = "restructuredtext en"
+
+user_agent = "%s | akismet.py/%s"
+DEFAULTAGENT = 'Python Interface by Fuzzyman/%s'
+
+isfile = os.path.isfile
+
+urllib2 = None
+try:
+ from google.appengine.api import urlfetch
+except ImportError:
+ import urllib2
+
+if urllib2 is None:
+ def _fetch_url(url, data, headers):
+ req = urlfetch.fetch(url=url, payload=data, method=urlfetch.POST, headers=headers)
+ if req.status_code == 200:
+ return req.content
+ raise Exception('Could not fetch Akismet URL: %s Response code: %s' %
+ (url, req.status_code))
+else:
+ def _fetch_url(url, data, headers):
+ req = urllib2.Request(url, data, headers)
+ h = urllib2.urlopen(req)
+ resp = h.read()
+ return resp
+
+
+class AkismetError(Exception):
+ """Base class for all akismet exceptions."""
+
+class APIKeyError(AkismetError):
+ """Invalid API key."""
+
+class Akismet(object):
+ """A class for working with the akismet API"""
+
+ baseurl = 'rest.akismet.com/1.1/'
+
+ def __init__(self, key=None, blog_url=None, agent=None):
+ """Automatically calls ``setAPIKey``."""
+ if agent is None:
+ agent = DEFAULTAGENT % __version__
+ self.user_agent = user_agent % (agent, __version__)
+ self.setAPIKey(key, blog_url)
+
+
+ def _getURL(self):
+ """
+ Fetch the url to make requests to.
+
+ This comprises of api key plus the baseurl.
+ """
+ return 'http://%s.%s' % (self.key, self.baseurl)
+
+
+ def _safeRequest(self, url, data, headers):
+ try:
+ resp = _fetch_url(url, data, headers)
+ except Exception, e:
+ raise AkismetError(str(e))
+ return resp
+
+
+ def setAPIKey(self, key=None, blog_url=None):
+ """
+ Set the wordpress API key for all transactions.
+
+ If you don't specify an explicit API ``key`` and ``blog_url`` it will
+ attempt to load them from a file called ``apikey.txt`` in the current
+ directory.
+
+ This method is *usually* called automatically when you create a new
+ ``Akismet`` instance.
+ """
+ if key is None and isfile('apikey.txt'):
+ the_file = [l.strip() for l in open('apikey.txt').readlines()
+ if l.strip() and not l.strip().startswith('#')]
+ try:
+ self.key = the_file[0]
+ self.blog_url = the_file[1]
+ except IndexError:
+ raise APIKeyError("Your 'apikey.txt' is invalid.")
+ else:
+ self.key = key
+ self.blog_url = blog_url
+
+
+ def verify_key(self):
+ """
+ This equates to the ``verify-key`` call against the akismet API.
+
+ It returns ``True`` if the key is valid.
+
+ The docs state that you *ought* to call this at the start of the
+ transaction.
+
+ It raises ``APIKeyError`` if you have not yet set an API key.
+
+ If the connection to akismet fails, it allows the normal ``HTTPError``
+ or ``URLError`` to be raised.
+ (*akismet.py* uses `urllib2 <http://docs.python.org/lib/module-urllib2.html>`_)
+ """
+ if self.key is None:
+ raise APIKeyError("Your have not set an API key.")
+ data = { 'key': self.key, 'blog': self.blog_url }
+ # this function *doesn't* use the key as part of the URL
+ url = 'http://%sverify-key' % self.baseurl
+ # we *don't* trap the error here
+ # so if akismet is down it will raise an HTTPError or URLError
+ headers = {'User-Agent' : self.user_agent}
+ resp = self._safeRequest(url, urlencode(data), headers)
+ if resp.lower() == 'valid':
+ return True
+ else:
+ return False
+
+ def _build_data(self, comment, data):
+ """
+ This function builds the data structure required by ``comment_check``,
+ ``submit_spam``, and ``submit_ham``.
+
+ It modifies the ``data`` dictionary you give it in place. (and so
+ doesn't return anything)
+
+ It raises an ``AkismetError`` if the user IP or user-agent can't be
+ worked out.
+ """
+ data['comment_content'] = comment
+ if not 'user_ip' in data:
+ try:
+ val = os.environ['REMOTE_ADDR']
+ except KeyError:
+ raise AkismetError("No 'user_ip' supplied")
+ data['user_ip'] = val
+ if not 'user_agent' in data:
+ try:
+ val = os.environ['HTTP_USER_AGENT']
+ except KeyError:
+ raise AkismetError("No 'user_agent' supplied")
+ data['user_agent'] = val
+ #
+ data.setdefault('referrer', os.environ.get('HTTP_REFERER', 'unknown'))
+ data.setdefault('permalink', '')
+ data.setdefault('comment_type', 'comment')
+ data.setdefault('comment_author', '')
+ data.setdefault('comment_author_email', '')
+ data.setdefault('comment_author_url', '')
+ data.setdefault('SERVER_ADDR', os.environ.get('SERVER_ADDR', ''))
+ data.setdefault('SERVER_ADMIN', os.environ.get('SERVER_ADMIN', ''))
+ data.setdefault('SERVER_NAME', os.environ.get('SERVER_NAME', ''))
+ data.setdefault('SERVER_PORT', os.environ.get('SERVER_PORT', ''))
+ data.setdefault('SERVER_SIGNATURE', os.environ.get('SERVER_SIGNATURE',
+ ''))
+ data.setdefault('SERVER_SOFTWARE', os.environ.get('SERVER_SOFTWARE',
+ ''))
+ data.setdefault('HTTP_ACCEPT', os.environ.get('HTTP_ACCEPT', ''))
+ data.setdefault('blog', self.blog_url)
+
+
+ def comment_check(self, comment, data=None, build_data=True, DEBUG=False):
+ """
+ This is the function that checks comments.
+
+ It returns ``True`` for spam and ``False`` for ham.
+
+ If you set ``DEBUG=True`` then it will return the text of the response,
+ instead of the ``True`` or ``False`` object.
+
+ It raises ``APIKeyError`` if you have not yet set an API key.
+
+ If the connection to Akismet fails then the ``HTTPError`` or
+ ``URLError`` will be propogated.
+
+ As a minimum it requires the body of the comment. This is the
+ ``comment`` argument.
+
+ Akismet requires some other arguments, and allows some optional ones.
+ The more information you give it, the more likely it is to be able to
+ make an accurate diagnosise.
+
+ You supply these values using a mapping object (dictionary) as the
+ ``data`` argument.
+
+ If ``build_data`` is ``True`` (the default), then *akismet.py* will
+ attempt to fill in as much information as possible, using default
+ values where necessary. This is particularly useful for programs
+ running in a {acro;CGI} environment. A lot of useful information
+ can be supplied from evironment variables (``os.environ``). See below.
+
+ You *only* need supply values for which you don't want defaults filled
+ in for. All values must be strings.
+
+ There are a few required values. If they are not supplied, and
+ defaults can't be worked out, then an ``AkismetError`` is raised.
+
+ If you set ``build_data=False`` and a required value is missing an
+ ``AkismetError`` will also be raised.
+
+ The normal values (and defaults) are as follows : ::
+
+ 'user_ip': os.environ['REMOTE_ADDR'] (*)
+ 'user_agent': os.environ['HTTP_USER_AGENT'] (*)
+ 'referrer': os.environ.get('HTTP_REFERER', 'unknown') [#]_
+ 'permalink': ''
+ 'comment_type': 'comment' [#]_
+ 'comment_author': ''
+ 'comment_author_email': ''
+ 'comment_author_url': ''
+ 'SERVER_ADDR': os.environ.get('SERVER_ADDR', '')
+ 'SERVER_ADMIN': os.environ.get('SERVER_ADMIN', '')
+ 'SERVER_NAME': os.environ.get('SERVER_NAME', '')
+ 'SERVER_PORT': os.environ.get('SERVER_PORT', '')
+ 'SERVER_SIGNATURE': os.environ.get('SERVER_SIGNATURE', '')
+ 'SERVER_SOFTWARE': os.environ.get('SERVER_SOFTWARE', '')
+ 'HTTP_ACCEPT': os.environ.get('HTTP_ACCEPT', '')
+
+ (*) Required values
+
+ You may supply as many additional 'HTTP_*' type values as you wish.
+ These should correspond to the http headers sent with the request.
+
+ .. [#] Note the spelling "referrer". This is a required value by the
+ akismet api - however, referrer information is not always
+ supplied by the browser or server. In fact the HTTP protocol
+ forbids relying on referrer information for functionality in
+ programs.
+ .. [#] The `API docs <http://akismet.com/development/api/>`_ state that this value
+ can be " *blank, comment, trackback, pingback, or a made up value*
+ *like 'registration'* ".
+ """
+ if self.key is None:
+ raise APIKeyError("Your have not set an API key.")
+ if data is None:
+ data = {}
+ if build_data:
+ self._build_data(comment, data)
+ if 'blog' not in data:
+ data['blog'] = self.blog_url
+ url = '%scomment-check' % self._getURL()
+ # we *don't* trap the error here
+ # so if akismet is down it will raise an HTTPError or URLError
+ headers = {'User-Agent' : self.user_agent}
+ resp = self._safeRequest(url, urlencode(data), headers)
+ if DEBUG:
+ return resp
+ resp = resp.lower()
+ if resp == 'true':
+ return True
+ elif resp == 'false':
+ return False
+ else:
+ # NOTE: Happens when you get a 'howdy wilbur' response !
+ raise AkismetError('missing required argument.')
+
+
+ def submit_spam(self, comment, data=None, build_data=True):
+ """
+ This function is used to tell akismet that a comment it marked as ham,
+ is really spam.
+
+ It takes all the same arguments as ``comment_check``, except for
+ *DEBUG*.
+ """
+ if self.key is None:
+ raise APIKeyError("Your have not set an API key.")
+ if data is None:
+ data = {}
+ if build_data:
+ self._build_data(comment, data)
+ url = '%ssubmit-spam' % self._getURL()
+ # we *don't* trap the error here
+ # so if akismet is down it will raise an HTTPError or URLError
+ headers = {'User-Agent' : self.user_agent}
+ self._safeRequest(url, urlencode(data), headers)
+
+
+ def submit_ham(self, comment, data=None, build_data=True):
+ """
+ This function is used to tell akismet that a comment it marked as spam,
+ is really ham.
+
+ It takes all the same arguments as ``comment_check``, except for
+ *DEBUG*.
+ """
+ if self.key is None:
+ raise APIKeyError("Your have not set an API key.")
+ if data is None:
+ data = {}
+ if build_data:
+ self._build_data(comment, data)
+ url = '%ssubmit-ham' % self._getURL()
+ # we *don't* trap the error here
+ # so if akismet is down it will raise an HTTPError or URLError
+ headers = {'User-Agent' : self.user_agent}
+ self._safeRequest(url, urlencode(data), headers)
0  example/blogango/conf/__init__.py
No changes.
5 example/blogango/conf/settings.py
@@ -0,0 +1,5 @@
+
+from django.conf import settings
+
+AKISMET_API_KEY = getattr(settings, 'AKISMET_API_KEY', '')
+AKISMET_COMMENT = bool(AKISMET_API_KEY)
27 example/blogango/context_processors.py
@@ -0,0 +1,27 @@
+
+from django.conf import settings
+from django.core.urlresolvers import reverse
+
+from taggit.models import Tag
+
+from blogango.models import Blog, BlogRoll
+from blogango.views import _get_archive_months
+
+def extra_context(request):
+ blog_rolls = BlogRoll.objects.filter(is_published=True)
+ tags = Tag.objects.all()
+ feed_url = getattr(settings, 'FEED_URL', reverse('blogango_feed', args=['latest']))
+
+ archive_months = _get_archive_months()
+
+ blog = Blog.objects.all()
+ blog = blog[0] if blog.count() > 0 else None
+
+ return {'blog': blog,
+ 'blog_rolls': blog_rolls,
+ 'tags': tags,
+ 'canonical_url': request.build_absolute_uri(),
+ 'feed_url': feed_url,
+ 'pingback_xmlrpc_url': request.build_absolute_uri(reverse('xmlrpc')),
+ 'archive_months': archive_months}
+
43 example/blogango/feeds.py
@@ -0,0 +1,43 @@
+from django.contrib.syndication.feeds import Feed
+from django.core.urlresolvers import reverse
+from django.core.exceptions import ObjectDoesNotExist
+
+from blogango.models import Blog, BlogEntry
+from taggit.models import Tag
+
+class main_feed(Feed):
+ try:
+ blog = Blog.objects.all()[0]
+ except:
+ class DummyBlog:
+ def __init__ (self):
+ self.title = ''
+ self.tag_line = ''
+ blog = DummyBlog()
+
+ title = blog.title
+ link = "/rss/latest/"
+ description = blog.tag_line
+
+ def items (self):
+ return BlogEntry.objects.filter(is_published=True)[:10]
+
+class CatFeed(Feed):
+ def get_object(self, bits):
+ if len(bits) != 1:
+ raise ObjectDoesNotExist
+ return Tag.objects.get(name__iexact=bits[0])
+
+ def title(self, obj):
+ return "%s" % obj.name
+
+ def link(self, obj):
+ if not obj:
+ raise FeedDoesNotExist
+ return reverse('blogango_tag_details', args=[obj.slug])
+
+ def description(self, obj):
+ return "Category: %s" % obj.name
+
+ def items(self, obj):
+ return BlogEntry.objects.filter(is_published=True, tags__in=[obj])
1  example/blogango/fixtures/initial_data.json
@@ -0,0 +1 @@
+[{"pk": 1, "model": "blogango.blog", "fields": {"recent_comments": 5, "recents": 5, "tag_line": "subdomain admin", "entries_per_page": 10, "title": "agiliq"}}]
46 example/blogango/forms.py
@@ -0,0 +1,46 @@
+
+from django import forms
+
+from blogango.models import Blog, BlogRoll
+
+class WideTextArea(forms.Textarea):
+ def __init__(self, *args, **kwargs):
+ kwargs.setdefault('attrs',{}).update({'rows': '20', 'cols':'80'})
+ super(WideTextArea, self).__init__(*args, **kwargs)
+
+class EntryForm(forms.Form):
+ title = forms.CharField(max_length=100, required=False)
+ text = forms.CharField(widget=WideTextArea, label='Entry')
+ slug = forms.CharField(max_length=100, required=False)
+ tags = forms.CharField(max_length=100, required=False)
+ is_page = forms.BooleanField(initial=False, label='Is it a page?', required=False)
+ comments_allowed = forms.BooleanField(initial=True, label='Are comments allowed?', required=False)
+ is_rte = forms.BooleanField(initial=True, label='Use Rick Text?', required=False)
+
+class CommentForm(forms.Form):
+ text = forms.CharField(widget=forms.Textarea(attrs={'class': 'textarea'}), label='Comment')
+ name = forms.CharField(max_length=100, widget=forms.TextInput(attrs={'class': 'textfield'}))
+ url = forms.URLField(required=False, widget=forms.TextInput(attrs={'class': 'textfield'}))
+ email = forms.EmailField(widget=forms.TextInput(attrs={'class': 'textfield'}))
+
+class TagForm(forms.Form):
+ tag_txt = forms.CharField(max_length=100, label='Tag as')
+
+class InstallForm(forms.ModelForm):
+
+ class Meta:
+ model = Blog
+ exclude = ('entries_per_page', 'recents', 'recent_comments')
+
+class PreferencesForm(forms.ModelForm):
+
+ class Meta:
+ model = Blog
+ exclude = ('title', 'tag_line')
+
+class BlogForm(forms.ModelForm):
+
+ class Meta:
+ model = BlogRoll
+ exclude = ('is_published',)
+
151 example/blogango/models.py
@@ -0,0 +1,151 @@
+from django.db import models
+from django.db.models import permalink
+from django.contrib.auth.models import User
+
+from taggit.managers import TaggableManager
+
+from subdomain_admin.models import SubModel
+
+class Blog(models.Model):
+ """Blog wide settings.
+ title:title of the Blog.
+ tag_line: Tagline/subtitle of the blog. This two are genearlly displayed on each page's header.
+ entries_per_page=Number of entries to display on each page.
+ recents: Number of recent entries to display in the sidebar.
+ recent_comments: Number of recent comments to display in the sidebar.
+ """
+
+ title = models.CharField(max_length=100)
+ tag_line = models.CharField(max_length=100)
+ entries_per_page = models.IntegerField(default=10)
+ recents = models.IntegerField(default=5)
+ recent_comments = models.IntegerField(default=5)
+
+ def __unicode__(self):
+ return self.title
+
+ def save(self):
+ """There should not be more than one Blog object"""
+ if Blog.objects.count() > 1 and self.id:
+ raise Exception("Only one blog object allowed.")
+ super(Blog, self).save() # Call the "real" save() method.
+
+
+
+class BlogEntry(SubModel):
+ """Each blog entry.
+ Title: Post title.
+ Slug: Post slug. These two if not given are inferred directly from entry text.
+ text = The main data for the post.
+ summary = The summary for the text. probably can can be derived from text, bit we dont want do do that each time main page is displayed.
+ created_on = The date this entry was created. Defaults to now.
+ Created by: The user who wrote this.
+ is_page: IS this a page or a post? Pages are the more important posts, which might be displayed differently. Defaults to false.
+ is_published: Is this page published. If yes then we would display this on site, otherwise no. Default to true.
+ comments_allowed: Are comments allowed on this post? Default to True
+ is_rte: Was this post done using a Rich text editor?"""
+
+ title = models.CharField(max_length=100)
+ slug = models.SlugField()
+ text = models.TextField()
+ summary = models.TextField()
+ created_on = models.DateTimeField(auto_now_add=True)
+ created_by = models.ForeignKey(User, unique=False)
+ is_page = models.BooleanField(default=False)
+ is_published = models.BooleanField(default=True)
+ comments_allowed = models.BooleanField(default=True)
+ is_rte = models.BooleanField(default=False)
+
+ meta_keywords = models.TextField(blank=True, null=True)
+ meta_description = models.TextField(blank=True, null=True)
+
+ tags = TaggableManager()
+
+ class Meta:
+ ordering = ['-created_on']
+ verbose_name_plural = 'Blog entries'
+
+ def __unicode__(self):
+ return self.title
+
+ def save(self):
+ if self.title == None or self.title == '':
+ self.title = _infer_title_or_slug(self.text)
+ if self.slug == None or self.slug == '':
+ self.slug = _infer_title_or_slug(self.text)
+ self.summary = _generate_summary(self.text)
+ if not self.meta_keywords:
+ self.meta_keywords = self.summary
+ if not self.meta_description:
+ self.meta_description = self.summary
+
+ super(BlogEntry, self).save() # Call the "real" save() method.
+
+ @permalink
+ def get_absolute_url(self):
+ return ('blogango_details', (), {'year': self.created_on.strftime('%Y'),
+ 'month': self.created_on.strftime('%m'),
+ 'slug': self.slug})
+
+ @permalink
+ def get_edit_url(self):
+ return ('blogango.views.edit_entry', [self.id])
+
+ def get_num_comments(self):
+ cmnt_count = Comment.objects.filter(comment_for=self).count()
+ return cmnt_count
+
+ # @property
+ # def blog_title(self):
+ # return self.title
+
+class Comment(models.Model):
+ """Comments for each blog.
+ text: The comment text.
+ comment_for: the Post/Page this comment is created for.
+ created_on: The date this comment was written on.
+ created_by: THe user who wrote this comment.
+ user_name = If created_by is null, this comment was by anonymous user. Name in that case.
+ email_id: Email-id, as in user_name.
+ is_spam: Is comment marked as spam? We do not display the comment in those cases."""
+
+ text = models.TextField()
+ comment_for = models.ForeignKey(BlogEntry)
+ created_on = models.DateTimeField(auto_now_add=True)
+ created_by = models.ForeignKey(User, unique=False, blank=True, null=True)
+ user_name = models.CharField(max_length=100)
+ user_url = models.CharField(max_length=100)
+ email_id = models.EmailField()
+ is_spam = models.BooleanField(default=False)
+
+ class Meta:
+ ordering = ['created_on']
+
+ def __unicode__(self):
+ return self.text
+
+ @permalink
+ def get_absolute_url (self):
+ #return '/comment/%s/' %self.id
+ return ('comment_details', self.id)
+
+
+class BlogRoll(SubModel):
+ url = models.URLField(unique=True)
+ text = models.CharField(max_length=100)
+ is_published = models.BooleanField(default=True)
+
+ def __unicode__(self):
+ return self.text
+
+ def get_absolute_url(self):
+ return self.url
+
+
+#Helper methods
+def _infer_title_or_slug(text):
+ return '-'.join(text.split()[:5])
+
+def _generate_summary(text):
+ return ' '.join(text.split()[:100])
+
32 example/blogango/search.py
@@ -0,0 +1,32 @@
+import urllib2, urllib
+from django.utils import simplejson
+from django.http import HttpResponse, HttpResponseRedirect
+from django.shortcuts import render_to_response
+import views
+
+
+site = 'blogango.com'
+
+def search (request):
+ query_term = ""
+ for term in request.GET['q']:
+ query_term += term
+ results = get_search_results('YLPjx2rV34F4hXcTnJYqYJUj9tANeqax76Ip2vADl9kKuByRNHgC4qafbATFoQ', query_term, site = site)
+ print [result['Url'] for result in results]
+ payload = {'results':results}
+ return views.render('search.html', request, payload)
+
+def get_search_results(appid, query, region ='us', type = 'all', results = 10, start = 0, format ='any', adult_ok = "", similar_ok = "", language = "", country = "", site = "", subscription = "", license = ''):
+ base_url = u'http://search.yahooapis.com/WebSearchService/V1/webSearch?'
+ params = locals()
+ result = _query_yahoo(base_url, params)
+ return result['ResultSet']['Result']
+
+def _query_yahoo(base_url, params):
+ params['output'] = 'json'
+ payload = urllib.urlencode(params)
+ url = base_url + payload
+ print url
+ response = urllib2.urlopen(url)
+ result = simplejson.load(response)
+ return result
9 example/blogango/templates/b-404.html
@@ -0,0 +1,9 @@
+{% extends 'base.html' %}
+
+{% block main %}
+<div id="columnA_2columns">
+<div id="message">
+<p>404 - Page does not exist!
+</div>
+</div>
+{% endblock %}
9 example/blogango/templates/b-500.html
@@ -0,0 +1,9 @@
+{% extends 'base.html' %}
+
+{% block main %}
+<div id="columnA_2columns">
+<div id="message">
+<p>500 - Internal server error!
+</div>
+</div>
+{% endblock %}
120 example/blogango/templates/base.html
@@ -0,0 +1,120 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<title>Blog {% block title %} {% endblock %}</title>
+<link href="{{ MEDIA_URL }}blogango/css/agiliqblog.css" rel="stylesheet" type="text/css" />
+<link href="{{ MEDIA_URL }}blogango/css/prettify.css" rel="stylesheet" type="text/css" />
+<link href='http://fonts.googleapis.com/css?family=Droid+Sans+Mono' rel='stylesheet' type='text/css' />
+
+{% if feed_url %}
+ <link rel="alternate" type="application/rss+xml" title="The Agiliq Blog - Django Web Development RSS Feed" href="{{feed_url}}" />
+{% endif %}
+{% if canonical_url %}
+ <link rel="canonical" href='{{ canonical_url }}' />
+{% endif %}
+{# <link rel="pingback" href="{% url xmlrpc %}" /> #}
+<link rel="pingback" href="{{ pingback_xmlrpc_url }}" />
+
+<script type="text/javascript" src="{{ MEDIA_URL }}blogango/js/prettify.js"></script>
+
+</head>
+<body>
+
+
+<div id="container">
+ <div id="header">
+ <a href="{% url blogango_index %}">{{ blog.title }}</a> {{ blog.tag_line }}
+ </div>
+
+ <!--LEFT PANEL STARTS-->
+ <div id="leftpanel">
+ {% block content %}
+ <!--BLOG POST STARTS-->
+ {% for entry in entries %}
+ <div class="leftblock">
+ <h1>{{ entry.title }}</h1>
+ <div class="filed">Filed in : <a href="#">Django Forms</a></div>
+ <div class="postdetails">
+ <img src="{{ MEDIA_URL }}blogango/images/blog.png" width="16" height="16" alt="Written by" />
+ <a href="#"> {{ entry.created_by }}</a> &nbsp;&nbsp;&nbsp;
+ <img src="{{ MEDIA_URL }}blogango/images/date_icon.png" width="16" height="16" alt="Date" />
+ &nbsp;26th May,2010.
+ </div>
+ <div class="postcontent">
+ <p>{{ entry.text|truncatewords_html:50|safe }}</p>
+ </div>
+ <div class="moreinfo"><a href="{{ entry.get_absolute_url }}">read more </a></div>
+ </div>
+ {% empty %}
+ <div class="leftblock">
+ You don't have any entries in your blog.
+ </div>
+ {% endfor %}
+ <!--BLOG POST ENDS-->
+ {% endblock %}
+ </div>
+ <!-- LEFT PANEL ENDS -->
+
+ <!--RIGHT PANEL STARTS-->
+ <div id="rightpanel">
+ {% if recents %}
+ <!--POSTS PANEL STARTS-->
+ <div class="rightblock">
+ <h1>BLOGPOSTS</h1>
+ <ul>
+ {% for recent in recents %}
+ <li><a href="{{ recent.get_absolute_url }}">{{ recent.title }}</a></li>
+ {% endfor %}
+ </ul>
+ </div>
+ <!--POSTS PANEL ENDS-->
+ {% endif %}
+
+ <!--FRIENDS PANEL STARTS-->
+ <div class="rightblock">
+ {% if blog_rolls %}
+ <h1 class="orange">BLOG ROLL</h1>
+ <ul>
+ {% for roll in blog_rolls %}
+ <li><a href="{{ roll.url }}">{{ roll.text }}</a></li>
+ {% endfor %}
+ </ul>
+ {% endif %}
+ <div class="clear"></div>
+ </div>
+ <!--FRIENDS PANEL ENDS-->
+
+ {% if tags %}
+ <!--CATEGORIES PANEL STARTS-->
+ <div class="rightblock">
+ <h1 class="brown">CATEGORIES</h1>
+ <ul>
+ {% for tag in tags %}
+ <li><a href="{% url blogango_tag_details tag.slug %}">{{ tag.name }}</a></li>
+ {% endfor %}
+ </ul>
+ </div>
+ <!--CATEGORIES PANEL ENDS-->
+ {% endif %}
+
+ {% if archive_months %}
+ <!--ARCHIVES PANEL STARTS-->
+ <div class="rightblock">
+ <h1 class="blue">ARCHIVES</h1>
+ <ul>
+ {% for month in archive_months %}
+ <li><a href="{% url blogango_archives month|date:'Y' month|date:'b' %}">{{ month|date:'F Y' }}</a></li>
+ {% endfor %}
+ </ul>
+ </div>
+ <!--ARCHIVES PANEL ENDS-->
+ {% endif %}
+ </div>
+ <!--RIGHT PANEL ENDS-->
+
+ <div class="clear"></div>
+</div>
+
+</body>
+</html>
23 example/blogango/templates/blogango/archive_view.html
@@ -0,0 +1,23 @@
+{% extends "base.html" %}
+
+{% block title %}
+- Archives
+{% endblock %}
+
+{% block content %}
+
+{% for entry in object_list %}
+ <div class="leftblock">
+ <h1><a href="{{ entry.get_absolute_url }}">{{ entry.title }}</a></h1>
+ {% include 'blogango/entry_controls.html' %}
+
+ <div class="postcontent">
+ <p>{{ entry.text|truncatewords_html:50|safe }}</p>
+ </div>
+ <div class="moreinfo"><a href="{{ entry.get_absolute_url }}">read more </a></div>
+ </div>
+
+{% endfor %}
+
+{% endblock %}
+
20 example/blogango/templates/blogango/author.html
@@ -0,0 +1,20 @@
+{% extends 'base.html' %}
+
+{% block title %}
+- Author {{ author }}
+{% endblock %}
+
+{% block content %}
+<div class="leftblock">
+ <h1>{{ author }}</h1>
+ <div class="postdetails postlist">
+ <h2> Posts by this author</h2>
+ <ul>
+ {% for post in author.blogentry_set.all %}
+ <li><a href="{{ post.get_absolute_url }}">{{ post.title }} ({{ post.created_on|date:'n/j/Y' }})</a></li>
+ {% endfor %}
+ </ul>
+ </div>
+ <div class="moreinfo"> <br/> </div>
+</div>
+{% endblock %}
22 example/blogango/templates/blogango/blogroll.html
@@ -0,0 +1,22 @@
+{% extends "base.html" %}
+
+{% block title %}
+{{block.super}} - Blogroll
+{% endblock %}
+
+{% block main %}
+
+<div id="message">
+Add links to the blogroll.
+</div>
+
+<div id="columnA_2columns">
+<form action="." method="post">
+<table>
+{{blogroll_form}}
+</table>
+<input type="submit" class="button" value="Add to blogroll" />
+</form>
+</div>
+
+{% endblock %}
8 example/blogango/templates/blogango/comment.html
@@ -0,0 +1,8 @@
+{% extends 'base.html' %}
+
+{% block main %}
+<div id="columnA_2columns">
+{{comment.text}}
+{% include 'blogango/commentcontrols.html' %}
+</div>
+{% endblock %}
5 example/blogango/templates/blogango/commentcontrols.html
@@ -0,0 +1,5 @@
+{% load humanize %}
+{% load markup %}
+<div class="commentcontrols">
+By {{comment.user_name}} on {{comment.created_on|naturalday}}
+</div>
24 example/blogango/templates/blogango/create.html
@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+
+{% block title %}
+{{block.super}} - Create
+{% endblock %}
+
+{% block main %}
+<div id="columnA_2columns">
+
+<div id="message">
+Create a new entry.
+</div>
+
+<form action="." method="post">
+{% csrf_token %}
+<table>
+{{create_form}}
+</table>
+<input type="submit" class="button" value="Post it" name="post" />
+<input type="submit" class="button" value="Save as draft" name="save" />
+</form>
+</div>
+
+{% endblock %}
29 example/blogango/templates/blogango/details.html
@@ -0,0 +1,29 @@
+{% extends 'base.html' %}
+{% load gravatar %}
+
+{% block title %}
+ {{block.super}} - {{entry.title}}
+{% endblock %}
+
+{% block content %}
+<div class="leftblock">
+ <h1>{{ entry.title }}</h1>
+
+ {% include 'blogango/entry_controls.html' %}
+
+ <div class="postcontent">
+ <p>{{ entry.text|safe }}</p>
+ </div>
+ <div class="moreinfo"> <br/> </div>
+</div>
+
+<div class="tags">
+{% for tag in tags %}
+ <a href="{{tag.get_absolute_url}}">{{tag.tag_txt}}</a>
+{% endfor %}
+</div>
+
+
+
+{% endblock %}
+
20 example/blogango/templates/blogango/edit_preferences.html
@@ -0,0 +1,20 @@
+{% extends "base.html" %}
+
+{% block title %}
+{{block.super}} - Install
+{% endblock %}
+
+{% block main %}
+<div id="columnA_2columns">
+<div id="message">
+<p>Edit your preferences, and watch the magic happen.</p>
+</div>
+<form action="." method="post">
+<table>
+{{install_form}}
+</table>
+<input name="install" type="submit" value="Update preferences" class="button"/>
+</form>
+</div>
+
+{% endblock %}
1  example/blogango/templates/blogango/eggs.html
@@ -0,0 +1 @@
+This is a template.
6 example/blogango/templates/blogango/entry_controls.html
@@ -0,0 +1,6 @@
+<div class="postdetails">
+ <img src="{{ MEDIA_URL }}blogango/images/blog.png" width="16" height="16" alt="Written by" />
+ <a href="{% url blogango_author entry.created_by %}"> {{ entry.created_by }}</a> &nbsp;&nbsp;&nbsp;
+ <img src="{{ MEDIA_URL }}blogango/images/date_icon.png" width="16" height="16" alt="Date" />
+ &nbsp;{{ entry.created_on|date:'jS N, Y' }}.
+</div>
26 example/blogango/templates/blogango/install.html
@@ -0,0 +1,26 @@
+{% extends "base.html" %}
+
+{% block title %}
+{{block.super}} - Install
+{% endblock %}
+
+{% block content %}
+<div class="leftblock">
+ <h1>Blog not created </h1>
+
+<div class="postcontent">
+ <p>Create a blog in '/admin/'</p>
+</div>
+
+{% comment %}
+<form action="." method="post">
+ <table>
+ {{install_form}}
+ </table>
+ <input name="install" type="submit" value="Install Blog" class="button"/>
+</form>
+{% endcomment %}
+
+</div>
+
+{% endblock %}
29 example/blogango/templates/blogango/mainpage.html
@@ -0,0 +1,29 @@
+{% extends 'base.html' %}
+
+{% block content%}
+
+<!--BLOG POST STARTS-->
+{% for entry in entries %}
+ <div class="leftblock">
+ <h1><a href="{{ entry.get_absolute_url }}">{{ entry.title }}</a></h1>
+ {% include 'blogango/entry_controls.html' %}
+
+ <div class="postcontent">
+ <p>{{ entry.text|truncatewords_html:50|safe }}</p>
+ </div>
+ <div class="moreinfo"><a href="{{ entry.get_absolute_url }}">read more </a></div>
+ </div>
+{% empty %}
+ <div class="leftblock">
+ <h1>No posts in blog</h1>
+ <div class="postcontent">
+ <p>
+ <br/>You don't have any entries in your blog,
+ you can post to your blog from site admin.
+ </p>
+ </div>
+ </div>
+{% endfor %}
+<!--BLOG POST ENDS-->
+
+{% endblock %}
27 example/blogango/templates/blogango/manage.html
@@ -0,0 +1,27 @@
+{% extends 'base.html' %}
+
+{% block title %}
+{{block.super}} - Manage
+{% endblock %}
+
+{% block main %}
+<div id="columnA_2columns">
+<ul>
+<li>
+<a href="{% url blogango_create %}">Write an entry.</a>
+</li>
+<li>
+<a href="{% url blogango_mod_entries %}">Edit previous entries.</a>
+</li>
+<li>
+<a href="{% url blogango_mod_comments %}">Moderate comments.</a>
+</li>
+<li>
+<a href="{% url blogango_blogroll %}">Add to blogroll.</a>
+</li>
+<li>
+<a href="{% url blogango_edit_preferences %}">Edit preferences.</a>
+</li>
+</ul>
+</div>
+{% endblock %}
32 example/blogango/templates/blogango/manage_entries.html
@@ -0,0 +1,32 @@
+{% extends 'base.html' %}
+
+{% block main %}
+<div id="columnA_2columns">
+
+<div id="message">
+Manage previous entries. Delete or unpublish.
+</div>
+
+<form action="." method="post">
+<table summary="" border="0" class="entries">
+<tr>
+<th>Entry</th>
+<th>View</th>
+<th>Edit</th>
+<th>Select</th>
+</tr>
+{% for entry in entries %}
+<tr>
+<td>{{entry.title}}</td>
+<td><a href="{{entry.get_absolute_url}}">view</a></td>
+<td><a href="{{entry.get_edit_url}}">edit</a></td>
+<td><input type="checkbox" name="entries" value="{{entry.id}}" /></td>
+</tr>
+{% endfor %}
+</table>
+
+<input type="submit" class="button" value="Delete" name="del" />
+<input type="submit" class="button" value="Unpublish" name="unpublish" />
+</form>
+</div>
+{% endblock %}
34 example/blogango/templates/blogango/mod_comment.html
@@ -0,0 +1,34 @@
+{% extends "base.html" %}
+{% load markup %}
+
+{% block title %}
+{{block.super}} - Comments
+{% endblock %}
+
+{% block main %}
+<div id="columnA_2columns">
+<div id="message">
+Manage comments. Down with spammers.
+</div>
+Comments
+<form action="." method="post">
+
+<table summary="" border="0" class="comments">
+{% for comment in comments %}
+<tr>
+<th>Comment</th>
+<th>Delete</th>
+<th>Mark as spam</th>
+</tr>
+<tr>
+<td>{{comment.text|textile}}</td>
+<td><input type="checkbox" name="delete" value="{{comment.id}}" /></td>
+<td><input type="checkbox" name="spam" value="{{comment.id}}" /></td>
+</tr>
+{% endfor %}
+</table>
+
+<input type="submit" class="button" value="Manage Comments" />
+</form>
+</div>
+{% endblock %}
20 example/blogango/templates/blogango/tag_details.html
@@ -0,0 +1,20 @@
+{% extends 'base.html' %}
+
+{% block title %}
+ {{block.super}} - Tags
+{% endblock %}
+
+{% block content %}
+<div class="leftblock">
+ <h1>Post with tag: {{ tag.name }}</h1>
+ <div class="postdetails">
+ <ul>
+ {% for entry in entries %}
+ <li>
+ <a href="{{entry.get_absolute_url}}">{{entry.title}}</a> <br />
+ </li>
+ {% endfor %}
+ </ul>
+ </div>
+</div>
+{% endblock %}
7 example/blogango/templates/blogango/topbar.html
@@ -0,0 +1,7 @@
+<div id="topbar">
+{% block topbar %}
+{% for page in pages %}
+<a href="{{page.get_absolute_url}}">{{page.title}}</a> <br />
+{% endfor %}
+{% endblock %}
+</div>
9 example/blogango/templates/entry_footer.html
@@ -0,0 +1,9 @@
+{% block entry_footer %}
+
+<ul class="post_footer">
+<li>
+<a href="{{entry.get_absolute_url}}">Read more</a>
+</li>
+</ul>
+
+{% endblock %}
29 example/blogango/templates/registration/login.html
@@ -0,0 +1,29 @@
+{% extends "base.html" %}
+
+{% block title %}
+{{block.super}} - Login
+{% endblock %}
+
+{% block main %}
+<div id="columnA_2columns">
+<div class="message">
+Login!
+</div>
+{% if form.has_errors %}
+<p>Your username and password didn't match. Please try again.</p>
+{% endif %}
+
+<form method="post" action=".">
+<table>
+<tr><td><label for="id_username">Username:</label></td><td>{{ form.username }}</td></tr>
+<tr><td><label for="id_password">Password:</label></td><td>{{ form.password }}</td></tr>
+</table>
+
+<input type="submit" value="login" />
+<input type="hidden" name="next" value="{{ next }}" />
+</form>
+</div>
+{% endblock %}
+
+{% block sidebar %}
+{% endblock %}
17 example/blogango/templates/registration/logout.html
@@ -0,0 +1,17 @@
+{% extends "base.html" %}
+
+{% block title %}
+{{block.super}} - Logout
+{% endblock %}
+
+{% block main %}
+<div id="columnA_2columns">
+<div class="message">
+Logged out.
+<a href="{% url auth_login %}">Login again</a>
+</div>
+</div>
+{% endblock %}
+
+{% block sidebar %}
+{% endblock %}
25 example/blogango/templates/registration/password_change.html
@@ -0,0 +1,25 @@
+{% extends "base.html" %}
+
+{% block title %}
+{{block.super}} - L
+{% endblock %}
+
+{% block main %}
+<div id="columnA_2columns">
+<div class="message">
+Change password
+</div>
+{% if form.has_errors %}
+<p>Your username and password didn't match. Please try again.</p>
+{% endif %}
+
+<form method="post" action=".">
+<table>
+{{form.manipulator}}
+</table>
+
+<input type="submit" value="login" />
+<input type="hidden" name="next" value="{{ next }}" />
+</form>
+</div>
+{% endblock %}
18 example/blogango/templates/search.html
@@ -0,0 +1,18 @@
+{% extends 'base.html' %}
+
+{% block main %}
+<div id="columnA_2columns">
+{% if not results %}
+<div id="message">
+There are no results to display! Change search terms to get the posts you are looking for.
+</div>
+{% endif %}
+{% for result in results %}
+<ul>
+<li><a href="{{result.Url}}">{{result.Title}}</a><br class="clear"/>
+{{result.Summary}}
+</li>
+</ul>
+{% endfor %}
+</div>
+{% endblock %}
0  example/blogango/templatetags/__init__.py
No changes.
12 example/blogango/templatetags/filters.py
@@ -0,0 +1,12 @@
+
+from django import template
+
+register = template.Library()
+
+from lib import ttp
+
+@register.filter
+def twitterify(tweet):
+ parse = ttp.Parser()
+ result = parse.parse(tweet)
+ return result.html
44 example/blogango/templatetags/gravatar.py
@@ -0,0 +1,44 @@
+
+### gravatar.py ###############
+### place inside a 'templatetags' directory inside the top level of a Django app (not project, must be inside an app)
+### at the top of your page template include this:
+### {% load gravatar %}
+### and to use the url do this:
+### <img src="{% gravatar_url 'someone@somewhere.com' %}"/>
+### or
+### <img src="{% gravatar_url sometemplatevariable %}"/>
+### just make sure to update the "default" image path below
+
+import urllib, hashlib
+
+from django import template
+
+register = template.Library()
+
+class GravatarUrlNode(template.Node):
+ def __init__(self, email):
+ self.email = template.Variable(email)
+
+ def render(self, context):
+ try:
+ email = self.email.resolve(context)
+ except template.VariableDoesNotExist:
+ return ''
+
+ default = "http://www.gravatar.com/avatar"
+ size = 50
+
+ gravatar_url = "http://www.gravatar.com/avatar/" + hashlib.md5(email).hexdigest() + "?"
+ gravatar_url += urllib.urlencode({'default':default, 'size':str(size)})
+
+ return gravatar_url
+
+@register.tag
+def gravatar_url(parser, token):
+ try:
+ tag_name, email = token.split_contents()
+
+ except ValueError:
+ raise template.TemplateSyntaxError, "%r tag requires a single argument" % token.contents.split()[0]
+
+ return GravatarUrlNode(email)
59 example/blogango/urls.py
@@ -0,0 +1,59 @@
+from django.conf.urls.defaults import *
+from django.contrib.auth.views import login, logout, password_change, password_reset
+from django.contrib.sitemaps import GenericSitemap
+
+from blogango import feeds
+from blogango.models import BlogEntry
+
+blog_info_dict = {
+ 'queryset': BlogEntry.objects.filter(is_published=True),
+ 'date_field': 'created_on',
+}
+sitemaps = {
+ 'blog': GenericSitemap(blog_info_dict, priority=0.5)
+}
+
+urlpatterns = patterns('blogango.views',
+ url(r'^welcome/$', 'welcome', name='blogango_welcome'),
+ url(r'^install/$', 'install_blog', name='blogango_install'),
+ url(r'^preferences/$', 'edit_preferences', name='blogango_edit_preferences'),
+
+ url(r'^$', 'index', name='blogango_index'),
+ url(r'^page/(?P<page>\d+)/$', 'index', name='blogango_page'),
+ url(r'^(?P<year>\d{4})/(?P<month>\d{2})/(?P<slug>[-\w]+)/$', 'details', name='blogango_details'),
+ url(r'^new/$', 'create_entry', name='blogango_create'),
+ url(r'^blogroll/$', 'create_blogroll', name='blogango_blogroll'),
+ url(r'^moderate/$', 'moderate_comments', name='blogango_mod_comments'),
+ url(r'^entries/$', 'mod_entries', name='blogango_mod_entries'),
+ url(r'^tag/(?P<tag_slug>\w+)/$','tag_details', name='blogango_tag_details'),
+ url(r'^manage/$', 'manage', name='blogango_manage'),
+ url(r'^edit/(?P<entry_id>\d+)/$', 'edit_entry', name='blogango_edit_entry'),
+ url(r'^comment/(?P<comment_id>\d+)/$', 'comment_details', name='blogango_comment_details'),
+ url(r'^author/(?P<username>[\w.@+-]+)/$', 'author', name='blogango_author'),
+
+)
+
+#search view
+urlpatterns += patterns('blogango.search',
+ url(r'^search/$', 'search', name = 'search'),
+)
+
+# sitemap.xml
+urlpatterns += patterns('django.contrib.sitemaps.views',
+ url(r'^sitemap\.xml$', 'sitemap', {'sitemaps': sitemaps}),
+)
+
+# Archive view
+urlpatterns += patterns('blogango.views',
+ url(r'^archive/(?P<year>\d+)/(?P<month>\w+)/$', 'monthly_view', name='blogango_archives')
+)
+
+urlpatterns += patterns('django_xmlrpc.views',
+ url(r'^xmlrpc/$', 'handle_xmlrpc', {}, name='xmlrpc'),
+)
+
+feeds = {'latest': feeds.main_feed, 'tag':feeds.CatFeed}
+# feeds = {'latest': feeds.main_feed}
+urlpatterns += patterns('',
+ url(r'^rss/(?P<url>.*)/$', 'django.contrib.syndication.views.feed', {'feed_dict': feeds}, name='blogango_feed')
+)
359 example/blogango/views.py
@@ -0,0 +1,359 @@
+from django.shortcuts import render_to_response, get_object_or_404
+from django.http import HttpResponse, HttpResponseRedirect
+from django.conf import settings
+from django.contrib.auth.decorators import login_required
+from django.contrib.auth.models import User
+from django.template import RequestContext
+from django.core.paginator import Paginator
+from django.core.urlresolvers import reverse
+from django.views.generic.date_based import archive_month
+from django.http import Http404
+from django.core.exceptions import ObjectDoesNotExist
+
+from blogango.models import Blog, BlogEntry, Comment, BlogRoll
+from blogango import forms as bforms
+
+from blogango.conf.settings import AKISMET_COMMENT, AKISMET_API_KEY
+
+def welcome(request):
+ return render_to_response('mainpage.html', {})
+
+
+def handle404(view_function):
+ def wrapper(*args, **kwargs):
+ try:
+ return view_function(*args, **kwargs)
+ except ObjectDoesNotExist:
+ raise Http404
+ return wrapper
+
+
+def index(request, page = 1):
+ if not _is_blog_installed():
+ return HttpResponseRedirect(reverse('blogango_install'))
+ page = int(page)
+ blog = Blog.objects.all()[0]
+ entries = BlogEntry.objects.filter(is_page=False, is_published=True).order_by('-created_on')
+ paginator = Paginator(entries, blog.entries_per_page)
+ page_ = paginator.page(page)
+ entries = page_.object_list
+ has_next = page_.has_next()
+ has_prev = page_.has_previous()
+ next = page + 1
+ prev = page - 1
+ payload = locals()
+ return render('blogango/mainpage.html', request, payload)
+
+
+def check_comment_spam(request, comment):
+ from blogango.akismet import Akismet, APIKeyError
+ api = Akismet(AKISMET_API_KEY, request.get_host(), request.META['HTTP_USER_AGENT'])
+
+ if api.verify_key():
+ akismet_data = {'user_ip': request.META['REMOTE_ADDR'],
+ 'user_agent': request.META['HTTP_USER_AGENT'],
+ 'comment_author': comment.user_name,
+ 'comment_author_email': comment.email_id,
+ 'comment_author_url': comment.user_url,
+ 'comment_type': 'comment'}
+
+ return api.comment_check(comment.text, akismet_data)
+ raise APIKeyError("Akismet API key is invalid.")
+
+
+@handle404
+def details(request, year, month, slug):
+ if not _is_blog_installed():
+ return HttpResponseRedirect(reverse('blogango_install'))
+
+ entry = BlogEntry.objects.get(created_on__year=year,
+ created_on__month=month,
+ slug=slug,
+ is_published=True)
+
+ if not entry.is_published:
+ raise Http404
+
+ if request.method == 'POST':
+ comment_f = bforms.CommentForm(request.POST)
+ if comment_f.is_valid():
+ comment_by = request.user if request.user.is_authenticated() else None
+ comment = Comment(text=comment_f.cleaned_data['text'],
+ created_by=comment_by,
+ comment_for=entry,
+ user_name=comment_f.cleaned_data['name'],
+ user_url=comment_f.cleaned_data['url'],
+ email_id=comment_f.cleaned_data['email'])
+ if AKISMET_COMMENT:
+ comment.is_spam = check_comment_spam(request, comment)
+ comment.save()
+ return HttpResponseRedirect('.')
+ else:
+ comment_f = bforms.CommentForm()
+
+ comments = Comment.objects.filter(comment_for=entry, is_spam=False)
+ # tags = Tag.objects.filter(tag_for=entry)
+ payload = {'entry': entry, 'comments': comments, 'comment_form': comment_f}
+ return render('blogango/details.html', request, payload)
+
+
+@handle404
+def comment_details(request, comment_id):
+ comment = Comment.objects.get(id=comment_id)
+ payload = locals()
+ return render('blogango/comment.html', request, payload)
+
+
+def tag_details(request, tag_slug):
+ from taggit.models import Tag
+ if Tag.objects.filter(slug=tag_slug).count() == 0:
+ raise Http404
+ tag = Tag.objects.get(slug=tag_slug)
+ entries = BlogEntry.objects.filter(is_published=True, tags__in=[tag])
+ feed_url = getattr(settings, 'FEED_URL', reverse('blogango_feed', args=['tag']) + tag.slug + '/')
+ payload = {'tag': tag, 'entries': entries, 'feed_url': feed_url}
+ return render('blogango/tag_details.html', request, payload)
+
+
+@login_required
+def create_entry(request):
+ if request.method == 'GET':
+ create = bforms.EntryForm()
+ elif request.method == 'POST':
+ create = bforms.EntryForm(request.POST)
+ if create.is_valid():
+ if request.POST.has_key('save'):
+ publish = False
+ elif request.POST.has_key('post'):
+ publish = True
+ entry = BlogEntry(created_by=request.user,
+ text=create.cleaned_data['text'],
+ title=create.cleaned_data['title'],
+ slug=create.cleaned_data['slug'],
+ is_page=create.cleaned_data['is_page'],
+ is_published=publish,
+ is_rte=create.cleaned_data['is_rte'])
+ entry.save()
+ tags = create.cleaned_data['tags']
+ tag_list = tags.split()
+ for tag in tag_list:
+ tag_, created = Tag.objects.get_or_create(tag_txt=tag.strip())
+ tag_.save()
+ entry.tag_set.add(tag_)
+ if request.POST.has_key('save'):
+ return HttpResponseRedirect('.')
+ elif request.POST.has_key('post'):
+ return HttpResponseRedirect(entry.get_absolute_url())
+ payload = {'create_form': create,}
+ return render('blogango/create.html', request, payload)
+
+
+@login_required
+def edit_entry(request, entry_id):
+ if request.method == 'GET':
+ entry = BlogEntry.objects.filter(id=entry_id).values()[0]
+ entry_ = BlogEntry.objects.get(id=entry_id)
+ tags = Tag.objects.filter(tag_for=entry_)
+ tags = [tag.tag_txt for tag in tags]
+ tag_ = " ".join(tags)
+ entry['tags'] = tag_
+ create = bforms.EntryForm(entry)
+ elif request.method == 'POST':
+ create = bforms.EntryForm(request.POST)
+ if create.is_valid():
+ entry = BlogEntry.objects.get(id=entry_id)
+ entry.text = create.cleaned_data['text']
+ entry.title = create.cleaned_data['title']
+ entry.slug = create.cleaned_data['slug']
+ entry.is_page = create.cleaned_data['is_page']
+ entry.comments_allowed = create.cleaned_data['comments_allowed']
+ if request.POST.has_key('save'):
+ publish = False
+ elif request.POST.has_key('post'):
+ publish = True
+ entry.is_published = publish
+ entry.save()
+ tags = Tag.objects.filter(tag_for=entry)
+ for tag in tags:
+ entry.tag_set.remove(tag)
+ tags_data = create.cleaned_data['tags']
+ tag_list = tags_data.split(' ')
+ for tag in tag_list:
+ tag_, created = Tag.objects.get_or_create(tag_txt=tag.strip())
+ tag_.save()
+ entry.tag_set.add(tag_)
+ return HttpResponseRedirect(entry.get_absolute_url())
+ payload = {'create_form': create,}
+ return render('blogango/create.html', request, payload)
+
+
+@login_required
+def mod_entries(request):
+ if request.method == 'GET':
+ entries = BlogEntry.objects.all()
+ payload = locals()
+ return render('blogango/manage_entries.html', request, payload)
+ if request.method == 'POST':
+ if request.POST.has_key("unpublish"):
+ entry_ids = request.POST['entries']
+ entries = BlogEntry.objects.filter(id__in=entry_ids)
+ for entry in entries:
+ entry.is_published = False
+ entry.save()
+ elif request.POST.has_key("del"):
+ entry_ids = request.POST['entries']
+ # print request.POST
+ # print entry_ids
+ BlogEntry.objects.filter(id__in=entry_ids).delete()
+ return HttpResponseRedirect('.')
+
+
+@login_required
+def moderate_comments(request):
+ if request.method == 'GET':
+ comments = Comment.objects.filter()
+ payload = {"comments": comments}
+ return render('blogango/mod_comment.html', request, payload)
+ elif request.method == 'POST':
+ if request.POST.has_key('spam'):
+ spammeds = request.POST['spam']
+ else:
+ spammeds = {}
+ for spammed in spammeds:
+ comment = Comment.objects.get(id = spammed)
+ comment.is_spam = True
+ comment.save()
+ if request.POST.has_key('delete'):
+ deleteds = request.POST['delete']
+ else:
+ deleteds = {}
+ for deleted in deleteds:
+ Comment.objects.get(id = deleted).delete()
+ return HttpResponseRedirect('.')
+
+
+@login_required
+def install_blog(request):
+ if _is_blog_installed():
+ return HttpResponseRedirect(reverse('blogango_index'))
+
+ if request.method == 'GET':
+ install_form = bforms.InstallForm()
+ if request.method == 'POST':
+ install_form = bforms.InstallForm(request.POST)
+ if install_form.is_valid():
+ install_form.save()
+ return HttpResponseRedirect(reverse('blogango_index'))
+ payload = {"install_form": install_form}
+ return render('blogango/install.html', request, payload)
+
+
+@login_required
+def create_blogroll(request):
+ if request.method == 'GET':
+ blogroll_form = bforms.BlogForm()
+ if request.method == 'POST':
+ blogroll_form = bforms.BlogForm(request.POST)
+ if blogroll_form.is_valid():
+ blogroll_form.save()
+ return HttpResponseRedirect('.')
+ payload = {"blogroll_form": blogroll_form}
+ return render('blogango/blogroll.html', request, payload)
+
+
+@login_required
+def edit_preferences(request):
+ if request.method == 'GET':
+ prefs_form = bforms.PreferencesForm(Blog.objects.all().values()[0])
+ if request.method == 'POST':
+ prefs_form = bforms.PreferencesForm(request.POST)
+ if prefs_form.is_valid():
+ blog = Blog.objects.all()[0]
+ # print blog.id
+ blog.entries_per_page = prefs_form.cleaned_data['entries_per_page']
+ blog.recents = prefs_form.cleaned_data['recents']
+ blog.recent_comments = prefs_form.cleaned_data['recents']
+ blog.save()
+ return HttpResponseRedirect('.')
+ payload = {"install_form": prefs_form}
+ return render('blogango/edit_preferences.html', request, payload)
+
+
+@login_required
+def manage(request):
+ return render('blogango/manage.html', request, {})
+
+
+def author(request, username):
+ author = get_object_or_404(User, username=username)
+ return render('blogango/author.html', request, {'author': author})
+
+
+def monthly_view(request, year, month):
+ # print year, month
+ queryset = BlogEntry.objects.filter(is_page=False, is_published=True)
+ return archive_month(request=request,
+ template_name='blogango/archive_view.html',
+ year=year,
+ month=month,
+ queryset=queryset,
+ date_field='created_on',
+ extra_context=_get_sidebar_objects(request))
+
+
+#Helper methods.
+def _is_blog_installed():
+ if Blog.objects.count() == 0:
+ return False
+ return True
+
+
+def render (template, request, payload):
+ """Wrapper on render_to_response.
+ Adds sidebar objects. Adds RequestContext"""
+ payload.update(_get_sidebar_objects(request))
+ return render_to_response(template, payload, context_instance=RequestContext(request),)
+
+
+def _get_sidebar_objects (request):
+ """Gets the objects which are always displayed in the sidebar"""
+ try:
+ blog = Blog.objects.all()[0]
+ except:
+ return {}
+ recents = BlogEntry.objects.filter(is_page = False, is_published = True).order_by('-created_on')[:blog.recents]
+ blogroll = BlogRoll.objects.all()
+ pages = BlogEntry.objects.filter(is_page = True, is_published = True)
+ recent_comments = Comment.objects.all().order_by('-created_on')[:blog.recent_comments]
+ # date_list = _get_archive_months()
+ return {'blog':blog, 'recents':recents, 'pages':pages, 'blogroll':blogroll, 'recent_comments':recent_comments}
+
+
+def _get_archive_months():
+ """Get the months for which at least one entry exists"""
+ dates = BlogEntry.objects.dates('created_on', 'month', order='DESC')
+ # print dates
+ # date_list = []
+ # for date in dates:
+ # date_list.append((date.strftime('%Y/%b'), date.strftime('%B %y')))
+ return dates
+
+
+def _generic_form_display(request, form_class):
+ if request.method == 'GET':
+ form_inst = form_class()
+ if request.method == 'POST':
+ form_inst = form_class(request.POST)
+ if form_inst.is_valid():
+ form_inst.save()
+ return HttpResponseRedirect('.')
+ payload = {"install_form": form_inst}
+ return render('blogango/install.html', request, payload)
+
+
+def generic(request): # A generic form processor.
+ if request.method == 'GET':
+ pass
+ if request.method == 'POST':
+ pass
+
12 example/doc.txt
@@ -0,0 +1,12 @@
+
+add these apps to INSTALLED_APPS
+ 'subdomains',
+ 'subdomain_admin',
+
+* Every user registerd on the site is member of the group 'SubUser'
+
+models inherited from `subdomain_admin.models.SubModel` are
+editable by the user belonging to the group 'SubUser'
+
+`from subdomain_admin.admin import SubdomainAdmin`
+
11 example/manage.py
@@ -0,0 +1,11 @@
+#!/usr/bin/env python
+from django.core.management import execute_manager
+try:
+ import settings # Assumed to be in the same directory.
+except ImportError:
+ import sys
+ sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ execute_manager(settings)
101 example/settings.py
@@ -0,0 +1,101 @@
+# Django settings for example project.
+
+DEBUG = True
+TEMPLATE_DEBUG = DEBUG
+
+ADMINS = (
+ # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
+ 'NAME': 'tmp/subdomain_admin.db', # Or path to database file if using sqlite3.
+ 'USER': '', # Not used with sqlite3.
+ 'PASSWORD': '', # Not used with sqlite3.
+ 'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
+ 'PORT': '', # Set to empty string for default. Not used with sqlite3.
+ }
+}
+
+# Local time zone for this installation. Choices can be found here:
+# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
+# although not all choices may be available on all operating systems.
+# On Unix systems, a value of None will cause Django to use the same
+# timezone as the operating system.
+# If running in a Windows environment this must be set to the same as your
+# system time zone.
+TIME_ZONE = 'America/Chicago'
+
+# Language code for this installation. All choices can be found here:
+# http://www.i18nguy.com/unicode/language-identifiers.html
+LANGUAGE_CODE = 'en-us'
+
+SITE_ID = 1
+
+# If you set this to False, Django will make some optimizations so as not
+# to load the internationalization machinery.
+USE_I18N = True
+
+# If you set this to False, Django will not format dates, numbers and
+# calendars according to the current locale
+USE_L10N = True
+
+# Absolute path to the directory that holds media.
+# Example: "/home/media/media.lawrence.com/"
+MEDIA_ROOT = 'site_media/'
+
+# URL that handles the media served from MEDIA_ROOT. Make sure to use a
+# trailing slash if there is a path component (optional in other cases).
+# Examples: "http://media.lawrence.com", "http://example.com/media/"
+MEDIA_URL = '/site_media/'
+
+# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
+# trailing slash.
+# Examples: "http://foo.com/media/", "/media/".
+ADMIN_MEDIA_PREFIX = '/media/'
+
+# Make this unique, and don't share it with anybody.
+SECRET_KEY = 't)bs4)owsu*!2_84^14i_a&$j)x7$cl&!cnlu)h!c1cu4bsif9'
+
+# List of callables that know how to import templates from various sources.
+TEMPLATE_LOADERS = (
+ 'django.template.loaders.filesystem.Loader',
+ 'django.template.loaders.app_directories.Loader',
+# 'django.template.loaders.eggs.Loader',
+)
+
+MIDDLEWARE_CLASSES = (
+ 'django.middleware.common.CommonMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+
+ 'subdomains.middleware.GetSubdomainMiddleware',
+ 'subdomains.middleware.ThreadLocals',
+ 'subdomains.middleware.RedirectOnInvalidSubdomain',
+)
+
+ROOT_URLCONF = 'example.urls'
+
+TEMPLATE_DIRS = (
+ # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
+ # Always use forward slashes, even on Windows.
+ # Don't forget to use absolute paths, not relative paths.
+)
+
+INSTALLED_APPS = (
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.sites',
+ 'django.contrib.messages',
+ 'django.contrib.admin',
+
+ 'subdomains',
+ 'subdomain_admin',
+ 'blogango',
+)
114 example/site_media/blogango/css/agiliqblog.css
@@ -0,0 +1,114 @@
+@charset "utf-8";
+/* CSS Document */
+/**************************************/
+/* GLOBALS */
+/**************************************/
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, font, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ font-weight: inherit;
+ font-style: inherit;
+ font-size: 100%;
+ font-family: inherit;
+ vertical-align: baseline;
+}
+ol, ul {
+ list-style: none;
+}
+
+body{ background:url(../images/bg.png) repeat-x; background-color:#4E4E4E; font:14px Helvetica, Arial, sans-serif; color:#000; line-height:22px;}
+#header a{font: bold 32px Helvetica, Arial, sans-serif; color:#585858;text-decoration: none;text-transform:uppercase;}
+h1{font:bold 18px Helvetica, Arial, sans-serif; color:#ffffff; line-height:50px; background-color:#8FB83C; padding-left:25px; }
+h1 a {color:#ffffff; text-decoration: none;}
+h1.green{font:bold 18px Helvetica, Arial, sans-serif; color:#ffffff; line-height:50px; background-color:#8FB83C; padding-left:25px;}
+h1.orange{font:bold 18px Helvetica, Arial, sans-serif; ; line-height:50px; background-color:#FF7800; padding-left:25px; }
+h1.brown{font:bold 18px Helvetica, Arial, sans-serif; line-height:50px; background-color:#7E4221; padding-left:25px; }
+h1.red{font:bold 18px Helvetica, Arial, sans-serif; line-height:50px; background-color:#A1001B; padding-left:25px; }
+h1.purple{font:bold 18px Helvetica, Arial, sans-serif; line-height:50px; background-color:#530072; padding-left:25px; }
+h1.blue{font:bold 18px Helvetica, Arial, sans-serif; line-height:50px; background-color:#00ADEF; padding-left:25px; }
+
+h2{font:bold 15px Helvetica, Arial, sans-serif; line-height:40px;}
+p.post{padding-bottom:20px;border-bottom:1px solid #A4A4A4; }
+
+#container{width:960px; margin:0px auto;}
+#header{width:940px; height:45px; display:block; margin:10px 0px 10px 0px; padding:35px 0px 0px 20px;}
+#leftpanel{width:600px; float:left;}
+#rightpanel{width:320px; float:right;}
+
+.general_block{ display:block; background-color:#FFF; padding:20px;}
+.comment_block{ display:block; background-color:#FFF; padding:20px 30px 20px 30px; margin:10px 0px 10px 0px;}
+.comment_block .leftpanel{ display:block; width:430px; float:left;}
+.comment_block .rightpanel{ display:block; width:100px; float:right; text-align:right; }
+.commentor{font:bold 14px Arial, Helvetica, sans-serif;}
+.commentor a {text-decoration: none;color:#0975B5;}
+.postdate{font:12px Arial, Helvetica, sans-serif; color:#666; padding-left:20px;}
+.comment_post { margin-top:10px; font:12px Arial, Helvetica, sans-serif; line-height:18px; }
+
+.rightblock{width:320px; display:block; background-color:#FFF; margin-bottom:20px;}
+.rightblock ul{ padding:25px 0px 25px 25px;}
+.rightblock li{ font:bold 11px Tahoma, Geneva, sans-serif; line-height:24px; color:#5E5E5E; text-decoration:none; }
+.rightblock li a{ font:bold 11px Tahoma, Geneva, sans-serif; line-height:24px; color:#5E5E5E; text-decoration:none; border-bottom:1px dashed #999; }
+.rightblock li a:hover{ font:bold 11px Tahoma, Geneva, sans-serif; line-height:24px; text-decoration:none; color:#000; }
+
+.rightblock_divided{width:320px; display:block; margin-bottom:20px;}
+.rightblock_divided .left{width:150px; float:left;background-color:#FFF;}
+.rightblock_divided .right{width:150px; float:right;background-color:#FFF;}
+.rightblock_divided ul{ padding:25px 0px 25px 25px;}
+.rightblock_divided li{ font:bold 11px Tahoma, Geneva, sans-serif; line-height:24px; color:#5E5E5E; text-decoration:none; }
+.rightblock_divided li a{ font:bold 11px Tahoma, Geneva, sans-serif; line-height:24px; color:#5E5E5E; text-decoration:none; border-bottom:1px dashed #999; }
+.rightblock_divided li a:hover{ font:bold 11px Tahoma, Geneva, sans-serif; line-height:24px; text-decoration:none; color:#000; }
+
+
+.leftblock{width:600px; display:block; background-color:#FFF; margin-bottom:10px;}
+
+
+.filed{ font:bold 12px Arial, Helvetica, sans-serif; color:#000; line-height:25px; padding-left:25px;}
+.filed a{font:12px Arial, Helvetica, sans-serif; line-height:25px;text-decoration:none; color:#5E5E5E; border-bottom:1px dashed #999;}
+.filed a:hover{font:12px Arial, Helvetica, sans-serif; line-height:25px;text-decoration:none; color:#000; border-bottom:1px dashed #999;}
+.postdetails{font:bold 12px Arial, Helvetica, sans-serif; color:#424242; line-height:25px; margin:0px 25px 0px 25px; padding-bottom:15px; background:url(../images/linesbg.png) repeat-x bottom left;}
+.postdetails a{font:bold 12px Arial, Helvetica, sans-serif; color:#000; line-height:25px; text-decoration:none; color:#424242; border-bottom:1px dashed #999;}
+.postdetails a:hover{font:bold 12px Arial, Helvetica, sans-serif; color:#000; line-height:25px; text-decoration:none; color:#000; }
+
+.postlist ul li { list-style-type: square;}
+
+.postcontent{font:14px Helvetica, Arial, sans-serif; color:#000; line-height:22px; margin:20px 25px 0px 25px; background:url(../images/linesbg2.png) repeat-x bottom left; display:block; padding-bottom:20px; }
+.postcontent ul{ margin:10px 0px 20px 0px; font-weight:bold;}
+.postcontent li{font:14px Helvetica, Arial, sans-serif; color:#000; line-height:22px; margin:0px 0px 0px 15px; list-style-type:disc;}
+
+.post_icon{ width:30px; height:30px;}
+
+.moreinfo{font:bold 11px Tahoma, Geneva, sans-serif; color:#666; line-height:40px; text-decoration:none; margin:0px 25px 0px 25px; }
+.moreinfo a{font:bold 11px Tahoma, Geneva, sans-serif; color:#666; line-height:40px; text-decoration:none; text-align:right; border-bottom:1px dashed #999; }
+
+.moreinfo a:hover{font:bold 11px Tahoma, Geneva, sans-serif; color:#000; line-height:40px; text-decoration:none; border-bottom:1px dashed #000; }
+
+/**************************************/
+/* FORMS */
+/**************************************/
+.textfield{width:495px; background-color:#F0F0F0; border:1px solid #D1D1D1; padding:5px;}
+.textarea{width:495px; background-color:#F0F0F0; border:1px solid #D1D1D1; padding:5px; margin-bottom:5px;}
+fieldset{border:3px solid #EAEAEA; padding:20px; font:bold 11px Tahoma, Geneva, sans-serif; line-height:30px; margin-top:10px;}
+legend{font:16px Arial, Helvetica, sans-serif; line-height:24px;}
+.but_right{float:right;}
+
+.submit{ text-align:right; padding:3px;}
+.footer{ clear:both; font:bold 11px Tahoma, Geneva, sans-serif; color:#FFF; line-height:40px; text-align:center;}
+.clear{clear:both;}