Adam Knight avatar Adam Knight committed 79aca95

Moving to a virtualenv-style setup with a proper requirements file.

--HG--
rename : __init__.py => sp/__init__.py
rename : lifestream/PLAN.textile => sp/lifestream/PLAN.textile
rename : lifestream/__init__.py => sp/lifestream/__init__.py
rename : lifestream/models.py => sp/lifestream/models.py
rename : lifestream/urls.py => sp/lifestream/urls.py
rename : lifestream/views.py => sp/lifestream/views.py
rename : manage.py => sp/manage.py
rename : settings.py => sp/settings.py
rename : sitepoet/__init__.py => sp/sitepoet/__init__.py
rename : sitepoet/admin.py => sp/sitepoet/admin.py
rename : sitepoet/context_preprocessors/__init__.py => sp/sitepoet/context_preprocessors/__init__.py
rename : sitepoet/drupal_support/__init__.py => sp/sitepoet/drupal_support/__init__.py
rename : sitepoet/drupal_support/management/__init__.py => sp/sitepoet/drupal_support/management/__init__.py
rename : sitepoet/drupal_support/management/commands/__init__.py => sp/sitepoet/drupal_support/management/commands/__init__.py
rename : sitepoet/drupal_support/management/commands/convertdrupal.py => sp/sitepoet/drupal_support/management/commands/convertdrupal.py
rename : sitepoet/drupal_support/models.py => sp/sitepoet/drupal_support/models.py
rename : sitepoet/management/__init__.py => sp/sitepoet/management/__init__.py
rename : sitepoet/management/commands/__init__.py => sp/sitepoet/management/commands/__init__.py
rename : sitepoet/managers.py => sp/sitepoet/managers.py
rename : sitepoet/metaweblog.py => sp/sitepoet/metaweblog.py
rename : sitepoet/middleware/__init__.py => sp/sitepoet/middleware/__init__.py
rename : sitepoet/models.py => sp/sitepoet/models.py
rename : sitepoet/templates/comment.html => sp/sitepoet/templates/comment.html
rename : sitepoet/templates/error.html => sp/sitepoet/templates/error.html
rename : sitepoet/templates/page.html => sp/sitepoet/templates/page.html
rename : sitepoet/templates/pager.html => sp/sitepoet/templates/pager.html
rename : sitepoet/templates/story.html => sp/sitepoet/templates/story.html
rename : sitepoet/templates/story_archive.html => sp/sitepoet/templates/story_archive.html
rename : sitepoet/templates/story_detail.html => sp/sitepoet/templates/story_detail.html
rename : sitepoet/templates/tag_cloud.html => sp/sitepoet/templates/tag_cloud.html
rename : sitepoet/templatetags/__init__.py => sp/sitepoet/templatetags/__init__.py
rename : sitepoet/templatetags/pager.py => sp/sitepoet/templatetags/pager.py
rename : sitepoet/templatetags/slugify.py => sp/sitepoet/templatetags/slugify.py
rename : sitepoet/tests.py => sp/sitepoet/tests.py
rename : sitepoet/urls.py => sp/sitepoet/urls.py
rename : sitepoet/views.py => sp/sitepoet/views.py
rename : sitepoet/xmlrpc.py => sp/sitepoet/xmlrpc.py
rename : templates/404.html => sp/templates/404.html
rename : templates/base.html => sp/templates/base.html
rename : urls.py => sp/urls.py

Comments (0)

Files changed (83)

 \.DS_Store
 \.pyc$
-libs/.*
 settings_local.py
 sitepoet.sqlite
-.svn
 wiki
+ve

Empty file removed.

Add a comment to this file

libs/__init__.py

Empty file removed.

lifestream/PLAN.textile

-*Lifestream*
-
-* The site should allow mirroring of my social sites in a Friendfeed-like way.  This could be by using Friendfeed or by using syncr or something like it to pull information off of sites and into the DB.  This should work with:
-** Reddit
-** Digg
-** delicious
-** magnolia
-** last.fm
-** hulu
-** twitter
-** facebook?
-** Flickr
-* The database of information should not expire and should have friendly URLs when possible that include the service, the date, and some information about the item.  This is an archive.
-* Initial import should go back as far as the APIs allow.
-* An RSS/Atom feed should be available.
-* There should be a way of quickly getting all items for a particular day as well for the blog module to make a daily post out of it (though it would not be a real post, it would be generated every time).
Add a comment to this file

lifestream/__init__.py

Empty file removed.

lifestream/models.py

-from django.db import models
-
-# Create your models here.
Add a comment to this file

lifestream/urls.py

Empty file removed.

lifestream/views.py

-# Create your views here.

manage.py

-#!/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)
+Django==1.2.5
+django-tagging==0.3.1
+wsgiref==0.1.2

settings.py

-import os, sys, django
-
-# Get some handy paths to make absolute paths relative to the project
-DJANGO_ROOT = os.path.dirname(os.path.realpath(django.__file__))
-SITE_ROOT = os.path.dirname(os.path.realpath(__file__))
-
-# Everything is relative to the install by default
-os.chdir(SITE_ROOT)
-
-# Treat modules in "./libs" as first-class modules
-sys.path.append(os.path.join(SITE_ROOT,'libs'))
-
-###
-
-# Django settings for sitepoet project.
-
-DEBUG = True
-TEMPLATE_DEBUG = DEBUG
-
-ADMINS = (
-	# ('Your Name', 'your_email@domain.com'),
-)
-
-MANAGERS = ADMINS
-
-DATABASE_ENGINE = 'sqlite3'			# 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
-DATABASE_NAME = 'sitepoet.sqlite'	# Or path to database file if using sqlite3.
-DATABASE_USER = ''					# Not used with sqlite3.
-DATABASE_PASSWORD = ''				# Not used with sqlite3.
-DATABASE_HOST = ''					# Set to empty string for localhost. Not used with sqlite3.
-DATABASE_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.
-# 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 = False
-
-# Absolute path to the directory that holds media.
-# Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = ''
-
-# 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 = ''
-
-# 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.
-## Define this in settings_local!
-# SECRET_KEY = ''
-
-# List of callables that know how to import templates from various sources.
-TEMPLATE_LOADERS = (
-	'django.template.loaders.filesystem.load_template_source',
-	'django.template.loaders.app_directories.load_template_source',
-)
-
-MIDDLEWARE_CLASSES = (
-	'django.middleware.gzip.GZipMiddleware',
-	'django.middleware.http.ConditionalGetMiddleware',
-	'sitepoet.middleware.RedirectionMiddleware',
-	'sitepoet.middleware.PageMiddleware',
-	'django.middleware.common.CommonMiddleware',
-	'django.contrib.sessions.middleware.SessionMiddleware',
-	'django.contrib.auth.middleware.AuthenticationMiddleware',
-)
-
-ROOT_URLCONF = '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.
-	"templates",
-)
-
-INSTALLED_APPS = (
-	'django.contrib.auth',
-	'django.contrib.contenttypes',
-	'django.contrib.sessions',
-	'django.contrib.sites',
-	'django.contrib.admin',
-	'django.contrib.admindocs',
-	'django.contrib.markup',
-	'django.contrib.humanize',
-	'django.contrib.comments',
-	'sitepoet',
-#	'sitepoet.drupal_support',
-	'tagging',
-	'threadedcomments',
-)
-
-TEMPLATE_CONTEXT_PROCESSORS = (
-    "django.core.context_processors.auth",
-    "django.core.context_processors.debug",
-    "django.core.context_processors.i18n",
-    "django.core.context_processors.media",
-    "django.core.context_processors.request",
-    "sitepoet.context_preprocessors.general",
-)
-
-COMMENTS_APP = 'threadedcomments'
-
-# Common Middleware
-
-APPEND_SLASH = True
-PREPEND_WWW = False
-USE_ETAGS = True
-
-# Some defaults
-
-SP_SITE_NAME = "SitePoet Site"
-SP_PAGE_SIZE = 10
-SP_URL_TEMPLATE = "$section$slug"
-
-try:
-	from settings_local import *
-except Exception, e:
-	print "No local settings found; using defaults."
Add a comment to this file

sitepoet/__init__.py

Empty file removed.

sitepoet/admin.py

-from django import forms
-from django.contrib import admin
-from django.utils.translation import ugettext_lazy as _
-
-from sitepoet.models import *
-
-## ACTIONS ##
-
-def action_mark_draft(model_admin, request, query_set):
-	query_set.update(status=ENTRY_STATUS_DRAFT)
-action_mark_draft.short_description = "Change status of selected items to Draft"
-
-def action_mark_published(model_admin, request, query_set):
-	query_set.update(status=ENTRY_STATUS_PUBLISHED)
-action_mark_published.short_description = "Change status of selected items to Published"
-
-def action_open_comments(model_admin, request, query_set):
-	query_set.update(allow_comments=COMMENTS_ENABLED)
-action_open_comments.short_description = "Allow comments on the selected items"
-
-def action_close_comments(model_admin, request, query_set):
-	query_set.update(allow_comments=COMMENTS_CLOSED)
-action_close_comments.short_description = "Close comments on the selected items"
-
-def action_disable_comments(model_admin, request, query_set):
-	query_set.update(allow_comments=COMMENTS_DISABLED)
-action_disable_comments.short_description = "Disable and hide comments on the selected items"
-
-### PAGES ###
-
-class PageForm(forms.ModelForm):
-	url = forms.RegexField(label=_("URL"), max_length=255, regex=r'^[-\w/]+$',
-		help_text	  = _("Example: '/about/contact/'. Make sure to have leading"
-					  " and trailing slashes."),
-		error_message = _("This value must contain only letters, numbers,"
-						  " underscores, dashes or slashes."))
-	class Meta:
-		model = Page
-
-
-class PageAdmin(admin.ModelAdmin):
-	form		   = PageForm
-	list_display   = ('title', 'absolute_url', 'visible', 'status', 'date_created', 'date_modified', 'date_published', 'date_hidden')
-	list_editable  = ('status',)
-	list_filter	   = ('date_published', 'date_modified', 'status')
-	date_hierarchy = 'date_published'
-	search_fields  = ('title', 'url')
-	actions		   = [action_mark_draft, action_mark_published]
-	fieldsets	   = [
-		('',				{'fields': ['title', 'url', 'sites']}),
-		(_('Content'),		{'fields': ['content_format', 'content', 'template_name']}),
-		(_('Availability'), {'fields': ['status', 'date_published', 'date_hidden'], 'classes':('collapse',)}),
-	]
-	
-	def absolute_url(self, obj):
-		return obj.get_absolute_url()
-
-admin.site.register(Page, PageAdmin)
-
-### STORIES ###
-
-class StoryAdmin(admin.ModelAdmin):
-	list_display = ('title', 'tags', 'status', 'allow_comments', 'date_published', 'date_hidden', 'date_modified')
-	list_editable = ('status',)
-	list_filter = ('date_published', 'date_hidden', 'date_modified', 'status')
-	date_hierarchy = 'date_published'
-	search_fields = ('title', '^user__username', 'slug')
-	prepopulated_fields = {"slug": ("title",)}
-	actions = [action_mark_draft, action_mark_published, action_open_comments, action_close_comments, action_disable_comments]
-	fieldsets = [
-		('',				{'fields': ['user', 'title', 'slug', 'tags', 'section']}),
-		('Content',			{'fields': ['teaser_format', 'teaser', 'content_format', 'content']}),
-		('Availability',	{'fields': ['status', 'date_published', 'date_hidden', 'allow_comments', 'sites'], 'classes':('collapse',)}),
-		]
-
-admin.site.register(Story, StoryAdmin)
-
-class ContentHistoryAdmin(admin.ModelAdmin):
-	list_display = ('title', 'content_type', 'object_id', 'field_name', 'date_created')
-	list_filter = ('field_name', 'date_created')
-	date_hierarchy = 'date_created'
-	search_fields = ('field_name', 'content')
-	
-	def title(self, obj):
-		return obj.__unicode__()
-	title.short_description = "Item Name"
-	
-admin.site.register(ContentHistory, ContentHistoryAdmin)
-
-### SECTIONS ###
-
-class SectionAdmin(admin.ModelAdmin):
-	list_display = ("name", "slug")
-	prepopulated_fields = {"slug": ("name",)}
-
-admin.site.register(Section, SectionAdmin)
-	
-### REDIRECTION ###
-
-class RedirectAdmin(admin.ModelAdmin):
-	list_display = ("original", "current", "content_type", "object_id", "permanent", "date_modified", "date_created")
-	fieldsets = (
-		("Source", {"fields": ["site", "original"]}),
-		("Destination", {"fields": ["current", "content_type", "object_id"]}),
-		("Options", {"fields": ["permanent"]})
-	)
-admin.site.register(Redirect, RedirectAdmin)

sitepoet/context_preprocessors/__init__.py

-from django.conf import settings
-from django.contrib.sites.models import Site
-
-def general(request):
-	return {
-		"site": Site.objects.get_current(),
-	}
Add a comment to this file

sitepoet/drupal_support/__init__.py

Empty file removed.

Add a comment to this file

sitepoet/drupal_support/management/__init__.py

Empty file removed.

Add a comment to this file

sitepoet/drupal_support/management/commands/__init__.py

Empty file removed.

sitepoet/drupal_support/management/commands/convertdrupal.py

-import datetime
-import re
-from optparse import make_option
-
-from django.db.models import Q
-from django.contrib.auth.models import User
-from django.contrib.contenttypes.models import ContentType
-from django.contrib.sites.models import Site
-from django.core.management.base import BaseCommand, CommandError
-
-from sitepoet.drupal_support.models import *
-from sitepoet.models import *
-from sitepoet.templatetags.slugify import slugify
-
-from threadedcomments.models import ThreadedComment
-
-def vancode2int(vancode):
-	if len(vancode):
-		result = int(vancode[1:], 36)
-	else:
-		result = None
-	return result
-
-class Command(BaseCommand):
-	# option_list = BaseCommand.option_list + (
-	# 	make_option('--format', default='json', dest='format', help='Specifies the output serialization format for fixtures.'),
-	# 	make_option('--indent', default=None, dest='indent', type='int', help='Specifies the indent level to use when pretty-printing output'),
-	# 	make_option('-e', '--exclude', dest='exclude',action='append', default=[], help='App to exclude (use multiple --exclude to exclude multiple apps).'),
-	# 	make_option('-v', '--verbose', dest='verbose', action="store", default="0", type="choice", choices=['0','1','2'], help='Verbose output'),
-	# )
-	help = 'Convert Drupal 6 tables to SP objects.	The tables must be in the same database as SP.'
-	# args = '[appname ...]'
-	
-	def handle(self, *app_labels, **options):
-		show_traceback = options.get('traceback', False)
-		verbose = int(options.get('verbosity', 0))
-		debug = (verbose == 2)
-		user = User.objects.get(pk=1)
-		
-		if verbose: print "Starting Drupal conversion."
-		if debug: print "* Debug logging enabled."
-		if debug: print "* Importing data with %s as the content owner." % user.username
-		
-		try:
-			# Get a list of nodes together and filter out blog nodes.
-			node_list = DrupalNode.objects.filter(type__in=("blog", "story", "page")).order_by("nid")
-			if verbose: print "Found %d nodes." % len(node_list)
-			
-			if len(node_list) == 0:
-				raise CommandError("No Drupal nodes found in the current database.")
-			
-			# Create some sections
-			(blog_section, c) = Section.objects.get_or_create(name="blog")
-			if c:
-				if debug: print "* Created blog section."
-				blog_section.save()
-			
-			(story_section, c) = Section.objects.get_or_create(name="story")
-			if c:
-				if debug: print "* Created story section."
-				story_section.save()
-			
-			for node in node_list:
-				# Our document object
-				obj = None
-				
-				# See if we've imported this node before
-				node_aliases = Redirect.objects.filter(original='/node/%d' % node.nid)
-				if node_aliases.count() > 0:
-					if verbose: print "Skipping node %d" % node.nid
-					continue
-				
-				# We haven't imported this.  Let's begin...
-				if verbose: print "Processing node (%d) %s" % (node.nid, node.title)
-				
-				if node.type == "blog" or node.type == "story":
-					if node.type == "blog":
-						if debug: print "Node is a blog."
-						section = blog_section
-					else:
-						if debug: print "Node is a story."
-						section = story_section
-					
-					# Create an Story for the node
-					obj = Story(
-						user = user,
-						title = node.title,
-						date_created = datetime.datetime.fromtimestamp(node.created),
-						date_modified = datetime.datetime.fromtimestamp(node.changed),
-						date_published = datetime.datetime.fromtimestamp(node.created),
-						slug = slugify(node.title),
-						allow_comments = node.comment,
-						status = node.status,
-						section = section,
-						)
-				
-					# Set the current content value
-					try:
-						contents = node.current_revision.get_parsed_contents()
-					
-						obj.teaser = contents['teaser']
-						obj.content = contents['body']
-					except DrupalNodeRevision.DoesNotExist, e:
-						if verbose: print "WARNING: No body content found for %s (%d)" % (obj.title, obj.id)
-				
-					# Save the story
-					obj.save()
-					
-					# Add to the current site
-					obj.sites.add(Site.objects.get_current())
-					
-					# Import terms as tags
-					terms = node.current_revision.terms.all()
-					tags = []
-					for term in terms:
-						tags.append(term.name)
-					obj.tags = ','.join(tags) + ','
-					if debug: print "* Set tags to: %s" % obj.tags
-					
-					# Ensure the modification date is proper
-					obj.date_modified = datetime.datetime.fromtimestamp(node.changed)
-					obj.save()
-					
-				elif node.type == "page":
-					if debug: print "Node is a page."
-					
-					# Create an object for the node
-					obj = Page(
-						title = node.title,
-						date_created = datetime.datetime.fromtimestamp(node.created),
-						date_modified = datetime.datetime.fromtimestamp(node.changed),
-						date_published = datetime.datetime.fromtimestamp(node.created),
-						status = node.status,
-						)
-					
-					# Set the current content value
-					try:
-						obj.content = node.current_revision.body
-					except DrupalNodeRevision.DoesNotExist, e:
-						if verbose: print "WARNING: No body content found for %s (%d)" % (obj.title, obj.id)
-					
-					# Save
-					obj.save()
-					
-					# Add to the current site
-					obj.sites.add(Site.objects.get_current())
-					
-					# Find the most recent URL for this object and assign it
-					aliases = DrupalUrlAlias.objects.filter(src='node/%d' % node.nid).order_by('-pid')
-					if aliases.count():
-						if debug: print "* Page aliases:", aliases
-						obj.url = '/' + aliases[0].dst
-					
-					# Ensure the modification date is proper
-					obj.date_modified = datetime.datetime.fromtimestamp(node.changed)
-					obj.save()
-				
-				else:
-					# Bail if we don't have something to work on
-					if verbose: print "Unsupported type:", node.type
-					continue
-				
-				# Lookup any URL aliases for this node and create redirects
-				aliases = DrupalUrlAlias.objects.filter(src='node/%d' % node.nid)
-				
-				# First, the core "node/1" links should still work
-				r = Redirect(original="/node/%d" % node.nid, target_object=obj)
-				r.save()
-				
-				# Now we get any others that were made
-				for alias in aliases:
-					dst = "/" + alias.dst
-					if obj.__class__ == Page and dst == obj.url: continue
-					try:
-						r = Redirect(original=dst, target_object=obj)
-						r.save()
-						if debug: print " Created redirect from", dst
-					except Exception, e:
-						if verbose: print "* Failed to create redirect from", dst, "to", obj, ":", e
-						continue
-				
-				# Create a ContentHistory for each old body value
-				if node.revisions.count() > 1:
-					for revision in node.revisions.all()[1:]:
-						date = datetime.datetime.fromtimestamp(revision.timestamp)
-						
-						contents = revision.get_parsed_contents()
-						
-						ch = ContentHistory(
-							date_created = date,
-							date_modified = date,
-							owner = obj,
-							field_name = "teaser",
-							content = contents['teaser'],
-						)
-						ch.save()
-						
-						ch = ContentHistory(
-							date_created = date,
-							date_modified = date,
-							owner = obj,
-							field_name = "content",
-							content = contents['body'],
-						)
-						ch.save()
-						
-						if debug: print " Added revision %s" % (ch.date_created)
-				
-				# Bring over the comments
-				comments = self.get_comments_for_node(node)
-				if debug: print "  Comments:", comments.all()
-				for comment in comments:
-					if debug: print "  Importing comment: ", comment.thread, comment.subject
-					
-					# To handle the nested comments:
-					# 	Break up the thread property: 01.01.01 -> [1,1,1]
-					# 	Pop off the last item, as that is the location of the current comment (which we cannot directly set)
-					# 	For each remaining level, get the right relationship in a loop:
-					# 		Set an object to the first listed comment.
-					# 		Pop that value off the array.
-					# 		If there're more items left, set the loop object to that child of the current object.
-					# 		When we run out of list items, we found the parent.
-					
-					# 01.00.05.07/ to 01.00.05.07 to [01,00,05,07]
-					parents = comment.thread[:-1].split('.')
-					# [1,0,5,7]
-					parents = map(vancode2int, parents)
-					# [1,0,5]
-					parents = parents[:-1]
-					if len(parents):
-						# [0,0,5]
-						parents[0] = parents[0] - 1
-					
-					if debug: print "** Parents:", parents, "(%s)" % comment.thread
-					
-					c_obj = None
-					if len(parents):
-						ct = ContentType.objects.get_for_model(obj.__class__)
-						obj_comments = ThreadedComment.objects.filter(content_type=ct, object_pk=str(obj.id)).order_by('submit_date')
-						if debug:
-							print "* Object comments (%d):" % obj_comments.count(), obj_comments
-							print "* Parents (%d):" % len(parents), parents
-						try:
-							c_obj = obj_comments[parents[0]]
-						except IndexError, e:
-							if verbose: print "*** Parent of comment subtree not found.  This can happen if a thread has a deleted comment."
-						parents = parents[1:]
-						while c_obj and len(parents):
-							if c_obj.children.count() > parents[0]:
-								c_obj = c_obj.children.all()[parents[0]]
-								parents = parents[1:]
-							else:
-								if verbose: print "** Object has %d comments, but the parent should be item %d" % (c_obj.children.count(), parents[0])
-								break
-						if debug and c_obj: print "** Suspected parent:", c_obj.title
-					
-					# Actually create the comment now
-					c = ThreadedComment(
-						content_object = obj,
-						title = unicode(comment.subject)[:200],
-						ip_address = unicode(comment.hostname)[:15],
-						user_name = unicode(comment.name)[:50],
-						user_email = unicode(comment.mail)[:75],
-						user_url = unicode(comment.homepage)[:200],
-						submit_date = datetime.datetime.fromtimestamp(comment.timestamp),
-						comment = unicode(comment.comment),
-						site = Site.objects.get_current(),
-						is_public = (not comment.status),
-					)
-					c.save()
-					
-					# Now that the comment exists, we can set the parent.
-					c.parent = c_obj
-					c.save()
-					
-					if verbose: print " Imported comment (%d) %s" % (comment.cid, comment.subject)
-				
-				if debug: print " Finished node: %s (%s)" % (obj.title, obj.get_absolute_url())
-			
-		except Exception, e:
-			if show_traceback:
-				raise
-			raise CommandError("Error: %s" % e)
-		
-		if verbose: print "Done."
-	
-	def get_comments_for_node(self, node):
-		'''
-		This generally requires a "real" database like Postgresql or MySQL.  Sqlite does not support SUBSTRING.
-		But, that's okay.  Drupal required MySQL anyway, so do the conversion on a copy of the DB there, then
-		migrate to whatever else you want to use.
-		'''
-		from django.db import connection
-		cursor = connection.cursor()
-		
-		cursor.execute("SELECT cid FROM comments as c WHERE nid = %s ORDER BY SUBSTRING(c.thread, 1, (LENGTH(c.thread) - 1))", [node.nid])
-		rows = cursor.fetchall()
-		ids = [c[0] for c in rows]
-		comments = DrupalComment.objects.filter(pk__in=ids)
-		
-		return comments

sitepoet/drupal_support/models.py

-import re
-from django.db import models
-from django.contrib import admin
-
-class DrupalComment(models.Model):
-	cid = models.IntegerField(primary_key=True)
-	pid = models.IntegerField(null=True, blank=True, default=0)
-	nid = models.ForeignKey("DrupalNode", db_column="nid")
-	uid = models.ForeignKey("DrupalUser", db_column="uid")
-	subject = models.CharField(null=True, blank=True, max_length=192)
-	comment = models.TextField(null=True, blank=True, default="")
-	hostname = models.CharField(null=True, blank=True, max_length=384)
-	timestamp = models.IntegerField(null=True, blank=True, default=0)
-	status = models.IntegerField(null=True, blank=True, default=0)
-	format = models.IntegerField(null=True, blank=True, default=0)
-	thread = models.CharField(null=True, blank=True, max_length=765)
-	name = models.CharField(null=True, blank=True, max_length=180)
-	mail = models.CharField(null=True, blank=True, max_length=192)
-	homepage = models.CharField(null=True, blank=True, max_length=765)
-	class Meta:
-		db_table = u'comments'
-
-class DrupalNode(models.Model):
-	nid = models.IntegerField(primary_key=True)
-	type = models.CharField(max_length=96, default="")
-	title = models.CharField(max_length=765, default="")
-	uid = models.ForeignKey("DrupalUser", db_column="uid")
-	status = models.IntegerField(default=0)
-	created = models.IntegerField(default=0)
-	changed = models.IntegerField(default=0)
-	comment = models.IntegerField(default=0)
-	promote = models.IntegerField(default=0)
-	moderate = models.IntegerField(default=0)
-	sticky = models.IntegerField(default=0)
-	current_revision = models.ForeignKey("DrupalNodeRevision", db_column="vid")
-	language = models.CharField(max_length=36, default="")
-	tnid = models.IntegerField(default=0)
-	translate = models.IntegerField(default=0)
-	
-	terms = models.ManyToManyField('DrupalTermData', through="DrupalTermNode", blank=True)
-
-	class Meta:
-		db_table = u'node'
-
-class DrupalNodeRevision(models.Model):
-	nid = models.ForeignKey("DrupalNode", db_column="nid", related_name="revisions")
-	vid = models.IntegerField(primary_key=True, default=0)
-	uid = models.ForeignKey("DrupalUser", db_column="uid")
-	title = models.CharField(max_length=765, default="")
-	body = models.TextField(default="")
-	teaser = models.TextField(default="")
-	timestamp = models.IntegerField(default=0)
-	format = models.IntegerField(default=0)
-	log = models.TextField(null=True, blank=True, default="")
-	
-	terms = models.ManyToManyField('DrupalTermData', through="DrupalTermNode", blank=True)
-	
-	class Meta:
-		db_table = u'node_revisions'
-	
-	def get_parsed_contents(self):
-		'''
-		Drupal stores a teaser and a body, but the teaser is almost always a substr of the body.
-		So, we need to see if that's the case.	If it is, then we break it up so we have a teaser and body.
-		If it's not, then we store each separately.
-		But first, we check for the easy case of "<!-- break -->"
-		'''
-		break_re = re.compile('(?P<before>.*?)<!--\s*break\s*-->(?P<after>.*)', re.S|re.I)
-		results = break_re.search(self.body)
-		
-		p_teaser = None
-		p_content = None
-		
-		if results:
-			# Found a break marker, so we're golden.
-			p_teaser = results.group('before')
-			p_content = results.group('after')
-		else:
-			# Did not find a break marker.	So we do some string math.
-			# If the teaser is found in the body, it is deleted.  If it is not found,
-			# then it's left alone.	 Should work...
-			p_teaser = self.teaser
-			p_content = re.sub(re.escape(self.teaser), '', self.body, 1)
-		
-		return {'teaser':p_teaser, 'body':p_content}
-
-class DrupalUrlAlias(models.Model):
-	pid = models.IntegerField(primary_key=True, default="")
-	src = models.CharField(max_length=255, default="")
-	dst = models.CharField(unique=True, max_length=255, default="")
-	language = models.CharField(max_length=36, default="")
-
-	def __unicode__(self):
-		return "%s to %s" % (self.src, self.dst)
-
-	class Meta:
-		db_table = u'url_alias'
-
-class DrupalUser(models.Model):
-	uid = models.IntegerField(primary_key=True)
-	name = models.CharField(null=True, unique=True, max_length=180, default="")
-	pass_field = models.CharField(null=True, max_length=96, db_column='pass', default="") # Field renamed because it was a Python reserved word. Field name made lowercase.
-	mail = models.CharField(null=True, max_length=192, blank=True, default="")
-	mode = models.IntegerField(null=True, default=0)
-	sort = models.IntegerField(null=True, blank=True, default=0)
-	threshold = models.IntegerField(null=True, blank=True, default=0)
-	theme = models.CharField(null=True, max_length=765, default="")
-	signature = models.CharField(null=True, max_length=765, default="")
-	created = models.IntegerField(null=True, default=0)
-	access = models.IntegerField(null=True, default=0)
-	status = models.IntegerField(null=True, default=0)
-	timezone = models.CharField(null=True, max_length=24, blank=True, default="")
-	language = models.CharField(null=True, max_length=36, default="")
-	picture = models.CharField(null=True, max_length=765, default="")
-	init = models.CharField(null=True, max_length=192, blank=True, default="")
-	data = models.TextField(null=True, blank=True, default="")
-	login = models.IntegerField(null=True, default=0)
-	class Meta:
-		db_table = u'users'
-
-admin.site.register(DrupalComment)
-admin.site.register(DrupalNode)
-admin.site.register(DrupalNodeRevision)
-admin.site.register(DrupalUrlAlias)
-admin.site.register(DrupalUser)
-
-class DrupalTermData(models.Model):
-	'''The actual terms'''
-	tid = models.IntegerField(primary_key=True)
-	vid = models.IntegerField()
-	name = models.CharField(max_length=765)
-	description = models.TextField(blank=True)
-	weight = models.IntegerField()
-	class Meta:
-		db_table = u'term_data'
-	def __unicode__(self):
-		return self.name
-
-class DrupalTermHierarchy(models.Model):
-	'''Term parents/children'''
-	tid = models.ForeignKey(DrupalTermData, primary_key=True, related_name="tid_set", db_column="tid")
-	parent = models.ForeignKey(DrupalTermData, primary_key=True, db_column="parent")
-	class Meta:
-		db_table = u'term_hierarchy'
-
-class DrupalTermNode(models.Model):
-	'''Mapping of terms to nodes'''
-	nid = models.ForeignKey(DrupalNode, primary_key=True, db_column="nid")
-	tid = models.ForeignKey(DrupalTermData, primary_key=True, db_column="tid")
-	vid = models.ForeignKey(DrupalNodeRevision, db_column='vid')
-	class Meta:
-		db_table = u'term_node'
-
-admin.site.register(DrupalTermData)
-admin.site.register(DrupalTermHierarchy)
-admin.site.register(DrupalTermNode)
-
-# class DrupalTermRelation(models.Model):
-#	  tid1 = models.IntegerField(unique=True)
-#	  tid2 = models.IntegerField()
-#	  trid = models.IntegerField(primary_key=True)
-#	  class Meta:
-#		  db_table = u'term_relation'
-# 
-# class DrupalTermSynonym(models.Model):
-#	  tid = models.IntegerField()
-#	  name = models.CharField(max_length=765)
-#	  tsid = models.IntegerField(primary_key=True)
-#	  class Meta:
-#		  db_table = u'term_synonym'
-# 
-# class DrupalVocabulary(models.Model):
-#	  vid = models.IntegerField(primary_key=True)
-#	  name = models.CharField(max_length=765)
-#	  description = models.TextField(blank=True)
-#	  help = models.CharField(max_length=765)
-#	  relations = models.IntegerField()
-#	  hierarchy = models.IntegerField()
-#	  multiple = models.IntegerField()
-#	  required = models.IntegerField()
-#	  weight = models.IntegerField()
-#	  module = models.CharField(max_length=765)
-#	  tags = models.IntegerField()
-#	  class Meta:
-#		  db_table = u'vocabulary'
Add a comment to this file

sitepoet/management/__init__.py

Empty file removed.

Add a comment to this file

sitepoet/management/commands/__init__.py

Empty file removed.

sitepoet/managers.py

-from django.db import models
-from django.db.models import Q
-from django.contrib.sites.models import Site
-import datetime
-
-class PublishedManager (models.Manager):
-	def get_query_set(self):
-		return super(PublishedManager,self).get_query_set().filter(
-			Q(date_published=None) | Q(date_published__lte=datetime.datetime.now),
-			Q(date_hidden=None) | Q(date_hidden__gt =datetime.datetime.now),
-			status = True,
-			sites__in=[Site.objects.get_current()]
-		)

sitepoet/metaweblog.py

-# http://www.allyourpixel.com/post/metaweblog-38-django/
-
-import urlparse
-from django.contrib.auth.models import User
-from sitepoet.models import *
-from tagging.models import Tag
-from tagging.utils import parse_tag_input
-from sitepoet.xmlrpc import public
-from django.conf import settings
-from django.core.urlresolvers import reverse
-import xmlrpclib # import DateTime
-
-def authenticated(pos=1):
-	"""
-	A decorator for functions that require authentication.
-	Assumes that the username & password are the second & third parameters.
-	Doesn't perform real authorization (yet), it just checks that the
-	user is_superuser.
-	"""
-	
-	def _decorate(func):
-		def _wrapper(*args, **kwargs):
-			username = args[pos+0]
-			password = args[pos+1]
-			args = args[0:pos]+args[pos+2:]
-			try:
-				user = User.objects.get(username__exact=username)
-			except User.DoesNotExist:
-				raise ValueError("Authentication Failure")
-			if not user.check_password(password):
-				raise ValueError("Authentication Failure")
-			if not user.is_superuser:
-				raise ValueError("Authorization Failure")
-			return func(user, *args, **kwargs)
-		
-		return _wrapper
-	return _decorate
-
-def full_url(url):
-	return urlparse.urljoin(settings.SITE_URL, url)
-
-# example... this is what wordpress returns:
-# {'permaLink': 'http://gabbas.wordpress.com/2006/05/09/hello-world/',
-#  'description': 'Welcome to <a href="http://wordpress.com/">Wordpress.com</a>. This is your first post. Edit or delete it and start blogging!',
-#  'title': 'Hello world!',
-#  'mt_excerpt': '',
-#  'userid': '217209',
-#  'dateCreated': <DateTime u'20060509T16:24:39' at 2c7580>,
-#  'link': 'http://gabbas.wordpress.com/2006/05/09/hello-world/',
-#  'mt_text_more': '',
-#  'mt_allow_comments': 1,
-#  'postid': '1',
-#  'categories': ['Uncategorized'],
-#  'mt_allow_pings': 1}
-
-def format_date(d):
-	if not d: return None
-	return xmlrpclib.DateTime(d.isoformat())
-
-def post_struct(post):
-	link = full_url(post.get_absolute_url())
-	struct = {
-		'postid': post.id,
-		'title': post.title,
-		'mt_basename': post.slug,
-		'link': link,
-		'permaLink': link,
-		'description': post.teaser,
-		'mt_tags': post.tags,
-		'userid': post.user.id,
-		# 'mt_excerpt': post.teaser,
-		'mt_text_more': post.content,
-		# 'mt_allow_comments': 1,
-		# 'mt_allow_pings': 1
-		'mt_convert_breaks': str(post.content_format), #Sadly, we have to use one format for both components.
-		}
-	if post.date_published:
-		struct['dateCreated'] = format_date(post.date_published)
-	
-	return struct
-
-def tag_struct(tag):
-	struct = {
-		'categoryId': tag.id,
-		'categoryName': tag.name,
-	}
-	return struct
-
-def _update_post(post, struct):
-	if struct.get('dateCreated', None):
-		post.date_published = struct['dateCreated']
-	if struct.has_key('title'):
-		post.title = struct['title']
-	if struct.has_key('description'):
-		post.teaser = struct['description']
-	if struct.has_key('mt_text_more'):
-		post.content = struct['mt_text_more']
-	if struct.has_key('mt_tags'):
-		post.tags = struct['mt_tags']
-	elif struct.has_key('mt_keywords'):
-		post.tags = struct('mt_keywords')
-	if struct.has_key('mt_basename'):
-		post.slug = struct['mt_basename']
-
-''' Blogger API V1 '''
-
-@public
-@authenticated(pos=2)
-def blogger_newPost(user, appkey, blogid, content, publish):
-	pass
-
-@public
-@authenticated(pos=2)
-def blogger_editPost(user, appkey, postid, content, publish):
-	pass
-
-@public
-@authenticated(pos=2)
-def blogger_deletePost(user, appkey, postid, publish):
-	post = Story.objects.get(id=postid)
-	post.delete()
-	return True
-
-@public
-@authenticated()
-def blogger_getUsersBlogs(user, appkey):
-	"""
-	an array of <struct>'s containing the ID (blogid), name
-	(blogName), and URL (url) of each blog.
-	"""
-	print "entered"
-	sections = Section.objects.all()
-	print "got sections"
-	result = [{
-			'blogid': section.slug,
-			'blogName': section.name,
-			'url': settings.SITE_URL + reverse('story-archive', kwargs={'section':section.slug})
-			} for section in sections]
-	print "results:", result
-	return result
-
-@public
-@authenticated()
-def blogger_getUserInfo(user, appkey):
-	return {
-		'nickname':user.username,
-		'userid':user.id,
-		'url':settings.SITE_URL,
-		'email':user.email,
-		'lastname':user.last_name,
-		'firstname':user.first_name,
-		}
-
-@public
-@authenticated()
-def metaWeblog_getCategories(user, blogid):
-	tags = Tag.objects.all()
-	result = [{'description':tag.name} for tag in tags]
-	return result
-
-@public
-@authenticated()
-def metaWeblog_getPost(user, postid):
-	post = Story.objects.get(id=postid)
-	return post_struct(post)
-
-@public
-@authenticated()
-def metaWeblog_getRecentPosts(user, blogid, num_posts):
-	posts = Story.objects.filter(section__name__exact=blogid).order_by('-date_published')[:int(num_posts)]
-	return [post_struct(post) for post in posts]
-
-@public
-@authenticated()
-def metaWeblog_newPost(user, blogid, struct, publish):
-	post = Story(status = publish)
-	post.user = user
-	_update_post(post, struct)
-	section = Section.objects.get(name__exact=blogid)
-	post.section = blogid
-	post.save()
-	return post.id
-
-@public
-@authenticated()
-def metaWeblog_editPost(user, postid, struct, publish):
-	post = Story.objects.get(id=postid)
-	post.status = publish
-	_update_post(post, struct)
-	post.save()
-	return True
-
-# http://qoli.de/blog/2007/nov/19/implementing-metaweblog-api/
-@public
-@authenticated()
-def metaWeblog_newMediaObject(user, blogid, struct):		
-	bits = b64decode(struct['bits'])
-	name = struct['name']
-	mime = struct['type']
-
-	attachment= Attachment(content= bits,
-				   filename= name,
-				   contenttype= mime)
-	attachment.save_content_file(name, bits)
-	attachment.save()
-
-	return {'url': attachment.get_content_url()}
-
-@public
-@authenticated()
-def mt_getPostCategories(user, postid):
-	post = Story.objects.get(id=int(postid))
-	if (post.tags):
-		tags = [tag_struct(t) for t in Tag.objects.filter(name__in=parse_tag_input(post.tags))]
-	else:
-		tags = []
-	return tags
-
-@public
-@authenticated()
-def mt_getCategoryList(user, blogid):
-	return [tag_struct(t) for t in Tag.objects.all()]
-
-@public
-def mt_supportedTextFilters():
-	return [{'label': f[1], 'key': str(f[0])} for f in CONTENT_FORMATTERS]

sitepoet/middleware/__init__.py

-from django.conf import settings
-from django.http import HttpResponsePermanentRedirect, HttpResponseRedirect, HttpResponseGone, Http404
-from django.template import Template, RequestContext, loader
-
-from sitepoet.models import Redirect, Page, Story
-from sitepoet.views import view_page
-
-class RedirectionMiddleware(object):
-	def process_response(self, request, response):
-		# If the response found something, return it.
-		if response.status_code != 404:
-			return response
-		try:
-			# Look for an exact redirect
-			path = request.path
-			paths = [path]
-			
-			# Also try without the trailing slash if someone tacked it on for us
-			if settings.APPEND_SLASH:
-				paths.append(path[:path.rfind('/')] + path[path.rfind('/')+1:])
-			
-			r = Redirect.objects.get(site__id__exact=settings.SITE_ID, original__in=paths)
-			
-			# If the destination is not visible, let the 404 shine.
-			if hasattr(r.target_object, 'visible') and r.target_object.visible == False:
-				return response
-			
-			# If there's a redirect, process it
-			destination = r.destination()
-			
-			# If we're about to tell someone to go where we already are, stop and think...
-			if destination == request.path_info:
-				return response
-			
-			if destination == None:
-				# No destination means it's been removed, so make it a dead end.
-				context = RequestContext(request, {
-					'title':'410 Gone',
-					'content':'the resource you seek / has been taken by time / seek answers within'
-				})
-				template = loader.get_template("error.html")
-				return HttpResponseGone(template.render(context))
-			
-			if r.permanent == True:
-				# 301 Moved
-				return HttpResponsePermanentRedirect(destination)
-			else:
-				# 302 Found
-				return HttpResponseRedirect(destination)
-		
-		except Redirect.DoesNotExist, e:
-			# Otherwise, return the original response
-			return response
-		except:
-			if settings.DEBUG:
-				raise
-		# If we're here, we didn't catch something -- just pass along the 404.
-		return response
-
-
-class PageMiddleware(object):
-	def process_response(self, request, response):
-		# If the response found something, return it.
-		if response.status_code != 404:
-			return response
-		# Otherwise, see if we can get a response from the view
-		try:
-			return view_page(request, request.path_info)
-		# If the view couldn't find a page, then return the original response (don't pollute)
-		except Http404:
-			return response
-		# If anything else goes wrong here, just ignore it and return the original response (again, don't pollute)
-		except:
-			if settings.DEBUG:
-				raise
-			return response

sitepoet/models.py

-#!/usr/bin/env python2.5
-
-### IMPORTS ###
-
-try:
-	import markdown
-except:
-	markdown = None
-
-try:
-	import textile
-except:
-	textile = None
-
-import datetime
-import string
-
-from django.core.urlresolvers import reverse
-from django.contrib.auth.models import User
-from django.contrib.sites.models import *
-from django.conf import settings
-from django.contrib.contenttypes import generic
-from django.contrib.contenttypes.models import ContentType
-from django.db import models
-from django.utils.safestring import mark_safe
-import tagging
-from tagging.fields import TagField
-
-from sitepoet.managers import *
-from sitepoet.templatetags.slugify import slugify
-
-
-### CONSTANTS ###
-
-COMMENTS_DISABLED = 0
-COMMENTS_CLOSED = 1
-COMMENTS_ENABLED =2
-
-COMMENT_STATES = (
-	(COMMENTS_DISABLED, 'Disabled'),
-	(COMMENTS_CLOSED, 'Closed'),
-	(COMMENTS_ENABLED, 'Enabled'),
-)
-
-
-ENTRY_STATUS_DRAFT = 0
-ENTRY_STATUS_PUBLISHED = 1
-
-ENTRY_STATUSES = (
-	(ENTRY_STATUS_DRAFT, "Draft"),
-	(ENTRY_STATUS_PUBLISHED, "Published"),
-)
-
-
-CONTENT_FORMAT_NONE = 0
-CONTENT_FORMAT_TEXTILE = 1
-CONTENT_FORMAT_MARKDOWN = 2
-
-CONTENT_FORMATTERS = (
-	(CONTENT_FORMAT_NONE, "None"),
-	(CONTENT_FORMAT_TEXTILE, "Textile"),
-	(CONTENT_FORMAT_MARKDOWN, "Markdown"),
-)
-
-
-### GLOBAL FUNCTIONS ###
-
-def format_text(format, text):
-	output = text
-	if format == CONTENT_FORMAT_TEXTILE:
-		if textile != None:
-			output = textile.textile(text)
-		else:
-			print "No Textile support found."
-	elif format == CONTENT_FORMAT_MARKDOWN:
-		if markdown != None:
-			output = markdown.markdown(text)
-		else:
-			print "No Markdown support found."
-	return mark_safe(output)
-
-
-### SPObject ###
-
-class SPObject (models.Model):
-	date_created = models.DateTimeField("Date Created", editable=False)
-	date_modified = models.DateTimeField("Date Modified", editable=False)
-	
-	class Meta:
-		abstract = True
-	
-	def __init__(self, *args, **kwargs):
-		super(SPObject, self).__init__(*args, **kwargs)
-		self._original_state = self._as_dict()
-	
-	def _as_dict(self):
-		'''
-		Returns the values of non-relationship model objects in a dictionary.
-		'''
-		return dict([(f.name, getattr(self, f.name)) for f in self._meta.local_fields if not f.rel])
-	
-	def get_changed_fields(self):
-		'''
-		Returns a dictionary where the key is the field that changed and the value is the original value.
-		'''
-		new_state = self._as_dict()
-		return dict([(key, value) for key, value in self._original_state.iteritems() if value != new_state[key]])
-	
-	def is_dirty_field(self, field):
-		''' Returns True if a field has been changed since the object was last saved. '''
-		try:
-			return self.__dict__.get(field, None) != self._original_state[field]
-		except KeyError:
-			return True
-	
-	def save(self, *args, **kwargs):
-		if not self.date_created:
-			self.date_created = datetime.datetime.now()
-		
-		''' If the modification date was either not set or not manually changed since we were made, update it. '''
-		if not self.date_modified or not self.is_dirty_field('date_modified'):
-			self.date_modified = datetime.datetime.now()
-				
-		super(SPObject, self).save(*args, **kwargs)
-		
-		self._original_state = self._as_dict()
-
-
-### Model Objects ###
-
-class Resource(SPObject):
-	# Properties
-	sites = models.ManyToManyField(Site, default=[settings.SITE_ID], null=False, blank=False, help_text='The site(s) this item is accessible at.')
-	title = models.CharField("Title", max_length=255, null=False, blank=False)
-	
-	teaser = models.TextField(null=True, blank=True)
-	teaser_format = models.PositiveIntegerField("Teaser Format", choices=CONTENT_FORMATTERS, default=CONTENT_FORMAT_TEXTILE, help_text="The formatter the teaser should be run through when rendering.")
-	content = models.TextField("Content", null=True, blank=True, help_text='The content to display on the page.')
-	content_format = models.PositiveIntegerField("Content Format", choices=CONTENT_FORMATTERS, default=CONTENT_FORMAT_TEXTILE, help_text="The formatter the content should be run through when rendering.")
-	
-	# Publishing status
-	status = models.IntegerField("Status", choices=ENTRY_STATUSES, default=ENTRY_STATUS_PUBLISHED, help_text="Only published items will be visible on the site.")
-	date_published = models.DateTimeField("Date Published", null=True, blank=True, default=datetime.datetime.now, help_text="Item will become visible after this date.  Future posting is supported.")
-	date_hidden = models.DateTimeField("Date Hidden", null=True, blank=True, help_text="Item will be hidden past this date.  No value indicates a perpetual item (most common).")
-		
-	# Object managers
-	objects = models.Manager()
-	published = PublishedManager()
-	
-	class Meta:
-		abstract = True
-	
-	@property
-	def visible(self):
-		return (self.status == ENTRY_STATUS_PUBLISHED and self.date_published < datetime.datetime.now())
-	
-	def __init__(self, *args, **kwargs):
-		super(Resource, self).__init__(*args, **kwargs)
-		if self.id != None:
-			self._original_url = self.get_absolute_url()
-		else:
-			self._original_url = ""
-	
-	def __unicode__(self):
-		return self.title
-	
-	@property
-	def formatted_teaser(self):
-		return format_text(self.teaser_format, self.teaser)
-	
-	@property
-	def formatted_content(self):
-		return format_text(self.content_format, self.content)
-	
-	def save(self, *args, **kwargs):
-		update_teaser_history = (self.teaser and self.is_dirty_field("teaser"))
-		update_content_history = (self.content and self.is_dirty_field("content"))
-		
-		super(Resource, self).save(*args, **kwargs)
-		
-		'''
-		Create a redirect with the current URL if the path has changed.
-		We use the current URL so that if this object is ever deleted or hidden, we can display a 410/Gone message.
-		Since we create these from the start of life for the object, all previous URLs will be recorded EXCEPT
-		for the case when two objects will have shared the same URL at some point, at which case the original
-		object wins (in the automated fashion; admins can always update the Redirect object manually).
-		'''
-		if self._original_url != self.get_absolute_url():
-			(r, c) = Redirect.objects.get_or_create(
-				original = self.get_absolute_url(),
-				defaults = {
-					'target_object': self,
-					'permanent': True
-				}
-			)
-			if c: r.save()
-			self._original_url = self.get_absolute_url()
-		
-		if update_teaser_history:
-			ch = ContentHistory(owner=self, field_name="teaser", content=self.teaser)
-			ch.save()
-		
-		if update_content_history:
-			ch = ContentHistory(owner=self, field_name="content", content=self.content)
-			ch.save()
-
-
-class Section(SPObject):
-	# TODO: Create admin class that auto-gens the slug
-	name = models.CharField("Section Name", max_length=255, null=False, blank=False)
-	slug = models.SlugField("Section Slug", max_length=255, null=False, blank=False)
-	# page = models.ForeignKey(Page, null=True, blank=True, help_text="A page to display instead of a section index.")
-	
-	class Meta:
-		verbose_name = "section"
-		verbose_name_plural = "sections"
-	
-	def __unicode__(self):
-		return self.name
-	
-	@models.permalink
-	def get_absolute_url(self):
-		return ('section-archive',(), {'section':self.slug})
-
-
-class Page(Resource):
-	url = models.CharField("URL", max_length=255, db_index=True, help_text="The URL for this page. Ensure it begins and ends with a slash.")
-	template_name = models.CharField("Template", max_length=255, blank=True, help_text="If specified, this template will be used instead of 'page.html'")
-
-	class Meta:
-		ordering = ('url',)
-		verbose_name = "page"
-		verbose_name_plural = "pages"
-		get_latest_by = 'date_published'
-		ordering = ['-date_published','-date_modified']
-	
-	def __unicode__(self):
-		return "%s; %s" % (self.url, self.title)
-
-	def get_absolute_url(self):
-		return self.url
-
-
-class Story(Resource):
-	slug = models.SlugField("URL Slug", max_length=255, null=False, blank=False)
-	user = models.ForeignKey(User, null=False, blank=False)
-	tags = TagField()
-	section = models.ForeignKey(Section, null=True, blank=True, help_text="The section of the site this story will reside in.")
-	show_metadata = models.BooleanField("Show Metadata", default=True, help_text="Toggles the display of author and date information.")
-	allow_comments = models.IntegerField("Comments", choices=COMMENT_STATES, default=COMMENTS_ENABLED, help_text="If comments are disabled then existing comments will be hidden.  Choose 'Closed' to display existing comments and prevent new comments from being added.")
-	
-	class Meta:
-		verbose_name = "story"
-		verbose_name_plural = "stories"
-		get_latest_by = 'date_published'
-		ordering = ['-date_published','-date_modified']
-	
-	def show_comments(self):
-		return self.allow_comments != COMMENTS_DISABLED
-	
-	def allow_commenting(self):
-		return self.allow_comments == COMMENTS_ENABLED
-	
-	def get_absolute_url(self):
-		if self.section:
-			section = self.section.slug
-		else:
-			section = None
-		
-		kwargs = {
-			'section': section,
-			'year':    self.date_published.year,
-			'month':   "%02d"%self.date_published.month,
-			'day':     "%02d"%self.date_published.day,
-			'slug':    self.slug,
-		}
-		return reverse('story-detail', kwargs = kwargs)
-
-### REDIRECTION ###
-
-class Redirect(SPObject):
-	site = models.ForeignKey(Site, default=settings.SITE_ID, help_text='The site this redirect is applicable to.')
-	original = models.CharField("Original path", max_length=255, unique=True, db_index=True,
-		help_text='The original path for this resource.  This must be an absolute path starting from the root of the site.')
-	current = models.CharField("Current path", max_length=255, null=True, blank=True,
-		help_text='The current path to this resource, if it is not an object.')
-	
-	content_type = models.ForeignKey(ContentType, null=True, blank=True, help_text='If this redirect is to an object, select an object type.')
-	object_id = models.PositiveIntegerField("Object ID", null=True, blank=True, help_text='The ID of the object this redirect will point to.')
-	target_object = generic.GenericForeignKey()
-	
-	permanent = models.BooleanField(default=True, help_text='Is this redirect permanent (code 301)?')
-	
-	class Meta:
-		verbose_name = "redirect"
-		verbose_name_plural = "redirects"
-		ordering = ['-date_created']
-	
-	def __unicode__(self):
-		return "%s to (%s, %s)" % (self.original, self.current, self.target_object)
-	
-	def destination(self):
-		destination = None
-		if self.current and len(self.current):
-			destination = self.current
-		elif self.target_object != None:
-			try:
-				destination = self.target_object.get_absolute_url()
-			except Exception, e:
-				pass
-		return destination
-
-
-### HISTORY ###
-
-class ContentHistory(SPObject):
-	content_type = models.ForeignKey(ContentType, null=True, blank=True)
-	object_id = models.PositiveIntegerField(null=True, blank=True)
-	owner = generic.GenericForeignKey()
-	field_name = models.CharField(max_length=255)
-	content = models.TextField("Content", null=True, blank=True)
-	
-	class Meta:
-		verbose_name = "content history"
-		verbose_name_plural = "content histories"
-		ordering = ['-date_created', 'content_type', 'object_id']
-	
-	def __unicode__(self):
-		return "%s: %s" % (self.owner, self.field_name)
-	
-	@classmethod
-	def history(owner, field):
-		#FIXME: owner won't work here, need to search by ID and PK
-		return ContentHistory.objects.filter(owner=owner, field_name=field).order_by('-date_created')
-

sitepoet/templates/comment.html

-<div class="comment" style="margin-left: {{comment.depth}}pc;">
-	<h3>{{comment.user_name}}</h3>
-	{% if comment.title %}<p class="title" /> {{comment.title}}{% endif %}
-	<p class="content" /> {{comment.comment}}
-	<ul class="links">
-		<li>Reply</li>
-		<li>Flag</li>
-		<li>Delete</li>
-		<li>Approve</li>
-	</ul>
-</div>

sitepoet/templates/error.html

-{% extends "base.html" %}
-{% block content %}
-<h1>{{title}}</h1>
-
-<p>{{content}}</p>
-{% endblock %}

sitepoet/templates/page.html

-{% extends "base.html" %}
-{% block content %}
-<div class="page">
-    <h2>
-        <a href="{{object.get_absolute_url}}">{{object.title}}</a>
-    </h2>
-    <p class="body">
-        {{object.formatted_content}}
-    </p>
-</div>
-{% endblock %}

sitepoet/templates/pager.html

-{% spaceless %}
-<span class="paginate-pages">{{ pages }} Pages</span>
-{% if show_first %}<span class="paginate-first"><a href="?page=1" title="First Page">&laquo;</a></span>{% endif %}
-{% if has_previous %}<span class="paginate-previous"><a href="?page={{ previous }}" title="Previous Page">&lt;</a></span>{% endif %}
-{% for num in page_numbers %}
-  {% ifequal num page %}
-    <span class="paginate-current" title="Current Page">{{ num }}</span>
-  {% else %}
-    <span class="paginate-link"><a href="?page={{ num }}" title="Page {{ num }}">{{ num }}</a></span>
-  {% endifequal %}
-{% endfor %}
-{% if has_next %}<span class="paginate-next"><a href="?page={{ next }}" title="Next Page">&gt;</a></span>{% endif %}
-{% if show_last %}<span class="paginate-last"><a href="?page={{ pages }}" title="Last Page">&raquo;</a></span>{% endif %}
-{% endspaceless %}

sitepoet/templates/story.html

-{% load markup %}
-<div class="story {% if options.teasers %}teaser{% else %}full{% endif %}">
-    <h2>
-        <a href="{{object.get_absolute_url}}">{{object.title}}</a>
-    </h2>
-    <div class="byline">
-        By <span class="author">{{object.user.username}}</span> on <span class="date">{{object.date_published|date}}</span>
-    </div>
-    <div class="teaser">
-        {{object.formatted_teaser}}
-    </div>
-{% if not options.teasers %}
-    <div class="body">
-        {{object.formatted_content}}
-    </div>
-	<p class="post-info">
-		{% load tagging_tags %}
-		{% tags_for_object object as tags %}
-		{% if tags.count %}
-		<p /><span class="tags"><span class="post-item-key">Tags:</span> <span class="post-item-value">{% for tag in tags %}<a href="{% url tag tag.name %}">{{tag}}</a>{% if not forloop.last %}, {% endif %}{% endfor %}</span></span></p>
-		{% endif %}
-	</p>
-{% endif %}
-</div>

sitepoet/templates/story_archive.html

-{% extends "base.html" %}
-{% load pager %}
-{% block content %}
-<div class="story-list">
-{% for object in page.object_list %}
-{% include "story.html" %}
-{% endfor %}
-</div>
-{% if page.has_other_pages %}
-<ul class="pager">
-{% pager 5 %}
-</ul>
-{% endif %}
-{% endblock %}

sitepoet/templates/story_detail.html

-{% extends "base.html" %}
-{% block content %}
-{% include "story.html" %}
-
-{% endblock %}

sitepoet/templates/tag_cloud.html

-{% load tagging_tags %}
-{% tag_cloud_for_model sitepoet.Story as tag_cloud with min_count=10 steps=5 %}
-<div class="cloud" style="width: 300px">{%for tag in tag_cloud%}
-	<span class="tag" style="font-size:{{tag.font_size}}ex; white-space: nowrap;"><a href="{% url tag tag.name %}">{{tag.name}}</a></span>{%endfor%}
-</div>
Add a comment to this file

sitepoet/templatetags/__init__.py

Empty file removed.

sitepoet/templatetags/pager.py

-from django import template
-
-register = template.Library()
-
-def pager(context, fuzz=2):
-	"""
-	Adds pagination context variables for use in displaying first, adjacent and
-	last page links in addition to those created by the object_list generic
-	view.
-	"""
-	
-	page = context['page']
-	paginator = page.paginator
-	
-	page_numbers = [n for n in range(page.number - fuzz, page.number + fuzz) if n in paginator.page_range]
-	
-	return {
-		"hits": len(paginator.object_list),
-		"results_per_page": paginator.per_page,
-		"page": page.number,
-		"pages": paginator.num_pages,
-		"page_numbers": page_numbers,
-		"next": page.next_page_number(),
-		"previous": page.previous_page_number(),
-		"has_next": page.has_next(),
-		"has_previous": page.has_previous(),
-		"show_first": 1 not in page_numbers,
-		"show_last": paginator.count not in page_numbers,
-	}
-
-register.inclusion_tag("pager.html", takes_context=True)(pager)

sitepoet/templatetags/slugify.py

-from django import template
-import re
-
-register = template.Library()
-
-@register.filter
-def slugify(string):
-	string = re.sub('\s+', '-', string)
-	string = re.sub('[^\w.-]', '', string)
-	string = string.strip('_.- ').lower()
-	return string or "no-title"

sitepoet/tests.py

-#!/usr/bin/env python
-
-import unittest
-from django.test import TestCase
-from django.conf import settings
-from sitepoet.models import *
-
-class PageTests(TestCase):
-	def setUp(self):
-		pass
-	
-	def testContentFormattingNone(self):
-		obj = Page(title="Format Test", path="/format-test")
-		obj.content = "*None* _test_ <b>woo!</b>"
-		obj.content_format = CONTENT_FORMAT_NONE
-		self.assertEqual(obj.formatted_content, obj.content, "Invalid result: "+obj.formatted_content)
-	
-	def testContentFormattingTextile(self):
-		obj = Page(title="Format Test", path="/format-test")
-		obj.content = "p(foo). *Textile* _test_"
-		obj.content_format = CONTENT_FORMAT_TEXTILE
-		self.assertEqual(obj.formatted_content, '\t<p class="foo"><strong>Textile</strong> <em>test</em></p>', "Invalid result: "+obj.formatted_content)
-	
-	def testContentFormattingMarkdown(self):
-		obj = Page(title="Format Test", path="/format-test")
-		obj.content = "*Markdown* _test_"
-		obj.content_format = CONTENT_FORMAT_MARKDOWN
-		self.assertEqual(obj.formatted_content, '<p><em>Markdown</em> <em>test</em></p>', "Invalid result: "+obj.formatted_content)
-	
-	def testAbsoluteURL(self):
-		obj = Page()
-		obj.title="URL Test"
-		obj.path="url-test"
-		obj.content="Hey, hey, we're the Monkees!"
-		obj.content_format=CONTENT_FORMAT_NONE
-		obj.save()
-
-		obj.sites = [settings.SITE_ID]
-		obj.save()
-		
-		url = obj.get_absolute_url()
-		
-		response = self.client.get(url)
-		# print response
-		self.assertEqual(response.status_code, 200, "Could not load page at the URL it claims it's at (%d): %s" % (response.status_code, url))
-		
-		obj.delete()
-	
-	def testRevisionHistory(self):
-		pass
-
-class RedirectTests(TestCase):
-	def setUp(self):
-		pass
-	
-	def testPathRedirectTemporary(self):
-		r = Redirect(original="/moved-temp", current="/success-temp", permanent=False)
-		r.save()
-		
-		response = self.client.get('/moved-temp')
-		self.assertEqual(response.status_code, 302, "Incorrect response code (%s); expected 302." % response.status_code)
-		self.assertEqual(response['Location'], "http://testserver/success-temp", "Unexpected destination: %s" % response['Location'])
-		
-		r.delete()
-	
-	def testPathRedirectPermanent(self):
-		r = Redirect(original="/moved-perm", current="/success-perm", permanent=True)
-		r.save()
-		
-		response = self.client.get('/moved-perm')
-		self.assertEqual(response.status_code, 301, "Incorrect response code (%s); expected 301." % response.status_code)
-		self.assertEqual(response['Location'], "http://testserver/success-perm", "Unexpected destination: %s" % response['Location'])
-		
-		r.delete()
-	
-	def testObjectRedirectTemporary(self):
-		obj = Page(title="TestTemp", path="test-temp", content="Lorum ipsum etc.")
-		obj.save()
-		obj.sites = [settings.SITE_ID]
-		obj.save()
-		
-		r = Redirect(original="/moved-obj-temp", target_object=obj, permanent=False)
-		r.save()
-		
-		response = self.client.get('/moved-obj-temp')
-		self.assertRedirects(response, "http://testserver" + obj.get_absolute_url(), status_code=302)
-		
-		r.delete()
-		obj.delete()
-	
-	def testObjectRedirectPermanent(self):
-		obj = Page(title="TestPerm", path="test-perm", content="Lorum ipsum etc.")
-		obj.save()
-		obj.sites = [settings.SITE_ID]
-		obj.save()
-		
-		r = Redirect(original="/moved-obj-perm", target_object=obj, permanent=True)
-		r.save()
-		
-		response = self.client.get('/moved-obj-perm')
-		self.assertRedirects(response, "http://testserver" + obj.get_absolute_url(), status_code=301)
-		
-		r.delete()
-		obj.delete()
-	
-	def testMissingObjectRedirect(self):
-		obj = Page(title="TestGone", path="/test-gone", content="Lorum ipsum etc.")
-		obj.save()
-		obj.sites = [settings.SITE_ID]
-		obj.save()
-		
-		r = Redirect(original="/missing-obj", target_object=obj)
-		r.save()
-		
-		obj.delete()
-		
-		response = self.client.get('/missing-obj')
-		self.assertEqual(response.status_code, 410, "Incorrect response code (%s); expected 410." % response.status_code)
-		try:
-			raise Exception, "Unexpected destination (should be empty): %s" % response['Location']
-		except KeyError:
-			pass
-		
-		r.delete()

sitepoet/urls.py

-from django.conf.urls.defaults import *
-from models import Story
-from django.conf import settings
-
-urlpatterns = patterns('',
-	# MetaWeblog access point
-	url(r'^api/', 'sitepoet.xmlrpc.view', kwargs={'module':'sitepoet.metaweblog'}),
-)
-
-urlpatterns += patterns('sitepoet.views',
-	# Comments
-    (r'^comments/', include('django.contrib.comments.urls')),
-	
-	# Front page
-	#  Enable this if you want the front page to not be an index of Stories, but a static Page.
-	# (r'^$', 'view_page', {'url':'/'}),
-	
-	# Articles
-	url(
-		regex  = r'^(?:(?P<section>[^/]+?)/)?(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>.+)/$',
-		view   = 'story_detail',
-		name   = "story-detail"
-	),
-	url(
-		regex  = r'^(?:(?P<section>[^/]+)/)?(?:(?P<year>\d{4})/)?(?:(?P<month>\d{1,2})/)?(?:(?P<day>\d{1,2})/)?$',
-		view   = 'story_archive',
-		kwargs = { "teasers": True },
-		name   = "story-archive"
-	),
-	
-	# Tags
-	url(
-		regex  = r'^tag/(?P<tagname>.+)/$',
-		view   = 'view_tag',
-		name   = 'tag',
-	)
-)
-
-urlpatterns += patterns('sitepoet.views',
-	(r'^(?P<url>.*)$', 'view_page'),
-)

sitepoet/views.py

-from django.core.paginator import Paginator, InvalidPage, EmptyPage
-from django.core.xheaders import populate_xheaders
-from django.http import HttpResponse, HttpResponseRedirect, Http404
-from django.template import loader, Context, RequestContext
-from django.shortcuts import render_to_response, get_object_or_404
-from django.conf import settings
-from django.contrib.sites.models import *
-
-from sitepoet.models import *
-
-from tagging.models import Tag, TaggedItem
-from tagging.utils import get_tag_list
-
-### Stories ###
-
-def story_detail(request, section=None, year=None, month=None, day=None, slug=None):
-	# Fetch a story
-	try:
-		story = Story.published.get(
-			section__slug = section,
-			date_published__year = int(year),
-			date_published__month = int(month),
-			date_published__day = int(day),
-			slug = slug
-			)
-	except Story.DoesNotExist, e:
-		# No result?  404.
-		raise Http404
-	
-	# Prepare the template data
-	context = {
-		"title":  story.title,
-		"object": story
-	}
-	
-	response = render_to_response("story_detail.html", context, context_instance=RequestContext(request))
-	populate_xheaders(request, response, Story, story.id)
-	return response
-
-
-def story_archive(request, section=None, year=None, month=None, day=None, teasers=False):
-	# If there's a section, try and load it then filter the stories by it
-	if section:
-		obj = get_object_or_404(Section, slug=section)
-		stories = Story.published.filter(section=obj)
-	else:
-		stories = Story.published.all()
-	
-	# Fetch a list of stories
-	if year:
-		stories = stories.filter(date_published__year=int(year))
-		if month:
-			stories = stories.filter(date_published__month=int(month))
-			if day:
-				stories = stories.filter(date_published__day=int(day))
-	
-	# Paginate the results
-	page = int(request.GET.get('page', 1))		# Requested page number
-	paginator = Paginator(stories.select_related(depth=2), settings.SP_PAGE_SIZE)
-	
-	try:
-		stories_page = paginator.page(page)
-	except (EmptyPage, InvalidPage):
-		stories_page = paginator.page(paginator.num_pages)
-	
-	# Prepare the context
-	context = {
-		"options": {
-			"teasers": teasers
-		},
-		"page": stories_page
-	}
-	
-	if year and month and day:
-		context['title'] = "%04d-%02d-%02d" % (int(year), int(month), int(day))
-	elif year and month:
-		context['title'] = "%04d-%02d" % (int(year), int(month))
-	elif year:
-		context['title'] = "%04d" % int(year)
-	
-	templates = []
-	if section:
-		templates.append("section/" + section + "/story_archive.html")
-	templates.append("story_archive.html")
-	
-	return render_to_response(templates, context, context_instance=RequestContext(request))
-
-
-def view_page(request, url):
-	# URLs begin with a slash, but a bad admin or urls.py can remove it.
-	if not url.startswith('/'):
-		url = "/" + url
-
-	try:
-		page = Page.published.get(url__exact=url, sites__id__exact=settings.SITE_ID)
-	
-	except Page.DoesNotExist, e:
-		# If there's no trailing slash, and we're using it, then try that URL next.
-		if settings.APPEND_SLASH and not url.endswith('/'):
-			return HttpResponseRedirect("%s/" % request.path_info)
-		
-		# Otherwise, give up.
-		print request.path_info, url
-		raise Http404
-	
-	except Page.MultipleObjectsReturned, e:
-		raise Http404 #What else to do?  You broke it, man.  One URL = One Object.
-
-	# Prepare the context
-	context = {
-		"title":  page.title,
-		"object": page,
-		"flatpage": page, #Let people use the same template to a degree
-	}
-
-	# Create the template list
-	if page.template_name:
-		templates = [page.template_name, "page.html"]
-	else:
-		templates = ["page.html"]
-
-	response = render_to_response(templates, context, context_instance=RequestContext(request))
-	populate_xheaders(request, response, Page, page.id)
-	return response
-
-def view_tag(request, tagname, teasers=True):
-	tags = get_tag_list(tagname)
-	if tags.count() == 0:
-		raise Http404
-	
-	items = TaggedItem.objects.get_intersection_by_model(Story, tags)
-	
-	# Paginate the results
-	page = int(request.GET.get('page', 1))		# Requested page number
-	paginator = Paginator(items.select_related(depth=2), settings.SP_PAGE_SIZE)
-	
-	try:
-		stories_page = paginator.page(page)
-	except (EmptyPage, InvalidPage):
-		stories_page = paginator.page(paginator.num_pages)
-	
-	context = {
-		"title": "Stories tagged %s" % tagname,
-		"options": {
-			"teasers": teasers
-		},
-		"page": stories_page,
-	}
-	
-	response = render_to_response("story_archive.html", context, context_instance=RequestContext(request))
-	return response

sitepoet/xmlrpc.py

-# ayp/xmlrpc.py
-# Greg Abbas
-# 2006 June 3
-# http://www.allyourpixels.com/
-# http://www.allyourpixel.com/post/xmlrpc-djgo/
-# 
-# based on Amit Upadhyay's xmlrpc implementation
-# http://nerdierthanthou.nfshost.com/2005/09/xmlrpc-support-for-django.html
-# 
-# My modifications include:
-#
-#   * Publishing functions in a module, not methods in a class (because that
-#     seemed more Djangoish to me).
-#   * If methods with dots in them (e.g. "metaWeblog.editPost") don't
-#     get mapped to a sequence of Python names, then the dots are converted
-#     to underscores. This makes it more natural to implement methods
-#     in an XMLRPC namespace, because it's really just a flat namespace
-#     that allows periods in its identifiers.
-#   * Made the server a view, so you don't need to explicitly instantiate
-#     a SimpleXMLRPCView.
-
-import sys
-import traceback
-import SimpleXMLRPCServer
-from django.http import HttpResponseServerError, HttpResponse
-
-def public(f):
-    f.public = True
-    return f
-
-def is_public(func):
-    return func is not None and hasattr(func, 'public') and func.public
-
-def dotify(name):
-    return name.replace('_','.')
-
-def list_public_methods(obj):
-    """Returns a list of attribute strings, found in the specified
-    object, which represent callable attributes"""
-
-    methods = SimpleXMLRPCServer.list_public_methods(obj)
-    methods = [dotify(m) for m in methods if is_public(getattr(obj, m, None))]
-    return methods
-
-def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
-    """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
-
-    Similar to the one in SimpleXMLRPCServer, except that it tries
-    converting dots to underscores, too.
-    """
-    
-    attrs = attr.split('.')
-    
-    if attrs[0].startswith('_'):
-        raise AttributeError('attempt to access private attribute "%s"' % i)
-    for i in range(len(attrs),0,-1):
-        name = '_'.join(attrs[:i])
-        obj0 = getattr(obj, name, None)
-        #print '%r %r -> %r' % (obj, name, obj0)
-        if obj0:
-            attrs = attrs[i:]
-            if len(attrs):
-                obj0 = resolve_dotted_attribute(
-                    obj0, '.'.join(attrs), allow_dotted_names)
-            return obj0
-        if not allow_dotted_names:
-            return None
-
-class SafeXMLRPCView(SimpleXMLRPCServer.SimpleXMLRPCDispatcher):
-    """ class SafeXMLRPCView
-
-        Checks for "public" attribute on callables before calling them.    
-    """
-    def system_listMethods(self):
-        """system.listMethods() => ['add', 'subtract', 'multiple']
-
-        Returns a list of the methods supported by the server."""
-
-        methods = self.funcs.keys()
-        if self.instance is not None:
-            # Instance can implement _listMethods to return a list of
-            # methods
-            if hasattr(self.instance, '_listMethods'):
-                methods = SimpleXMLRPCServer.remove_duplicates(
-                        methods + self.instance._listMethods()
-                    )
-            # if the instance has a _dispatch method then we
-            # don't have enough information to provide a list
-            # of methods
-            elif not hasattr(self.instance, '_dispatch'):
-                methods = SimpleXMLRPCServer.remove_duplicates(
-                        methods + list_public_methods(self.instance)
-                    )
-        methods.sort()
-        return methods
-
-    system_listMethods.public = True
-    
-    def system_methodSignature(self, method_name):
-        return SimpleXMLRPCView.system_methodSignature(self, method_name)
-    system_methodSignature.public = True
-
-    def system_methodHelp(self, method_name):