|
| 1 | +import json |
| 2 | + |
| 3 | +from django.utils.html import escape |
| 4 | +from django import forms |
| 5 | +from django.core.urlresolvers import reverse as urlreverse |
| 6 | + |
| 7 | +import debug # pyflakes:ignore |
| 8 | + |
| 9 | +from ietf.doc.models import Document, DocAlias |
| 10 | + |
| 11 | +def tokeninput_id_doc_name_json(objs): |
| 12 | + return json.dumps([{ "id": o.pk, "name": escape(o.name) } for o in objs]) |
| 13 | + |
| 14 | +class AutocompletedDocumentsField(forms.CharField): |
| 15 | + """Tokenizing autocompleted multi-select field for choosing |
| 16 | + documents using jquery.tokeninput.js. |
| 17 | +
|
| 18 | + The field uses a comma-separated list of primary keys in a |
| 19 | + CharField element as its API, the tokeninput Javascript adds some |
| 20 | + selection magic on top of this so we have to pass it a JSON |
| 21 | + representation of ids and user-understandable labels.""" |
| 22 | + |
| 23 | + def __init__(self, |
| 24 | + max_entries=None, # max number of selected objs |
| 25 | + model=Document, |
| 26 | + hint_text="Type in name to search for document", |
| 27 | + doc_type="draft", |
| 28 | + *args, **kwargs): |
| 29 | + kwargs["max_length"] = 10000 |
| 30 | + self.max_entries = max_entries |
| 31 | + self.doc_type = doc_type |
| 32 | + self.model = model |
| 33 | + |
| 34 | + super(AutocompletedDocumentsField, self).__init__(*args, **kwargs) |
| 35 | + |
| 36 | + self.widget.attrs["class"] = "tokenized-field" |
| 37 | + self.widget.attrs["data-hint-text"] = hint_text |
| 38 | + if self.max_entries != None: |
| 39 | + self.widget.attrs["data-max-entries"] = self.max_entries |
| 40 | + |
| 41 | + def parse_tokenized_value(self, value): |
| 42 | + return [x.strip() for x in value.split(",") if x.strip()] |
| 43 | + |
| 44 | + def prepare_value(self, value): |
| 45 | + if not value: |
| 46 | + value = "" |
| 47 | + if isinstance(value, basestring): |
| 48 | + pks = self.parse_tokenized_value(value) |
| 49 | + value = self.model.objects.filter(pk__in=pks, type=self.doc_type) |
| 50 | + if isinstance(value, self.model): |
| 51 | + value = [value] |
| 52 | + |
| 53 | + self.widget.attrs["data-pre"] = tokeninput_id_doc_name_json(value) |
| 54 | + |
| 55 | + # doing this in the constructor is difficult because the URL |
| 56 | + # patterns may not have been fully constructed there yet |
| 57 | + self.widget.attrs["data-ajax-url"] = urlreverse("ajax_tokeninput_search_docs", kwargs={ |
| 58 | + "doc_type": self.doc_type, |
| 59 | + "model_name": self.model.__name__.lower() |
| 60 | + }) |
| 61 | + |
| 62 | + return ",".join(o.pk for o in value) |
| 63 | + |
| 64 | + def clean(self, value): |
| 65 | + value = super(AutocompletedDocumentsField, self).clean(value) |
| 66 | + pks = self.parse_tokenized_value(value) |
| 67 | + |
| 68 | + objs = self.model.objects.filter(pk__in=pks) |
| 69 | + |
| 70 | + found_pks = [str(o.pk) for o in objs] |
| 71 | + failed_pks = [x for x in pks if x not in found_pks] |
| 72 | + if failed_pks: |
| 73 | + raise forms.ValidationError(u"Could not recognize the following documents: {pks}. You can only input documents already registered in the Datatracker.".format(pks=", ".join(failed_pks))) |
| 74 | + |
| 75 | + if self.max_entries != None and len(objs) > self.max_entries: |
| 76 | + raise forms.ValidationError(u"You can select at most %s entries only." % self.max_entries) |
| 77 | + |
| 78 | + return objs |
| 79 | + |
| 80 | +class AutocompletedDocAliasField(AutocompletedDocumentsField): |
| 81 | + def __init__(self, model=DocAlias, *args, **kwargs): |
| 82 | + super(AutocompletedDocAliasField, self).__init__(model=model, *args, **kwargs) |
| 83 | + |
0 commit comments