Skip to content

Commit b7f45db

Browse files
committed
added ways to see and edit PersonExtResources.
- Legacy-Id: 17869
1 parent 38ba0f4 commit b7f45db

6 files changed

Lines changed: 180 additions & 2 deletions

File tree

ietf/ietfauth/tests.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -647,4 +647,44 @@ def test_send_apikey_report(self):
647647
self.assertIn("API key usage", mail['subject'])
648648
self.assertIn(" %s times" % count, body)
649649
self.assertIn(date, body)
650+
651+
def test_edit_person_extresources(self):
652+
url = urlreverse('ietf.ietfauth.views.edit_person_externalresources')
653+
person = PersonFactory()
654+
655+
r = self.client.get(url)
656+
self.assertNotEqual(r.status_code, 200)
657+
658+
self.client.login(username=person.user.username,password=person.user.username+'+password')
659+
660+
r = self.client.get(url)
661+
self.assertEqual(r.status_code,200)
662+
q = PyQuery(r.content)
663+
self.assertEqual(len(q('form textarea[id=id_resources]')),1)
664+
665+
badlines = (
666+
'github_repo https://github3.com/some/repo',
667+
'github_notify badaddr',
668+
'website /not/a/good/url'
669+
'notavalidtag blahblahblah'
670+
)
671+
672+
for line in badlines:
673+
r = self.client.post(url, dict(resources=line, submit="1"))
674+
self.assertEqual(r.status_code, 200)
675+
q = PyQuery(r.content)
676+
self.assertTrue(q('.alert-danger'))
677+
678+
goodlines = """
679+
github_repo https://github.com/some/repo Some display text
680+
github_notify notify@example.com
681+
github_username githubuser
682+
website http://example.com/http/is/fine
683+
"""
684+
685+
r = self.client.post(url, dict(resources=goodlines, submit="1"))
686+
self.assertEqual(r.status_code,302)
687+
self.assertEqual(person.personextresource_set.count(), 4)
688+
self.assertEqual(person.personextresource_set.get(name__slug='github_repo').display_name, 'Some display text')
689+
650690

ietf/ietfauth/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
url(r'^logout/$', logout),
1919
url(r'^password/$', views.change_password),
2020
url(r'^profile/$', views.profile),
21+
url(r'^editexternalresources/$', views.edit_person_externalresources),
2122
url(r'^reset/$', views.password_reset),
2223
url(r'^reset/confirm/(?P<auth>[^/]+)/$', views.confirm_password_reset),
2324
url(r'^review/$', views.review_overview),

ietf/ietfauth/views.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,15 @@
6767
from ietf.ietfauth.htpasswd import update_htpasswd_file
6868
from ietf.ietfauth.utils import role_required, has_role
6969
from ietf.mailinglists.models import Subscribed, Whitelisted
70+
from ietf.name.models import ExtResourceName
7071
from ietf.person.models import Person, Email, Alias, PersonalApiKey, PERSON_API_KEY_VALUES
7172
from ietf.review.models import ReviewerSettings, ReviewWish, ReviewAssignment
7273
from ietf.review.utils import unavailable_periods_to_list, get_default_filter_re
7374
from ietf.doc.fields import SearchableDocumentField
7475
from ietf.utils.decorators import person_required
7576
from ietf.utils.mail import send_mail
77+
from ietf.utils.validators import validate_external_resource_value
78+
7679

7780
def index(request):
7881
return render(request, 'registration/index.html')
@@ -286,6 +289,79 @@ def profile(request):
286289
'settings':settings,
287290
})
288291

292+
@login_required
293+
@person_required
294+
def edit_person_externalresources(request):
295+
class PersonExtResourceForm(forms.Form):
296+
resources = forms.CharField(widget=forms.Textarea, label="Additional Resources", required=False,
297+
help_text=("Format: 'tag value (Optional description)'."
298+
" Separate multiple entries with newline. When the value is a URL, use https:// where possible.") )
299+
300+
def clean_resources(self):
301+
lines = [x.strip() for x in self.cleaned_data["resources"].splitlines() if x.strip()]
302+
errors = []
303+
for l in lines:
304+
parts = l.split()
305+
if len(parts) == 1:
306+
errors.append("Too few fields: Expected at least tag and value: '%s'" % l)
307+
elif len(parts) >= 2:
308+
name_slug = parts[0]
309+
try:
310+
name = ExtResourceName.objects.get(slug=name_slug)
311+
except ObjectDoesNotExist:
312+
errors.append("Bad tag in '%s': Expected one of %s" % (l, ', '.join([ o.slug for o in ExtResourceName.objects.all() ])))
313+
continue
314+
value = parts[1]
315+
try:
316+
validate_external_resource_value(name, value)
317+
except ValidationError as e:
318+
e.message += " : " + value
319+
errors.append(e)
320+
if errors:
321+
raise ValidationError(errors)
322+
return lines
323+
324+
def format_resources(resources, fs="\n"):
325+
res = []
326+
for r in resources:
327+
if r.display_name:
328+
res.append("%s %s (%s)" % (r.name.slug, r.value, r.display_name.strip('()')))
329+
else:
330+
res.append("%s %s" % (r.name.slug, r.value))
331+
# TODO: This is likely problematic if value has spaces. How then to delineate value and display_name? Perhaps in the short term move to comma or pipe separation.
332+
# Might be better to shift to a formset instead of parsing these lines.
333+
return fs.join(res)
334+
335+
person = request.user.person
336+
337+
old_resources = format_resources(person.personextresource_set.all())
338+
339+
if request.method == 'POST':
340+
form = PersonExtResourceForm(request.POST)
341+
if form.is_valid():
342+
old_resources = sorted(old_resources.splitlines())
343+
new_resources = sorted(form.cleaned_data['resources'])
344+
if old_resources != new_resources:
345+
person.personextresource_set.all().delete()
346+
for u in new_resources:
347+
parts = u.split(None, 2)
348+
name = parts[0]
349+
value = parts[1]
350+
display_name = ' '.join(parts[2:]).strip('()')
351+
person.personextresource_set.create(value=value, name_id=name, display_name=display_name)
352+
new_resources = format_resources(person.personextresource_set.all())
353+
messages.success(request,"Person resources updated.")
354+
else:
355+
messages.info(request,"No change in Person resources.")
356+
return redirect('ietf.ietfauth.views.profile')
357+
else:
358+
form = PersonExtResourceForm(initial={'resources': old_resources, })
359+
360+
info = "Valid tags:<br><br> %s" % ', '.join([ o.slug for o in ExtResourceName.objects.all().order_by('slug') ])
361+
# May need to explain the tags more - probably more reason to move to a formset.
362+
title = "Additional person resources"
363+
return render(request, 'ietfauth/edit_field.html',dict(person=person, form=form, title=title, info=info) )
364+
289365
def confirm_new_email(request, auth):
290366
try:
291367
username, email = django.core.signing.loads(auth, salt="add_email", max_age=settings.DAYS_TO_EXPIRE_REGISTRATION_LINK * 24 * 60 * 60)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{% extends "base.html" %}
2+
{# Copyright The IETF Trust 2015, All Rights Reserved #}
3+
{% load origin %}
4+
5+
{% load bootstrap3 %}
6+
7+
{% block title %}
8+
{{ title }} {{ person.plain_name }}
9+
{% endblock %}
10+
11+
{% block content %}
12+
{% origin %}
13+
<h1>{{ title }}<br><small>{{ person.plain_name }}</small></h1>
14+
15+
<p>
16+
<b>
17+
{{ info|safe }}
18+
</b>
19+
</p>
20+
21+
22+
<form enctype="multipart/form-data" method="post">
23+
{% csrf_token %}
24+
{% bootstrap_form form %}
25+
26+
{% buttons %}
27+
<!-- Regarding placement of buttons: https://www.lukew.com/ff/entry.asp?571 -->
28+
<button type="submit" class="btn btn-primary" name="submit" value="Save">Submit</button>
29+
<a class="btn btn-default pull-right" href="{% url "ietf.ietfauth.views.profile" %}">Back</a>
30+
{% endbuttons %}
31+
</form>
32+
33+
{% endblock %}

ietf/templates/person/profile.html

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,19 @@ <h2 id="roles">Roles</h2>
5757
</div>
5858
{% endif %}
5959

60-
60+
{% if person.personextresource_set.exists %}
61+
<div class="col-md-12">
62+
<h2 id="extresources">External Resources</h2>
63+
<table class="table">
64+
{% for extres in person.personextresource_set.all %}
65+
<tr>
66+
<td class="col-md-1"><span title="{{ extres.name.name }}">{% firstof extres.display_name extres.name.name %}</span></td>
67+
<td class="col-md-11">{{extres.value}}</td>
68+
</tr>
69+
{% endfor %}
70+
</table>
71+
</div>
72+
{% endif %}
6173

6274
<div class="col-md-6">
6375
<h2 id="rfcs">RFCs</h2>

ietf/templates/registration/edit_profile.html

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ <h1>Profile for {{ user.username }}</h1>
9494
<label class="col-sm-2 control-label">Nomcom Eligible</label>
9595
<div class="col-sm-1 form-control-static">{{person|is_nomcom_eligible|yesno:'Yes,No,No'}}</div>
9696
<div class="col-sm-9">
97-
<p class="alert alert-info form-control-static ">
97+
<p class="alert alert-info form-control-static">
9898
This calculation is EXPERIMENTAL.<br/>
9999

100100
If you believe it is incorrect, make sure you've added all the
@@ -111,6 +111,22 @@ <h1>Profile for {{ user.username }}</h1>
111111
</div>
112112
</div>
113113

114+
<div class="form-group">
115+
<label class="col-sm-2 control-label">External Resources</label>
116+
<div class="col-sm-10 form-control-static">
117+
{% for extres in person.personextresource_set.all %}
118+
<div class="row">
119+
<div class="col-sm-1"><span title="{{ extres.name.name}}">{% firstof extres.display_name extres.name.name %}</span></div>
120+
<div class="col-sm-11">{{extres.value}}
121+
{% if forloop.first %}&nbsp;<a href="{% url 'ietf.ietfauth.views.edit_person_externalresources' %}"><span class="fa fa-pencil"></span></a>{% endif %}
122+
</div>
123+
</div>
124+
{% empty %}
125+
<div class="row"><div class="col-sm-1">None&nbsp;<a href="{% url 'ietf.ietfauth.views.edit_person_externalresources' %}"><span class="fa fa-pencil"></span></a></div></div>
126+
{% endfor %}
127+
</div>
128+
</div>
129+
114130
<div class="form-group">
115131
<label class="col-sm-2 control-label">Email addresses</label>
116132
<div class="col-sm-10">

0 commit comments

Comments
 (0)