Permalink
Browse files

Initial commit

  • Loading branch information...
dominno committed Mar 11, 2010
0 parents commit 7dd045fb12cbab39e9b222c8006406fa29e5f769
Showing with 2,821 additions and 0 deletions.
  1. +10 −0 .gitignore
  2. +27 −0 LICENSE
  3. +5 −0 MANIFEST.in
  4. +236 −0 README.txt
  5. +78 −0 bootstrap.py
  6. +26 −0 buildout.cfg
  7. +17 −0 docs/HISTORY.txt
  8. +3 −0 setup.cfg
  9. +33 −0 setup.py
  10. 0 src/example_project/__init__.py
  11. +4 −0 src/example_project/development.py
  12. 0 src/example_project/example_app/__init__.py
  13. +10 −0 src/example_project/example_app/admin.py
  14. +19 −0 src/example_project/example_app/models.py
  15. +2 −0 src/example_project/production.py
  16. +75 −0 src/example_project/settings.py
  17. +21 −0 src/example_project/urls.py
  18. +208 −0 src/moderation/__init__.py
  19. +137 −0 src/moderation/admin.py
  20. 0 src/moderation/conf/__init__.py
  21. +4 −0 src/moderation/conf/settings.py
  22. +87 −0 src/moderation/diff.py
  23. +75 −0 src/moderation/fields.py
  24. +12 −0 src/moderation/fixtures/test_moderation.json
  25. +75 −0 src/moderation/fixtures/test_users.json
  26. +18 −0 src/moderation/forms.py
  27. +42 −0 src/moderation/managers.py
  28. +130 −0 src/moderation/models.py
  29. +56 −0 src/moderation/notifications.py
  30. +5 −0 src/moderation/signals.py
  31. +73 −0 src/moderation/templates/moderation/moderate_object.html
  32. +4 −0 src/moderation/templates/moderation/moderated_objects_list.html
  33. +3 −0 src/moderation/templates/moderation/notification_message_moderator.txt
  34. +13 −0 src/moderation/templates/moderation/notification_message_user.txt
  35. +1 −0 src/moderation/templates/moderation/notification_subject_moderator.txt
  36. +1 −0 src/moderation/templates/moderation/notification_subject_user.txt
  37. +8 −0 src/moderation/tests/__init__.py
  38. +81 −0 src/moderation/tests/test_admin.py
  39. 0 src/moderation/tests/test_app/__init__.py
  40. +14 −0 src/moderation/tests/test_app/models.py
  41. +122 −0 src/moderation/tests/test_diff.py
  42. +70 −0 src/moderation/tests/test_forms.py
  43. +79 −0 src/moderation/tests/test_menagers.py
  44. +220 −0 src/moderation/tests/test_models.py
  45. +46 −0 src/moderation/tests/test_notifications.py
  46. +466 −0 src/moderation/tests/test_register.py
  47. +20 −0 src/moderation/tests/test_settings.py
  48. +13 −0 src/moderation/tests/test_urls.py
  49. +34 −0 src/moderation/tests/test_utils_functions.py
  50. 0 src/moderation/tests/utils/__init__.py
  51. +44 −0 src/moderation/tests/utils/request_factory.py
  52. +87 −0 src/moderation/tests/utils/testsettingsmanager.py
  53. +7 −0 src/moderation/urls.py
@@ -0,0 +1,10 @@
+*.pyc
+*.pyo
+.installed.cfg
+bin
+develop-eggs
+dist
+downloads
+eggs
+parts
+src/*.egg-info
27 LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) Dominik Szopa.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of django-template-repl nor the names of its contributors may be used
+ to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,5 @@
+include LICENSE
+include README.txt
+include HISTORY.txt
+recursive-include src/moderation/templates *
+recursive-include src/moderation/fixtures *
@@ -0,0 +1,236 @@
+Introduction
+============
+
+django-moderation is reusable application for Django framework, that allows to
+moderate any model objects.
+
+Possible use cases:
+
+- User creates his profile, profile is not visible on site.
+ It will be visible on site when moderator approves it.
+- User change his profile, old profile data is visible on site.
+ New data will be visible on site when moderator approves it.
+
+Features:
+
+- configurable admin integration(data changed in admin can be visible on
+ site when moderator approves it)
+- moderation queue in admin
+- html differences of changes between versions of objects
+- configurable email notifications
+- custom model form that allows to edit changed data of object
+- 100% PEP8 correct code
+- test coverage > 80%
+
+
+Requirements
+============
+
+python >= 2.4
+
+django >= 1.1
+
+
+Installation
+===========
+
+Download source code from http://github.com/dominno/django-moderation and run installation script:
+
+$> python setup.py install
+
+
+Configuration
+=============
+
+1. Add to your INSTALLED_APPS in your settings.py:
+
+ ``moderation``
+2. Run command ``manage.py syncdb``
+3. Register Models with moderation
+
+ from django.db import models
+ import moderation
+
+
+ class YourModel(models.Model):
+ pass
+
+ moderation.register(YourModel)
+
+4. Register admin class with your Model
+
+ from django.contrib import admin
+ from moderation.admin import ModerationAdmin
+
+
+ class YourModelAdmin(ModerationAdmin):
+ """Admin settings go here."""
+
+ admin.site.register(YourModel, YourModelAdmin)
+
+If you want to disable integration of moderation in admin,
+add admin_intergration_enabled = False to your admin class:
+
+ class YourModelAdmin(ModerationAdmin):
+ admin_intergration_enabled = False
+
+ admin.site.register(YourModel, YourModelAdmin)
+
+
+How django-moderation works
+===========================
+
+ When you change existing object or create new one, it will not be publicly
+ available until moderator approves it. It will be stored in ModeratedObject model.
+
+ your_model = YourModel(description='test')
+ your_model.save()
+
+ YourModel.objects.get(pk=your_model.pk)
+ Traceback (most recent call last):
+ DoesNotExist: YourModel matching query does not exist.
+
+When you will approve object, then it will be publicly available.
+
+ your_model.moderated_object.approve(moderatated_by=user,
+ reason='Reason for approve')
+
+ YourModel.objects.get(pk=1)
+ <YourModel: YourModel object>
+
+You can access changed object by calling changed_object on moderated_object:
+
+ your_model.moderated_object.changed_object
+ <YourModel: YourModel object>
+
+This is deserialized version of object that was changed.
+
+Now when you will change an object, old version of it will be available publicly,
+new version will be saved in moderated_object
+
+ your_model.description = 'New description'
+ your_model.save()
+
+ your_model = YourModel.objects.get(pk=1)
+ your_model.__dict__
+ {'id': 1, 'description': 'test'}
+
+ your_model.moderated_object.changed_object.__dict__
+ {'id': 1, 'description': 'New description'}
+
+ your_model.moderated_object.approve(moderatated_by=user,
+ reason='Reason for approve')
+
+ your_model = YourModel.objects.get(pk=1)
+ your_model.__dict__
+ {'id': 1, 'description': 'New description'}
+
+Email notifications
+===================
+
+By default when user change object that is under moderation,
+e-mail notification is send to moderator. It will inform him
+that object was changed and need to be moderated.
+
+When moderator approves or reject object changes then e-mail
+notification is send to user that changed this object. It will
+inform user if his changes were accepted or rejected and inform him
+why it was rejected or approved.
+
+How to overwrite email notification templates
+---------------------------------------------
+
+E-mail notifications use following templates:
+
+- moderation/notification_subject_moderator.txt
+- moderation/notification_message_moderator.txt
+- moderation/notification_subject_user.txt
+- moderation/notification_message_user.txt
+
+Default context:
+
+``content_type`` - content type object of moderated object
+``moderated_object`` - ModeratedObject instance
+``site`` - current Site instance
+
+
+How to pass extra context to email notification templates
+---------------------------------------------------------
+
+If you want to pass extra context to email notification methods
+you new need to create new class that subclass BaseModerationNotification class.
+
+ class CustomModerationNotification(BaseModerationNotification):
+ def inform_moderator(self,
+ subject_template='moderation/notification_subject_moderator.txt',
+ message_template='moderation/notification_message_moderator.txt',
+ extra_context=None):
+ '''Send notification to moderator'''
+ extra_context={'test':'test'}
+ super(CustomModerationNotification, self).inform_moderator(subject_template,
+ message_template,
+ extra_context)
+
+ def inform_user(self, user,
+ subject_template='moderation/notification_subject_user.txt',
+ message_template='moderation/notification_message_user.txt',
+ extra_context=None)
+ '''Send notification to user when object is approved or rejected'''
+ extra_context={'test':'test'}
+ super(CustomModerationNotification, self).inform_user(user,
+ subject_template,
+ message_template,
+ extra_context)
+
+
+Next register it with moderation as notification_class:
+
+ moderation.register(YourModel, notification_class=CustomModerationNotification)
+
+
+
+Signals
+=======
+
+``moderation.signals.pre_moderation`` - signal send before object is approved or rejected
+
+Arguments sent with this signal:
+
+``sender``
+ The model class.
+``instance``
+ Instance of model class that is moderated
+``status``
+ Moderation status, 0 - rejected, 1 - approved
+
+
+``moderation.signals.post_moderation`` - signal send after object is approved or rejected
+
+Arguments sent with this signal:
+
+``sender``
+ The model class.
+``instance``
+ Instance of model class that is moderated
+``status``
+ Moderation status, 0 - rejected, 1 - approved
+
+
+Forms
+=====
+
+When creating ModelForms for models that are under moderation use
+BaseModeratedObjectForm class as ModelForm class. Thanks to that form will initialized
+with data from changed_object.
+
+ from moderation.forms import BaseModeratedObjectForm
+
+
+ class ModeratedObjectForm(BaseModeratedObjectForm):
+
+ class Meta:
+ model = MyModel
+
+
+
+
@@ -0,0 +1,78 @@
+#!/usr/bin/env python
+##############################################################################
+#
+# Copyright (c) 2006 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Bootstrap a buildout-based project
+
+Simply run this script in a directory containing a buildout.cfg.
+The script accepts buildout command-line options, so you can
+use the -c option to specify an alternate configuration file.
+
+$Id$
+"""
+
+import os, shutil, sys, tempfile, urllib2
+
+tmpeggs = tempfile.mkdtemp()
+
+is_jython = sys.platform.startswith('java')
+
+try:
+ import pkg_resources
+except ImportError:
+ ez = {}
+ exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py'
+ ).read() in ez
+ ez['use_setuptools'](to_dir=tmpeggs, download_delay=0)
+
+ import pkg_resources
+
+if sys.platform == 'win32':
+ def quote(c):
+ if ' ' in c:
+ return '"%s"' % c # work around spawn lamosity on windows
+ else:
+ return c
+else:
+ def quote (c):
+ return c
+
+cmd = 'from setuptools.command.easy_install import main; main()'
+ws = pkg_resources.working_set
+
+if is_jython:
+ import subprocess
+
+ assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd',
+ quote(tmpeggs), 'zc.buildout'],
+ env=dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ).wait() == 0
+
+else:
+ assert os.spawnle(
+ os.P_WAIT, sys.executable, quote (sys.executable),
+ '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout',
+ dict(os.environ,
+ PYTHONPATH=
+ ws.find(pkg_resources.Requirement.parse('setuptools')).location
+ ),
+ ) == 0
+
+ws.add_entry(tmpeggs)
+ws.require('zc.buildout')
+import zc.buildout.buildout
+zc.buildout.buildout.main(sys.argv[1:] + ['bootstrap'])
+shutil.rmtree(tmpeggs)
@@ -0,0 +1,26 @@
+[buildout]
+parts = python django-1.1
+develop = .
+eggs =
+ django-moderation
+ ipdb
+ pep8
+ django-test-extensions
+ figleaf
+ django-mailer
+
+
+
+[python]
+recipe = zc.recipe.egg
+interpreter = python
+eggs = ${buildout:eggs}
+
+[django-1.1]
+recipe = djangorecipe
+version = 1.1.1
+projectegg = example_project
+project = example_project
+test = moderation
+testrunner = test-1.1
+eggs = ${buildout:eggs}
Oops, something went wrong.

0 comments on commit 7dd045f

Please sign in to comment.