| 824 | | return render_to_response('admin_change_form', context_instance=c) |
|---|
| 825 | | change_stage_new = staff_member_required(change_stage_new) |
|---|
| 826 | | |
|---|
| 827 | | def _get_template(opts, app_label, add=False, change=False, show_delete=False, form_url=''): |
|---|
| 828 | | admin_field_objs = opts.admin.get_field_objs(opts) |
|---|
| 829 | | ordered_objects = opts.get_ordered_objects()[:] |
|---|
| 830 | | auto_populated_fields = [f for f in opts.fields if f.prepopulate_from] |
|---|
| 831 | | t = ['{% extends "base_site" %}\n'] |
|---|
| 832 | | t.append('{% block extrahead %}') |
|---|
| 833 | | |
|---|
| 834 | | # Put in any necessary JavaScript imports. |
|---|
| 835 | | javascript_imports = ['%sjs/core.js' % ADMIN_MEDIA_PREFIX, '%sjs/admin/RelatedObjectLookups.js' % ADMIN_MEDIA_PREFIX] |
|---|
| 836 | | if auto_populated_fields: |
|---|
| 837 | | javascript_imports.append('%sjs/urlify.js' % ADMIN_MEDIA_PREFIX) |
|---|
| 838 | | if opts.has_field_type(meta.DateTimeField) or opts.has_field_type(meta.TimeField) or opts.has_field_type(meta.DateField): |
|---|
| 839 | | javascript_imports.extend(['%sjs/calendar.js' % ADMIN_MEDIA_PREFIX, '%sjs/admin/DateTimeShortcuts.js' % ADMIN_MEDIA_PREFIX]) |
|---|
| 840 | | if ordered_objects: |
|---|
| 841 | | javascript_imports.extend(['%sjs/getElementsBySelector.js' % ADMIN_MEDIA_PREFIX, '%sjs/dom-drag.js' % ADMIN_MEDIA_PREFIX, '%sjs/admin/ordering.js' % ADMIN_MEDIA_PREFIX]) |
|---|
| 842 | | if opts.admin.js: |
|---|
| 843 | | javascript_imports.extend(opts.admin.js) |
|---|
| 844 | | seen_collapse = False |
|---|
| 845 | | for _, options in admin_field_objs: |
|---|
| 846 | | if not seen_collapse and 'collapse' in options.get('classes', ''): |
|---|
| 847 | | seen_collapse = True |
|---|
| 848 | | javascript_imports.append('%sjs/admin/CollapsedFieldsets.js' % ADMIN_MEDIA_PREFIX) |
|---|
| 849 | | try: |
|---|
| 850 | | for field_list in options['fields']: |
|---|
| 851 | | for f in field_list: |
|---|
| 852 | | if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface: |
|---|
| 853 | | javascript_imports.extend(['%sjs/SelectBox.js' % ADMIN_MEDIA_PREFIX, '%sjs/SelectFilter2.js' % ADMIN_MEDIA_PREFIX]) |
|---|
| 854 | | raise StopIteration |
|---|
| 855 | | except StopIteration: |
|---|
| 856 | | break |
|---|
| 857 | | for j in javascript_imports: |
|---|
| 858 | | t.append('<script type="text/javascript" src="%s"></script>' % j) |
|---|
| 859 | | |
|---|
| 860 | | t.append('{% endblock %}\n') |
|---|
| 861 | | if ordered_objects: |
|---|
| 862 | | coltype = 'colMS' |
|---|
| 863 | | else: |
|---|
| 864 | | coltype = 'colM' |
|---|
| 865 | | t.append('{%% block coltype %%}%s{%% endblock %%}\n' % coltype) |
|---|
| 866 | | t.append('{%% block bodyclass %%}%s-%s change-form{%% endblock %%}\n' % (app_label, opts.object_name.lower())) |
|---|
| 867 | | breadcrumb_title = add and "Add %s" % opts.verbose_name or '{{ original|striptags|truncatewords:"18" }}' |
|---|
| 868 | | t.append('{%% block breadcrumbs %%}{%% if not is_popup %%}<div class="breadcrumbs"><a href="../../../">Home</a> › <a href="../">%s</a> › %s</div>{%% endif %%}{%% endblock %%}\n' % \ |
|---|
| 869 | | (capfirst(opts.verbose_name_plural), breadcrumb_title)) |
|---|
| 870 | | t.append('{% block content %}<div id="content-main">\n') |
|---|
| 871 | | if change: |
|---|
| 872 | | t.append('{% if not is_popup %}') |
|---|
| 873 | | t.append('<ul class="object-tools"><li><a href="history/" class="historylink">History</a></li>') |
|---|
| 874 | | if hasattr(opts.get_model_module().Klass, 'get_absolute_url'): |
|---|
| 875 | | t.append('<li><a href="/r/%s/{{ object_id }}/" class="viewsitelink">View on site</a></li>' % opts.get_content_type_id()) |
|---|
| 876 | | t.append('</ul>\n') |
|---|
| 877 | | t.append('{% endif %}') |
|---|
| 878 | | t.append('<form ') |
|---|
| 879 | | if opts.has_field_type(meta.FileField): |
|---|
| 880 | | t.append('enctype="multipart/form-data" ') |
|---|
| 881 | | t.append('action="%s" method="post">\n' % form_url) |
|---|
| 882 | | t.append('{% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %}') |
|---|
| 883 | | if opts.admin.save_on_top: |
|---|
| 884 | | t.extend(_get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects)) |
|---|
| 885 | | t.append('{% if form.error_dict %}<p class="errornote">Please correct the error{{ form.error_dict.items|pluralize }} below.</p>{% endif %}\n') |
|---|
| 886 | | for fieldset_name, options in admin_field_objs: |
|---|
| 887 | | t.append('<fieldset class="module aligned %s">\n\n' % options.get('classes', '')) |
|---|
| 888 | | if fieldset_name: |
|---|
| 889 | | t.append('<h2>%s</h2>\n' % fieldset_name) |
|---|
| 890 | | for field_list in options['fields']: |
|---|
| 891 | | t.append(_get_admin_field(field_list, 'form.', False, add, change)) |
|---|
| 892 | | for f in field_list: |
|---|
| 893 | | if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface: |
|---|
| 894 | | t.append('<script type="text/javascript">addEvent(window, "load", function(e) { SelectFilter.init("id_%s", "%s", %s, %r); });</script>\n' % (f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX)) |
|---|
| 895 | | t.append('</fieldset>\n') |
|---|
| 896 | | if ordered_objects and change: |
|---|
| 897 | | t.append('<fieldset class="module"><h2>Ordering</h2>') |
|---|
| 898 | | t.append('<div class="form-row{% if form.order_.errors %} error{% endif %} ">\n') |
|---|
| 899 | | t.append('{% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %}') |
|---|
| 900 | | t.append('<p><label for="id_order_">Order:</label> {{ form.order_ }}</p>\n') |
|---|
| 901 | | t.append('</div></fieldset>\n') |
|---|
| 902 | | for rel_obj, rel_field in opts.get_inline_related_objects(): |
|---|
| 903 | | var_name = rel_obj.object_name.lower() |
|---|
| 904 | | field_list = [f for f in rel_obj.fields + rel_obj.many_to_many if f.editable and f != rel_field] |
|---|
| 905 | | |
|---|
| 906 | | t.append('<fieldset class="module%s">\n' % ((rel_field.rel.edit_inline != meta.TABULAR) and ' aligned' or '')) |
|---|
| 907 | | view_on_site = '' |
|---|
| 908 | | if change and hasattr(rel_obj, 'get_absolute_url'): |
|---|
| 909 | | view_on_site = '{%% if %s.original %%}<a href="/r/{{ %s.content_type_id }}/{{ %s.original.id }}/">View on site</a>{%% endif %%}' % (var_name, var_name, var_name) |
|---|
| 910 | | if rel_field.rel.edit_inline == meta.TABULAR: |
|---|
| 911 | | t.append('<h2>%s</h2>\n<table>\n' % capfirst(rel_obj.verbose_name_plural)) |
|---|
| 912 | | t.append('<thead><tr>') |
|---|
| 913 | | for f in field_list: |
|---|
| 914 | | if isinstance(f, meta.AutoField): |
|---|
| 915 | | continue |
|---|
| 916 | | t.append('<th%s>%s</th>' % (f.blank and ' class="optional"' or '', capfirst(f.verbose_name))) |
|---|
| 917 | | t.append('</tr></thead>\n') |
|---|
| 918 | | t.append('{%% for %s in form.%s %%}\n' % (var_name, rel_obj.module_name)) |
|---|
| 919 | | if change: |
|---|
| 920 | | for f in field_list: |
|---|
| 921 | | if use_raw_id_admin(f): |
|---|
| 922 | | t.append('{%% if %s.original %%}' % var_name) |
|---|
| 923 | | t.append('<tr class="row-label {% cycle row1,row2 %}">') |
|---|
| 924 | | t.append('<td colspan="%s"><strong>{{ %s.original }}</strong></td>' % (30, var_name)) |
|---|
| 925 | | t.append('</tr>{% endif %}\n') |
|---|
| 926 | | break |
|---|
| 927 | | t.append('{%% if %s %%}\n' % ' or '.join(['%s.%s.errors' % (var_name, f.name) for f in field_list])) |
|---|
| 928 | | t.append('<tr class="errorlist"><td colspan="%s">%s</td></tr>\n{%% endif %%}\n' % \ |
|---|
| 929 | | (len(field_list), ''.join(['{{ %s.%s.html_error_list }}' % (var_name, f.name) for f in field_list]))) |
|---|
| 930 | | t.append('<tr class="{% cycle row1,row2 %}">\n') |
|---|
| 931 | | hidden_fields = [] |
|---|
| 932 | | for f in field_list: |
|---|
| 933 | | form_widget = _get_admin_field_form_widget(f, var_name+'.', True, add, change) |
|---|
| 934 | | # Don't put AutoFields within a <td>, because they're hidden. |
|---|
| 935 | | if not isinstance(f, meta.AutoField): |
|---|
| 936 | | # Fields with raw_id_admin=True get class="nowrap". |
|---|
| 937 | | if use_raw_id_admin(f): |
|---|
| 938 | | t.append('<td class="nowrap {%% if %s.%s.errors %%}error"{%% endif %%}">%s</td>\n' % (var_name, f.name, form_widget)) |
|---|
| 939 | | else: |
|---|
| 940 | | t.append('<td{%% if %s.%s.errors %%} class="error"{%% endif %%}>%s</td>\n' % (var_name, f.name, form_widget)) |
|---|
| 941 | | else: |
|---|
| 942 | | hidden_fields.append(form_widget) |
|---|
| 943 | | if hasattr(rel_obj, 'get_absolute_url'): |
|---|
| 944 | | t.append('<td>%s</td>\n' % view_on_site) |
|---|
| 945 | | t.append('</tr>\n') |
|---|
| 946 | | t.append('{% endfor %}\n</table>\n') |
|---|
| 947 | | # Write out the hidden fields. We didn't write them out earlier |
|---|
| 948 | | # because it would've been invalid HTML. |
|---|
| 949 | | t.append('{%% for %s in form.%s %%}\n' % (var_name, rel_obj.module_name)) |
|---|
| 950 | | t.extend(hidden_fields) |
|---|
| 951 | | t.append('{% endfor %}\n') |
|---|
| 952 | | else: # edit_inline == STACKED |
|---|
| 953 | | t.append('{%% for %s in form.%s %%}' % (var_name, rel_obj.module_name)) |
|---|
| 954 | | t.append('<h2>%s #{{ forloop.counter }}</h2>' % capfirst(rel_obj.verbose_name)) |
|---|
| 955 | | if view_on_site: |
|---|
| 956 | | t.append('<p>%s</p>' % view_on_site) |
|---|
| 957 | | for f in field_list: |
|---|
| 958 | | # Don't put AutoFields within the widget -- just use the field. |
|---|
| 959 | | if isinstance(f, meta.AutoField): |
|---|
| 960 | | t.append(_get_admin_field_form_widget(f, var_name+'.', True, add, change)) |
|---|
| 961 | | else: |
|---|
| 962 | | t.append(_get_admin_field([f], var_name+'.', True, add, change)) |
|---|
| 963 | | t.append('{% endfor %}\n') |
|---|
| 964 | | t.append('</fieldset>\n') |
|---|
| 965 | | t.extend(_get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects)) |
|---|
| 966 | | if add: |
|---|
| 967 | | # Add focus to the first field on the form, if this is an "add" form. |
|---|
| 968 | | t.append('<script type="text/javascript">document.getElementById("id_%s").focus();</script>' % \ |
|---|
| 969 | | admin_field_objs[0][1]['fields'][0][0].get_manipulator_field_names('')[0]) |
|---|
| 970 | | if auto_populated_fields: |
|---|
| 971 | | t.append('<script type="text/javascript">') |
|---|
| 972 | | for field in auto_populated_fields: |
|---|
| 973 | | if change: |
|---|
| 974 | | t.append('document.getElementById("id_%s")._changed = true;' % field.name) |
|---|
| 975 | | else: |
|---|
| 976 | | t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name) |
|---|
| 977 | | for f in field.prepopulate_from: |
|---|
| 978 | | t.append('document.getElementById("id_%s").onkeyup = function() { var e = document.getElementById("id_%s"); if (!e._changed) { e.value = URLify(%s, %s);}};' % \ |
|---|
| 979 | | (f, field.name, ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]), field.maxlength)) |
|---|
| 980 | | t.append('</script>\n') |
|---|
| 981 | | if change and ordered_objects: |
|---|
| 982 | | t.append('{% if form.order_objects %}<ul id="orderthese">{% for object in form.order_objects %}') |
|---|
| 983 | | t.append('<li id="p{%% firstof %(x)s %%}"><span id="handlep{%% firstof %(x)s %%}">{{ object|truncatewords:"5" }}</span></li>' % \ |
|---|
| 984 | | {'x': ' '.join(['object.%s' % o.pk.name for o in ordered_objects])}) |
|---|
| 985 | | t.append('{% endfor %}</ul>{% endif %}\n') |
|---|
| 986 | | t.append('</form>\n</div>\n{% endblock %}') |
|---|
| 987 | | return ''.join(t) |
|---|
| 988 | | |
|---|
| 989 | | def _get_admin_field(field_list, name_prefix, rel, add, change): |
|---|
| 990 | | "Returns the template code for editing the given list of fields in the admin template." |
|---|
| 991 | | field_names = [] |
|---|
| 992 | | for f in field_list: |
|---|
| 993 | | field_names.extend(f.get_manipulator_field_names(name_prefix)) |
|---|
| 994 | | div_class_names = ['form-row', '{%% if %s %%} error{%% endif %%}' % ' or '.join(['%s.errors' % n for n in field_names])] |
|---|
| 995 | | # Assumes BooleanFields won't be stacked next to each other! |
|---|
| 996 | | if isinstance(field_list[0], meta.BooleanField): |
|---|
| 997 | | div_class_names.append('checkbox-row') |
|---|
| 998 | | t = [] |
|---|
| 999 | | t.append('<div class="%s">\n' % ' '.join(div_class_names)) |
|---|
| 1000 | | for n in field_names: |
|---|
| 1001 | | t.append('{%% if %s.errors %%}{{ %s.html_error_list }}{%% endif %%}\n' % (n, n)) |
|---|
| 1002 | | for i, field in enumerate(field_list): |
|---|
| 1003 | | label_name = 'id_%s%s' % ((rel and "%s{{ forloop.counter0 }}." % name_prefix or ""), field.get_manipulator_field_names('')[0]) |
|---|
| 1004 | | # BooleanFields are a special case, because the checkbox widget appears to |
|---|
| 1005 | | # the *left* of the label. |
|---|
| 1006 | | if isinstance(field, meta.BooleanField): |
|---|
| 1007 | | t.append(_get_admin_field_form_widget(field, name_prefix, rel, add, change)) |
|---|
| 1008 | | t.append(' <label for="%s" class="vCheckboxLabel">%s</label>' % (label_name, capfirst(field.verbose_name))) |
|---|
| 1009 | | else: |
|---|
| 1010 | | class_names = [] |
|---|
| 1011 | | if not field.blank: |
|---|
| 1012 | | class_names.append('required') |
|---|
| 1013 | | if i > 0: |
|---|
| 1014 | | class_names.append('inline') |
|---|
| 1015 | | t.append('<label for="%s"%s>%s:</label> ' % (label_name, class_names and ' class="%s"' % ' '.join(class_names) or '', capfirst(field.verbose_name))) |
|---|
| 1016 | | t.append(_get_admin_field_form_widget(field, name_prefix, rel, add, change)) |
|---|
| 1017 | | if change and field.primary_key: |
|---|
| 1018 | | t.append('{{ %soriginal.%s }}' % ((rel and name_prefix or ''), field.name)) |
|---|
| 1019 | | if change and use_raw_id_admin(field): |
|---|
| 1020 | | if isinstance(field.rel, meta.ManyToOne): |
|---|
| 1021 | | if_bit = '%soriginal.get_%s' % (rel and name_prefix or '', field.name) |
|---|
| 1022 | | obj_repr = if_bit + '|truncatewords:"14"' |
|---|
| 1023 | | elif isinstance(field.rel, meta.ManyToMany): |
|---|
| 1024 | | if_bit = '%soriginal.get_%s_list' % (rel and name_prefix or '', field.name) |
|---|
| 1025 | | obj_repr = if_bit + '|join:", "|truncatewords:"14"' |
|---|
| 1026 | | t.append('{%% if %s %%} <strong>{{ %s }}</strong>{%% endif %%}' % (if_bit, obj_repr)) |
|---|
| 1027 | | if field.help_text: |
|---|
| 1028 | | t.append('<p class="help">%s</p>\n' % field.help_text) |
|---|
| 1029 | | t.append('</div>\n\n') |
|---|
| 1030 | | return ''.join(t) |
|---|
| 1031 | | |
|---|
| 1032 | | def _get_admin_field_form_widget(field, name_prefix, rel, add, change): |
|---|
| 1033 | | "Returns JUST the formfield widget for the field's admin interface." |
|---|
| 1034 | | field_names = field.get_manipulator_field_names(name_prefix) |
|---|
| 1035 | | if isinstance(field, meta.DateTimeField): |
|---|
| 1036 | | return '<p class="datetime">Date: {{ %s }}<br />Time: {{ %s }}</p>' % tuple(field_names) |
|---|
| 1037 | | t = ['{{ %s }}' % n for n in field_names] |
|---|
| 1038 | | if change and isinstance(field, meta.FileField): |
|---|
| 1039 | | return '{%% if %soriginal.%s %%}Currently: <a href="{{ %soriginal.get_%s_url }}">{{ %soriginal.%s }}</a><br />Change: %s{%% else %%}%s{%% endif %%}' % \ |
|---|
| 1040 | | (name_prefix, field.name, name_prefix, field.name, name_prefix, field.name, ''.join(t), ''.join(t)) |
|---|
| 1041 | | field_id = 'id_%s%s' % ((rel and "%s{{ forloop.counter0 }}." % name_prefix or ""), field.get_manipulator_field_names('')[0]) |
|---|
| 1042 | | # raw_id_admin fields get the little lookup link next to them |
|---|
| 1043 | | if use_raw_id_admin(field): |
|---|
| 1044 | | t.append(' <a href="../../../%s/%s/" class="related-lookup" id="lookup_%s" onclick="return showRelatedObjectLookupPopup(this);">' % \ |
|---|
| 1045 | | (field.rel.to.app_label, field.rel.to.module_name, field_id)) |
|---|
| 1046 | | t.append('<img src="%simg/admin/selector-search.gif" width="16" height="16" alt="Lookup" /></a>' % ADMIN_MEDIA_PREFIX) |
|---|
| 1047 | | # fields with relationships to editable objects get an "add another" link, |
|---|
| 1048 | | # but only if the field doesn't have raw_admin ('cause in that case they get |
|---|
| 1049 | | # the "add" button in the popup) |
|---|
| 1050 | | elif field.rel and (isinstance(field.rel, meta.ManyToOne) or isinstance(field.rel, meta.ManyToMany)) and field.rel.to.admin: |
|---|
| 1051 | | t.append('{%% if perms.%s.%s %%}' % (field.rel.to.app_label, field.rel.to.get_add_permission())) |
|---|
| 1052 | | t.append(' <a href="../../../%s/%s/add/" class="add-another" id="add_%s" onclick="return showAddAnotherPopup(this);">' % \ |
|---|
| 1053 | | (field.rel.to.app_label, field.rel.to.module_name, field_id)) |
|---|
| 1054 | | t.append('<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="Add Another" /></a>' % ADMIN_MEDIA_PREFIX) |
|---|
| 1055 | | t.append('{% endif %}') |
|---|
| 1056 | | return ''.join(t) |
|---|
| 1057 | | |
|---|
| 1058 | | def add_stage(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None): |
|---|
| 1059 | | mod, opts = _get_mod_opts(app_label, module_name) |
|---|
| 1060 | | if not request.user.has_perm(app_label + '.' + opts.get_add_permission()): |
|---|
| 1061 | | raise PermissionDenied |
|---|
| 1062 | | manipulator = mod.AddManipulator() |
|---|
| 1063 | | if request.POST: |
|---|
| 1064 | | new_data = request.POST.copy() |
|---|
| 1065 | | if opts.has_field_type(meta.FileField): |
|---|
| 1066 | | new_data.update(request.FILES) |
|---|
| 1067 | | errors = manipulator.get_validation_errors(new_data) |
|---|
| 1068 | | if not errors and not request.POST.has_key("_preview"): |
|---|
| 1069 | | for f in opts.many_to_many: |
|---|
| 1070 | | if f.rel.raw_id_admin: |
|---|
| 1071 | | new_data.setlist(f.name, new_data[f.name].split(",")) |
|---|
| 1072 | | manipulator.do_html2python(new_data) |
|---|
| 1073 | | new_object = manipulator.save(new_data) |
|---|
| 1074 | | pk_value = getattr(new_object, opts.pk.column) |
|---|
| 1075 | | log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.ADDITION) |
|---|
| 1076 | | msg = 'The %s "%s" was added successfully.' % (opts.verbose_name, new_object) |
|---|
| 1077 | | # Here, we distinguish between different save types by checking for |
|---|
| 1078 | | # the presence of keys in request.POST. |
|---|
| 1079 | | if request.POST.has_key("_continue"): |
|---|
| 1080 | | request.user.add_message("%s You may edit it again below." % msg) |
|---|
| 1081 | | if request.POST.has_key("_popup"): |
|---|
| 1082 | | post_url_continue += "?_popup=1" |
|---|
| 1083 | | return HttpResponseRedirect(post_url_continue % pk_value) |
|---|
| 1084 | | if request.POST.has_key("_popup"): |
|---|
| 1085 | | return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \ |
|---|
| 1086 | | (pk_value, repr(new_object).replace('"', '\\"'))) |
|---|
| 1087 | | elif request.POST.has_key("_addanother"): |
|---|
| 1088 | | request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) |
|---|
| 1089 | | return HttpResponseRedirect(request.path) |
|---|
| 1090 | | else: |
|---|
| 1091 | | request.user.add_message(msg) |
|---|
| 1092 | | return HttpResponseRedirect(post_url) |
|---|
| 1093 | | if request.POST.has_key("_preview"): |
|---|
| 1094 | | manipulator.do_html2python(new_data) |
|---|
| 1095 | | else: |
|---|
| 1096 | | new_data = {} |
|---|
| 1097 | | # Add default data. |
|---|
| 1098 | | for f in opts.fields: |
|---|
| 1099 | | if f.has_default(): |
|---|
| 1100 | | new_data.update( f.flatten_data() ) |
|---|
| 1101 | | # In required many-to-one fields with only one available choice, |
|---|
| 1102 | | # select that one available choice. Note: We have to check that |
|---|
| 1103 | | # the length of choices is *2*, not 1, because SelectFields always |
|---|
| 1104 | | # have an initial "blank" value. |
|---|
| 1105 | | elif not f.blank and ((isinstance(f.rel, meta.ManyToOne) and not f.rel.raw_id_admin) or f.choices) and len(manipulator[f.name].choices) == 2: |
|---|
| 1106 | | new_data[f.name] = manipulator[f.name].choices[1][0] |
|---|
| 1107 | | # In required many-to-many fields with only one available choice, |
|---|
| 1108 | | # select that one available choice. |
|---|
| 1109 | | for f in opts.many_to_many: |
|---|
| 1110 | | if not f.blank and not f.rel.edit_inline and not f.rel.raw_id_admin and len(manipulator[f.name].choices) == 1: |
|---|
| 1111 | | new_data[f.name] = [manipulator[f.name].choices[0][0]] |
|---|
| 1112 | | # Add default data for related objects. |
|---|
| 1113 | | for rel_opts, rel_field in opts.get_inline_related_objects(): |
|---|
| 1114 | | var_name = rel_opts.object_name.lower() |
|---|
| 1115 | | for i in range(rel_field.rel.num_in_admin): |
|---|
| 1116 | | for f in rel_opts.fields + rel_opts.many_to_many: |
|---|
| 1117 | | if f.has_default(): |
|---|
| 1118 | | for field_name in f.get_manipulator_field_names(''): |
|---|
| 1119 | | new_data['%s.%d.%s' % (var_name, i, field_name)] = f.get_default() |
|---|
| 1120 | | # Override the defaults with request.GET, if it exists. |
|---|
| 1121 | | new_data.update(request.GET) |
|---|
| 1122 | | errors = {} |
|---|
| 1123 | | |
|---|
| 1124 | | # Populate the FormWrapper. |
|---|
| 1125 | | form = formfields.FormWrapper(manipulator, new_data, errors) |
|---|
| 1126 | | for rel_opts, rel_field in opts.get_inline_related_objects(): |
|---|
| 1127 | | var_name = rel_opts.object_name.lower() |
|---|
| 1128 | | wrapper = [] |
|---|
| 1129 | | for i in range(rel_field.rel.num_in_admin): |
|---|
| 1130 | | collection = {} |
|---|
| 1131 | | for f in rel_opts.fields + rel_opts.many_to_many: |
|---|
| 1132 | | if f.editable and f != rel_field and not isinstance(f, meta.AutoField): |
|---|
| 1133 | | for field_name in f.get_manipulator_field_names(''): |
|---|
| 1134 | | full_field_name = '%s.%d.%s' % (var_name, i, field_name) |
|---|
| 1135 | | field = manipulator[full_field_name] |
|---|
| 1136 | | data = field.extract_data(new_data) |
|---|
| 1137 | | collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) |
|---|
| 1138 | | wrapper.append(formfields.FormFieldCollection(collection)) |
|---|
| 1139 | | setattr(form, rel_opts.module_name, wrapper) |
|---|
| 1140 | | |
|---|
| 1141 | | c = Context(request, { |
|---|
| 1142 | | 'title': 'Add %s' % opts.verbose_name, |
|---|
| 1143 | | "form": form, |
|---|
| 1144 | | "is_popup": request.REQUEST.has_key("_popup"), |
|---|
| 1145 | | }) |
|---|
| 1146 | | if object_id_override is not None: |
|---|
| 1147 | | c['object_id'] = object_id_override |
|---|
| 1148 | | raw_template = _get_template(opts, app_label, add=True, show_delete=show_delete, form_url=form_url) |
|---|
| 1149 | | t = loader.get_template_from_string(raw_template) |
|---|
| 1150 | | return HttpResponse(t.render(c)) |
|---|
| 1151 | | add_stage = staff_member_required(add_stage) |
|---|
| 1152 | | |
|---|
| 1153 | | def change_stage(request, app_label, module_name, object_id): |
|---|
| 1154 | | mod, opts = _get_mod_opts(app_label, module_name) |
|---|
| 1155 | | if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): |
|---|
| 1156 | | raise PermissionDenied |
|---|
| 1157 | | if request.POST and request.POST.has_key("_saveasnew"): |
|---|
| 1158 | | return add_stage(request, app_label, module_name, form_url='../add/') |
|---|
| 1159 | | try: |
|---|
| 1160 | | manipulator = mod.ChangeManipulator(object_id) |
|---|
| 1161 | | except ObjectDoesNotExist: |
|---|
| 1162 | | raise Http404 |
|---|
| 1163 | | |
|---|
| 1164 | | inline_related_objects = opts.get_inline_related_objects() |
|---|
| 1165 | | if request.POST: |
|---|
| 1166 | | new_data = request.POST.copy() |
|---|
| 1167 | | if opts.has_field_type(meta.FileField): |
|---|
| 1168 | | new_data.update(request.FILES) |
|---|
| 1169 | | |
|---|
| 1170 | | errors = manipulator.get_validation_errors(new_data) |
|---|
| 1171 | | if not errors and not request.POST.has_key("_preview"): |
|---|
| 1172 | | for f in opts.many_to_many: |
|---|
| 1173 | | if f.rel.raw_id_admin: |
|---|
| 1174 | | new_data.setlist(f.name, new_data[f.name].split(",")) |
|---|
| 1175 | | manipulator.do_html2python(new_data) |
|---|
| 1176 | | new_object = manipulator.save(new_data) |
|---|
| 1177 | | pk_value = getattr(new_object, opts.pk.column) |
|---|
| 1178 | | |
|---|
| 1179 | | # Construct the change message. |
|---|
| 1180 | | change_message = [] |
|---|
| 1181 | | if manipulator.fields_added: |
|---|
| 1182 | | change_message.append('Added %s.' % get_text_list(manipulator.fields_added, 'and')) |
|---|
| 1183 | | if manipulator.fields_changed: |
|---|
| 1184 | | change_message.append('Changed %s.' % get_text_list(manipulator.fields_changed, 'and')) |
|---|
| 1185 | | if manipulator.fields_deleted: |
|---|
| 1186 | | change_message.append('Deleted %s.' % get_text_list(manipulator.fields_deleted, 'and')) |
|---|
| 1187 | | change_message = ' '.join(change_message) |
|---|
| 1188 | | if not change_message: |
|---|
| 1189 | | change_message = 'No fields changed.' |
|---|
| 1190 | | |
|---|
| 1191 | | log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.CHANGE, change_message) |
|---|
| 1192 | | msg = 'The %s "%s" was changed successfully.' % (opts.verbose_name, new_object) |
|---|
| 1193 | | if request.POST.has_key("_continue"): |
|---|
| 1194 | | request.user.add_message("%s You may edit it again below." % msg) |
|---|
| 1195 | | if request.REQUEST.has_key('_popup'): |
|---|
| 1196 | | return HttpResponseRedirect(request.path + "?_popup=1") |
|---|
| 1197 | | else: |
|---|
| 1198 | | return HttpResponseRedirect(request.path) |
|---|
| 1199 | | elif request.POST.has_key("_saveasnew"): |
|---|
| 1200 | | request.user.add_message('The %s "%s" was added successfully. You may edit it again below.' % (opts.verbose_name, new_object)) |
|---|
| 1201 | | return HttpResponseRedirect("../%s/" % pk_value) |
|---|
| 1202 | | elif request.POST.has_key("_addanother"): |
|---|
| 1203 | | request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) |
|---|
| 1204 | | return HttpResponseRedirect("../add/") |
|---|
| 1205 | | else: |
|---|
| 1206 | | request.user.add_message(msg) |
|---|
| 1207 | | return HttpResponseRedirect("../") |
|---|
| 1208 | | if request.POST.has_key("_preview"): |
|---|
| 1209 | | manipulator.do_html2python(new_data) |
|---|
| 1210 | | else: |
|---|
| 1211 | | # Populate new_data with a "flattened" version of the current data. |
|---|
| 1212 | | new_data = {} |
|---|
| 1213 | | obj = manipulator.original_object |
|---|
| 1214 | | for f in opts.fields: |
|---|
| 1215 | | new_data.update(f.flatten_data(obj)) |
|---|
| 1216 | | for f in opts.many_to_many: |
|---|
| 1217 | | get_list_func = getattr(obj, 'get_%s_list' % f.rel.singular) |
|---|
| 1218 | | if f.rel.raw_id_admin: |
|---|
| 1219 | | new_data[f.name] = ",".join([str(getattr(i, f.rel.to.pk.column)) for i in get_list_func()]) |
|---|
| 1220 | | elif not f.rel.edit_inline: |
|---|
| 1221 | | new_data[f.name] = [getattr(i, f.rel.to.pk.column) for i in get_list_func()] |
|---|
| 1222 | | for rel_obj, rel_field in inline_related_objects: |
|---|
| 1223 | | var_name = rel_obj.object_name.lower() |
|---|
| 1224 | | for i, rel_instance in enumerate(getattr(obj, 'get_%s_list' % opts.get_rel_object_method_name(rel_obj, rel_field))()): |
|---|
| 1225 | | for f in rel_obj.fields: |
|---|
| 1226 | | if f.editable and f != rel_field: |
|---|
| 1227 | | for k, v in f.flatten_data(rel_instance).items(): |
|---|
| 1228 | | new_data['%s.%d.%s' % (var_name, i, k)] = v |
|---|
| 1229 | | for f in rel_obj.many_to_many: |
|---|
| 1230 | | new_data['%s.%d.%s' % (var_name, i, f.column)] = [j.id for j in getattr(rel_instance, 'get_%s_list' % f.rel.singular)()] |
|---|
| 1231 | | |
|---|
| 1232 | | # If the object has ordered objects on its admin page, get the existing |
|---|
| 1233 | | # order and flatten it into a comma-separated list of IDs. |
|---|
| 1234 | | id_order_list = [] |
|---|
| 1235 | | for rel_obj in opts.get_ordered_objects(): |
|---|
| 1236 | | id_order_list.extend(getattr(obj, 'get_%s_order' % rel_obj.object_name.lower())()) |
|---|
| 1237 | | if id_order_list: |
|---|
| 1238 | | new_data['order_'] = ','.join(map(str, id_order_list)) |
|---|
| 1239 | | errors = {} |
|---|
| 1240 | | |
|---|
| 1241 | | # Populate the FormWrapper. |
|---|
| 1242 | | form = formfields.FormWrapper(manipulator, new_data, errors) |
|---|
| 1243 | | form.original = manipulator.original_object |
|---|
| 1244 | | form.order_objects = [] |
|---|
| 1245 | | for rel_opts, rel_field in inline_related_objects: |
|---|
| 1246 | | var_name = rel_opts.object_name.lower() |
|---|
| 1247 | | wrapper = [] |
|---|
| 1248 | | orig_list = getattr(manipulator.original_object, 'get_%s_list' % opts.get_rel_object_method_name(rel_opts, rel_field))() |
|---|
| 1249 | | count = len(orig_list) + rel_field.rel.num_extra_on_change |
|---|
| 1250 | | if rel_field.rel.min_num_in_admin: |
|---|
| 1251 | | count = max(count, rel_field.rel.min_num_in_admin) |
|---|
| 1252 | | if rel_field.rel.max_num_in_admin: |
|---|
| 1253 | | count = min(count, rel_field.rel.max_num_in_admin) |
|---|
| 1254 | | for i in range(count): |
|---|
| 1255 | | collection = {'original': (i < len(orig_list) and orig_list[i] or None)} |
|---|
| 1256 | | for f in rel_opts.fields + rel_opts.many_to_many: |
|---|
| 1257 | | if f.editable and f != rel_field: |
|---|
| 1258 | | for field_name in f.get_manipulator_field_names(''): |
|---|
| 1259 | | |
|---|