Skip to content

Commit

Permalink
bug 665122 EditorToolbar model to admin the ckeditor_config.js file; …
Browse files Browse the repository at this point in the history
…convert wiki to south; code cleanups
  • Loading branch information
groovecoder committed Jul 19, 2011
1 parent f019fa8 commit dfb9ee3
Show file tree
Hide file tree
Showing 13 changed files with 504 additions and 34 deletions.
24 changes: 2 additions & 22 deletions apps/demos/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@

from threadedcomments.models import ThreadedComment, FreeThreadedComment

from utils import generate_filename_and_delete_previous

from actioncounters.fields import ActionCounterField

from embedutils import VideoEmbedURLField
Expand Down Expand Up @@ -193,28 +195,6 @@ def upload_to(instance, filename):
base=get_root_for_submission(instance), field_fn=field_fn)
return upload_to

def generate_filename_and_delete_previous(ffile, name, before_delete=None):
"""Generate a new filename for a file upload field; delete the previously
uploaded file."""

new_filename = ffile.field.generate_filename(ffile.instance, name)

try:
# HACK: Speculatively re-fetching the original object makes me feel
# wasteful and dirty. But, I can't think of another way to get
# to the original field's value. Should be cached, though.
# see also - http://code.djangoproject.com/ticket/11663#comment:10
orig_instance = ffile.instance.__class__.objects.get(id=ffile.instance.id)
orig_field_file = getattr(orig_instance, ffile.field.name)
orig_filename = orig_field_file.name

if orig_filename and new_filename != orig_filename:
if before_delete: before_delete(orig_field_file)
orig_field_file.delete()
except ffile.instance.__class__.DoesNotExist:
pass

return new_filename


class ReplacingFieldZipFile(FieldFile):
Expand Down
3 changes: 3 additions & 0 deletions apps/sumo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from django.db import models

import caching.base
from south.modelsinspector import add_introspection_rules

# Our apps should subclass ManagerBase instead of models.Manager or
# caching.base.CachingManager directly.
Expand Down Expand Up @@ -53,3 +54,5 @@ def __init__(self, max_length=7, default=settings.LANGUAGE_CODE,
return super(LocaleField, self).__init__(
max_length=max_length, default=default, choices=choices,
*args, **kwargs)

add_introspection_rules([], ["^sumo\.models\.LocaleField"])
3 changes: 3 additions & 0 deletions apps/tags/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.db import models

from south.modelsinspector import add_ignored_fields
from taggit.managers import TaggableManager

from tags.forms import TagField
Expand Down Expand Up @@ -28,3 +29,5 @@ class BigVocabTaggableMixin(models.Model):

class Meta:
abstract = True

add_ignored_fields(["tags\.models\.BigVocabTaggableManager"])
7 changes: 3 additions & 4 deletions apps/wiki/admin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.contrib import admin

from wiki.models import Document, Revision
from wiki.models import Document, Revision, EditorToolbar


class DocumentAdmin(admin.ModelAdmin):
Expand All @@ -12,8 +12,7 @@ class DocumentAdmin(admin.ModelAdmin):
readonly_fields = ('id', 'current_revision')
search_fields = ('title',)

class RevisionAdmin(admin.ModelAdmin):
pass

admin.site.register(Document, DocumentAdmin)
admin.site.register(Revision, RevisionAdmin)
admin.site.register(Revision, admin.ModelAdmin)
admin.site.register(EditorToolbar, admin.ModelAdmin)
1 change: 1 addition & 0 deletions apps/wiki/fixtures/wiki/toolbars.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"pk": 1, "model": "wiki.editortoolbar", "fields": {"config_file": "js/ckeditor_config.js", "creator": 6}}]
255 changes: 255 additions & 0 deletions apps/wiki/migrations/0001_initial.py

Large diffs are not rendered by default.

156 changes: 156 additions & 0 deletions apps/wiki/migrations/0002_auto__add_editortoolbar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# 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 'EditorToolbar'
db.create_table('wiki_editortoolbar', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('creator', self.gf('django.db.models.fields.related.ForeignKey')(related_name='created_toolbars', to=orm['auth.User'])),
('default', self.gf('django.db.models.fields.BooleanField')(default=False)),
('config_file', self.gf('utils.OverwritingFileField')(max_length=100)),
))
db.send_create_signal('wiki', ['EditorToolbar'])


def backwards(self, orm):

# Deleting model 'EditorToolbar'
db.delete_table('wiki_editortoolbar')


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'})
},
'notifications.watch': {
'Meta': {'object_name': 'Watch'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'null': 'True', 'blank': 'True'}),
'email': ('django.db.models.fields.EmailField', [], {'db_index': 'True', 'max_length': '75', 'null': 'True', 'blank': 'True'}),
'event_type': ('django.db.models.fields.CharField', [], {'max_length': '30', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'object_id': ('django.db.models.fields.PositiveIntegerField', [], {'null': 'True', 'db_index': 'True'}),
'secret': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'})
},
'taggit.tag': {
'Meta': {'object_name': 'Tag'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100', 'db_index': 'True'})
},
'taggit.taggeditem': {
'Meta': {'object_name': 'TaggedItem'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"})
},
'wiki.document': {
'Meta': {'unique_together': "(('parent', 'locale'), ('title', 'locale'), ('slug', 'locale'))", 'object_name': 'Document'},
'category': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}),
'current_revision': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'current_for+'", 'null': 'True', 'to': "orm['wiki.Revision']"}),
'html': ('django.db.models.fields.TextField', [], {}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_localizable': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'db_index': 'True'}),
'is_template': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'locale': ('sumo.models.LocaleField', [], {'default': "'en-US'", 'max_length': '7', 'db_index': 'True'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'translations'", 'null': 'True', 'to': "orm['wiki.Document']"}),
'related_documents': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['wiki.Document']", 'through': "orm['wiki.RelatedDocument']", 'symmetrical': 'False'}),
'slug': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
'wiki.editortoolbar': {
'Meta': {'object_name': 'EditorToolbar'},
'config_file': ('utils.OverwritingFileField', [], {'max_length': '100'}),
'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_toolbars'", 'to': "orm['auth.User']"}),
'default': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'wiki.firefoxversion': {
'Meta': {'unique_together': "(('item_id', 'document'),)", 'object_name': 'FirefoxVersion'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'firefox_version_set'", 'to': "orm['wiki.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'item_id': ('django.db.models.fields.IntegerField', [], {})
},
'wiki.helpfulvote': {
'Meta': {'object_name': 'HelpfulVote'},
'anonymous_id': ('django.db.models.fields.CharField', [], {'max_length': '40', 'db_index': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now', 'db_index': 'True'}),
'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'poll_votes'", 'null': 'True', 'to': "orm['auth.User']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'poll_votes'", 'to': "orm['wiki.Document']"}),
'helpful': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user_agent': ('django.db.models.fields.CharField', [], {'max_length': '1000'})
},
'wiki.operatingsystem': {
'Meta': {'unique_together': "(('item_id', 'document'),)", 'object_name': 'OperatingSystem'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'operating_system_set'", 'to': "orm['wiki.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'item_id': ('django.db.models.fields.IntegerField', [], {})
},
'wiki.relateddocument': {
'Meta': {'ordering': "['-in_common']", 'object_name': 'RelatedDocument'},
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'related_from'", 'to': "orm['wiki.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'in_common': ('django.db.models.fields.IntegerField', [], {}),
'related': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'related_to'", 'to': "orm['wiki.Document']"})
},
'wiki.revision': {
'Meta': {'object_name': 'Revision'},
'based_on': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['wiki.Revision']", 'null': 'True', 'blank': 'True'}),
'comment': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'content': ('django.db.models.fields.TextField', [], {}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'creator': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'created_revisions'", 'to': "orm['auth.User']"}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'revisions'", 'to': "orm['wiki.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_approved': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
'keywords': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'reviewed': ('django.db.models.fields.DateTimeField', [], {'null': 'True'}),
'reviewer': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'reviewed_revisions'", 'null': 'True', 'to': "orm['auth.User']"}),
'significance': ('django.db.models.fields.IntegerField', [], {'null': 'True'}),
'summary': ('django.db.models.fields.TextField', [], {})
}
}

complete_apps = ['wiki']
Empty file.
32 changes: 29 additions & 3 deletions apps/wiki/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,22 @@
from django.db import models
from django.http import Http404

from south.modelsinspector import add_introspection_rules

from notifications.models import NotificationsMixin
from sumo import ProgrammingError
from sumo_locales import LOCALES
from sumo.models import ModelBase, LocaleField
from sumo.urlresolvers import reverse, split_path
from tags.models import BigVocabTaggableMixin
from utils import OverwritingFileField
from wiki import TEMPLATE_TITLE_PREFIX


ALLOWED_TAGS = ALLOWED_TAGS + ['span','p','h1','h2','h3','pre','code','dl','dt','dd']
ALLOWED_ATTRIBUTES['span'] = ['style',]
ALLOWED_TAGS = ALLOWED_TAGS + [
'span', 'p', 'h1', 'h2', 'h3', 'pre', 'code', 'dl', 'dt', 'dd'
]
ALLOWED_ATTRIBUTES['span'] = ['style', ]

# Disruptiveness of edits to translated versions. Numerical magnitude indicate
# the relative severity.
Expand Down Expand Up @@ -596,7 +601,9 @@ def __unicode__(self):
def content_cleaned(self):
from bleach import Bleach
bleach = Bleach()
return bleach.clean(self.content, attributes=ALLOWED_ATTRIBUTES,tags=ALLOWED_TAGS)
return bleach.clean(
self.content, attributes=ALLOWED_ATTRIBUTES, tags=ALLOWED_TAGS
)


# FirefoxVersion and OperatingSystem map many ints to one Document. The
Expand Down Expand Up @@ -641,6 +648,23 @@ class Meta(object):
ordering = ['-in_common']


def toolbar_config_upload_to(instance, filename):
"""upload_to builder for toolbar config files"""
if (instance.default and instance.default == True):
return 'js/ckeditor_config.js'
else:
return 'js/ckeditor_config_%s.js' % instance.creator.id


class EditorToolbar(ModelBase):
creator = models.ForeignKey(User, related_name='created_toolbars')
default = models.BooleanField(default=False)
config_file = OverwritingFileField(upload_to=toolbar_config_upload_to)

def __unicode__(self):
return self.config_file.name


def get_current_or_latest_revision(document, reviewed_only=True):
"""Returns current revision if there is one, else the last created
revision."""
Expand All @@ -655,3 +679,5 @@ def get_current_or_latest_revision(document, reviewed_only=True):
rev = revs[0]

return rev

add_introspection_rules([], ["^utils\.OverwritingFileField"])
47 changes: 45 additions & 2 deletions lib/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import commonware.log
import lockfile

from django.db import models
from django.db.models.fields.files import FieldFile

log = commonware.log.getLogger('mdn.basket')
htmlparser = HTMLParser.HTMLParser()
Expand Down Expand Up @@ -42,9 +44,9 @@ def wrapper(self, *args, **kwargs):
def cached_property(*args, **kw):
# Handles invocation as a direct decorator or
# with intermediate keyword arguments.
if args: # @cached_property
if args: # @cached_property
return CachedProperty(args[0])
else: # @cached_property(name=..., writable=...)
else: # @cached_property(name=..., writable=...)
return lambda f: CachedProperty(f, **kw)


Expand Down Expand Up @@ -92,6 +94,7 @@ def entity_decode(str):

import jingo


class JingoTemplateLoaderWrapper():

def __init__(self, template):
Expand All @@ -103,6 +106,7 @@ def render(self, context):
context_dict.update(d)
return self.template.render(context_dict)


class JingoTemplateLoader():
"""Quick & dirty adaptor to load jinja2 templates via jingo"""
is_usable = True
Expand All @@ -113,3 +117,42 @@ def get_template(self, template_name, template_dirs=None):
template = jingo.env.get_template(template_name)
return JingoTemplateLoaderWrapper(template)


def generate_filename_and_delete_previous(ffile, name, before_delete=None):
"""Generate a new filename for a file upload field; delete the previously
uploaded file."""

new_filename = ffile.field.generate_filename(ffile.instance, name)

try:
# HACK: Speculatively re-fetching the original object makes me feel
# wasteful and dirty. But, I can't think of another way to get
# to the original field's value. Should be cached, though.
# see also - http://code.djangoproject.com/ticket/11663#comment:10
orig_instance = ffile.instance.__class__.objects.get(
id=ffile.instance.id
)
orig_field_file = getattr(orig_instance, ffile.field.name)
orig_filename = orig_field_file.name

if orig_filename and new_filename != orig_filename:
if before_delete:
before_delete(orig_field_file)
orig_field_file.delete()
except ffile.instance.__class__.DoesNotExist:
pass

return new_filename


class OverwritingFieldFile(FieldFile):
def save(self, name, content, save=True):
# new_filename = generate_filename_and_delete_previous(self, name)
name = self.field.generate_filename(self.instance, name)
self.storage.delete(name)
super(OverwritingFieldFile, self).save(name, content, save)


class OverwritingFileField(models.FileField):
"""This field causes an uploaded file to replace existing file on disk."""
attr_class = OverwritingFieldFile
Loading

0 comments on commit dfb9ee3

Please sign in to comment.