Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Progress on moderation/flagging: admin ui mostly works, but widget ne…
…eds to be redone so we don't end up 404 after delete.
- Loading branch information
Showing
9 changed files
with
643 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
237 changes: 237 additions & 0 deletions
237
ebpub/ebpub/db/migrations/0024_auto__add_field_schema_allow_flagging.py
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Copyright 2011 OpenPlans, and contributors | ||
# | ||
# This file is part of ebpub | ||
# | ||
# ebpub is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# ebpub is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with ebpub. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
|
||
""" | ||
Models and admin views to support users flagging and administrators | ||
removing flagged content. | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
# Copyright 2011 OpenPlans and contributors | ||
# | ||
# This file is part of ebpub | ||
# | ||
# ebpub is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# ebpub is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with ebpub. If not, see <http://www.gnu.org/licenses/>. | ||
# | ||
|
||
from django.contrib.gis import admin | ||
from ebpub.geoadmin import OSMModelAdmin | ||
from .models import NewsItemFlag | ||
|
||
from django.forms.widgets import Input, MultiWidget | ||
from django.utils.safestring import mark_safe | ||
|
||
from django import forms | ||
|
||
class SubmitInput(Input): | ||
input_type = 'submit' | ||
|
||
APPROVE=u'approve item' | ||
REJECT=u'delete item' | ||
|
||
class ModerationWidget(MultiWidget): | ||
|
||
# Two buttons whose output is a single string. | ||
# There's probably a cleaner/shorter way to do this? | ||
|
||
def __init__(self, attrs=None): | ||
widgets=[SubmitInput(attrs={'value': APPROVE}), | ||
SubmitInput(attrs={'value': REJECT})] | ||
MultiWidget.__init__(self, widgets, attrs=attrs) | ||
|
||
def decompress(self, value): | ||
# We don't actually care about this, but get | ||
# NotImplementedError without it. | ||
return [value, value] | ||
|
||
def format_output(self, rendered_widgets): | ||
""" | ||
Given a list of rendered widgets (as strings), returns a Unicode string | ||
representing the HTML for the whole lot. | ||
This hook allows you to format the HTML design of the widgets, if | ||
needed. | ||
""" | ||
return u' '.join(rendered_widgets) | ||
|
||
def value_from_datadict(self, data, files, name): | ||
result = MultiWidget.value_from_datadict(self, data, files, name) | ||
# What we have is a list of the strings that were submitted to | ||
# our sub-widgets and Nones for the others. | ||
# Since input is destined for a single CharField, merge it. | ||
result = u' '.join([s for s in result if s]) | ||
return result | ||
|
||
class ModerationForm(forms.ModelForm): | ||
class Meta: | ||
model = NewsItemFlag | ||
|
||
def __init__(self, *args, **kwargs): | ||
self.request = kwargs.pop('request', None) | ||
super(ModerationForm, self).__init__(*args, **kwargs) | ||
|
||
moderate = forms.CharField(widget=ModerationWidget(), required=False) | ||
|
||
def clean(self): | ||
moderation = self.cleaned_data.get('moderate') | ||
if moderation == APPROVE: | ||
self.cleaned_data['state'] = u'approved' | ||
elif moderation == REJECT: | ||
self.cleaned_data['state'] = u'deleted' | ||
if self.instance: | ||
item = self.instance.news_item | ||
if item is not None: | ||
msg = u'Deleted news item %d' % item.id | ||
item.delete() | ||
# XXX self.instance will get auto-deleted anyway | ||
# XXX can i somehow trigger a redirect?? argh i don't | ||
# have a response and we don't have redirects as exceptions! | ||
self.instance.news_item = None | ||
if self.request is not None: | ||
from django.contrib import messages | ||
messages.add_message(self.request, messages.INFO, msg) | ||
|
||
|
||
class NewsItemFlagAdmin(OSMModelAdmin): | ||
|
||
form = ModerationForm | ||
|
||
def get_form(self, request, obj=None, **kwargs): | ||
# Jumping through hoops to make request available | ||
# to the form instance, so it can be accessed during clean(). | ||
# See eg http://stackoverflow.com/questions/1057252/django-how-do-i-access-the-request-object-or-any-other-variable-in-a-forms-cle | ||
_base = super(NewsItemFlagAdmin, self).get_form(request, obj, **kwargs) | ||
class ModelFormMetaClass(_base): | ||
def __new__(cls, *args, **kwargs): | ||
kwargs['request'] = request | ||
return _base(*args, **kwargs) | ||
return ModelFormMetaClass | ||
|
||
list_display = ('item_title', 'item_schema', 'state', 'reason', 'submitted', 'updated') | ||
search_fields = ('item_title', 'item_description', 'comment',) | ||
|
||
list_filter = ('reason', 'state', 'news_item__schema__slug',) | ||
# XXX TODO: Allow deleting the NewsItem directly from our form. | ||
|
||
raw_id_fields = ('news_item',) | ||
|
||
date_hierarchy = 'submitted' | ||
readonly_fields = ('item_title', 'item_schema', 'item_description', 'item_url', | ||
'item_pub_date', | ||
'submitted', | ||
) | ||
|
||
admin.site.register(NewsItemFlag, NewsItemFlagAdmin) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
# 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 'NewsItemFlag' | ||
db.create_table('moderation_newsitemflag', ( | ||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), | ||
('news_item', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='moderation_newsitemflag_related', null=True, to=orm['db.NewsItem'])), | ||
('reason', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), | ||
('email', self.gf('django.db.models.fields.CharField')(default='anonymous', max_length=128, null=True, blank=True)), | ||
('comment', self.gf('django.db.models.fields.CharField')(max_length=512)), | ||
('submitted', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), | ||
('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)), | ||
('state', self.gf('django.db.models.fields.CharField')(default='new', max_length=64, db_index=True, blank=True)), | ||
)) | ||
db.send_create_signal('moderation', ['NewsItemFlag']) | ||
|
||
# Adding model 'CommentFlag' | ||
db.create_table('moderation_commentflag', ( | ||
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), | ||
('news_item', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='moderation_commentflag_related', null=True, to=orm['db.NewsItem'])), | ||
('reason', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), | ||
('email', self.gf('django.db.models.fields.CharField')(default='anonymous', max_length=128, null=True, blank=True)), | ||
('comment', self.gf('django.db.models.fields.CharField')(max_length=512)), | ||
('submitted', self.gf('django.db.models.fields.DateTimeField')(auto_now_add=True, blank=True)), | ||
('updated', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, blank=True)), | ||
('state', self.gf('django.db.models.fields.CharField')(default='new', max_length=64, db_index=True, blank=True)), | ||
)) | ||
db.send_create_signal('moderation', ['CommentFlag']) | ||
|
||
|
||
def backwards(self, orm): | ||
|
||
# Deleting model 'NewsItemFlag' | ||
db.delete_table('moderation_newsitemflag') | ||
|
||
# Deleting model 'CommentFlag' | ||
db.delete_table('moderation_commentflag') | ||
|
||
|
||
models = { | ||
'db.location': { | ||
'Meta': {'ordering': "('slug',)", 'unique_together': "(('slug', 'location_type'),)", 'object_name': 'Location'}, | ||
'area': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}), | ||
'city': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'blank': 'True'}), | ||
'description': ('django.db.models.fields.TextField', [], {'blank': 'True'}), | ||
'display_order': ('django.db.models.fields.SmallIntegerField', [], {}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'last_mod_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'null': 'True', 'blank': 'True'}), | ||
'location': ('django.contrib.gis.db.models.fields.GeometryField', [], {'null': 'True'}), | ||
'location_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.LocationType']"}), | ||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
'normalized_name': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}), | ||
'population': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), | ||
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '32', 'db_index': 'True'}), | ||
'source': ('django.db.models.fields.CharField', [], {'max_length': '64'}), | ||
'user_id': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}) | ||
}, | ||
'db.locationtype': { | ||
'Meta': {'ordering': "('name',)", 'object_name': 'LocationType'}, | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'is_browsable': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
'is_significant': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), | ||
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
'plural_name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), | ||
'scope': ('django.db.models.fields.CharField', [], {'max_length': '64'}), | ||
'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}) | ||
}, | ||
'db.newsitem': { | ||
'Meta': {'ordering': "('title',)", 'object_name': 'NewsItem'}, | ||
'description': ('django.db.models.fields.TextField', [], {}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'item_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date.today', 'db_index': 'True'}), | ||
'last_modification': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}), | ||
'location': ('django.contrib.gis.db.models.fields.GeometryField', [], {'null': 'True', 'blank': 'True'}), | ||
'location_name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
'location_object': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': "orm['db.Location']"}), | ||
'location_set': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['db.Location']", 'null': 'True', 'through': "orm['db.NewsItemLocation']", 'blank': 'True'}), | ||
'pub_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}), | ||
'schema': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Schema']"}), | ||
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}), | ||
'url': ('django.db.models.fields.TextField', [], {'blank': 'True'}) | ||
}, | ||
'db.newsitemlocation': { | ||
'Meta': {'unique_together': "(('news_item', 'location'),)", 'object_name': 'NewsItemLocation'}, | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'location': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.Location']"}), | ||
'news_item': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['db.NewsItem']"}) | ||
}, | ||
'db.schema': { | ||
'Meta': {'ordering': "('name',)", 'object_name': 'Schema'}, | ||
'allow_charting': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'allow_comments': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'allow_flagging': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'can_collapse': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'date_name': ('django.db.models.fields.CharField', [], {'default': "'Date'", 'max_length': '32'}), | ||
'date_name_plural': ('django.db.models.fields.CharField', [], {'default': "'Dates'", 'max_length': '32'}), | ||
'grab_bag': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), | ||
'grab_bag_headline': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128', 'blank': 'True'}), | ||
'has_newsitem_detail': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'importance': ('django.db.models.fields.SmallIntegerField', [], {'default': '0'}), | ||
'indefinite_article': ('django.db.models.fields.CharField', [], {'max_length': '2'}), | ||
'intro': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), | ||
'is_event': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}), | ||
'is_special_report': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), | ||
'last_updated': ('django.db.models.fields.DateField', [], {}), | ||
'map_color': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), | ||
'map_icon_url': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), | ||
'min_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.date(1970, 1, 1)'}), | ||
'name': ('django.db.models.fields.CharField', [], {'max_length': '32'}), | ||
'number_in_overview': ('django.db.models.fields.SmallIntegerField', [], {'default': '5'}), | ||
'plural_name': ('django.db.models.fields.CharField', [], {'max_length': '32'}), | ||
'short_description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), | ||
'short_source': ('django.db.models.fields.CharField', [], {'default': "'One-line description of where this information came from.'", 'max_length': '128', 'blank': 'True'}), | ||
'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '32', 'db_index': 'True'}), | ||
'source': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), | ||
'summary': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}), | ||
'update_frequency': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '64', 'blank': 'True'}), | ||
'uses_attributes_in_list': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) | ||
}, | ||
'moderation.commentflag': { | ||
'Meta': {'ordering': "('news_item',)", 'object_name': 'CommentFlag'}, | ||
'comment': ('django.db.models.fields.CharField', [], {'max_length': '512'}), | ||
'email': ('django.db.models.fields.CharField', [], {'default': "'anonymous'", 'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'news_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'moderation_commentflag_related'", 'null': 'True', 'to': "orm['db.NewsItem']"}), | ||
'reason': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), | ||
'state': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '64', 'db_index': 'True', 'blank': 'True'}), | ||
'submitted': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), | ||
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) | ||
}, | ||
'moderation.newsitemflag': { | ||
'Meta': {'ordering': "('news_item',)", 'object_name': 'NewsItemFlag'}, | ||
'comment': ('django.db.models.fields.CharField', [], {'max_length': '512'}), | ||
'email': ('django.db.models.fields.CharField', [], {'default': "'anonymous'", 'max_length': '128', 'null': 'True', 'blank': 'True'}), | ||
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), | ||
'news_item': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'moderation_newsitemflag_related'", 'null': 'True', 'to': "orm['db.NewsItem']"}), | ||
'reason': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), | ||
'state': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '64', 'db_index': 'True', 'blank': 'True'}), | ||
'submitted': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), | ||
'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}) | ||
} | ||
} | ||
|
||
complete_apps = ['moderation'] |
Empty file.
Oops, something went wrong.