Addition of content reporting

New app called "moderation" which handles the reporting, and in the future, management of incorrect records in the database.
This commit is contained in:
2013-04-06 01:07:33 +01:00
parent d5584246d2
commit b8d4b4f82d
19 changed files with 612 additions and 2 deletions

View File

5
app/moderation/admin.py Normal file
View File

@@ -0,0 +1,5 @@
from django.contrib import admin
from moderation.models import FlagType, FlaggedObject
admin.site.register(FlagType, admin.ModelAdmin)
admin.site.register(FlaggedObject, admin.ModelAdmin)

View File

@@ -0,0 +1,95 @@
# -*- coding: 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 'FlagType'
db.create_table(u'moderation_flagtype', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=200)),
('is_active', self.gf('django.db.models.fields.BooleanField')(default=True)),
))
db.send_create_signal(u'moderation', ['FlagType'])
# Adding model 'FlaggedObject'
db.create_table(u'moderation_flaggedobject', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('object_id', self.gf('django.db.models.fields.PositiveIntegerField')()),
('object_type', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['contenttypes.ContentType'])),
('flag_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='+', to=orm['moderation.FlagType'])),
('note', self.gf('django.db.models.fields.TextField')()),
('status', self.gf('django.db.models.fields.PositiveIntegerField')(default=1)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(blank=True, related_name='+', null=True, to=orm['auth.User'])),
))
db.send_create_signal(u'moderation', ['FlaggedObject'])
def backwards(self, orm):
# Deleting model 'FlagType'
db.delete_table(u'moderation_flagtype')
# Deleting model 'FlaggedObject'
db.delete_table(u'moderation_flaggedobject')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'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'}),
u'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'})
},
u'moderation.flaggedobject': {
'Meta': {'object_name': 'FlaggedObject'},
'flag_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': u"orm['moderation.FlagType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'object_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'status': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': u"orm['auth.User']"})
},
u'moderation.flagtype': {
'Meta': {'object_name': 'FlagType'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
}
}
complete_apps = ['moderation']

View File

37
app/moderation/models.py Normal file
View File

@@ -0,0 +1,37 @@
from django.conf import settings
from django.db import models
try:
from django.contrib.auth import get_user_model
USER_MODEL = get_user_model()
except ImportError:
from django.contrib.auth.models import User as USER_MODEL
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
DEFAULT_FLAGGING_STATUS = (
(1, 'Reported'),
(2, 'Updated by Moderator'),
(3, 'Deleted by Moderator'),
(4, 'No Action'),
)
FLAGGING_STATUS = getattr(settings, 'FLAGGING_STATUS', DEFAULT_FLAGGING_STATUS)
class FlagType(models.Model):
name = models.CharField(max_length=200)
is_active = models.BooleanField(default=True)
def __unicode__(self):
return self.name
class FlaggedObject(models.Model):
object_id = models.PositiveIntegerField()
object_type = models.ForeignKey(ContentType)
generic_obj = generic.GenericForeignKey('object_type', 'object_id')
flag_type = models.ForeignKey('moderation.FlagType', related_name='+')
note = models.TextField()
status = models.PositiveIntegerField(choices=FLAGGING_STATUS, default=1)
user = models.ForeignKey(USER_MODEL, related_name='+', null=True, blank=True)

View File

@@ -0,0 +1 @@
<a class="btn btn-small" href="#report-modal" data-toggle="modal">Report an Issue</a>

View File

@@ -0,0 +1,81 @@
<div id="report-modal" class="modal hide fade">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h3>Report {{ object }}</h3>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="control-group">
<label class="control-label" for="type">Type of Issue</label>
<div class="controls">
<select id="type">
{% for type in flagtypes %}
<option value="{{ type.pk }}">{{ type.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="note">Note</label>
<div class="controls">
<textarea id="note" rows=5 class="input-xlarge"></textarea>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<a id="report-button" href="#" class="btn btn-primary" onclick="submit_report()" data-loading-text="Submitting...">Report</a>
<script type="text/javascript">
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
function bootstrap_alert(message, cls) {
$('#alert-container').append('<div class="alert alert-'+cls+'"><a class="close" data-dismiss="alert">&times;</a>'+message+'</div>')
}
function submit_report(){
$('#report-button').button('loading');
var data = {
app: '{{ contenttype.app_label }}',
model: '{{ contenttype.model }}',
id: {{ object.pk }},
flag_type: $('select#type').val(),
note: $('textarea#note').val()
}
console.log(data);
$.ajax({
type: 'POST',
url: '{% url "moderation_flagobject" %}',
data: JSON.stringify(data),
dataType: 'json',
contentType: "application/json; charset=utf-8",
headers: {
'X-CSRFToken': getCookie('csrftoken')
}
}).done(function(data) {
bootstrap_alert('Report successfully created for the moderators to review', 'success');
$('#report-modal').modal('hide');
$('#report-button').button('reset');
console.log(data);
}).error(function(data){
console.log(data);
bootstrap_alert('An error was encountered while creating the report. Please try again later.', 'error');
$('#report-modal').modal('hide');
$('#report-button').button('reset');
});
return false;
}
</script>
</div>
</div>

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,18 @@
from django import template
from django.contrib.contenttypes.models import ContentType
from ..models import FlagType
register = template.Library()
@register.inclusion_tag('moderation/flag_form.html')
def flag_form(obj):
return {
'flagtypes': FlagType.objects.filter(is_active=True),
'object': obj,
'contenttype': ContentType.objects.get_for_model(obj),
}
@register.inclusion_tag('moderation/flag_button.html')
def flag_button():
return {}

132
app/moderation/tests.py Normal file
View File

@@ -0,0 +1,132 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
import json
from django.test import TestCase
from django.core.urlresolvers import reverse
try:
from django.contrib.auth import get_user_model
USER_MODEL = get_user_model()
except ImportError:
from django.contrib.auth.models import User as USER_MODEL
from moderation.models import FlagType, FlaggedObject
class FlagObjectViewTestCase(TestCase):
def setUp(self):
self.u1 = USER_MODEL.objects.create_user('user1', 'test@test.com', 'user1')
self.f1 = FlagType.objects.create(name='Test')
def tearDown(self):
self.u1.delete()
self.f1.delete()
def post_to_flagobject(self, data={}):
return self.client.post(reverse('moderation_flagobject'), data, content_type='application/json', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
def test_invalid_json(self):
resp = self.post_to_flagobject('')
self.assertEqual(resp.status_code, 400)
content = json.loads(resp.content)
self.assertEqual(content['result'], 'invalid-request')
self.assertEqual(content['reason'], 'invalid-request')
def test_invalid_json_2(self):
resp = self.post_to_flagobject({'data': '1234567890'})
self.assertEqual(resp.status_code, 400)
content = json.loads(resp.content)
self.assertEqual(content['result'], 'invalid-request')
self.assertEqual(content['reason'], 'invalid-json')
def test_missing_app_model_json(self):
resp = self.post_to_flagobject(json.dumps({'invalid': 'invalid'}))
self.assertEqual(resp.status_code, 400)
content = json.loads(resp.content)
self.assertEqual(content['result'], 'invalid-request')
self.assertEqual(content['reason'], 'no-app-or-model')
def test_unknown_type(self):
resp = self.post_to_flagobject(json.dumps({
'app': 'moderation',
'model': 'xxxxxxxxxxxxxxxxxxxx',
'id': 999999,
'flag_type': 1,
'note': 'test note',
}))
self.assertEqual(resp.status_code, 400)
content = json.loads(resp.content)
self.assertEqual(content['result'], 'invalid-request')
self.assertEqual(content['reason'], 'unknown-object-type')
def test_missing_object(self):
resp = self.post_to_flagobject(json.dumps({
'app': 'moderation',
'model': 'flaggedobject',
'id': 999999,
'flag_type': 1,
'note': 'test note',
}))
self.assertEqual(resp.status_code, 400)
content = json.loads(resp.content)
self.assertEqual(content['result'], 'invalid-request')
self.assertEqual(content['reason'], 'does-not-exist')
def test_missing_fields(self):
resp = self.post_to_flagobject(json.dumps({
'app': 'moderation',
'model': 'flaggedobject',
'id': 999999,
'flag_type': 1,
}))
self.assertEqual(resp.status_code, 400)
content = json.loads(resp.content)
self.assertEqual(content['result'], 'invalid-request')
self.assertEqual(content['reason'], 'missing-fields')
def test_flagging_authenticated(self):
self.client.login(username='user1', password='user1')
resp = self.post_to_flagobject(json.dumps({
'app': 'moderation',
'model': 'flagtype',
'id': self.f1.pk,
'flag_type': 1,
'note': 'test note',
}))
self.assertEqual(resp.status_code, 200)
content = json.loads(resp.content)
self.assertEqual(content['result'], 'ok')
self.assertIsNotNone(content['flag_id'])
flag = FlaggedObject.objects.get(pk=content['flag_id'])
self.assertIsNotNone(flag)
self.assertEqual(flag.user, self.u1)
self.assertEqual(flag.flag_type, self.f1)
def test_flagging_unauthenticated(self):
self.client.logout()
resp = self.post_to_flagobject(json.dumps({
'app': 'moderation',
'model': 'flagtype',
'id': self.f1.pk,
'flag_type': 1,
'note': 'test note',
}))
self.assertEqual(resp.status_code, 200)
content = json.loads(resp.content)
self.assertEqual(content['result'], 'ok')
self.assertIsNotNone(content['flag_id'])
flag = FlaggedObject.objects.get(pk=content['flag_id'])
self.assertIsNotNone(flag)
self.assertEqual(flag.user, None)
self.assertEqual(flag.flag_type, self.f1)

6
app/moderation/urls.py Normal file
View File

@@ -0,0 +1,6 @@
from django.conf.urls import patterns, url
from moderation.views import FlagObjectView
urlpatterns = patterns('',
url(r'^flag/$', FlagObjectView.as_view(), name='moderation_flagobject'),
)

57
app/moderation/views.py Normal file
View File

@@ -0,0 +1,57 @@
import json
from django.http import HttpResponseBadRequest
from django.views.generic import View
from django.contrib.contenttypes.models import ContentType
from braces.views import AjaxResponseMixin, JSONResponseMixin
from moderation.models import FlaggedObject
class FlagObjectView(JSONResponseMixin, AjaxResponseMixin, View):
"""
Ajax capable view to flag a object for moderation
"""
def render_invalid_response(self, reason='invalid-request'):
return HttpResponseBadRequest(content=json.dumps({
'result': 'invalid-request',
'reason': reason,
}), content_type=self.get_content_type())
def post(self, request, *args, **kwargs):
pass
def post_ajax(self, request, *args, **kwargs):
data = request.raw_post_data
if not data:
return self.render_invalid_response()
try:
data = json.loads(data)
except ValueError:
return self.render_invalid_response('invalid-json')
if not isinstance(data, dict):
return self.render_invalid_response('invalid-json')
if not 'app' in data or not 'model' in data:
return self.render_invalid_response('no-app-or-model')
if not 'flag_type' in data or not 'note' in data:
return self.render_invalid_response('missing-fields')
try:
ct = ContentType.objects.get(app_label=data['app'], model=data['model'])
except ContentType.DoesNotExist:
return self.render_invalid_response('unknown-object-type')
cls = ct.model_class()
try:
cls.objects.get(pk=data['id'])
except cls.DoesNotExist:
return self.render_invalid_response('does-not-exist')
if request.user.is_authenticated():
user = request.user
else:
user = None
flag = FlaggedObject(object_type=ct, object_id=data['id'], flag_type_id=data['flag_type'],
note=data['note'], user=user)
flag.save()
result = {
'result': 'ok',
'flag_id': flag.pk,
}
return self.render_json_response(result)

View File

@@ -0,0 +1,164 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
depends_on = (
('moderation', '0001_initial'),
)
def forwards(self, orm):
orm['moderation.FlagType'].objects.create(name='Incorrect address')
orm['moderation.FlagType'].objects.create(name='Incorrect contact details')
orm['moderation.FlagType'].objects.create(name='Does not exist')
orm['moderation.FlagType'].objects.create(name='Owner request')
orm['moderation.FlagType'].objects.create(name='Wrong store type')
def backwards(self, orm):
pass
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'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': u"orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
u'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': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'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'}),
u'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'})
},
u'moderation.flaggedobject': {
'Meta': {'object_name': 'FlaggedObject'},
'flag_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': u"orm['moderation.FlagType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'object_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'status': ('django.db.models.fields.PositiveIntegerField', [], {'default': '1'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'+'", 'null': 'True', 'to': u"orm['auth.User']"})
},
u'moderation.flagtype': {
'Meta': {'object_name': 'FlagType'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'})
},
'stores.address': {
'Meta': {'object_name': 'Address'},
'address1': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'address2': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'address3': ('django.db.models.fields.CharField', [], {'max_length': '200', 'blank': 'True'}),
'city': ('django.db.models.fields.CharField', [], {'max_length': "'50'"}),
'country': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'addresses'", 'to': "orm['stores.Country']"}),
'county': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'addresses'", 'null': 'True', 'to': "orm['stores.County']"}),
'geo_latitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
'geo_longitude': ('django.db.models.fields.FloatField', [], {'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'postcode': ('django.db.models.fields.CharField', [], {'max_length': '20'})
},
'stores.brand': {
'Meta': {'ordering': "['name']", 'object_name': 'Brand'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
},
'stores.chain': {
'Meta': {'ordering': "['name']", 'object_name': 'Chain'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'editable_chains'", 'null': 'True', 'to': u"orm['auth.User']"}),
'head_office': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['stores.Address']", 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'long_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'blank': 'True'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
},
'stores.claimrequest': {
'Meta': {'object_name': 'ClaimRequest'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'note': ('django.db.models.fields.TextField', [], {}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'object_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
'status': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'claims'", 'to': u"orm['auth.User']"})
},
'stores.country': {
'Meta': {'ordering': "['name']", 'object_name': 'Country'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'iso_code': ('django.db.models.fields.CharField', [], {'max_length': '3'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'stores.county': {
'Meta': {'ordering': "['name']", 'object_name': 'County'},
'country': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'counties'", 'null': 'True', 'to': "orm['stores.Country']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'stores.link': {
'Meta': {'object_name': 'Link'},
'account_name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'account_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': "orm['stores.LinkType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}),
'object_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"})
},
'stores.linktype': {
'Meta': {'ordering': "['name']", 'object_name': 'LinkType'},
'icon': ('django.db.models.fields.CharField', [], {'default': "'icon-globe'", 'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'url_format': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
'stores.store': {
'Meta': {'ordering': "['name']", 'object_name': 'Store'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'address': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'stores'", 'to': "orm['stores.Address']"}),
'brands': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['stores.Brand']", 'null': 'True', 'blank': 'True'}),
'chain': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'stores'", 'null': 'True', 'to': "orm['stores.Chain']"}),
'editor': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'editable_stores'", 'null': 'True', 'to': u"orm['auth.User']"}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'long_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '200'}),
'phone': ('django.db.models.fields.CharField', [], {'max_length': '25', 'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '200', 'blank': 'True'}),
'store_type': ('django.db.models.fields.IntegerField', [], {'default': '1'}),
'website': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'})
}
}
complete_apps = ['moderation', 'stores']
symmetrical = True

View File

@@ -2,6 +2,7 @@
{% load markdown_deux_tags %}
{% load staticfiles %}
{% load waffle_tags %}
{% load moderation %}
{% block title %}
{{ chain.name }}
@@ -49,6 +50,7 @@
{% else %}
<p>No description / write up available for this chain at the moment.</p>
{% endif %}
<p>{% flag_button %}</p>
</div>
</div>
@@ -74,4 +76,5 @@
</div>
</div>
</div>
{% flag_form chain %}
{% endblock %}

View File

@@ -1,13 +1,14 @@
{% extends "base.html" %}
{% load markdown_deux_tags %}
{% load waffle_tags %}
{% load moderation %}
{% block title %}
{{ store.name }}
{% endblock %}
{% block style %}
<style type="text/css">
<style type="text/css" xmlns="http://www.w3.org/1999/html">
.vcard ul {
list-style-type: none;
}
@@ -55,7 +56,9 @@ $(document).ready(function(){initialize_map_store()});
{% else %}
<p>No description / write up available for this store at the moment.</p>
{% endif %}
<p>{% flag_button %}</p>
{% if store.brands.count %}
<h3>Brands Stocked</h3>
<ul>
@@ -105,5 +108,6 @@ $(document).ready(function(){initialize_map_store()});
</noscript>
</div>
</div>
{% flag_form store %}
</div>
{% endblock %}

View File

@@ -77,6 +77,7 @@ INSTALLED_APPS = [
'bootstrapform',
'registration',
'haystack',
'moderation',
'stores',
]

View File

@@ -55,7 +55,9 @@
</div>
</div>
</div>
<div id="alert-container">
{% if messages %}
{% for message in messages %}
<div{% if message.tags %} class="alert alert-{{ message.tags }}"{% endif %}>
<a class="close" data-dismiss="alert" href="#">&times;</a>
@@ -63,6 +65,7 @@
</div>
{% endfor %}
{% endif %}
</div>
{% block content %}
{% endblock %}

View File

@@ -9,6 +9,7 @@ urlpatterns = patterns('',
url(r'user/', include('registration.backends.default.urls')),
url(r'user/', include('django.contrib.auth.urls')),
url(r'^search/', include('haystack.urls')),
url(r'^moderation/', include('moderation.urls')),
url('', include('stores.urls'))
)

View File

@@ -5,6 +5,7 @@ geopy>=0.95
django-markdown-deux>=1.0.4
django-bootstrap-form>=1.4
django-extra-views>=0.6.2
-e git+git://github.com/brack3t/django-braces.git@e0a8f04419eee0ca5809a62cffd3f5d1e80bb059#egg=django_braces
-e hg+https://bitbucket.org/alper/django-registration@1cddccb#egg=django_registration
requests
pyelasticsearch