Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
dominno committed Mar 11, 2010
0 parents commit 7dd045f
Show file tree
Hide file tree
Showing 53 changed files with 2,821 additions and 0 deletions.
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.pyc
*.pyo
.installed.cfg
bin
develop-eggs
dist
downloads
eggs
parts
src/*.egg-info
27 changes: 27 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -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.
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include LICENSE
include README.txt
include HISTORY.txt
recursive-include src/moderation/templates *
recursive-include src/moderation/fixtures *
236 changes: 236 additions & 0 deletions README.txt
Original file line number Diff line number Diff line change
@@ -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




78 changes: 78 additions & 0 deletions bootstrap.py
Original file line number Diff line number Diff line change
@@ -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)
26 changes: 26 additions & 0 deletions buildout.cfg
Original file line number Diff line number Diff line change
@@ -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}
Loading

0 comments on commit 7dd045f

Please sign in to comment.