Skip to content

Commit

Permalink
allow easily adding new Genres (required using a template & js file f…
Browse files Browse the repository at this point in the history
…rom new django versions)
  • Loading branch information
bcail committed Jan 9, 2015
1 parent 4de0770 commit 89ff2b0
Show file tree
Hide file tree
Showing 8 changed files with 194 additions and 1 deletion.
9 changes: 8 additions & 1 deletion forms.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django import forms
from pagedown.widgets import AdminPagedownWidget
from .models import Biography, Essay, Genre
from .widgets import AddAnotherWidgetWrapper


class BiographyModelForm(forms.ModelForm):
Expand Down Expand Up @@ -31,6 +32,12 @@ class AnnotationForm(forms.Form):
original_title = forms.CharField()
original_title_language = forms.CharField(required=False)
english_title = forms.CharField(required=False)
genre = forms.ModelChoiceField(required=False, queryset=Genre.objects)
genre = forms.ModelChoiceField(required=False, queryset=Genre.objects.all().order_by('text'),
widget=AddAnotherWidgetWrapper(forms.Select(), Genre))
abstract = forms.CharField(required=False, widget=forms.Textarea)


class NewGenreForm(forms.ModelForm):
class Meta:
model = Genre

91 changes: 91 additions & 0 deletions static/rome/js/RelatedObjectLookups.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Handles related-objects functionality: lookup link for raw_id_fields
// and Add Another links.

function html_unescape(text) {
// Unescape a string that was escaped using django.utils.html.escape.
text = text.replace(/&lt;/g, '<');
text = text.replace(/&gt;/g, '>');
text = text.replace(/&quot;/g, '"');
text = text.replace(/&#39;/g, "'");
text = text.replace(/&amp;/g, '&');
return text;
}

// IE doesn't accept periods or dashes in the window name, but the element IDs
// we use to generate popup window names may contain them, therefore we map them
// to allowed characters in a reversible way so that we can locate the correct
// element when the popup window is dismissed.
function id_to_windowname(text) {
text = text.replace(/\./g, '__dot__');
text = text.replace(/\-/g, '__dash__');
return text;
}

function windowname_to_id(text) {
text = text.replace(/__dot__/g, '.');
text = text.replace(/__dash__/g, '-');
return text;
}

function showAdminPopup(triggeringLink, name_regexp) {
var name = triggeringLink.id.replace(name_regexp, '');
name = id_to_windowname(name);
var href = triggeringLink.href;
if (href.indexOf('?') == -1) {
href += '?_popup=1';
} else {
href += '&_popup=1';
}
var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
win.focus();
return false;
}

function showRelatedObjectLookupPopup(triggeringLink) {
return showAdminPopup(triggeringLink, /^lookup_/);
}

function dismissRelatedLookupPopup(win, chosenId) {
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + chosenId;
} else {
document.getElementById(name).value = chosenId;
}
win.close();
}

function showAddAnotherPopup(triggeringLink) {
return showAdminPopup(triggeringLink, /^add_/);
}

function dismissAddAnotherPopup(win, newId, newRepr) {
// newId and newRepr are expected to have previously been escaped by
// django.utils.html.escape.
newId = html_unescape(newId);
newRepr = html_unescape(newRepr);
var name = windowname_to_id(win.name);
var elem = document.getElementById(name);
var o;
if (elem) {
var elemName = elem.nodeName.toUpperCase();
if (elemName == 'SELECT') {
o = new Option(newRepr, newId);
elem.options[elem.options.length] = o;
o.selected = true;
} else if (elemName == 'INPUT') {
if (elem.className.indexOf('vManyToManyRawIdAdminField') != -1 && elem.value) {
elem.value += ',' + newId;
} else {
elem.value = newId;
}
}
} else {
var toId = name + "_to";
o = new Option(newRepr, newId);
SelectBox.add_to_cache(toId, o);
SelectBox.redisplay(toId);
}
win.close();
}
1 change: 1 addition & 0 deletions templates/rome_templates/new_annotation.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
padding-left:10px;
}
</style>
<script src="{% static 'rome/js/RelatedObjectLookups.js' %}"></script>
<script src="{% static 'rome/js/jquery-1.11.1.min.js' %}" type="text/javascript"></script>
</head>
<body>
Expand Down
9 changes: 9 additions & 0 deletions templates/rome_templates/new_genre.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>
<head></head>
<body>
<form action="" method="post">{% csrf_token %}
{{form.as_p}}
<input type="submit" value="Submit" />
</form>
</body>
</html>
9 changes: 9 additions & 0 deletions templates/rome_templates/popup_response.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head><title></title></head>
<body>
<script type="text/javascript">
opener.dismissAddAnotherPopup(window, "{{ value }}", "{{ obj }}");
</script>
</body>
</html>
2 changes: 2 additions & 0 deletions urls_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@
url(r'^people/$', views.biography_list, name='people'),
url(r'^people/(?P<trp_id>\d+)/$', views.biography_detail, name='person_detail'),
url(r'^people/(?P<trp_id>\d+)/TEI/$', views.person_detail_tei, name='person_detail_tei'),

url(r'^genres/new/$', views.new_genre, name='new_genre'),
)
17 changes: 17 additions & 0 deletions views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
from django.core.paginator import Paginator
from django.core.urlresolvers import reverse
from django.shortcuts import render
from django.template.response import SimpleTemplateResponse
from django.utils.html import escape, escapejs

import json
from operator import itemgetter, methodcaller
Expand Down Expand Up @@ -553,3 +555,18 @@ def new_annotation(request, book_id, page_id):
return render(request, 'rome_templates/new_annotation.html',
{'form': form, 'person_formset': person_formset, 'inscription_formset': inscription_formset, 'image_link': image_link})

def new_genre(request):
from .forms import NewGenreForm
if request.method == 'POST':
form = NewGenreForm(request.POST)
if form.is_valid():
genre = form.save()
return SimpleTemplateResponse('rome_templates/popup_response.html', {
'pk_value': escape(genre._get_pk_val()),
'value': escape(genre.serializable_value(genre._meta.pk.attname)),
'obj': escapejs(genre)})
else:
form = NewGenreForm()

return render(request, 'rome_templates/new_genre.html', {'form': form})

57 changes: 57 additions & 0 deletions widgets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import copy
from django import forms
from django.core.urlresolvers import reverse
from django.contrib.admin.templatetags.admin_static import static
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _


class AddAnotherWidgetWrapper(forms.Widget):
"""
This class is a wrapper to a given widget to add the add icon for the
admin interface. Modeled after
django.contrib.admin.widgets.RelatedFieldWidgetWrapper
"""
def __init__(self, widget, model):
self.is_hidden = widget.is_hidden
self.needs_multipart_form = widget.needs_multipart_form
self.attrs = widget.attrs
self.choices = widget.choices
self.widget = widget
self.model = model

def __deepcopy__(self, memo):
obj = copy.copy(self)
obj.widget = copy.deepcopy(self.widget, memo)
obj.attrs = self.widget.attrs
memo[id(self)] = obj
return obj

@property
def media(self):
return self.widget.media

def render(self, name, value, *args, **kwargs):
model = self.model
info = (model._meta.app_label, model._meta.object_name.lower())
self.widget.choices = self.choices
output = [self.widget.render(name, value, *args, **kwargs)]
related_url = reverse('new_genre')
output.append((' <a href="%s" class="add-another" id="add_id_%s" ' + 'onclick="return showAddAnotherPopup(this);">') % (related_url, name))
output.append('<img src="%s" width="10" height="10" alt="%s"/></a>' % (static('admin/img/icon_addlink.gif'), _('Add Another')))
return mark_safe(''.join(output))

def build_attrs(self, extra_attrs=None, **kwargs):
"Helper function for building an attribute dictionary."
self.attrs = self.widget.build_attrs(extra_attrs=None, **kwargs)
return self.attrs

def value_from_datadict(self, data, files, name):
return self.widget.value_from_datadict(data, files, name)

def _has_changed(self, initial, data):
return self.widget._has_changed(initial, data)

def id_for_label(self, id_):
return self.widget.id_for_label(id_)

0 comments on commit 89ff2b0

Please sign in to comment.