Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Imported Django from private SVN repository (created from r. 8825)

git-svn-id: http://code.djangoproject.com/svn/django/trunk@3 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit ed114e15106192b22ebb78ef5bf5bce72b419d13 1 parent 07ffc7d
@adrianholovaty adrianholovaty authored
Showing with 13,851 additions and 0 deletions.
  1. 0  django/__init__.py
  2. 0  django/bin/__init__.py
  3. +15 −0 django/bin/daily_cleanup.py
  4. +412 −0 django/bin/django-admin.py
  5. 0  django/bin/profiling/__init__.py
  6. +34 −0 django/bin/profiling/gather_profile_stats.py
  7. +22 −0 django/bin/profiling/handler.py
  8. +45 −0 django/bin/setup.py
  9. +36 −0 django/bin/validate.py
  10. 0  django/conf/__init__.py
  11. 0  django/conf/app_template/__init__.py
  12. +1 −0  django/conf/app_template/models/__init__.py
  13. +3 −0  django/conf/app_template/models/app_name.py
  14. 0  django/conf/app_template/urls/__init__.py
  15. +5 −0 django/conf/app_template/urls/app_name.py
  16. 0  django/conf/app_template/views/__init__.py
  17. +199 −0 django/conf/global_settings.py
  18. 0  django/conf/project_template/__init__.py
  19. 0  django/conf/project_template/apps/__init__.py
  20. 0  django/conf/project_template/settings/__init__.py
  21. +31 −0 django/conf/project_template/settings/main.py
  22. +42 −0 django/conf/settings.py
  23. 0  django/conf/urls/__init__.py
  24. +56 −0 django/conf/urls/admin.py
  25. +6 −0 django/conf/urls/admin_password_reset.py
  26. +12 −0 django/conf/urls/comments.py
  27. +17 −0 django/conf/urls/defaults.py
  28. +5 −0 django/conf/urls/flatfiles.py
  29. +19 −0 django/conf/urls/registration.py
  30. +6 −0 django/conf/urls/rss.py
  31. +5 −0 django/conf/urls/shortcut.py
  32. 0  django/core/__init__.py
  33. +255 −0 django/core/cache.py
  34. +28 −0 django/core/db/__init__.py
  35. 0  django/core/db/backends/__init__.py
  36. +107 −0 django/core/db/backends/mysql.py
  37. +109 −0 django/core/db/backends/postgresql.py
  38. +32 −0 django/core/db/base.py
  39. +42 −0 django/core/db/typecasts.py
  40. +466 −0 django/core/defaultfilters.py
  41. +743 −0 django/core/defaulttags.py
  42. +26 −0 django/core/exceptions.py
  43. +79 −0 django/core/extensions.py
  44. +759 −0 django/core/formfields.py
  45. +157 −0 django/core/handler.py
  46. +51 −0 django/core/mail.py
  47. +2,142 −0 django/core/meta.py
  48. +76 −0 django/core/paginator.py
  49. +136 −0 django/core/rss.py
  50. +488 −0 django/core/template.py
  51. +18 −0 django/core/template_file.py
  52. +142 −0 django/core/template_loader.py
  53. +96 −0 django/core/urlresolvers.py
  54. +420 −0 django/core/validators.py
  55. +22 −0 django/core/xheaders.py
  56. 0  django/middleware/__init__.py
  57. +120 −0 django/middleware/admin.py
  58. +104 −0 django/middleware/common.py
  59. +18 −0 django/middleware/doc.py
  60. +91 −0 django/models/__init__.py
  61. +290 −0 django/models/auth.py
  62. +281 −0 django/models/comments.py
  63. +107 −0 django/models/core.py
  64. 0  django/parts/__init__.py
  65. 0  django/parts/admin/__init__.py
  66. +93 −0 django/parts/admin/doc.py
  67. 0  django/parts/auth/__init__.py
  68. +48 −0 django/parts/auth/anonymoususers.py
  69. +46 −0 django/parts/auth/formfields.py
  70. 0  django/parts/media/__init__.py
  71. +6 −0 django/parts/media/photos.py
  72. +7 −0 django/templatetags/__init__.py
  73. +331 −0 django/templatetags/comments.py
  74. +45 −0 django/templatetags/log.py
  75. 0  django/tests/__init__.py
  76. +119 −0 django/tests/cache_tests.py
  77. +102 −0 django/tests/template_inheritance.py
  78. +707 −0 django/tests/template_tests.py
  79. 0  django/utils/__init__.py
  80. +171 −0 django/utils/datastructures.py
  81. +317 −0 django/utils/dateformat.py
  82. +27 −0 django/utils/dates.py
  83. +152 −0 django/utils/feedgenerator.py
  84. +110 −0 django/utils/html.py
  85. +319 −0 django/utils/httpwrappers.py
  86. +22 −0 django/utils/images.py
  87. +42 −0 django/utils/stopwords.py
  88. +108 −0 django/utils/text.py
  89. +46 −0 django/utils/timesince.py
  90. +13 −0 django/utils/xmlutils.py
  91. 0  django/views/__init__.py
  92. 0  django/views/admin/__init__.py
  93. +328 −0 django/views/admin/doc.py
  94. +1,089 −0 django/views/admin/main.py
  95. +70 −0 django/views/admin/template.py
  96. 0  django/views/auth/__init__.py
  97. +62 −0 django/views/auth/login.py
  98. 0  django/views/comments/__init__.py
  99. +347 −0 django/views/comments/comments.py
  100. +34 −0 django/views/comments/karma.py
  101. +82 −0 django/views/comments/userflags.py
  102. 0  django/views/core/__init__.py
  103. +34 −0 django/views/core/flatfiles.py
  104. 0  django/views/decorators/__init__.py
  105. +12 −0 django/views/decorators/auth.py
  106. +64 −0 django/views/decorators/cache.py
  107. +72 −0 django/views/defaults.py
  108. 0  django/views/generic/__init__.py
  109. +223 −0 django/views/generic/date_based.py
  110. +106 −0 django/views/generic/list_detail.py
  111. 0  django/views/registration/__init__.py
  112. +109 −0 django/views/registration/passwords.py
  113. 0  django/views/rss/__init__.py
  114. +12 −0 django/views/rss/rss.py
View
0  django/__init__.py
No changes.
View
0  django/bin/__init__.py
No changes.
View
15 django/bin/daily_cleanup.py
@@ -0,0 +1,15 @@
+"Daily cleanup file"
+
+from django.core.db import db
+
+DOCUMENTATION_DIRECTORY = '/home/html/documentation/'
+
+def clean_up():
+ # Clean up old database records
+ cursor = db.cursor()
+ cursor.execute("DELETE FROM auth_sessions WHERE start_time < NOW() - INTERVAL '2 weeks'")
+ cursor.execute("DELETE FROM registration_challenges WHERE request_date < NOW() - INTERVAL '1 week'")
+ db.commit()
+
+if __name__ == "__main__":
+ clean_up()
View
412 django/bin/django-admin.py
@@ -0,0 +1,412 @@
+#!/usr/bin/python2.3
+from django.core import db, meta
+import django
+import os, re, sys
+
+MODULE_TEMPLATE = ''' {%% if perms.%(app)s.%(addperm)s or perms.%(app)s.%(changeperm)s %%}
+ <tr>
+ <th>{%% if perms.%(app)s.%(changeperm)s %%}<a href="/%(app)s/%(mod)s/">{%% endif %%}%(name)s{%% if perms.%(app)s.%(changeperm)s %%}</a>{%% endif %%}</th>
+ <td class="x50">{%% if perms.%(app)s.%(addperm)s %%}<a href="/%(app)s/%(mod)s/add/" class="addlink">{%% endif %%}Add{%% if perms.%(app)s.%(addperm)s %%}</a>{%% endif %%}</td>
+ <td class="x75">{%% if perms.%(app)s.%(changeperm)s %%}<a href="/%(app)s/%(mod)s/" class="changelink">{%% endif %%}Change{%% if perms.%(app)s.%(changeperm)s %%}</a>{%% endif %%}</td>
+ </tr>
+ {%% endif %%}'''
+
+APP_ARGS = '[app app ...]'
+
+PROJECT_TEMPLATE_DIR = django.__path__[0] + '/conf/%s_template'
+
+def _get_packages_insert(app_label):
+ return "INSERT INTO packages (label, name) VALUES ('%s', '%s');" % (app_label, app_label)
+
+def _get_permission_codename(action, opts):
+ return '%s_%s' % (action, opts.object_name.lower())
+
+def _get_all_permissions(opts):
+ "Returns (codename, name) for all permissions in the given opts."
+ perms = []
+ if opts.admin:
+ for action in ('add', 'change', 'delete'):
+ perms.append((_get_permission_codename(action, opts), 'Can %s %s' % (action, opts.verbose_name)))
+ return perms + list(opts.permissions)
+
+def _get_permission_insert(name, codename, opts):
+ return "INSERT INTO auth_permissions (name, package, codename) VALUES ('%s', '%s', '%s');" % \
+ (name.replace("'", "''"), opts.app_label, codename)
+
+def _get_contenttype_insert(opts):
+ return "INSERT INTO content_types (name, package, python_module_name) VALUES ('%s', '%s', '%s');" % \
+ (opts.verbose_name, opts.app_label, opts.module_name)
+
+def _is_valid_dir_name(s):
+ return bool(re.search(r'^\w+$', s))
+
+def get_sql_create(mod):
+ "Returns a list of the CREATE TABLE SQL statements for the given module."
+ final_output = []
+ for klass in mod._MODELS:
+ opts = klass._meta
+ table_output = []
+ for f in opts.fields:
+ if isinstance(f, meta.ForeignKey):
+ rel_field = f.rel.to.get_field(f.rel.field_name)
+ # If the foreign key points to an AutoField, the foreign key
+ # should be an IntegerField, not an AutoField. Otherwise, the
+ # foreign key should be the same type of field as the field
+ # to which it points.
+ if rel_field.__class__.__name__ == 'AutoField':
+ data_type = 'IntegerField'
+ else:
+ rel_field.__class__.__name__
+ else:
+ rel_field = f
+ data_type = f.__class__.__name__
+ col_type = db.DATA_TYPES[data_type]
+ if col_type is not None:
+ field_output = [f.name, col_type % rel_field.__dict__]
+ field_output.append('%sNULL' % (not f.null and 'NOT ' or ''))
+ if f.unique:
+ field_output.append('UNIQUE')
+ if f.primary_key:
+ field_output.append('PRIMARY KEY')
+ if f.rel:
+ field_output.append('REFERENCES %s (%s)' % \
+ (f.rel.to.db_table, f.rel.to.get_field(f.rel.field_name).name))
+ table_output.append(' '.join(field_output))
+ if opts.order_with_respect_to:
+ table_output.append('_order %s NULL' % db.DATA_TYPES['IntegerField'])
+ for field_constraints in opts.unique_together:
+ table_output.append('UNIQUE (%s)' % ", ".join(field_constraints))
+
+ full_statement = ['CREATE TABLE %s (' % opts.db_table]
+ for i, line in enumerate(table_output): # Combine and add commas.
+ full_statement.append(' %s%s' % (line, i < len(table_output)-1 and ',' or ''))
+ full_statement.append(');')
+ final_output.append('\n'.join(full_statement))
+
+ for klass in mod._MODELS:
+ opts = klass._meta
+ for f in opts.many_to_many:
+ table_output = ['CREATE TABLE %s_%s (' % (opts.db_table, f.name)]
+ table_output.append(' id %s NOT NULL PRIMARY KEY,' % db.DATA_TYPES['AutoField'])
+ table_output.append(' %s_id %s NOT NULL REFERENCES %s (%s),' % \
+ (opts.object_name.lower(), db.DATA_TYPES['IntegerField'], opts.db_table, opts.pk.name))
+ table_output.append(' %s_id %s NOT NULL REFERENCES %s (%s),' % \
+ (f.rel.to.object_name.lower(), db.DATA_TYPES['IntegerField'], f.rel.to.db_table, f.rel.to.pk.name))
+ table_output.append(' UNIQUE (%s_id, %s_id)' % (opts.object_name.lower(), f.rel.to.object_name.lower()))
+ table_output.append(');')
+ final_output.append('\n'.join(table_output))
+ return final_output
+get_sql_create.help_doc = "Prints the CREATE TABLE SQL statements for the given app(s)."
+get_sql_create.args = APP_ARGS
+
+def get_sql_delete(mod):
+ "Returns a list of the DROP TABLE SQL statements for the given module."
+ try:
+ cursor = db.db.cursor()
+ except:
+ cursor = None
+ output = []
+ for klass in mod._MODELS:
+ try:
+ if cursor is not None:
+ # Check whether the table exists.
+ cursor.execute("SELECT 1 FROM %s LIMIT 1" % klass._meta.db_table)
+ except:
+ # The table doesn't exist, so it doesn't need to be dropped.
+ pass
+ else:
+ output.append("DROP TABLE %s;" % klass._meta.db_table)
+ for klass in mod._MODELS:
+ opts = klass._meta
+ for f in opts.many_to_many:
+ try:
+ if cursor is not None:
+ cursor.execute("SELECT 1 FROM %s_%s LIMIT 1" % (opts.db_table, f.name))
+ except:
+ pass
+ else:
+ output.append("DROP TABLE %s_%s;" % (opts.db_table, f.name))
+ output.append("DELETE FROM packages WHERE label = '%s';" % mod._MODELS[0]._meta.app_label)
+ return output
+get_sql_delete.help_doc = "Prints the DROP TABLE SQL statements for the given app(s)."
+get_sql_delete.args = APP_ARGS
+
+def get_sql_reset(mod):
+ "Returns a list of the DROP TABLE SQL, then the CREATE TABLE SQL, for the given module."
+ return get_sql_delete(mod) + get_sql_all(mod)
+get_sql_reset.help_doc = "Prints the DROP TABLE SQL, then the CREATE TABLE SQL, for the given app(s)."
+get_sql_reset.args = APP_ARGS
+
+def get_sql_initial_data(mod):
+ "Returns a list of the initial INSERT SQL statements for the given module."
+ output = []
+ app_label = mod._MODELS[0]._meta.app_label
+ output.append(_get_packages_insert(app_label))
+ app_dir = os.path.normpath(os.path.join(os.path.dirname(mod.__file__), '../sql'))
+ for klass in mod._MODELS:
+ opts = klass._meta
+ # Add custom SQL, if it's available.
+ sql_file_name = os.path.join(app_dir, opts.module_name + '.sql')
+ if os.path.exists(sql_file_name):
+ fp = open(sql_file_name, 'r')
+ output.append(fp.read())
+ fp.close()
+ # Content types.
+ output.append(_get_contenttype_insert(opts))
+ # Permissions.
+ for codename, name in _get_all_permissions(opts):
+ output.append(_get_permission_insert(name, codename, opts))
+ return output
+get_sql_initial_data.help_doc = "Prints the initial INSERT SQL statements for the given app(s)."
+get_sql_initial_data.args = APP_ARGS
+
+def get_sql_sequence_reset(mod):
+ "Returns a list of the SQL statements to reset PostgreSQL sequences for the given module."
+ output = []
+ for klass in mod._MODELS:
+ for f in klass._meta.fields:
+ if isinstance(f, meta.AutoField):
+ output.append("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));" % (klass._meta.db_table, f.name, f.name, klass._meta.db_table))
+ return output
+get_sql_sequence_reset.help_doc = "Prints the SQL statements for resetting PostgreSQL sequences for the given app(s)."
+get_sql_sequence_reset.args = APP_ARGS
+
+def get_sql_indexes(mod):
+ "Returns a list of the CREATE INDEX SQL statements for the given module."
+ output = []
+ for klass in mod._MODELS:
+ for f in klass._meta.fields:
+ if f.db_index:
+ unique = f.unique and "UNIQUE " or ""
+ output.append("CREATE %sINDEX %s_%s ON %s (%s);" % \
+ (unique, klass._meta.db_table, f.name, klass._meta.db_table, f.name))
+ return output
+get_sql_indexes.help_doc = "Prints the CREATE INDEX SQL statements for the given app(s)."
+get_sql_indexes.args = APP_ARGS
+
+def get_sql_all(mod):
+ "Returns a list of CREATE TABLE SQL and initial-data insert for the given module."
+ return get_sql_create(mod) + get_sql_initial_data(mod)
+get_sql_all.help_doc = "Prints the CREATE TABLE and initial-data SQL statements for the given app(s)."
+get_sql_all.args = APP_ARGS
+
+def database_check(mod):
+ "Checks that everything is properly installed in the database for the given module."
+ cursor = db.db.cursor()
+ app_label = mod._MODELS[0]._meta.app_label
+
+ # Check that the package exists in the database.
+ cursor.execute("SELECT 1 FROM packages WHERE label = %s", [app_label])
+ if cursor.rowcount < 1:
+# sys.stderr.write("The '%s' package isn't installed.\n" % app_label)
+ print _get_packages_insert(app_label)
+
+ # Check that the permissions and content types are in the database.
+ perms_seen = {}
+ contenttypes_seen = {}
+ for klass in mod._MODELS:
+ opts = klass._meta
+ perms = _get_all_permissions(opts)
+ perms_seen.update(dict(perms))
+ contenttypes_seen[opts.module_name] = 1
+ for codename, name in perms:
+ cursor.execute("SELECT 1 FROM auth_permissions WHERE package = %s AND codename = %s", (app_label, codename))
+ if cursor.rowcount < 1:
+# sys.stderr.write("The '%s.%s' permission doesn't exist.\n" % (app_label, codename))
+ print _get_permission_insert(name, codename, opts)
+ cursor.execute("SELECT 1 FROM content_types WHERE package = %s AND python_module_name = %s", (app_label, opts.module_name))
+ if cursor.rowcount < 1:
+# sys.stderr.write("The '%s.%s' content type doesn't exist.\n" % (app_label, opts.module_name))
+ print _get_contenttype_insert(opts)
+
+ # Check that there aren't any *extra* permissions in the DB that the model
+ # doesn't know about.
+ cursor.execute("SELECT codename FROM auth_permissions WHERE package = %s", (app_label,))
+ for row in cursor.fetchall():
+ try:
+ perms_seen[row[0]]
+ except KeyError:
+# sys.stderr.write("A permission called '%s.%s' was found in the database but not in the model.\n" % (app_label, row[0]))
+ print "DELETE FROM auth_permissions WHERE package='%s' AND codename = '%s';" % (app_label, row[0])
+
+ # Check that there aren't any *extra* content types in the DB that the
+ # model doesn't know about.
+ cursor.execute("SELECT python_module_name FROM content_types WHERE package = %s", (app_label,))
+ for row in cursor.fetchall():
+ try:
+ contenttypes_seen[row[0]]
+ except KeyError:
+# sys.stderr.write("A content type called '%s.%s' was found in the database but not in the model.\n" % (app_label, row[0]))
+ print "DELETE FROM content_types WHERE package='%s' AND python_module_name = '%s';" % (app_label, row[0])
+database_check.help_doc = "Checks that everything is installed in the database for the given app(s) and prints SQL statements if needed."
+database_check.args = APP_ARGS
+
+def get_admin_index(mod):
+ "Returns admin-index template snippet (in list form) for the given module."
+ output = []
+ app_label = mod._MODELS[0]._meta.app_label
+ output.append('{%% if perms.%s %%}' % app_label)
+ output.append('<div class="module"><h2>%s</h2><table>' % app_label.title())
+ for klass in mod._MODELS:
+ if klass._meta.admin:
+ output.append(MODULE_TEMPLATE % {
+ 'app': app_label,
+ 'mod': klass._meta.module_name,
+ 'name': meta.capfirst(klass._meta.verbose_name_plural),
+ 'addperm': klass._meta.get_add_permission(),
+ 'changeperm': klass._meta.get_change_permission(),
+ })
+ output.append('</table></div>')
+ output.append('{% endif %}')
+ return output
+get_admin_index.help_doc = "Prints the admin-index template snippet for the given app(s)."
+get_admin_index.args = APP_ARGS
+
+def init():
+ "Initializes the database with auth and core."
+ auth = meta.get_app('auth')
+ core = meta.get_app('core')
+ try:
+ cursor = db.db.cursor()
+ for sql in get_sql_create(core) + get_sql_create(auth) + get_sql_initial_data(core) + get_sql_initial_data(auth):
+ cursor.execute(sql)
+ except Exception, e:
+ sys.stderr.write("Error: The database couldn't be initialized. Here's the full exception:\n%s\n" % e)
+ db.db.rollback()
+ sys.exit(1)
+ db.db.commit()
+init.args = ''
+
+def install(mod):
+ "Executes the equivalent of 'get_sql_all' in the current database."
+ sql_list = get_sql_all(mod)
+ try:
+ cursor = db.db.cursor()
+ for sql in sql_list:
+ cursor.execute(sql)
+ except Exception, e:
+ mod_name = mod.__name__[mod.__name__.rindex('.')+1:]
+ sys.stderr.write("""Error: %s couldn't be installed. Possible reasons:
+ * The database isn't running or isn't configured correctly.
+ * At least one of the database tables already exists.
+ * The SQL was invalid.
+Hint: Look at the output of '%s sqlall %s'. That's the SQL this command wasn't able to run.
+The full error: %s\n""" % \
+ (mod_name, __file__, mod_name, e))
+ db.db.rollback()
+ sys.exit(1)
+ db.db.commit()
+install.args = APP_ARGS
+
+def _start_helper(app_or_project, name, directory, other_name=''):
+ other = {'project': 'app', 'app': 'project'}[app_or_project]
+ if not _is_valid_dir_name(name):
+ sys.stderr.write("Error: %r is not a valid %s name. Please use only numbers, letters and underscores.\n" % (name, app_or_project))
+ sys.exit(1)
+ top_dir = os.path.join(directory, name)
+ try:
+ os.mkdir(top_dir)
+ except OSError, e:
+ sys.stderr.write("Error: %s\n" % e)
+ sys.exit(1)
+ template_dir = PROJECT_TEMPLATE_DIR % app_or_project
+ for d, subdirs, files in os.walk(template_dir):
+ relative_dir = d[len(template_dir)+1:].replace('%s_name' % app_or_project, name)
+ if relative_dir:
+ os.mkdir(os.path.join(top_dir, relative_dir))
+ for f in files:
+ fp_old = open(os.path.join(d, f), 'r')
+ fp_new = open(os.path.join(top_dir, relative_dir, f.replace('%s_name' % app_or_project, name)), 'w')
+ fp_new.write(fp_old.read().replace('{{ %s_name }}' % app_or_project, name).replace('{{ %s_name }}' % other, other_name))
+ fp_old.close()
+ fp_new.close()
+
+def startproject(project_name, directory):
+ "Creates a Django project for the given project_name in the given directory."
+ _start_helper('project', project_name, directory)
+startproject.help_doc = "Creates a Django project directory structure for the given project name in the current directory."
+startproject.args = "[projectname]"
+
+def startapp(app_name, directory):
+ "Creates a Django app for the given project_name in the given directory."
+ # Determine the project_name a bit naively -- by looking at the name of
+ # the parent directory.
+ project_dir = os.path.normpath(os.path.join(directory, '../'))
+ project_name = os.path.basename(project_dir)
+ _start_helper('app', app_name, directory, project_name)
+ settings_file = os.path.join(project_dir, 'settings/main.py')
+ if os.path.exists(settings_file):
+ try:
+ settings_contents = open(settings_file, 'r').read()
+ fp = open(settings_file, 'w')
+ except IOError:
+ pass
+ else:
+ settings_contents = re.sub(r'(?s)\b(INSTALLED_APPS\s*=\s*\()(.*?)\)', "\\1\n '%s',\\2)" % app_name, settings_contents)
+ fp.write(settings_contents)
+ fp.close()
+startapp.help_doc = "Creates a Django app directory structure for the given app name in the current directory."
+startapp.args = "[appname]"
+
+def usage():
+ sys.stderr.write("Usage: %s [action]\n" % sys.argv[0])
+
+ available_actions = ACTION_MAPPING.keys()
+ available_actions.sort()
+ sys.stderr.write("Available actions:\n")
+ for a in available_actions:
+ func = ACTION_MAPPING[a]
+ sys.stderr.write(" %s %s-- %s\n" % (a, func.args, getattr(func, 'help_doc', func.__doc__)))
+ sys.exit(1)
+
+ACTION_MAPPING = {
+ 'adminindex': get_admin_index,
+# 'dbcheck': database_check,
+ 'sql': get_sql_create,
+ 'sqlall': get_sql_all,
+ 'sqlclear': get_sql_delete,
+ 'sqlindexes': get_sql_indexes,
+ 'sqlinitialdata': get_sql_initial_data,
+ 'sqlreset': get_sql_reset,
+ 'sqlsequencereset': get_sql_sequence_reset,
+ 'startapp': startapp,
+ 'startproject': startproject,
+ 'init': init,
+ 'install': install,
+}
+
+if __name__ == "__main__":
+ try:
+ action = sys.argv[1]
+ except IndexError:
+ usage()
+ if not ACTION_MAPPING.has_key(action):
+ usage()
+ if action == 'init':
+ init()
+ sys.exit(0)
+ elif action in ('startapp', 'startproject'):
+ try:
+ name = sys.argv[2]
+ except IndexError:
+ usage()
+ ACTION_MAPPING[action](name, os.getcwd())
+ sys.exit(0)
+ elif action == 'dbcheck':
+ mod_list = meta.get_all_installed_modules()
+ else:
+ try:
+ mod_list = [meta.get_app(app_label) for app_label in sys.argv[2:]]
+ except ImportError, e:
+ sys.stderr.write("Error: %s. Are you sure your INSTALLED_APPS setting is correct?\n" % e)
+ sys.exit(1)
+ if not mod_list:
+ usage()
+ if action not in ('adminindex', 'dbcheck', 'install', 'sqlindexes'):
+ print "BEGIN;"
+ for mod in mod_list:
+ output = ACTION_MAPPING[action](mod)
+ if output:
+ print '\n'.join(output)
+ if action not in ('adminindex', 'dbcheck', 'install', 'sqlindexes'):
+ print "COMMIT;"
View
0  django/bin/profiling/__init__.py
No changes.
View
34 django/bin/profiling/gather_profile_stats.py
@@ -0,0 +1,34 @@
+"""
+gather_profile_stats.py /path/to/dir/of/profiles
+
+Note that the aggregated profiles must be read with pstats.Stats, not
+hotshot.stats (the formats are incompatible)
+"""
+
+from hotshot import stats
+import pstats
+import sys, os
+
+def gather_stats(p):
+ profiles = {}
+ for f in os.listdir(p):
+ if f.endswith('.agg.prof'):
+ path = f[:-9]
+ prof = pstats.Stats(os.path.join(p, f))
+ elif f.endswith('.prof'):
+ bits = f.split('.')
+ path = ".".join(bits[:-3])
+ prof = stats.load(os.path.join(p, f))
+ else:
+ continue
+ print "Processing %s" % f
+ if profiles.has_key(path):
+ profiles[path].add(prof)
+ else:
+ profiles[path] = prof
+ os.unlink(os.path.join(p, f))
+ for (path, prof) in profiles.items():
+ prof.dump_stats(os.path.join(p, "%s.agg.prof" % path))
+
+if __name__ == '__main__':
+ gather_stats(sys.argv[1])
View
22 django/bin/profiling/handler.py
@@ -0,0 +1,22 @@
+import hotshot, time, os
+from django.core.handler import CoreHandler
+
+PROFILE_DATA_DIR = "/var/log/cmsprofile/"
+
+def handler(req):
+ '''
+ Handler that uses hotshot to store profile data.
+
+ Stores profile data in PROFILE_DATA_DIR. Since hotshot has no way (that I
+ know of) to append profile data to a single file, each request gets its own
+ profile. The file names are in the format <url>.<n>.prof where <url> is
+ the request path with "/" replaced by ".", and <n> is a timestamp with
+ microseconds to prevent overwriting files.
+
+ Use the gather_profile_stats.py script to gather these individual request
+ profiles into aggregated profiles by request path.
+ '''
+ profname = "%s.%.3f.prof" % (req.uri.strip("/").replace('/', '.'), time.time())
+ profname = os.path.join(PROFILE_DATA_DIR, profname)
+ prof = hotshot.Profile(profname)
+ return prof.runcall(CoreHandler(), req)
View
45 django/bin/setup.py
@@ -0,0 +1,45 @@
+"""
+Usage:
+
+python setup.py bdist
+python setup.py sdist
+"""
+
+from distutils.core import setup
+import os
+
+# Whether to include the .py files, rather than just .pyc's. Doesn't do anything yet.
+INCLUDE_SOURCE = True
+
+# Determines which apps are bundled with the distribution.
+INSTALLED_APPS = ('auth', 'categories', 'comments', 'core', 'media', 'news', 'polls', 'registration', 'search', 'sms', 'staff')
+
+# First, lump together all the generic, core packages that need to be included.
+packages = [
+ 'django',
+ 'django.core',
+ 'django.templatetags',
+ 'django.utils',
+ 'django.views',
+]
+for a in INSTALLED_APPS:
+ for dirname in ('parts', 'templatetags', 'views'):
+ if os.path.exists('django/%s/%s/' % (dirname, a)):
+ packages.append('django.%s.%s' % (dirname, a))
+
+# Next, add individual modules.
+py_modules = [
+ 'django.cron.daily_cleanup',
+ 'django.cron.search_indexer',
+]
+py_modules += ['django.models.%s' % a for a in INSTALLED_APPS]
+
+setup(
+ name = 'django',
+ version = '1.0',
+ packages = packages,
+ py_modules = py_modules,
+ url = 'http://www.ljworld.com/',
+ author = 'World Online',
+ author_email = 'cms-support@ljworld.com',
+)
View
36 django/bin/validate.py
@@ -0,0 +1,36 @@
+from django.core import meta
+
+def validate_app(app_label):
+ mod = meta.get_app(app_label)
+ for klass in mod._MODELS:
+ try:
+ validate_class(klass)
+ except AssertionError, e:
+ print e
+
+def validate_class(klass):
+ opts = klass._meta
+ # Fields.
+ for f in opts.fields:
+ if isinstance(f, meta.ManyToManyField):
+ assert isinstance(f.rel, meta.ManyToMany), "ManyToManyField %s should have 'rel' set to a ManyToMany instance." % f.name
+ # Inline related objects.
+ for rel_opts, rel_field in opts.get_inline_related_objects():
+ assert len([f for f in rel_opts.fields if f.core]) > 0, "At least one field in %s should have core=True, because it's being edited inline by %s." % (rel_opts.object_name, opts.object_name)
+ # All related objects.
+ related_apps_seen = []
+ for rel_opts, rel_field in opts.get_all_related_objects():
+ if rel_opts in related_apps_seen:
+ assert rel_field.rel.related_name is not None, "Relationship in field %s.%s needs to set 'related_name' because more than one %s object is referenced in %s." % (rel_opts.object_name, rel_field.name, opts.object_name, rel_opts.object_name)
+ related_apps_seen.append(rel_opts)
+ # Etc.
+ if opts.admin is not None:
+ assert opts.admin.ordering or opts.ordering, "%s needs to set 'ordering' on either its 'admin' or its model, because it has 'admin' set." % opts.object_name
+
+if __name__ == "__main__":
+ import sys
+ try:
+ validate_app(sys.argv[1])
+ except IndexError:
+ sys.stderr.write("Usage: %s [appname]\n" % __file__)
+ sys.exit(1)
View
0  django/conf/__init__.py
No changes.
View
0  django/conf/app_template/__init__.py
No changes.
View
1  django/conf/app_template/models/__init__.py
@@ -0,0 +1 @@
+__all__ = ['{{ app_name }}']
View
3  django/conf/app_template/models/app_name.py
@@ -0,0 +1,3 @@
+from django.core import meta
+
+# Create your models here.
View
0  django/conf/app_template/urls/__init__.py
No changes.
View
5 django/conf/app_template/urls/app_name.py
@@ -0,0 +1,5 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('{{ project_name }}.apps.{{ app_name }}.views',
+# (r'', ''),
+)
View
0  django/conf/app_template/views/__init__.py
No changes.
View
199 django/conf/global_settings.py
@@ -0,0 +1,199 @@
+# Default Django settings. Override these with settings in the module
+# pointed-to by the DJANGO_SETTINGS_MODULE environment variable.
+
+import re
+
+####################
+# CORE #
+####################
+
+DEBUG = False
+
+# Whether to use the "Etag" header. This saves bandwidth but slows down performance.
+USE_ETAGS = False
+
+# people who get code error notifications
+ADMINS = (('Adrian Holovaty','aholovaty@ljworld.com'), ('Jacob Kaplan-Moss', 'jacob@lawrence.com'))
+
+# These IP addresses:
+# * See debug comments, when DEBUG is true
+# * Receive x-headers
+INTERNAL_IPS = (
+ '24.124.4.220', # World Online offices
+ '24.124.1.4', # https://admin.6newslawrence.com/
+ '24.148.30.138', # Adrian home
+ '127.0.0.1', # localhost
+)
+
+# Local time zone for this installation. All choices can be found here:
+# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
+TIME_ZONE = 'America/Chicago'
+
+# Language code for this installation. All choices can be found here:
+# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
+# http://blogs.law.harvard.edu/tech/stories/storyReader$15
+LANGUAGE_CODE = 'en-us'
+
+# Not-necessarily-technical managers of the site. They get broken link
+# notifications and other various e-mails.
+MANAGERS = ADMINS
+
+# which e-mail address error messages come from
+SERVER_EMAIL = None
+
+# Whether to send broken-link e-mails
+SEND_BROKEN_LINK_EMAILS = True
+
+# postgres database connection info
+DATABASE_ENGINE = 'postgresql'
+DATABASE_NAME = 'cms'
+DATABASE_USER = 'apache'
+DATABASE_PASSWORD = ''
+DATABASE_HOST = '' # set to empty string for localhost
+
+# host for sending e-mail
+EMAIL_HOST = 'localhost'
+
+# name of the session cookie
+AUTH_SESSION_COOKIE = 'rizzo'
+
+# name of the authorization profile module (below django.apps)
+AUTH_PROFILE_MODULE = ''
+
+# list of locations of the template source files, in search order
+TEMPLATE_DIRS = []
+
+# default e-mail address to use for various automated correspondence from the site managers
+DEFAULT_FROM_EMAIL = 'webmaster@ljworld.com'
+
+# whether to append trailing slashes to URLs
+APPEND_SLASH = True
+
+# whether to prepend the "www." subdomain to URLs
+PREPEND_WWW = False
+
+# list of regular expressions representing User-Agent strings that are not
+# allowed to visit any page, CMS-wide. Use this for bad robots/crawlers.
+DISALLOWED_USER_AGENTS = (
+ re.compile(r'^NaverBot.*'),
+ re.compile(r'^EmailSiphon.*'),
+ re.compile(r'^SiteSucker.*'),
+ re.compile(r'^sohu-search')
+)
+
+ABSOLUTE_URL_OVERRIDES = {}
+
+# list of allowed prefixes for the {% ssi %} tag
+ALLOWED_INCLUDE_ROOTS = ('/home/html',)
+
+# if this is a admin settings module, this should be a list of
+# settings modules for which this admin is an admin for
+ADMIN_FOR = []
+
+# 404s that may be ignored
+IGNORABLE_404_STARTS = ('/cgi-bin/', '/_vti_bin', '/_vti_inf')
+IGNORABLE_404_ENDS = ('mail.pl', 'mailform.pl', 'mail.cgi', 'mailform.cgi', 'favicon.ico', '.php')
+
+##############
+# Middleware #
+##############
+
+# List of middleware classes to use. Order is important; in the request phase,
+# this middleware classes will be applied in the order given, and in the
+# response phase the middleware will be applied in reverse order.
+MIDDLEWARE_CLASSES = (
+ "django.middleware.common.CommonMiddleware",
+ "django.middleware.doc.XViewMiddleware",
+)
+
+#########
+# CACHE #
+#########
+
+# The cache backend to use. See the docstring in django.core.cache for the
+# values this can be set to.
+CACHE_BACKEND = 'simple://'
+
+####################
+# REGISTRATION #
+####################
+
+# E-mail addresses at these domains cannot sign up for accounts
+BANNED_EMAIL_DOMAINS = [
+ 'mailinator.com', 'dodgeit.com', 'spamgourmet.com', 'mytrashmail.com'
+]
+REGISTRATION_COOKIE_DOMAIN = None # set to a string like ".lawrence.com", or None for standard domain cookie
+
+# If this is set to True, users will be required to fill out their profile
+# (defined by AUTH_PROFILE_MODULE) before they will be allowed to create
+# an account.
+REGISTRATION_REQUIRES_PROFILE = False
+
+####################
+# COMMENTS #
+####################
+
+COMMENTS_ALLOW_PROFANITIES = False
+
+# The group ID that designates which users are banned.
+# Set to None if you're not using it.
+COMMENTS_BANNED_USERS_GROUP = 19
+
+# The group ID that designates which users can moderate comments.
+# Set to None if you're not using it.
+COMMENTS_MODERATORS_GROUP = 20
+
+# The group ID that designates the users whose comments should be e-mailed to MANAGERS.
+# Set to None if you're not using it.
+COMMENTS_SKETCHY_USERS_GROUP = 22
+
+# The system will e-mail MANAGERS the first COMMENTS_FIRST_FEW comments by each
+# user. Set this to 0 if you want to disable it.
+COMMENTS_FIRST_FEW = 10
+
+BANNED_IPS = (
+ # Dupont Stainmaster / GuessWho / a variety of other names (back when we had free comments)
+ '204.94.104.99', '66.142.59.23', '220.196.165.142',
+ # (Unknown)
+ '64.65.191.117',
+# # Jimmy_Olsen / Clark_Kent / Bruce_Wayne
+# # Unbanned on 2005-06-17, because other people want to register from this address.
+# '12.106.111.10',
+ # hoof_hearted / hugh_Jass / Ferd_Burfel / fanny_farkel
+ '24.124.72.20', '170.135.241.46',
+ # Zac_McGraw
+ '198.74.20.74', '198.74.20.75',
+)
+
+####################
+# BLOGS #
+####################
+
+# E-mail addresses to notify when a new blog entry is posted live
+BLOGS_EMAILS_TO_NOTIFY = []
+
+####################
+# PLACES #
+####################
+
+# A list of IDs -- *as integers, not strings* -- that are considered the "main"
+# cities served by this installation. Probably just one.
+MAIN_CITY_IDS = (1,) # Lawrence
+
+# A list of IDs -- *as integers, not strings* -- that are considered "local" by
+# this installation.
+LOCAL_CITY_IDS = (1, 3) # Lawrence and Kansas City, MO
+
+####################
+# THUMBNAILS #
+####################
+
+THUMB_ALLOWED_WIDTHS = (90, 120, 180, 240, 450)
+
+####################
+# VARIOUS ROOTS #
+####################
+
+# This is the new media root and URL! Use it, and only it!
+MEDIA_ROOT = '/home/media/media.lawrence.com/'
+MEDIA_URL = 'http://media.lawrence.com'
View
0  django/conf/project_template/__init__.py
No changes.
View
0  django/conf/project_template/apps/__init__.py
No changes.
View
0  django/conf/project_template/settings/__init__.py
No changes.
View
31 django/conf/project_template/settings/main.py
@@ -0,0 +1,31 @@
+# Django settings for {{ app_name }} project.
+
+DEBUG = False
+
+ADMINS = (
+ # ('Your Name', 'your_email@domain.com'),
+)
+
+MANAGERS = ADMINS
+
+LANGUAGE_CODE = 'en-us'
+
+DATABASE_ENGINE = 'postgresql' # Either 'postgresql' or 'mysql'.
+DATABASE_NAME = ''
+DATABASE_USER = ''
+DATABASE_HOST = '' # Set to empty string for localhost.
+
+# 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.
+# Example: "http://media.lawrence.com"
+MEDIA_URL = ''
+
+TEMPLATE_DIRS = (
+ # Put strings here, like "/home/html/django_templates".
+)
+
+INSTALLED_APPS = (
+)
View
42 django/conf/settings.py
@@ -0,0 +1,42 @@
+"""
+Settings and configuration for Django.
+
+Values will be read from the module specified by the DJANGO_SETTINGS_MODULE environment
+variable, and then from django.conf.global_settings; see the global settings file for
+a list of all possible variables.
+"""
+
+import os
+import sys
+from django.conf import global_settings
+
+# get a reference to this module (why isn't there a __module__ magic var?)
+me = sys.modules[__name__]
+
+# update this dict from global settings (but only for ALL_CAPS settings)
+for setting in dir(global_settings):
+ if setting == setting.upper():
+ setattr(me, setting, getattr(global_settings, setting))
+
+# try to load DJANGO_SETTINGS_MODULE
+try:
+ mod = __import__(os.environ['DJANGO_SETTINGS_MODULE'], '', '', [''])
+except (KeyError, ImportError, ValueError):
+ pass
+else:
+ for setting in dir(mod):
+ if setting == setting.upper():
+ setattr(me, setting, getattr(mod, setting))
+
+# save DJANGO_SETTINGS_MODULE in case anyone in the future cares
+me.SETTINGS_MODULE = os.environ.get('DJANGO_SETTINGS_MODULE', '')
+
+# move the time zone info into os.environ
+os.environ['TZ'] = me.TIME_ZONE
+
+# finally, clean up my namespace
+for k in dir(me):
+ if not k.startswith('_') and k != 'me' and k != k.upper():
+ delattr(me, k)
+del me, k
+
View
0  django/conf/urls/__init__.py
No changes.
View
56 django/conf/urls/admin.py
@@ -0,0 +1,56 @@
+from django.conf.urls.defaults import *
+from django.conf.settings import INSTALLED_APPS
+
+urlpatterns = (
+ ('^/?$', 'django.views.admin.main.index'),
+ ('^logout/$', 'django.views.admin.main.logout'),
+ ('^password_change/$', 'django.views.registration.passwords.password_change'),
+ ('^password_change/done/$', 'django.views.registration.passwords.password_change_done'),
+ ('^template_validator/$', 'django.views.admin.template.template_validator'),
+
+ # Documentation
+ ('^doc/$', 'django.views.admin.doc.doc_index'),
+ ('^doc/bookmarklets/$', 'django.views.admin.doc.bookmarklets'),
+ ('^doc/tags/$', 'django.views.admin.doc.template_tag_index'),
+ ('^doc/filters/$', 'django.views.admin.doc.template_filter_index'),
+ ('^doc/views/$', 'django.views.admin.doc.view_index'),
+ ('^doc/views/jump/$', 'django.views.admin.doc.jump_to_view'),
+ ('^doc/views/(?P<view>[^/]+)/$', 'django.views.admin.doc.view_detail'),
+ ('^doc/models/$', 'django.views.admin.doc.model_index'),
+ ('^doc/models/(?P<model>[^/]+)/$', 'django.views.admin.doc.model_detail'),
+)
+
+if 'ellington.events' in INSTALLED_APPS:
+ urlpatterns += (
+ ("^events/usersubmittedevents/(?P<object_id>\d+)/$", 'ellington.events.views.admin.user_submitted_event_change_stage'),
+ ("^events/usersubmittedevents/(?P<object_id>\d+)/delete/$", 'ellington.events.views.admin.user_submitted_event_delete_stage'),
+ )
+
+if 'ellington.news' in INSTALLED_APPS:
+ urlpatterns += (
+ ("^stories/preview/$", 'ellington.news.views.admin.story_preview'),
+ ("^stories/js/inlinecontrols/$", 'ellington.news.views.admin.inlinecontrols_js'),
+ ("^stories/js/inlinecontrols/(?P<label>[-\w]+)/$", 'ellington.news.views.admin.inlinecontrols_js_specific'),
+ )
+
+if 'ellington.alerts' in INSTALLED_APPS:
+ urlpatterns += (
+ ("^alerts/send/$", 'ellington.alerts.views.admin.send_alert_form'),
+ ("^alerts/send/do/$", 'ellington.alerts.views.admin.send_alert_action'),
+ )
+
+if 'ellington.media' in INSTALLED_APPS:
+ urlpatterns += (
+ ('^media/photos/caption/(?P<photo_id>\d+)/$', 'ellington.media.views.admin.get_exif_caption'),
+ )
+
+urlpatterns += (
+ # Metasystem admin pages
+ ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/$', 'django.views.admin.main.change_list'),
+ ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.views.admin.main.add_stage'),
+ ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>\d+)/$', 'django.views.admin.main.change_stage'),
+ ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>\d+)/delete/$', 'django.views.admin.main.delete_stage'),
+ ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>\d+)/history/$', 'django.views.admin.main.history'),
+ ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/jsvalidation/$', 'django.views.admin.jsvalidation.jsvalidation'),
+)
+urlpatterns = patterns('', *urlpatterns)
View
6 django/conf/urls/admin_password_reset.py
@@ -0,0 +1,6 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('django.views',
+ (r'^/?$', 'registration.passwords.password_reset', {'is_admin_site' : True}),
+ (r'^done/$', 'registration.passwords.password_reset_done'),
+)
View
12 django/conf/urls/comments.py
@@ -0,0 +1,12 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('django.views',
+ (r'^post/$', 'comments.comments.post_comment'),
+ (r'^postfree/$', 'comments.comments.post_free_comment'),
+ (r'^posted/$', 'comments.comments.comment_was_posted'),
+ (r'^karma/vote/(?P<comment_id>\d+)/(?P<vote>up|down)/$', 'comments.karma.vote'),
+ (r'^flag/(?P<comment_id>\d+)/$', 'comments.userflags.flag'),
+ (r'^flag/(?P<comment_id>\d+)/done/$', 'comments.userflags.flag_done'),
+ (r'^delete/(?P<comment_id>\d+)/$', 'comments.userflags.delete'),
+ (r'^delete/(?P<comment_id>\d+)/done/$', 'comments.userflags.delete_done'),
+)
View
17 django/conf/urls/defaults.py
@@ -0,0 +1,17 @@
+from django.core.urlresolvers import RegexURLMultiplePattern, RegexURLPattern
+
+__all__ = ['handler404', 'handler500', 'include', 'patterns']
+
+handler404 = 'django.views.defaults.page_not_found'
+handler500 = 'django.views.defaults.server_error'
+
+include = lambda urlconf_module: [urlconf_module]
+
+def patterns(prefix, *tuples):
+ pattern_list = []
+ for t in tuples:
+ if type(t[1]) == list:
+ pattern_list.append(RegexURLMultiplePattern(t[0], t[1][0]))
+ else:
+ pattern_list.append(RegexURLPattern(t[0], prefix and (prefix + '.' + t[1]) or t[1], *t[2:]))
+ return pattern_list
View
5 django/conf/urls/flatfiles.py
@@ -0,0 +1,5 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('django.views',
+ (r'^(?P<url>.*)$', 'core.flatfiles.flat_file'),
+)
View
19 django/conf/urls/registration.py
@@ -0,0 +1,19 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('',
+ (r'^login/$', 'django.views.auth.login.login'),
+ (r'^logout/$', 'django.views.auth.login.logout'),
+ (r'^login_another/$', 'django.views.auth.login.logout_then_login'),
+
+ (r'^register/$', 'ellington.registration.views.registration.signup'),
+ (r'^register/(?P<challenge_string>\w{32})/$', 'ellington.registration.views.registration.register_form'),
+
+ (r'^profile/$', 'ellington.registration.views.profile.profile'),
+ (r'^profile/welcome/$', 'ellington.registration.views.profile.profile_welcome'),
+ (r'^profile/edit/$', 'ellington.registration.views.profile.edit_profile'),
+
+ (r'^password_reset/$', 'django.views.registration.passwords.password_reset'),
+ (r'^password_reset/done/$', 'django.views.registration.passwords.password_reset_done'),
+ (r'^password_change/$', 'django.views.registration.passwords.password_change'),
+ (r'^password_change/done/$', 'django.views.registration.passwords.password_change_done'),
+)
View
6 django/conf/urls/rss.py
@@ -0,0 +1,6 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('django.views',
+ (r'^(?P<slug>\w+)/$', 'rss.rss.feed'),
+ (r'^(?P<slug>\w+)/(?P<param>[\w/]+)/$', 'rss.rss.feed'),
+)
View
5 django/conf/urls/shortcut.py
@@ -0,0 +1,5 @@
+from django.conf.urls.defaults import *
+
+urlpatterns = patterns('django.views',
+ (r'^(?P<content_type_id>\d+)/(?P<object_id>\d+)/$', 'defaults.shortcut'),
+)
View
0  django/core/__init__.py
No changes.
View
255 django/core/cache.py
@@ -0,0 +1,255 @@
+"""
+Caching framework.
+
+This module defines set of cache backends that all conform to a simple API.
+In a nutshell, a cache is a set of values -- which can be any object that
+may be pickled -- identified by string keys. For the complete API, see
+the abstract Cache object, below.
+
+Client code should not access a cache backend directly; instead
+it should use the get_cache() function. This function will look at
+settings.CACHE_BACKEND and use that to create and load a cache object.
+
+The CACHE_BACKEND setting is a quasi-URI; examples are:
+
+ memcached://127.0.0.1:11211/ A memcached backend; the server is running
+ on localhost port 11211.
+
+ pgsql://tablename/ A pgsql backend (the pgsql backend uses
+ the same database/username as the rest of
+ the CMS, so only a table name is needed.)
+
+ file:///var/tmp/django.cache/ A file-based cache at /var/tmp/django.cache
+
+ simple:/// A simple single-process memory cache; you
+ probably don't want to use this except for
+ testing. Note that this cache backend is
+ NOT threadsafe!
+
+All caches may take arguments; these are given in query-string style. Valid
+arguments are:
+
+ timeout
+ Default timeout, in seconds, to use for the cache. Defaults
+ to 5 minutes (300 seconds).
+
+ max_entries
+ For the simple, file, and database backends, the maximum number of
+ entries allowed in the cache before it is cleaned. Defaults to
+ 300.
+
+ cull_percentage
+ The percentage of entries that are culled when max_entries is reached.
+ The actual percentage is 1/cull_percentage, so set cull_percentage=3 to
+ cull 1/3 of the entries when max_entries is reached.
+
+ A value of 0 for cull_percentage means that the entire cache will be
+ dumped when max_entries is reached. This makes culling *much* faster
+ at the expense of more cache misses.
+
+For example:
+
+ memcached://127.0.0.1:11211/?timeout=60
+ pgsql://tablename/?timeout=120&max_entries=500&cull_percentage=4
+
+Invalid arguments are silently ignored, as are invalid values of known
+arguments.
+
+So far, only the memcached and simple backend have been implemented; backends
+using postgres, and file-system storage are planned.
+"""
+
+##############
+# Exceptions #
+##############
+
+class InvalidCacheBackendError(Exception):
+ pass
+
+################################
+# Abstract base implementation #
+################################
+
+class _Cache:
+
+ def __init__(self, params):
+ timeout = params.get('timeout', 300)
+ try:
+ timeout = int(timeout)
+ except (ValueError, TypeError):
+ timeout = 300
+ self.default_timeout = timeout
+
+ def get(self, key, default=None):
+ '''
+ Fetch a given key from the cache. If the key does not exist, return
+ default, which itself defaults to None.
+ '''
+ raise NotImplementedError
+
+ def set(self, key, value, timeout=None):
+ '''
+ Set a value in the cache. If timeout is given, that timeout will be
+ used for the key; otherwise the default cache timeout will be used.
+ '''
+ raise NotImplementedError
+
+ def delete(self, key):
+ '''
+ Delete a key from the cache, failing silently.
+ '''
+ raise NotImplementedError
+
+ def get_many(self, keys):
+ '''
+ Fetch a bunch of keys from the cache. For certain backends (memcached,
+ pgsql) this can be *much* faster when fetching multiple values.
+
+ Returns a dict mapping each key in keys to its value. If the given
+ key is missing, it will be missing from the response dict.
+ '''
+ d = {}
+ for k in keys:
+ val = self.get(k)
+ if val is not None:
+ d[k] = val
+ return d
+
+ def has_key(self, key):
+ '''
+ Returns True if the key is in the cache and has not expired.
+ '''
+ return self.get(key) is not None
+
+###########################
+# memcached cache backend #
+###########################
+
+try:
+ import memcache
+except ImportError:
+ _MemcachedCache = None
+else:
+ class _MemcachedCache(_Cache):
+ """Memcached cache backend."""
+
+ def __init__(self, server, params):
+ _Cache.__init__(self, params)
+ self._cache = memcache.Client([server])
+
+ def get(self, key, default=None):
+ val = self._cache.get(key)
+ if val is None:
+ return default
+ else:
+ return val
+
+ def set(self, key, value, timeout=0):
+ self._cache.set(key, value, timeout)
+
+ def delete(self, key):
+ self._cache.delete(key)
+
+ def get_many(self, keys):
+ return self._cache.get_multi(keys)
+
+##################################
+# Single-process in-memory cache #
+##################################
+
+import time
+
+class _SimpleCache(_Cache):
+ """Simple single-process in-memory cache"""
+
+ def __init__(self, host, params):
+ _Cache.__init__(self, params)
+ self._cache = {}
+ self._expire_info = {}
+
+ max_entries = params.get('max_entries', 300)
+ try:
+ self._max_entries = int(max_entries)
+ except (ValueError, TypeError):
+ self._max_entries = 300
+
+ cull_frequency = params.get('cull_frequency', 3)
+ try:
+ self._cull_frequency = int(cull_frequency)
+ except (ValueError, TypeError):
+ self._cull_frequency = 3
+
+ def get(self, key, default=None):
+ now = time.time()
+ exp = self._expire_info.get(key, now)
+ if exp is not None and exp < now:
+ del self._cache[key]
+ del self._expire_info[key]
+ return default
+ else:
+ return self._cache.get(key, default)
+
+ def set(self, key, value, timeout=None):
+ if len(self._cache) >= self._max_entries:
+ self._cull()
+ if timeout is None:
+ timeout = self.default_timeout
+ self._cache[key] = value
+ self._expire_info[key] = time.time() + timeout
+
+ def delete(self, key):
+ try:
+ del self._cache[key]
+ except KeyError:
+ pass
+ try:
+ del self._expire_info[key]
+ except KeyError:
+ pass
+
+ def has_key(self, key):
+ return self._cache.has_key(key)
+
+ def _cull(self):
+ if self._cull_frequency == 0:
+ self._cache.clear()
+ self._expire_info.clear()
+ else:
+ doomed = [k for (i, k) in enumerate(self._cache) if i % self._cull_frequency == 0]
+ for k in doomed:
+ self.delete(k)
+
+##########################################
+# Read settings and load a cache backend #
+##########################################
+
+from cgi import parse_qsl
+
+_BACKENDS = {
+ 'memcached' : _MemcachedCache,
+ 'simple' : _SimpleCache,
+}
+
+def get_cache(backend_uri):
+ if backend_uri.find(':') == -1:
+ raise InvalidCacheBackendError("Backend URI must start with scheme://")
+ scheme, rest = backend_uri.split(':', 1)
+ if not rest.startswith('//'):
+ raise InvalidCacheBackendError("Backend URI must start with scheme://")
+ if scheme not in _BACKENDS.keys():
+ raise InvalidCacheBackendError("%r is not a valid cache backend" % scheme)
+
+ host = rest[2:]
+ qpos = rest.find('?')
+ if qpos != -1:
+ params = dict(parse_qsl(rest[qpos+1:]))
+ host = rest[:qpos]
+ else:
+ params = {}
+ if host.endswith('/'):
+ host = host[:-1]
+
+ return _BACKENDS[scheme](host, params)
+
+from django.conf.settings import CACHE_BACKEND
+cache = get_cache(CACHE_BACKEND)
View
28 django/core/db/__init__.py
@@ -0,0 +1,28 @@
+"""
+This is the core database connection.
+
+All CMS code assumes database SELECT statements cast the resulting values as such:
+ * booleans are mapped to Python booleans
+ * dates are mapped to Python datetime.date objects
+ * times are mapped to Python datetime.time objects
+ * timestamps are mapped to Python datetime.datetime objects
+
+Right now, we're handling this by using psycopg's custom typecast definitions.
+If we move to a different database module, we should ensure that it either
+performs the appropriate typecasting out of the box, or that it has hooks that
+let us do that.
+"""
+
+from django.conf.settings import DATABASE_ENGINE
+
+dbmod = __import__('django.core.db.backends.%s' % DATABASE_ENGINE, '', '', [''])
+
+DatabaseError = dbmod.DatabaseError
+db = dbmod.DatabaseWrapper()
+dictfetchone = dbmod.dictfetchone
+dictfetchmany = dbmod.dictfetchmany
+dictfetchall = dbmod.dictfetchall
+dictfetchall = dbmod.dictfetchall
+get_last_insert_id = dbmod.get_last_insert_id
+OPERATOR_MAPPING = dbmod.OPERATOR_MAPPING
+DATA_TYPES = dbmod.DATA_TYPES
View
0  django/core/db/backends/__init__.py
No changes.
View
107 django/core/db/backends/mysql.py
@@ -0,0 +1,107 @@
+"""
+MySQL database backend for Django.
+
+Requires MySQLdb: http://sourceforge.net/projects/mysql-python
+"""
+
+from django.core.db import base, typecasts
+import MySQLdb as Database
+from MySQLdb.converters import conversions
+from MySQLdb.constants import FIELD_TYPE
+import types
+
+DatabaseError = Database.DatabaseError
+
+django_conversions = conversions.copy()
+django_conversions.update({
+ types.BooleanType: typecasts.rev_typecast_boolean,
+ FIELD_TYPE.DATETIME: typecasts.typecast_timestamp,
+ FIELD_TYPE.DATE: typecasts.typecast_date,
+ FIELD_TYPE.TIME: typecasts.typecast_time,
+})
+
+class DatabaseWrapper:
+ def __init__(self):
+ self.connection = None
+ self.queries = []
+
+ def cursor(self):
+ from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PASSWORD, DEBUG
+ if self.connection is None:
+ self.connection = Database.connect(user=DATABASE_USER, db=DATABASE_NAME,
+ passwd=DATABASE_PASSWORD, host=DATABASE_HOST, conv=django_conversions)
+ if DEBUG:
+ return base.CursorDebugWrapper(self.connection.cursor(), self)
+ return self.connection.cursor()
+
+ def commit(self):
+ pass
+
+ def rollback(self):
+ pass
+
+ def close(self):
+ if self.connection is not None:
+ self.connection.close()
+ self.connection = None
+
+def dictfetchone(cursor):
+ "Returns a row from the cursor as a dict"
+ raise NotImplementedError
+
+def dictfetchmany(cursor, number):
+ "Returns a certain number of rows from a cursor as a dict"
+ raise NotImplementedError
+
+def dictfetchall(cursor):
+ "Returns all rows from a cursor as a dict"
+ raise NotImplementedError
+
+def get_last_insert_id(cursor, table_name, pk_name):
+ cursor.execute("SELECT LAST_INSERT_ID()")
+ return cursor.fetchone()[0]
+
+OPERATOR_MAPPING = {
+ 'exact': '=',
+ 'iexact': 'LIKE',
+ 'contains': 'LIKE',
+ 'icontains': 'LIKE',
+ 'ne': '!=',
+ 'gt': '>',
+ 'gte': '>=',
+ 'lt': '<',
+ 'lte': '<=',
+ 'startswith': 'LIKE',
+ 'endswith': 'LIKE'
+}
+
+# This dictionary maps Field objects to their associated MySQL column
+# types, as strings. Column-type strings can contain format strings; they'll
+# be interpolated against the values of Field.__dict__ before being output.
+# If a column type is set to None, it won't be included in the output.
+DATA_TYPES = {
+ 'AutoField': 'mediumint(9) auto_increment',
+ 'BooleanField': 'bool',
+ 'CharField': 'varchar(%(maxlength)s)',
+ 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
+ 'DateField': 'date',
+ 'DateTimeField': 'datetime',
+ 'EmailField': 'varchar(75)',
+ 'FileField': 'varchar(100)',
+ 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)',
+ 'ImageField': 'varchar(100)',
+ 'IntegerField': 'integer',
+ 'IPAddressField': 'char(15)',
+ 'ManyToManyField': None,
+ 'NullBooleanField': 'bool',
+ 'PhoneNumberField': 'varchar(20)',
+ 'PositiveIntegerField': 'integer UNSIGNED',
+ 'PositiveSmallIntegerField': 'smallint UNSIGNED',
+ 'SlugField': 'varchar(50)',
+ 'SmallIntegerField': 'smallint',
+ 'TextField': 'text',
+ 'TimeField': 'time',
+ 'URLField': 'varchar(200)',
+ 'USStateField': 'varchar(2)',
+ 'XMLField': 'text',
+}
View
109 django/core/db/backends/postgresql.py
@@ -0,0 +1,109 @@
+"""
+PostgreSQL database backend for Django.
+
+Requires psycopg 1: http://initd.org/projects/psycopg1
+"""
+
+from django.core.db import base, typecasts
+import psycopg as Database
+
+DatabaseError = Database.DatabaseError
+
+class DatabaseWrapper:
+ def __init__(self):
+ self.connection = None
+ self.queries = []
+
+ def cursor(self):
+ from django.conf.settings import DATABASE_USER, DATABASE_NAME, DATABASE_HOST, DATABASE_PASSWORD, DEBUG, TIME_ZONE
+ if self.connection is None:
+ # Note that "host=" has to be last, because it might be blank.
+ self.connection = Database.connect("user=%s dbname=%s password=%s host=%s" % \
+ (DATABASE_USER, DATABASE_NAME, DATABASE_PASSWORD, DATABASE_HOST))
+ self.connection.set_isolation_level(1) # make transactions transparent to all cursors
+ cursor = self.connection.cursor()
+ cursor.execute("SET TIME ZONE %s", [TIME_ZONE])
+ if DEBUG:
+ return base.CursorDebugWrapper(cursor, self)
+ return cursor
+
+ def commit(self):
+ return self.connection.commit()
+
+ def rollback(self):
+ if self.connection:
+ return self.connection.rollback()
+
+ def close(self):
+ if self.connection is not None:
+ self.connection.close()
+ self.connection = None
+
+def dictfetchone(cursor):
+ "Returns a row from the cursor as a dict"
+ return cursor.dictfetchone()
+
+def dictfetchmany(cursor, number):
+ "Returns a certain number of rows from a cursor as a dict"
+ return cursor.dictfetchmany(number)
+
+def dictfetchall(cursor):
+ "Returns all rows from a cursor as a dict"
+ return cursor.dictfetchall()
+
+def get_last_insert_id(cursor, table_name, pk_name):
+ cursor.execute("SELECT CURRVAL('%s_%s_seq')" % (table_name, pk_name))
+ return cursor.fetchone()[0]
+
+# Register these custom typecasts, because Django expects dates/times to be
+# in Python's native (standard-library) datetime/time format, whereas psycopg
+# use mx.DateTime by default.
+Database.register_type(Database.new_type((1082,), "DATE", typecasts.typecast_date))
+Database.register_type(Database.new_type((1083,1266), "TIME", typecasts.typecast_time))
+Database.register_type(Database.new_type((1114,1184), "TIMESTAMP", typecasts.typecast_timestamp))
+Database.register_type(Database.new_type((16,), "BOOLEAN", typecasts.typecast_boolean))
+
+OPERATOR_MAPPING = {
+ 'exact': '=',
+ 'iexact': 'ILIKE',
+ 'contains': 'LIKE',
+ 'icontains': 'ILIKE',
+ 'ne': '!=',
+ 'gt': '>',
+ 'gte': '>=',
+ 'lt': '<',
+ 'lte': '<=',
+ 'startswith': 'LIKE',
+ 'endswith': 'LIKE'
+}
+
+# This dictionary maps Field objects to their associated PostgreSQL column
+# types, as strings. Column-type strings can contain format strings; they'll
+# be interpolated against the values of Field.__dict__ before being output.
+# If a column type is set to None, it won't be included in the output.
+DATA_TYPES = {
+ 'AutoField': 'serial',
+ 'BooleanField': 'boolean',
+ 'CharField': 'varchar(%(maxlength)s)',
+ 'CommaSeparatedIntegerField': 'varchar(%(maxlength)s)',
+ 'DateField': 'date',
+ 'DateTimeField': 'timestamp with time zone',
+ 'EmailField': 'varchar(75)',
+ 'FileField': 'varchar(100)',
+ 'FloatField': 'numeric(%(max_digits)s, %(decimal_places)s)',
+ 'ImageField': 'varchar(100)',
+ 'IntegerField': 'integer',
+ 'IPAddressField': 'inet',
+ 'ManyToManyField': None,
+ 'NullBooleanField': 'boolean',
+ 'PhoneNumberField': 'varchar(20)',
+ 'PositiveIntegerField': 'integer CHECK (%(name)s >= 0)',
+ 'PositiveSmallIntegerField': 'smallint CHECK (%(name)s >= 0)',
+ 'SlugField': 'varchar(50)',
+ 'SmallIntegerField': 'smallint',
+ 'TextField': 'text',
+ 'TimeField': 'time',
+ 'URLField': 'varchar(200)',
+ 'USStateField': 'varchar(2)',
+ 'XMLField': 'text',
+}
View
32 django/core/db/base.py
@@ -0,0 +1,32 @@
+from time import time
+
+class CursorDebugWrapper:
+ def __init__(self, cursor, db):
+ self.cursor = cursor
+ self.db = db
+
+ def execute(self, sql, params=[]):
+ start = time()
+ result = self.cursor.execute(sql, params)
+ stop = time()
+ self.db.queries.append({
+ 'sql': sql % tuple(params),
+ 'time': "%.3f" % (stop - start),
+ })
+ return result
+
+ def executemany(self, sql, param_list):
+ start = time()
+ result = self.cursor.executemany(sql, param_list)
+ stop = time()
+ self.db.queries.append({
+ 'sql': 'MANY: ' + sql + ' ' + str(tuple(param_list)),
+ 'time': "%.3f" % (stop - start),
+ })
+ return result
+
+ def __getattr__(self, attr):
+ if self.__dict__.has_key(attr):
+ return self.__dict__[attr]
+ else:
+ return getattr(self.cursor, attr)
View
42 django/core/db/typecasts.py
@@ -0,0 +1,42 @@
+import datetime
+
+###############################################
+# Converters from database (string) to Python #
+###############################################
+
+def typecast_date(s):
+ return s and datetime.date(*map(int, s.split('-'))) # returns None if s is null
+
+def typecast_time(s): # does NOT store time zone information
+ if not s: return None
+ bits = s.split(':')
+ if len(bits[2].split('.')) > 1: # if there is a decimal (e.g. '11:16:36.181305')
+ return datetime.time(int(bits[0]), int(bits[1]), int(bits[2].split('.')[0]),
+ int(bits[2].split('.')[1].split('-')[0]))
+ else: # no decimal was found (e.g. '12:30:00')
+ return datetime.time(int(bits[0]), int(bits[1]), int(bits[2].split('.')[0]), 0)
+
+def typecast_timestamp(s): # does NOT store time zone information
+ if not s: return None
+ d, t = s.split()
+ dates = d.split('-')
+ times = t.split(':')
+ seconds = times[2]
+ if '.' in seconds: # check whether seconds have a fractional part
+ seconds, microseconds = seconds.split('.')
+ else:
+ microseconds = '0'
+ return datetime.datetime(int(dates[0]), int(dates[1]), int(dates[2]),
+ int(times[0]), int(times[1]), int(seconds.split('-')[0]),
+ int(microseconds.split('-')[0]))
+
+def typecast_boolean(s):
+ if s is None: return None
+ return str(s)[0].lower() == 't'
+
+###############################################
+# Converters from Python to database (string) #
+###############################################
+
+def rev_typecast_boolean(obj, d):
+ return obj and '1' or '0'
View
466 django/core/defaultfilters.py
@@ -0,0 +1,466 @@
+"Default variable filters"
+
+import template, re, random
+
+###################
+# STRINGS #
+###################
+
+def addslashes(value, _):
+ "Adds slashes - useful for passing strings to JavaScript, for example."
+ return value.replace('"', '\\"').replace("'", "\\'")
+
+def capfirst(value, _):
+ "Capitalizes the first character of the value"
+ value = str(value)
+ return value and value[0].upper() + value[1:]
+
+def fix_ampersands(value, _):
+ "Replaces ampersands with ``&amp;`` entities"
+ from django.utils.html import fix_ampersands
+ return fix_ampersands(value)
+
+def floatformat(text, _):
+ """
+ Displays a floating point number as 34.2 (with one decimal places) - but
+ only if there's a point to be displayed
+ """
+ if not text:
+ return ''
+ if text - int(text) < 0.1:
+ return int(text)
+ return "%.1f" % text
+
+def linenumbers(value, _):
+ "Displays text with line numbers"
+ from django.utils.html import escape
+ lines = value.split('\n')
+ # Find the maximum width of the line count, for use with zero padding string format command
+ width = str(len(str(len(lines))))
+ for i, line in enumerate(lines):
+ lines[i] = ("%0" + width + "d. %s") % (i + 1, escape(line))
+ return '\n'.join(lines)
+
+def lower(value, _):
+ "Converts a string into all lowercase"
+ return value.lower()
+
+def make_list(value, _):
+ """
+ Returns the value turned into a list. For an integer, it's a list of
+ digits. For a string, it's a list of characters.
+ """
+ return list(str(value))
+
+def slugify(value, _):
+ "Converts to lowercase, removes non-alpha chars and converts spaces to hyphens"
+ value = re.sub('[^\w\s]', '', value).strip().lower()
+ return re.sub('\s+', '-', value)
+
+def stringformat(value, arg):
+ """
+ Formats the variable according to the argument, a string formatting specifier.
+ This specifier uses Python string formating syntax, with the exception that
+ the leading "%" is dropped.
+
+ See http://docs.python.org/lib/typesseq-strings.html for documentation
+ of Python string formatting
+ """
+ try:
+ return ("%" + arg) % value
+ except (ValueError, TypeError):
+ return ""
+
+def title(value, _):
+ "Converts a string into titlecase"
+ return re.sub("([a-z])'([A-Z])", lambda m: m.group(0).lower(), value.title())
+
+def truncatewords(value, arg):
+ """
+ Truncates a string after a certain number of words
+
+ Argument: Number of words to truncate after
+ """
+ from django.utils.text import truncate_words
+ try:
+ length = int(arg)
+ except ValueError: # invalid literal for int()
+ return value # Fail silently.
+ if not isinstance(value, basestring):
+ value = str(value)
+ return truncate_words(value, length)
+
+def upper(value, _):
+ "Converts a string into all uppercase"
+ return value.upper()
+
+def urlencode(value, _):
+ "Escapes a value for use in a URL"
+ import urllib
+ return urllib.quote(value)
+
+def urlize(value, _):
+ "Converts URLs in plain text into clickable links"
+ from django.utils.html import urlize
+ return urlize(value, nofollow=True)
+
+def urlizetrunc(value, limit):
+ """
+ Converts URLs into clickable links, truncating URLs to the given character limit
+
+ Argument: Length to truncate URLs to.
+ """
+ from django.utils.html import urlize
+ return urlize(value, trim_url_limit=int(limit), nofollow=True)
+
+def wordcount(value, _):
+ "Returns the number of words"
+ return len(value.split())
+
+def wordwrap(value, arg):
+ """
+ Wraps words at specified line length
+
+ Argument: number of words to wrap the text at.
+ """
+ from django.utils.text import wrap
+ return wrap(value, int(arg))
+
+def ljust(value, arg):
+ """
+ Left-aligns the value in a field of a given width
+
+ Argument: field size
+ """
+ return str(value).ljust(int(arg))
+
+def rjust(value, arg):
+ """
+ Right-aligns the value in a field of a given width
+
+ Argument: field size
+ """
+ return str(value).rjust(int(arg))
+
+def center(value, arg):
+ "Centers the value in a field of a given width"
+ return str(value).center(int(arg))
+
+def cut(value, arg):
+ "Removes all values of arg from the given string"
+ return value.replace(arg, '')
+
+###################
+# HTML STRINGS #
+###################
+
+def escape(value, _):
+ "Escapes a string's HTML"
+ from django.utils.html import escape
+ return escape(value)
+
+def linebreaks(value, _):
+ "Converts newlines into <p> and <br />s"
+ from django.utils.html import linebreaks
+ return linebreaks(value)
+
+def linebreaksbr(value, _):
+ "Converts newlines into <br />s"
+ return value.replace('\n', '<br />')
+
+def removetags(value, tags):
+ "Removes a space separated list of [X]HTML tags from the output"
+ tags = [re.escape(tag) for tag in tags.split()]
+ tags_re = '(%s)' % '|'.join(tags)
+ starttag_re = re.compile('<%s(>|(\s+[^>]*>))' % tags_re)
+ endtag_re = re.compile('</%s>' % tags_re)
+ value = starttag_re.sub('', value)
+ value = endtag_re.sub('', value)
+ return value
+
+def striptags(value, _):
+ "Strips all [X]HTML tags"
+ from django.utils.html import strip_tags
+ if not isinstance(value, basestring):
+ value = str(value)
+ return strip_tags(value)
+
+###################
+# LISTS #
+###################
+
+def dictsort(value, arg):
+ """
+ Takes a list of dicts, returns that list sorted by the property given in
+ the argument.
+ """
+ decorated = [(template.resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
+ decorated.sort()
+ return [item[1] for item in decorated]
+
+def dictsortreversed(value, arg):
+ """
+ Takes a list of dicts, returns that list sorted in reverse order by the
+ property given in the argument.
+ """
+ decorated = [(template.resolve_variable('var.' + arg, {'var' : item}), item) for item in value]
+ decorated.sort()
+ decorated.reverse()
+ return [item[1] for item in decorated]
+
+def first(value, _):
+ "Returns the first item in a list"
+ try:
+ return value[0]
+ except IndexError:
+ return ''
+
+def join(value, arg):
+ "Joins a list with a string, like Python's ``str.join(list)``"
+ try:
+ return arg.join(map(str, value))
+ except AttributeError: # fail silently but nicely
+ return value
+
+def length(value, _):
+ "Returns the length of the value - useful for lists"
+ return len(value)
+
+def length_is(value, arg):
+ "Returns a boolean of whether the value's length is the argument"
+ return len(value) == int(arg)
+
+def random(value, _):
+ "Returns a random item from the list"
+ return random.choice(value)
+
+def slice_(value, arg):
+ """
+ Returns a slice of the list.
+
+ Uses the same syntax as Python's list slicing; see
+ http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice
+ for an introduction.
+ """
+ try:
+ start, finish = arg.split(':')
+ except ValueError: # unpack list of wrong size
+ return value # fail silently but nicely
+ try:
+ if start and finish:
+ return value[int(start):int(finish)]
+ if start:
+ return value[int(start):]
+ if finish:
+ return value[:int(finish)]
+ except TypeError:
+ pass
+ return value
+
+def unordered_list(value, _):
+ """
+ Recursively takes a self-nested list and returns an HTML unordered list --
+ WITHOUT opening and closing <ul> tags.
+
+ The list is assumed to be in the proper format. For example, if ``var`` contains
+ ``['States', [['Kansas', [['Lawrence', []], ['Topeka', []]]], ['Illinois', []]]]``,
+ then ``{{ var|unordered_list }}`` would return::
+
+ <li>States
+ <ul>
+ <li>Kansas
+ <ul>
+ <li>Lawrence</li>
+ <li>Topeka</li>
+ </ul>
+ </li>
+ <li>Illinois</li>
+ </ul>
+ </li>
+ """
+ def _helper(value, tabs):
+ indent = '\t' * tabs
+ if value[1]:
+ return '%s<li>%s\n%s<ul>\n%s\n%s</ul>\n%s</li>' % (indent, value[0], indent,
+ '\n'.join([unordered_list(v, tabs+1) for v in value[1]]), indent, indent)
+ else:
+ return '%s<li>%s</li>' % (indent, value[0])
+ return _helper(value, 1)
+
+###################
+# INTEGERS #
+###################
+
+def add(value, arg):
+ "Adds the arg to the value"
+ return int(value) + int(arg)
+
+def get_digit(value, arg):
+ """
+ Given a whole number, returns the requested digit of it, where 1 is the
+ right-most digit, 2 is the second-right-most digit, etc. Returns the
+ original value for invalid input (if input or argument is not an integer,
+ or if argument is less than 1). Otherwise, output is always an integer.
+ """
+ try:
+ arg = int(arg)
+ value = int(value)
+ except ValueError:
+ return value # Fail silently for an invalid argument
+ if arg < 1:
+ return value
+ try:
+ return int(str(value)[-arg])
+ except IndexError:
+ return 0
+
+###################
+# DATES #
+###################
+
+def date(value, arg):
+ "Formats a date according to the given format"
+ from django.utils.dateformat import format
+ return format(value, arg)
+
+def time(value, arg):
+ "Formats a time according to the given format"
+ from django.utils.dateformat import time_format
+ return time_format(value, arg)
+
+def timesince(value, _):
+ 'Formats a date as the time since that date (i.e. "4 days, 6 hours")'
+ from django.utils.timesince import timesince
+ return timesince(value)
+
+###################
+# LOGIC #
+###################
+
+def default(value, arg):
+ "If value is unavailable, use given default"
+ return value or arg
+
+def divisibleby(value, arg):
+ "Returns true if the value is devisible by the argument"
+ return int(value) % int(arg) == 0
+
+def yesno(value, arg):
+ """
+ Given a string mapping values for true, false and (optionally) None,
+ returns one of those strings accoding to the value:
+
+ ========== ====================== ==================================
+ Value Argument Outputs
+ ========== ====================== ==================================
+ ``True`` ``"yeah,no,maybe"`` ``yeah``
+ ``False`` ``"yeah,no,maybe"`` ``no``
+ ``None`` ``"yeah,no,maybe"`` ``maybe``
+ ``None`` ``"yeah,no"`` ``"no"`` (converts None to False
+ if no mapping for None is given.
+ ========== ====================== ==================================
+ """
+ bits = arg.split(',')
+ if len(bits) < 2:
+ return value # Invalid arg.
+ try:
+ yes, no, maybe = bits
+ except ValueError: # unpack list of wrong size (no "maybe" value provided)
+ yes, no, maybe = bits, bits[1]
+ if value is None:
+ return maybe
+ if value:
+ return yes
+ return no
+
+###################
+# MISC #
+###################
+
+def filesizeformat(bytes, _):
+ """
+ Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
+ bytes, etc).
+ """
+ bytes = float(bytes)
+ if bytes < 1024:
+ return "%d byte%s" % (bytes, bytes != 1 and 's' or '')
+ if bytes < 1024 * 1024:
+ return "%.1f KB" % (bytes / 1024)
+ if bytes < 1024 * 1024 * 1024:
+ return "%.1f MB" % (bytes / (1024 * 1024))
+ return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
+
+def pluralize(value, _):
+ "Returns 's' if the value is not 1, for '1 vote' vs. '2 votes'"
+ try:
+ if int(value) != 1:
+ return 's'
+ except ValueError: # invalid string that's not a number
+ pass
+ except TypeError: # value isn't a string or a number; maybe it's a list?
+ try:
+ if len(value) != 1:
+ return 's'
+ except TypeError: # len() of unsized object
+ pass
+ return ''
+
+def phone2numeric(value, _):
+ "Takes a phone number and converts it in to its numerical equivalent"
+ from django.utils.text import phone2numeric
+ return phone2numeric(value)
+
+def pprint(value, _):
+ "A wrapper around pprint.pprint -- for debugging, really"
+ from pprint import pformat
+ return pformat(value)
+
+# Syntax: template.register_filter(name of filter, callback, has_argument)
+template.register_filter('add', add, True)
+template.register_filter('addslashes', addslashes, False)
+template.register_filter('capfirst', capfirst, False)
+template.register_filter('center', center, True)
+template.register_filter('cut', cut, True)
+template.register_filter('date', date, True)
+template.register_filter('default', default, True)
+template.register_filter('dictsort', dictsort, True)
+template.register_filter('dictsortreversed', dictsortreversed, True)
+template.register_filter('divisibleby', divisibleby, True)
+template.register_filter('escape', escape, False)
+template.register_filter('filesizeformat', filesizeformat, False)
+template.register_filter('first', first, False)
+template.register_filter('fix_ampersands', fix_ampersands, False)
+template.register_filter('floatformat', floatformat, False)
+template.register_filter('get_digit', get_digit, True)
+template.register_filter('join', join, True)
+template.register_filter('length', length, False)
+template.register_filter('length_is', length_is, True)
+template.register_filter('linebreaks', linebreaks, False)
+template.register_filter('linebreaksbr', linebreaksbr, False)
+template.register_filter('linenumbers', linenumbers, False)
+template.register_filter('ljust', ljust, True)
+template.register_filter('lower', lower, False)
+template.register_filter('make_list', make_list, False)
+template.register_filter('phone2numeric', phone2numeric, False)
+template.register_filter('pluralize', pluralize, False)
+template.register_filter('pprint', pprint, False)
+template.register_filter('removetags', removetags, True)
+template.register_filter('random', random, False)
+template.register_filter('rjust', rjust, True)
+template.register_filter('slice', slice_, True)
+template.register_filter('slugify', slugify, False)
+template.register_filter('stringformat', stringformat, True)
+template.register_filter('striptags', striptags, False)
+template.register_filter('time', time, True)
+template.register_filter('timesince', timesince, False)
+template.register_filter('title', title, False)
+template.register_filter('truncatewords', truncatewords, True)
+template.register_filter('unordered_list', unordered_list, False)
+template.register_filter('upper', upper, False)
+template.register_filter('urlencode', urlencode, False)
+template.register_filter('urlize', urlize, False)
+template.register_filter('urlizetrunc', urlizetrunc, True)
+template.register_filter('wordcount', wordcount, False)
+template.register_filter('wordwrap', wordwrap, True)
+template.register_filter('yesno', yesno, True)
View
743 django/core/defaulttags.py
@@ -0,0 +1,743 @@
+"Default tags used by the template system, available to all templates."
+
+import sys
+import template
+
+class CommentNode(template.Node):
+ def render(self, context):
+ return ''
+
+class CycleNode(template.Node):
+ def __init__(self, cyclevars):
+ self.cyclevars = cyclevars
+ self.cyclevars_len = len(cyclevars)
+ self.counter = -1
+
+ def render(self, context):
+ self.counter += 1
+ return self.cyclevars[self.counter % self.cyclevars_len]
+
+class DebugNode(template.Node):
+ def render(self, context):
+ from pprint import pformat
+ output = [pformat(val) for val in context]
+ output.append('\n\n')
+ output.append(pformat(sys.modules))
+ return ''.join(output)
+
+class FilterNode(template.Node):
+ def __init__(self, filters, nodelist):
+ self.filters, self.nodelist = filters, nodelist
+
+ def render(self, context):
+ output = self.nodelist.render(context)
+ # apply filters
+ for f in self.filters:
+ output = template.registered_filters[f[0]][0](output, f[1])
+ return output
+
+class FirstOfNode(template.Node):
+ def __init__(self, vars):
+ self.vars = vars
+
+ def render(self, context):
+ for var in self.vars:
+ value = template.resolve_variable(var, context)
+ if value:
+ return str(value)
+ return ''
+
+class ForNode(template.Node):
+ def __init__(self, loopvar, sequence, reversed, nodelist_loop):
+ self.loopvar, self.sequence = loopvar, sequence
+ self.reversed = reversed
+ self.nodelist_loop = nodelist_loop
+
+ def __repr__(self):
+ if self.reversed:
+ reversed = ' reversed'
+ else:
+ reversed = ''
+ return "<For Node: for %s in %s, tail_len: %d%s>" % \
+ (self.loopvar, self.sequence, len(self.nodelist_loop), reversed)
+
+ def __iter__(self):
+ for node in self.nodelist_loop:
+ yield node
+
+ def get_nodes_by_type(self, nodetype):
+ nodes = []
+ if isinstance(self, nodetype):
+ nodes.append(self)
+ nodes.extend(self.nodelist_loop.get_nodes_by_type(nodetype))
+ return nodes
+
+ def render(self, context):
+ nodelist = template.NodeList()
+ if context.has_key('forloop'):
+ parentloop = context['forloop']
+ else:
+ parentloop = {}
+ context.push()
+ try:
+ values = template.resolve_variable_with_filters(self.sequence, context)
+ except template.VariableDoesNotExist:
+ values = []
+ if values is None:
+ values = []
+ len_values = len(values)
+ if self.reversed:
+ # From http://www.python.org/doc/current/tut/node11.html
+ def reverse(data):
+ for index in range(len(data)-1, -1, -1):
+ yield data[index]
+ values = reverse(values)
+ for i, item in enumerate(values):
+ context['forloop'] = {
+ # shortcuts for current loop iteration number
+ 'counter0': i,
+ 'counter': i+1,
+ # boolean values designating first and last times through loop
+ 'first': (i == 0),
+ 'last': (i == len_values - 1),
+ 'parentloop': parentloop,
+ }
+ context[self.loopvar] = item
+ for node in self.nodelist_loop:
+ nodelist.append(node.render(context))
+ context.pop()
+ return nodelist.render(context)
+
+class IfChangedNode(template.Node):
+ def __init__(self, nodelist):
+ self.nodelist = nodelist
+ self._last_seen = None
+
+ def render(self, context):
+ content = self.nodelist.render(context)
+ if content != self._last_seen:
+ firstloop = (self._last_seen == None)
+ self._last_seen = content
+ context.push()
+ context['ifchanged'] = {'firstloop': firstloop}
+ content = self.nodelist.render(context)
+ context.pop()
+ return content
+ else:
+ return ''
+
+class IfNotEqualNode(template.Node):
+ def __init__(self, var1, var2, nodelist):
+ self.var1, self.var2, self.nodelist = var1, var2, nodelist
+
+ def __repr__(self):
+ return "<IfNotEqualNode>"
+
+ def render(self, context):
+ if template.resolve_variable(self.var1, context) != template.resolve_variable(