diff --git a/README.rst b/README.rst index 59b48ce..fe66c96 100644 --- a/README.rst +++ b/README.rst @@ -76,7 +76,8 @@ The custom manager returns a special GatekeeperQuerySet with a few extra filters >>> MyModel.objects.all().approved() # approved by moderator >>> MyModel.objects.all().pending() # pending in moderation queue >>> MyModel.objects.all().rejected() # rejected by moderator - >>> MyModel.objects.all().flagged() # flagged + >>> MyModel.objects.all().not_rejected() # approved by moderator and pending in moderation queue + >>> MyModel.objects.all().flagged() # flagged >>> MyModel.objects.all().not_flagged() # not flagged These are implemented on the `GatekeeperQuerySet` itself so that they can be chained: diff --git a/gatekeeper/__init__.py b/gatekeeper/__init__.py index 48b952c..086a5e8 100644 --- a/gatekeeper/__init__.py +++ b/gatekeeper/__init__.py @@ -4,18 +4,18 @@ __license__ = "BSD" from django.conf import settings -from django.contrib.auth.models import User +from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.db.models import Manager, signals from django.dispatch import Signal -from gatekeeper.middleware import get_current_user +from gatekeeper.middleware import get_current_user, get_current_user_ip from gatekeeper.models import ModeratedObject import datetime REJECTED_STATUS = -1 -PENDING_STATUS = 0 +PENDING_STATUS = 0 APPROVED_STATUS = 1 ENABLE_AUTOMODERATION = getattr(settings, "GATEKEEPER_ENABLE_AUTOMODERATION", False) @@ -27,6 +27,8 @@ post_flag = Signal(providing_args=["instance"]) def _get_automod_user(): + User = get_user_model() + try: return User.objects.get(username__exact="gatekeeper_automod") except User.DoesNotExist: @@ -59,16 +61,18 @@ def _default_long_desc(obj): def register(model, import_unmoderated=False, auto_moderator=None, long_desc=None, manager_name='objects', status_name='moderation_status', flagged_name='flagged', moderation_object_name='moderation_object', - base_manager=None): + base_manager=None, moderator_list=None, notify_moderators=None): if not model in registered_models: signals.post_save.connect(save_handler, sender=model) signals.pre_delete.connect(delete_handler, sender=model) # pass extra params onto add_fields to define what fields are named - add_fields(model, manager_name, status_name, flagged_name, + add_fields(model, manager_name, status_name, flagged_name, moderation_object_name, base_manager) registered_models[model] = { 'auto_moderator': auto_moderator, 'long_desc': long_desc or _default_long_desc, + 'moderator_list': moderator_list, + 'notify_moderators': notify_moderators, } if import_unmoderated: try: @@ -119,6 +123,10 @@ def pending(self): def rejected(self): return self._by_status(status_name, REJECTED_STATUS) + def not_rejected(self): + where_clause = '%s != %%s' % (status_name) + return self.extra(where=[where_clause], params=[REJECTED_STATUS]) + def flagged(self): return self._by_status(flagged_name, True) @@ -146,9 +154,9 @@ def get_query_set(self): '_moderation_status':'%s.moderation_status' % GATEKEEPER_TABLE, '_flagged':'%s.flagged' % GATEKEEPER_TABLE} where = ['%s.content_type_id=%s' % (GATEKEEPER_TABLE, content_type), - '%s.object_id=%s.%s' % (GATEKEEPER_TABLE, db_table, + '%s.object_id=%s.%s' % (GATEKEEPER_TABLE, db_table, pk_name)] - tables=[GATEKEEPER_TABLE] + tables = [GATEKEEPER_TABLE] # build extra query then copy model/query to a GatekeeperQuerySet q = super(GatekeeperManager, self).get_query_set().extra( @@ -179,11 +187,14 @@ def _get_moderation_object(self): def save_handler(sender, instance, **kwargs): if kwargs.get('created', None): + user = get_current_user() mo = ModeratedObject( moderation_status=DEFAULT_STATUS, content_object=instance, - timestamp=datetime.datetime.now()) + timestamp=datetime.datetime.now(), + created_by=user if user and not user.is_anonymous() else None, + created_ip=get_current_user_ip()) mo.save() # do automoderation @@ -199,34 +210,49 @@ def save_handler(sender, instance, **kwargs): # do old-style automoderation if automoderator did nothing if ENABLE_AUTOMODERATION and mo.moderation_status == PENDING_STATUS: - user = get_current_user() if user and user.has_perm('gatekeeper.change_moderatedobject'): mo.approve(user) + else: + mo = ModeratedObject.objects.get(object_id=instance.id, + content_type=ContentType.objects.get_for_model(instance)) + - if MODERATOR_LIST: + if callable(registered_models[instance.__class__]['notify_moderators']): + if not registered_models[instance.__class__]['notify_moderators'](instance): + return - from django.contrib.sites.models import Site - domain = Site.objects.get(id=settings.SITE_ID).domain + moderator_list = None + if MODERATOR_LIST: + moderator_list = MODERATOR_LIST + if callable(registered_models[instance.__class__]['moderator_list']): + moderator_list = registered_models[instance.__class__]['moderator_list'](instance) + + if moderator_list: + + from django.contrib.sites.models import Site + domain = Site.objects.get(id=settings.SITE_ID).domain - status = mo.get_moderation_status_display() - instance_class = instance.__class__.__name__ - long_desc = registered_models[instance.__class__]['long_desc'] + status = mo.get_moderation_status_display() + instance_class = instance.__class__.__name__ + long_desc = registered_models[instance.__class__]['long_desc'] - # message - message = _long_desc(instance, long_desc) - if status == 'Pending': - message += "\n\nTo moderate, go to http://%s/admin/gatekeeper/moderatedobject/?ot=desc&o=2" % (message, domain) + # message + message = _long_desc(instance, long_desc) + if status == 'Pending': + if callable(getattr(instance, 'get_absolute_url', None)): + message += "\n\nTo view, go to http://%s%s" % (domain, instance.get_absolute_url()) + message += "\n\nTo moderate, go to http://%s/admin/gatekeeper/moderatedobject/?ot=desc&moderation_status__exact=0&o=2" % domain # subject key = "%s:%s" % (instance_class, status) if mo.moderation_status_by and mo.moderation_status_by.username == 'gatekeeper_automod': key = "%s:auto" % key - subject = "[%s] New gatekeeper object on %s" % (key, domain) + subject = "[%s] Item requires moderation on %s" % (key, domain) - # sender - from_addr = settings.DEFAULT_FROM_EMAIL + # sender + from_addr = settings.DEFAULT_FROM_EMAIL - send_mail(subject, message, from_addr, MODERATOR_LIST, fail_silently=True) + send_mail(subject, message, from_addr, moderator_list, fail_silently=True) def delete_handler(sender, instance, **kwargs): try: diff --git a/gatekeeper/admin.py b/gatekeeper/admin.py index 6666d79..db6274f 100644 --- a/gatekeeper/admin.py +++ b/gatekeeper/admin.py @@ -1,10 +1,37 @@ from django.contrib import admin +from django.template.loader import render_to_string + from gatekeeper.models import ModeratedObject +from django.utils.translation import ugettext_lazy as _ + + class ModeratedObjectAdmin(admin.ModelAdmin): - list_display = ('object_name', 'timestamp', 'moderation_status', 'flagged') + list_display = ('object_name', 'timestamp', 'created_by', 'created_ip', \ + 'moderation_status', 'moderation_status_by', \ + 'flagged', 'object_change_admin_link') list_editable = ('moderation_status','flagged') - list_filter = ['moderation_status','flagged','content_type'] + list_filter = ['moderation_status', 'flagged', 'content_type'] + ordering = ['-timestamp', ] + raw_id_fields = ['created_by', 'flagged_by', 'moderation_status_by'] + #readonly_fields = ['created_by', 'created_ip', 'timestamp'] + radio_fields = {'moderation_status': admin.HORIZONTAL} + fieldsets = ( + (_('Moderation'), { + 'fields': ['moderation_status', 'moderation_status_by', 'moderation_status_date', 'moderation_reason'], + }), + (_('Flagged'), { + 'fields': ['flagged', 'flagged_by', 'flagged_date'], + }), + (_('Meta'), { + 'fields': [ ('content_type', 'object_id'), ('created_ip', 'created_by',)], + }) + ) + + def long_desc(self, obj): + return "%s" % render_to_string('moderation/long_desc.html', {'obj': obj.content_object}) + long_desc.short_description = 'Object Description' + long_desc.allow_tags = True def object_name(self, obj): return "%s" % obj diff --git a/gatekeeper/locale/ru/LC_MESSAGES/django.mo b/gatekeeper/locale/ru/LC_MESSAGES/django.mo new file mode 100644 index 0000000..d54d2a6 Binary files /dev/null and b/gatekeeper/locale/ru/LC_MESSAGES/django.mo differ diff --git a/gatekeeper/locale/ru/LC_MESSAGES/django.po b/gatekeeper/locale/ru/LC_MESSAGES/django.po new file mode 100644 index 0000000..fccebe1 --- /dev/null +++ b/gatekeeper/locale/ru/LC_MESSAGES/django.po @@ -0,0 +1,96 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# Volf , 2010. +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2011-02-05 00:17+0500\n" +"PO-Revision-Date: 2010-09-15 23:54+0600\n" +"Last-Translator: Volf \n" +"Language-Team: LANGUAGE \n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"X-Generator: Virtaal 0.6.1\n" + +#: admin.py:16 +msgid "Moderation" +msgstr "Состояние модерации" + +#: admin.py:19 +msgid "Flagged" +msgstr "Отметки модерации" + +#: admin.py:22 +msgid "Meta" +msgstr "Информация об объекте" + +#: models.py:11 +msgid "Approved" +msgstr "Одобрено" + +#: models.py:12 +msgid "Pending" +msgstr "В ожидании" + +#: models.py:13 +msgid "Rejected" +msgstr "Отклонено" + +#: models.py:36 +msgid "moderation status" +msgstr "состояние" + +#: models.py:38 +msgid "moderation status by" +msgstr "статус установил" + +#: models.py:40 +msgid "moderation status date" +msgstr "дата модерации" + +#: models.py:42 +msgid "moderation reason" +msgstr "причина" + +#: models.py:44 +msgid "flagged" +msgstr "отмечено" + +#: models.py:47 +msgid "flagged by" +msgstr "отметил пользователь" + +#: models.py:49 +msgid "flagged date" +msgstr "дата отметки" + +#: models.py:52 +msgid "content type" +msgstr "тип содержимого" + +#: models.py:53 +msgid "object id" +msgstr "Идентификатор объекта" + +#: models.py:56 +msgid "Created user IP" +msgstr "IP пользователя" + +#: models.py:59 +msgid "created by" +msgstr "создано пользователем" + +#: models.py:107 +msgid "View" +msgstr "Показать" + +#: models.py:111 +msgid "link" +msgstr "Ссылка" + diff --git a/gatekeeper/middleware.py b/gatekeeper/middleware.py index 5438059..c34239b 100644 --- a/gatekeeper/middleware.py +++ b/gatekeeper/middleware.py @@ -8,6 +8,10 @@ def get_current_user(): return getattr(_thread_locals, 'gatekeeper_user', None) +def get_current_user_ip(): + return getattr(_thread_locals, 'gatekeeper_user_ip', None) + class GatekeeperMiddleware(object): def process_request(self, request): - _thread_locals.gatekeeper_user = getattr(request, 'user', None) \ No newline at end of file + _thread_locals.gatekeeper_user = getattr(request, 'user', None) + _thread_locals.gatekeeper_user_ip = request.META['REMOTE_ADDR'] diff --git a/gatekeeper/migrations/0001_initial.py b/gatekeeper/migrations/0001_initial.py new file mode 100644 index 0000000..981f17d --- /dev/null +++ b/gatekeeper/migrations/0001_initial.py @@ -0,0 +1,87 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'ModeratedObject' + db.create_table('gatekeeper_moderatedobject', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('timestamp', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, null=True, blank=True)), + ('moderation_status', self.gf('django.db.models.fields.IntegerField')()), + ('moderation_status_by', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True, blank=True)), + ('moderation_status_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('moderation_reason', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True)), + ('flagged', self.gf('django.db.models.fields.BooleanField')(default=False)), + ('flagged_by', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='flagged objects', null=True, to=orm['auth.User'])), + ('flagged_date', self.gf('django.db.models.fields.DateTimeField')(null=True, blank=True)), + ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])), + ('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()), + )) + db.send_create_signal('gatekeeper', ['ModeratedObject']) + + + def backwards(self, orm): + + # Deleting model 'ModeratedObject' + db.delete_table('gatekeeper_moderatedobject') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'gatekeeper.moderatedobject': { + 'Meta': {'ordering': "['timestamp']", 'object_name': 'ModeratedObject'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'flagged': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'flagged_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'flagged objects'", 'null': 'True', 'to': "orm['auth.User']"}), + 'flagged_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'moderation_reason': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'moderation_status': ('django.db.models.fields.IntegerField', [], {}), + 'moderation_status_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'moderation_status_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['gatekeeper'] diff --git a/gatekeeper/migrations/0002_add_field_moderatedobject_created_ip__add_field_moderatedobject_.py b/gatekeeper/migrations/0002_add_field_moderatedobject_created_ip__add_field_moderatedobject_.py new file mode 100644 index 0000000..6e7de51 --- /dev/null +++ b/gatekeeper/migrations/0002_add_field_moderatedobject_created_ip__add_field_moderatedobject_.py @@ -0,0 +1,82 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding field 'ModeratedObject.created_ip' + db.add_column('gatekeeper_moderatedobject', 'created_ip', self.gf('django.db.models.fields.IPAddressField')(max_length=15, null=True, blank=True), keep_default=False) + + # Adding field 'ModeratedObject.created_by' + db.add_column('gatekeeper_moderatedobject', 'created_by', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='created_moderated_objects', null=True, to=orm['auth.User']), keep_default=False) + + + def backwards(self, orm): + + # Deleting field 'ModeratedObject.created_ip' + db.delete_column('gatekeeper_moderatedobject', 'created_ip') + + # Deleting field 'ModeratedObject.created_by' + db.delete_column('gatekeeper_moderatedobject', 'created_by_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'gatekeeper.moderatedobject': { + 'Meta': {'ordering': "['timestamp']", 'object_name': 'ModeratedObject'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'created_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'created_moderated_objects'", 'null': 'True', 'to': "orm['auth.User']"}), + 'created_ip': ('django.db.models.fields.IPAddressField', [], {'max_length': '15', 'null': 'True', 'blank': 'True'}), + 'flagged': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'flagged_by': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'flagged objects'", 'null': 'True', 'to': "orm['auth.User']"}), + 'flagged_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'moderation_reason': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}), + 'moderation_status': ('django.db.models.fields.IntegerField', [], {}), + 'moderation_status_by': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}), + 'moderation_status_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}) + } + } + + complete_apps = ['gatekeeper'] diff --git a/gatekeeper/migrations/__init__.py b/gatekeeper/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gatekeeper/models.py b/gatekeeper/models.py index dd51e39..5e7313e 100644 --- a/gatekeeper/models.py +++ b/gatekeeper/models.py @@ -1,15 +1,15 @@ from django.conf import settings -from django.db import models -from django.contrib.auth.models import User -from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes import generic +from django.contrib.contenttypes.models import ContentType +from django.db import models +from django.utils.translation import ugettext_lazy as _ import datetime import gatekeeper STATUS_CHOICES = ( - (1, "Approved"), - (0, "Pending"), - (-1, "Rejected"), + (1, _("Approved")), + (0, _("Pending")), + (-1, _("Rejected")), ) STATUS_ON_FLAG = getattr(settings, "GATEKEEPER_STATUS_ON_FLAG", None) @@ -28,27 +28,40 @@ class ModeratedObject(models.Model): objects = ModeratedObjectManager() - timestamp = models.DateTimeField(auto_now_add=True, blank=True, null=True) - - moderation_status = models.IntegerField(choices=STATUS_CHOICES) - moderation_status_by = models.ForeignKey(User, blank=True, null=True) - moderation_status_date = models.DateTimeField(blank=True, null=True) - moderation_reason = models.CharField(max_length=100, blank=True) - - flagged = models.BooleanField(default=False) - flagged_by = models.ForeignKey(User, blank=True, null=True, - related_name='flagged_objects') - flagged_date = models.DateTimeField(blank=True, null=True) - - content_type = models.ForeignKey(ContentType) - object_id = models.PositiveIntegerField() + timestamp = models.DateTimeField(auto_now_add=True, blank=True, null=True, \ + verbose_name="created date") + + moderation_status = models.IntegerField(choices=STATUS_CHOICES, + verbose_name=_('moderation status')) + moderation_status_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, + verbose_name=_('moderation status by')) + moderation_status_date = models.DateTimeField(blank=True, null=True, + verbose_name=_('moderation status date')) + moderation_reason = models.CharField(max_length=100, blank=True, + verbose_name=_('moderation reason')) + + flagged = models.BooleanField(default=False, verbose_name=_('flagged')) + flagged_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, + related_name='flagged objects', + verbose_name=_('flagged by')) + flagged_date = models.DateTimeField(blank=True, null=True, + verbose_name=_('flagged date')) + + content_type = models.ForeignKey(ContentType, + verbose_name=_('content type')) + object_id = models.PositiveIntegerField(verbose_name=_('object id')) content_object = generic.GenericForeignKey('content_type', 'object_id') + created_ip = models.IPAddressField(_('Created user IP'), blank=True, null=True) + created_by = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, \ + related_name="created_moderated_objects", + verbose_name=_('created by')) + class Meta: ordering = ['timestamp'] def __unicode__(self): - return "[%s] %s" % (self.get_moderation_status_display(), + return "[%s] %s" % (self.get_moderation_status_display(), self.content_object) def get_absolute_url(self): @@ -80,3 +93,32 @@ def approve(self, user, reason=''): def reject(self, user, reason=''): self._moderate(-1, user, reason) + + @models.permalink + def object_change_admin_url(self): + return ('admin:%s_%s_change' % (self.content_type.app_label, + self.content_type.model), + [self.object_id, ]) + + def object_change_admin_link(self): + try: + return u'%s' % \ + (self.object_change_admin_url(), _('View')) + except: + return self.get_absolute_url() + object_change_admin_link.allow_tags = True + object_change_admin_link.short_description = _('link') + + +class ModerationMixin(object): + """ + Chek object moderation status + """ + def is_approved(self): + return self.moderation_status == gatekeeper.APPROVED_STATUS + + def is_rejected(self): + return self.moderation_status == gatekeeper.REJECTED_STATUS + + def is_pending(self): + return self.moderation_status == gatekeeper.PENDING_STATUS