From: Stephen Burrows Date: Sun, 10 Jul 2011 19:27:15 +0000 (-0400) Subject: Merge remote-tracking branch 'ithinksw/develop' into develop X-Git-Tag: philo-0.9.1^2~3^2~6 X-Git-Url: http://git.ithinksw.org/philo.git/commitdiff_plain/d0dbd3635a4c1d1c47040fd17f5a8d2bec08a7e9?hp=c927b03ad4dbc6fca8dfbbc59d378435208d2fdd Merge remote-tracking branch 'ithinksw/develop' into develop --- diff --git a/philo/forms/widgets.py b/philo/forms/widgets.py new file mode 100644 index 0000000..d223605 --- /dev/null +++ b/philo/forms/widgets.py @@ -0,0 +1,30 @@ +from django.forms.widgets import Textarea +from django.utils import simplejson as json + +__all__ = ('EmbedWidget',) + +class EmbedWidget(Textarea): + """A form widget with the HTML class embedding and an embedded list of content-types.""" + def __init__(self, attrs=None): + from philo.models import value_content_type_limiter + + content_types = value_content_type_limiter.classes + data = [] + + for content_type in content_types: + data.append({'app_label': content_type._meta.app_label, 'object_name': content_type._meta.object_name.lower(), 'verbose_name': unicode(content_type._meta.verbose_name)}) + + json_ = json.dumps(data) + + default_attrs = {'class': 'embedding vLargeTextField', 'data-content-types': json_ } + + if attrs: + default_attrs.update(attrs) + + super(EmbedWidget, self).__init__(default_attrs) + + class Media: + css = { + 'all': ('philo/css/EmbedWidget.css',), + } + js = ('philo/js/EmbedWidget.js',) \ No newline at end of file diff --git a/philo/models/fields/__init__.py b/philo/models/fields/__init__.py index 7ab4326..575b3a4 100644 --- a/philo/models/fields/__init__.py +++ b/philo/models/fields/__init__.py @@ -9,14 +9,20 @@ from django.utils.translation import ugettext_lazy as _ from philo.forms.fields import JSONFormField from philo.utils.registry import RegistryIterator from philo.validators import TemplateValidator, json_validator +from philo.forms.widgets import EmbedWidget #from philo.models.fields.entities import * -class TemplateField(models.TextField): +class TemplateField(models.Field): """A :class:`TextField` which is validated with a :class:`.TemplateValidator`. ``allow``, ``disallow``, and ``secure`` will be passed into the validator's construction.""" def __init__(self, allow=None, disallow=None, secure=True, *args, **kwargs): super(TemplateField, self).__init__(*args, **kwargs) self.validators.append(TemplateValidator(allow, disallow, secure)) + + def formfield(self, **kwargs): + defaults = {'widget': EmbedWidget} + defaults.update(kwargs) + return super(TemplateField, self).formfield(**defaults) class JSONDescriptor(object): diff --git a/philo/static/philo/css/EmbedWidget.css b/philo/static/philo/css/EmbedWidget.css new file mode 100644 index 0000000..525e5e3 --- /dev/null +++ b/philo/static/philo/css/EmbedWidget.css @@ -0,0 +1,51 @@ +.embed-widget{ + float:left; +} +.embed-toolbar{ + border:1px solid #CCC; + border-bottom:0; + padding:3px 5px; + background:#EEE -webkit-linear-gradient(#F5F5F5, #DDD); + background:#EEE -moz-linear-gradient(#F5F5F5, #DDD); + background-color:#EEE; +} +.embed-widget textarea{ + margin-top:0; +} +.embed-widget button, .embed-widget select{ + vertical-align:middle; + margin-right:3px; +} +.embed-toolbar button{ + background:#FFF; + border:1px solid #CCC; + border-radius:3px; + -webkit-border-radius:3px; + -moz-border-radius:3px; + color:#666; +} +.embed-toolbar button:hover{ + color:#444; +} +.embed-toolbar button:active{ + color:#FFF; + background:#666; + border-color:#666; +} + +.grappelli .embed-widget{ + background:#DDD; + padding:2px; + border:1px solid #CCC; + border-radius:5px; + -webkit-border-radius:5px; + -moz-border-radius:5px; + display:inline-block; + margin:0 -3px; +} +.grappelli .embed-toolbar{ + padding:0; + padding-bottom:3px; + background:none; + border:none; +} \ No newline at end of file diff --git a/philo/static/philo/js/EmbedWidget.js b/philo/static/philo/js/EmbedWidget.js new file mode 100644 index 0000000..f946740 --- /dev/null +++ b/philo/static/philo/js/EmbedWidget.js @@ -0,0 +1,152 @@ +;(function ($) { + var widget = window.embedWidget; + + widget = { + options: {}, + optgroups: {}, + init: function () { + var EmbedFields = widget.EmbedFields = $('.embedding'), + EmbedWidgets = widget.EmbedWidgets, + EmbedBars = widget.EmbedBars, + EmbedButtons = widget.EmbedButtons, + EmbedSelects = widget.EmbedSelects; + + EmbedFields.wrap($('
')); + EmbedWidgets = $('.embed-widget'); + EmbedWidgets.prepend($('
')); + EmbedBars = $('.embed-toolbar'); + EmbedBars.append(''); + EmbedButtons = $('.embed-button'); + EmbedSelects = $('.embed-select'); + + widget.parseContentTypes(); + EmbedSelects.each(widget.populateSelect); + + EmbedButtons.click(widget.buttonHandler); + + // overload the dismissRelatedLookupPopup function + oldDismissRelatedLookupPopup = window.dismissRelatedLookupPopup; + window.dismissRelatedLookupPopup = function (win, chosenId) { + var name = windowname_to_id(win.name), + elem = $('#'+win.name), val; + // if the original element was an embed widget, run our script + if (elem.parent().hasClass('embed-widget')) { + contenttype = $('select',elem.parent()).val(); + widget.appendEmbed(elem, contenttype, chosenId); + elem.focus(); + win.close(); + return; + } + // otherwise, do what you usually do + oldDismissRelatedLookupPopup.apply(this, arguments); + } + + // overload the dismissAddAnotherPopup function + oldDismissAddAnotherPopup = window.dismissAddAnotherPopup; + window.dismissAddAnotherPopup = function (win, newId, newRepr) { + var name = windowname_to_id(win.name), + elem = $('#'+win.name), val; + if (elem.parent().hasClass('embed-widget')) { + dismissRelatedLookupPopup(win, newId); + } + // otherwise, do what you usually do + oldDismissAddAnotherPopup.apply(this, arguments); + } + + // Add grappelli to the body class if the admin is grappelli. This will allow us to customize styles accordingly. + if (window.grappelli) { + $(document.body).addClass('grappelli'); + } + }, + parseContentTypes: function () { + var string = widget.EmbedFields.eq(0).attr('data-content-types'), + data = $.parseJSON(string), + i=0, + current_app_label = '', + optgroups = {}; + + // this loop relies on data being clustered by app + for(i=0; i < data.length; i++){ + item = data[i] + // run this next loop every time we encounter a new app label + if (item.app_label !== current_app_label) { + current_app_label = item.app_label; + optgroups[current_app_label] = {} + } + optgroups[current_app_label][item.verbose_name] = [item.app_label,item.object_name].join('.'); + + widget.optgroups = optgroups; + } + }, + populateSelect: function () { + var $this = $(this), + optgroups = widget.optgroups, + optgroup_els = {}, + optgroup_el, group; + + // append a title + $this.append(''); + + // for each group + for (name in optgroups){ + if(optgroups.hasOwnProperty(name)){ + // assign the group to variable group, temporarily + group = optgroups[name]; + // create an element for this group and assign it to optgroup_el, temporarily + optgroup_el = optgroup_els[name] = $(''); + // append this element to the select menu + $this.append(optgroup_el); + // for each item in the group + for (name in group) { + // append an option to the optgroup + optgroup_el.append(''); + } + } + } + }, + buttonHandler: function (e) { + var $this = $(this), + select = $this.prev('select'), + embed_widget = $this.closest('.embed-widget'), + textarea = embed_widget.children('.embedding').eq(0), + val, app_label, object_name, + href, + win; + + // prevent the button from submitting the form + e.preventDefault(); + + // handle the case that they haven't chosen a type to embed + if (select.val()==='') { + alert('Please select a media type to embed.'); + textarea.focus(); + return; + } + + // split the val into app and object + val = select.val(); + app_label = val.split('.')[0]; + object_name = val.split('.')[1]; + + // generate the url for the popup + // TODO: Find a better way to get the admin URL if possible. This will break if the URL patterns for the admin ever change. + href=['../../../', app_label, '/', object_name, '/?pop=1'].join(''); + + // open a new window + win = window.open(href, id_to_windowname(textarea.attr('id')), 'height=500,width=980,resizable=yes,scrollbars=yes'); + }, + appendEmbed: function (textarea, embed_type, embed_id) { + var $textarea = $(textarea), + textarea = $textarea[0], // make sure we're *not* working with a jQuery object + current_selection = [textarea.selectionStart, textarea.selectionEnd], + current_text = $textarea.val(), + embed_string = ['{% embed', embed_type, embed_id, '%}'].join(' '), + new_text = current_text.substring(0, current_selection[0]) + embed_string + current_text.substring(current_selection[1]), + new_cursor_pos = current_selection[0]+embed_string.length; + $textarea.val(new_text); + textarea.setSelectionRange(new_cursor_pos, new_cursor_pos); + } + } + + $(widget.init); +}(django.jQuery)); \ No newline at end of file