Merged [6629] from tterriberry@mozilla.com:
Allow wgchairs to edit replaces relationships.
This actually allows anyone with can_edit_stream_info permission
to edit the list. This does draft name completion, but does not
currently filter those names for likely replacements. Styling is
also basically non-existent.
Fixes #1002
- Legacy-Id: 6639
Note: SVN reference [6629] has been migrated to Git commit 9ef29a323f
This commit is contained in:
commit
f029c88347
|
@ -75,8 +75,10 @@ urlpatterns += patterns('',
|
|||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/submit-to-iesg/$', views_draft.to_iesg, name='doc_to_iesg'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/resurrect/$', views_draft.resurrect, name='doc_resurrect'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/addcomment/$', views_doc.add_comment, name='doc_add_comment'),
|
||||
url(r'^ajax/internet_draft/?$', views_draft.doc_ajax_internet_draft, name="doc_ajax_internet_draft"),
|
||||
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/stream/$', views_draft.change_stream, name='doc_change_stream'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/replaces/$', views_draft.replaces, name='doc_change_replaces'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/notify/$', views_draft.edit_notices, name='doc_change_notify'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/status/$', views_draft.change_intention, name='doc_change_intended_status'),
|
||||
url(r'^(?P<name>[A-Za-z0-9._+-]+)/edit/telechat/$', views_draft.telechat_date, name='doc_change_telechat_date'),
|
||||
|
|
|
@ -8,6 +8,7 @@ from django.core.urlresolvers import reverse as urlreverse
|
|||
from django.template.loader import render_to_string
|
||||
from django.template import RequestContext
|
||||
from django import forms
|
||||
from django.utils import simplejson
|
||||
from django.utils.html import strip_tags
|
||||
from django.db.models import Max
|
||||
from django.conf import settings
|
||||
|
@ -23,6 +24,7 @@ from ietf.doc.lastcall import request_last_call
|
|||
from ietf.utils.textupload import get_cleaned_text_file_content
|
||||
from ietf.person.forms import EmailsField
|
||||
from ietf.group.models import Group
|
||||
from ietf.secr.lib import jsonapi
|
||||
|
||||
from ietf.ietfworkflows.models import Stream
|
||||
from ietf.ietfworkflows.utils import update_stream
|
||||
|
@ -268,6 +270,143 @@ def change_stream(request, name):
|
|||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
@jsonapi
|
||||
def doc_ajax_internet_draft(request):
|
||||
if request.method != 'GET' or not request.GET.has_key('term'):
|
||||
return { 'success' : False, 'error' : 'No term submitted or not GET' }
|
||||
q = request.GET.get('term')
|
||||
results = DocAlias.objects.filter(name__icontains=q)
|
||||
if (results.count() > 20):
|
||||
results = results[:20]
|
||||
elif results.count() == 0:
|
||||
return { 'success' : False, 'error' : "No results" }
|
||||
response = [dict(id=r.id, label=r.name) for r in results]
|
||||
return response
|
||||
|
||||
def collect_email_addresses(emails, doc):
|
||||
for author in doc.authors.all():
|
||||
if author.address not in emails:
|
||||
emails[author.address] = '"%s"' % (author.person.name)
|
||||
if doc.group.acronym != 'none':
|
||||
for role in doc.group.role_set.filter(name='chair'):
|
||||
if role.email.address not in emails:
|
||||
emails[role.email.address] = '"%s"' % (role.person.name)
|
||||
if doc.group.type.slug == 'wg':
|
||||
address = '%s-ads@tools.ietf.org' % doc.group.acronym
|
||||
if address not in emails:
|
||||
emails[address] = '"%s-ads"' % (doc.group.acronym)
|
||||
elif doc.group.type.slug == 'rg':
|
||||
email = doc.group.parent.role_set.filter(name='char')[0].email
|
||||
if email.address not in emails:
|
||||
emails[email.address] = '"%s"' % (email.person.name)
|
||||
if doc.shepherd:
|
||||
address = doc.shepherd.email_address();
|
||||
if address not in emails:
|
||||
emails[address] = '"%s"' % (doc.shepherd.name)
|
||||
return emails
|
||||
|
||||
class ReplacesForm(forms.Form):
|
||||
replaces = forms.CharField(max_length=512,widget=forms.HiddenInput)
|
||||
comment = forms.CharField(widget=forms.Textarea, required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.doc = kwargs.pop('doc')
|
||||
super(ReplacesForm, self).__init__(*args, **kwargs)
|
||||
drafts = {}
|
||||
for d in self.doc.related_that_doc("replaces"):
|
||||
drafts[d.id] = d.document.name
|
||||
self.initial['replaces'] = simplejson.dumps(drafts)
|
||||
|
||||
def clean_replaces(self):
|
||||
data = self.cleaned_data['replaces'].strip()
|
||||
if data:
|
||||
ids = [int(x) for x in simplejson.loads(data)]
|
||||
else:
|
||||
return []
|
||||
objects = []
|
||||
for id in ids:
|
||||
try:
|
||||
d = DocAlias.objects.get(pk=id)
|
||||
except DocAlias.DoesNotExist, e:
|
||||
raise forms.ValidationError("ERROR: %s not found for id %d" % DocAlias._meta.verbos_name, id)
|
||||
if d.document == self.doc:
|
||||
raise forms.ValidationError("ERROR: A draft can't replace itself")
|
||||
if d.document.type_id == "draft" and d.document.get_state_slug() == "rfc":
|
||||
raise forms.ValidationError("ERROR: A draft can't replace an RFC")
|
||||
objects.append(d)
|
||||
return objects
|
||||
|
||||
def replaces(request, name):
|
||||
"""Change 'replaces' set of a Document of type 'draft' , notifying parties
|
||||
as necessary and logging the change as a comment."""
|
||||
doc = get_object_or_404(Document, docalias__name=name)
|
||||
if doc.type_id != 'draft':
|
||||
raise Http404
|
||||
if not (has_role(request.user, ("Secretariat", "Area Director"))
|
||||
or is_authorized_in_doc_stream(request.user, doc)):
|
||||
return HttpResponseForbidden("You do not have the necessary permissions to view this page")
|
||||
login = request.user.get_profile()
|
||||
if request.method == 'POST':
|
||||
form = ReplacesForm(request.POST, doc=doc)
|
||||
if form.is_valid():
|
||||
new_replaces = set(form.cleaned_data['replaces'])
|
||||
comment = form.cleaned_data['comment'].strip()
|
||||
old_replaces = set(doc.related_that_doc("replaces"))
|
||||
if new_replaces != old_replaces:
|
||||
save_document_in_history(doc)
|
||||
emails = {}
|
||||
emails = collect_email_addresses(emails, doc)
|
||||
relationship = DocRelationshipName.objects.get(slug='replaces')
|
||||
for d in old_replaces:
|
||||
if d not in new_replaces:
|
||||
emails = collect_email_addresses(emails, d.document)
|
||||
RelatedDocument.objects.filter(source=doc, target=d,
|
||||
relationship=relationship).delete()
|
||||
for d in new_replaces:
|
||||
if d not in old_replaces:
|
||||
emails = collect_email_addresses(emails, d.document)
|
||||
RelatedDocument.objects.create(source=doc, target=d,
|
||||
relationship=relationship)
|
||||
e = DocEvent(doc=doc,by=login,type='changed_document')
|
||||
new_replaces_names = ", ".join([d.name for d in new_replaces])
|
||||
if not new_replaces_names:
|
||||
new_replaces_names = "None"
|
||||
old_replaces_names = ", ".join([d.name for d in old_replaces])
|
||||
if not old_replaces_names:
|
||||
old_replaces_names = "None"
|
||||
e.desc = u"Set of documents this document replaces changed to <b>%s</b> from %s"% (new_replaces_names, old_replaces_names)
|
||||
e.save()
|
||||
email_desc = e.desc
|
||||
if comment:
|
||||
c = DocEvent(doc=doc,by=login,type="added_comment")
|
||||
c.desc = comment
|
||||
c.save()
|
||||
email_desc += "\n"+c.desc
|
||||
doc.time = e.time
|
||||
doc.save()
|
||||
email_list = []
|
||||
for key in sorted(emails):
|
||||
if emails[key]:
|
||||
email_list.append('%s <%s>' % (emails[key], key))
|
||||
else:
|
||||
email_list.append('<%s>' % key)
|
||||
email_string = ", ".join(email_list)
|
||||
send_mail(request, email_string,
|
||||
"DraftTracker Mail System <iesg-secretary@ietf.org>",
|
||||
"%s updated by %s" % (doc.file_tag, login),
|
||||
"doc/mail/change_notice.txt",
|
||||
dict(text=html_to_text(email_desc),
|
||||
doc=doc,
|
||||
url=settings.IDTRACKER_BASE_URL + doc.get_absolute_url()))
|
||||
return HttpResponseRedirect(doc.get_absolute_url())
|
||||
else:
|
||||
form = ReplacesForm(doc=doc)
|
||||
return render_to_response('idrfc/change_replaces.html',
|
||||
dict(form=form,
|
||||
doc=doc,
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
class ChangeIntentionForm(forms.Form):
|
||||
intended_std_level = forms.ModelChoiceField(IntendedStdLevelName.objects.filter(used=True), empty_label="(None)", required=True, label="Intended RFC status")
|
||||
comment = forms.CharField(widget=forms.Textarea, required=False)
|
||||
|
|
|
@ -35,8 +35,19 @@
|
|||
{% if resurrected_by %}- resurrect requested by {{ resurrected_by }}{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if replaces %}<div>Replaces: {{ replaces|join:", "|urlize_ietf_docs }}</div>{% endif %}
|
||||
{% if can_edit_stream_info %}
|
||||
<div>
|
||||
{% if replaces %}
|
||||
<a class="editlink" href="{% url doc_change_replaces name=doc.name %}">Replaces</a>:
|
||||
{{ replaces|join:", "|urlize_ietf_docs }}
|
||||
{% else %}
|
||||
<a class="editlink" href="{% url doc_change_replaces name=doc.name %}">Replaces: None</a>
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
{% if replaces %}<div>Replaces: {{ replaces|join:", "|urlize_ietf_docs }}</div>{% endif %}
|
||||
{% endif %}
|
||||
{% if replaced_by %}<div>Replaced by: {{ replaced_by|join:", "|urlize_ietf_docs }}</div>{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
86
ietf/templates/idrfc/change_replaces.html
Normal file
86
ietf/templates/idrfc/change_replaces.html
Normal file
|
@ -0,0 +1,86 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Change which documents {{ doc }} replaces{% endblock %}
|
||||
{% block pagehead %}{{ block.super }}
|
||||
<!--TODO: The template will later load another version of jQuery.
|
||||
We should eliminate the duplication.-->
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/jquery-1.5.1.min.js"></script>
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/jquery-ui-1.8.9.min.js"></script>
|
||||
<script type="text/javascript" src="{{ SECR_STATIC_URL }}js/jquery.json-2.2.min.js"></script>
|
||||
<link rel="stylesheet" href="{{ SECR_STATIC_URL }}css/redmond/jquery-ui-1.8.9.custom.css" type="text/css" media="screen" charset="utf-8"/>
|
||||
|
||||
<script type="text/javascript">
|
||||
// Free up the $ and jQuery names.
|
||||
j=$.noConflict(true);
|
||||
j(document).ready(function($) {
|
||||
function add_to_list(list, id, label) {
|
||||
list.append('<li><a href="' + id
|
||||
+ '"><img src="{{ SECR_STATIC_URL }}img/delete.png" alt="delete"></a> '
|
||||
+ label + '</li>');
|
||||
}
|
||||
|
||||
function setup_ajax(field, list, searchfield, url) {
|
||||
var datastore = {};
|
||||
window.field = field;
|
||||
if (field.val() != '') {
|
||||
datastore = $.parseJSON(field.val());
|
||||
}
|
||||
$.each(datastore, function(k, v) {
|
||||
add_to_list(list, k, v);
|
||||
});
|
||||
searchfield.autocomplete({
|
||||
source: url,
|
||||
minLength: 1,
|
||||
select: function(event, ui) {
|
||||
datastore[ui.item.id] = ui.item.label;
|
||||
field.val($.toJSON(datastore));
|
||||
searchfield.val('');
|
||||
add_to_list(list, ui.item.id, ui.item.label);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
// Automatically select the first element in the autocomplete list, so
|
||||
// that hitting Enter adds it.
|
||||
$(".ui-autocomplete-input").live("autocompleteopen", function() {
|
||||
var autocomplete = $(this).data("autocomplete");
|
||||
var menu = autocomplete.menu;
|
||||
menu.activate($.Event({ type: "mouseenter" }),
|
||||
menu.element.children().first());
|
||||
});
|
||||
list.delegate("a", "click", function() {
|
||||
delete datastore[$(this).attr("href")];
|
||||
field.val($.toJSON(datastore));
|
||||
$(this).closest("li").remove();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
setup_ajax($("#id_replaces"), $("#replaces_list"),
|
||||
$("#id_replaces_search"), "{% url doc_ajax_internet_draft %}");
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>Change which documents {{ doc }} replaces</h1>
|
||||
|
||||
<form class="change-replaces" action="" method="post">
|
||||
{{ form.non_field_errors }}
|
||||
{{ form.replaces.label_tag }}
|
||||
<input type="text" id="id_replaces_search">
|
||||
{{ form.replaces }}
|
||||
<ul id="replaces_list"></ul>
|
||||
{{ form.replaces.errors }}
|
||||
<table>
|
||||
<tr>
|
||||
<td>{{ form.comment.label_tag }}</td>
|
||||
<td>{{ form.comment.errors }} {{ form.comment }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="actions">
|
||||
<a href="{{ doc.get_absolute_url }}">Back</a>
|
||||
<input type="submit" value="Save"/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
{% endblock %}
|
Loading…
Reference in a new issue