Skip to content

Commit 7587d56

Browse files
committed
Added GroupExtResources to the group about page, and added the ability to edit them.
- Legacy-Id: 17685
1 parent 2de9eb9 commit 7587d56

File tree

6 files changed

+134
-12
lines changed

6 files changed

+134
-12
lines changed

ietf/doc/tests_draft.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1135,7 +1135,6 @@ def test_edit_doc_extresources(self):
11351135
q = PyQuery(r.content)
11361136
self.assertEqual(len(q('form textarea[id=id_resources]')),1)
11371137

1138-
# AMHERE
11391138
badlines = (
11401139
'github_repo https://github3.com/some/repo',
11411140
'github_notify badaddr',

ietf/doc/views_draft.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,11 +1320,11 @@ def format_resources(resources, fs="\n"):
13201320
value = parts[1]
13211321
display_name = ' '.join(parts[2:]).strip('()')
13221322
doc.docextresource_set.create(value=value, name_id=name, display_name=display_name)
1323-
new_resources = format_resources(doc.docextresource_set.all())
1324-
e = DocEvent(doc=doc, rev=doc.rev, by=request.user.person, type='changed_document')
1325-
e.desc = "Changed document external resources from:\n\n%s\n\nto:\n\n%s" % (old_resources, new_resources)
1326-
e.save()
1327-
doc.save_with_history([e])
1323+
new_resources = format_resources(doc.docextresource_set.all())
1324+
e = DocEvent(doc=doc, rev=doc.rev, by=request.user.person, type='changed_document')
1325+
e.desc = "Changed document external resources from:\n\n%s\n\nto:\n\n%s" % (old_resources, new_resources)
1326+
e.save()
1327+
doc.save_with_history([e])
13281328
messages.success(request,"Document resources updated.")
13291329
else:
13301330
messages.info(request,"No change in Document resources.")

ietf/group/forms.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@
1313
# Django imports
1414
from django import forms
1515
from django.utils.html import mark_safe # type:ignore
16+
from django.core.exceptions import ValidationError, ObjectDoesNotExist
1617

1718
# IETF imports
1819
from ietf.group.models import Group, GroupHistory, GroupStateName
19-
from ietf.name.models import ReviewTypeName
20+
from ietf.name.models import ReviewTypeName, ExtResourceName
2021
from ietf.person.fields import SearchableEmailsField, PersonEmailChoiceField
2122
from ietf.person.models import Person
2223
from ietf.review.models import ReviewerSettings, UnavailablePeriod, ReviewSecretarySettings
@@ -26,6 +27,7 @@
2627
from ietf.utils.text import strip_suffix
2728
#from ietf.utils.ordereddict import insert_after_in_ordered_dict
2829
from ietf.utils.fields import DatepickerDateField, MultiEmailField
30+
from ietf.utils.validators import validate_external_resource_value
2931

3032
# --- Constants --------------------------------------------------------
3133

@@ -82,6 +84,7 @@ class GroupForm(forms.Form):
8284
list_subscribe = forms.CharField(max_length=255, required=False)
8385
list_archive = forms.CharField(max_length=255, required=False)
8486
urls = forms.CharField(widget=forms.Textarea, label="Additional URLs", help_text="Format: https://site/path (Optional description). Separate multiple entries with newline. Prefer HTTPS URLs where possible.", required=False)
87+
resources = forms.CharField(widget=forms.Textarea, label="Additional Resources", help_text="UPDATEME: Format: https://site/path (Optional description). Separate multiple entries with newline. Prefer HTTPS URLs where possible.", required=False)
8588
closing_note = forms.CharField(widget=forms.Textarea, label="Closing note", required=False)
8689

8790
def __init__(self, *args, **kwargs):
@@ -127,6 +130,12 @@ def __init__(self, *args, **kwargs):
127130
for f in keys:
128131
if f != field and not (f == 'closing_note' and field == 'state'):
129132
del self.fields[f]
133+
if 'resources' in self.fields:
134+
info = "Format: 'tag value (Optional description)'. " \
135+
+ "Separate multiple entries with newline. When the value is a URL, use https:// where possible.<br>" \
136+
+ "Valid tags: %s" % ', '.join([ o.slug for o in ExtResourceName.objects.all().order_by('slug') ])
137+
self.fields['resources'].help_text = mark_safe('<div>'+info+'</div>')
138+
130139

131140
def clean_acronym(self):
132141
# Changing the acronym of an already existing group will cause 404s all
@@ -186,6 +195,30 @@ def clean_acronym(self):
186195
def clean_urls(self):
187196
return [x.strip() for x in self.cleaned_data["urls"].splitlines() if x.strip()]
188197

198+
def clean_resources(self):
199+
lines = [x.strip() for x in self.cleaned_data["resources"].splitlines() if x.strip()]
200+
errors = []
201+
for l in lines:
202+
parts = l.split()
203+
if len(parts) == 1:
204+
errors.append("Too few fields: Expected at least tag and value: '%s'" % l)
205+
elif len(parts) >= 2:
206+
name_slug = parts[0]
207+
try:
208+
name = ExtResourceName.objects.get(slug=name_slug)
209+
except ObjectDoesNotExist:
210+
errors.append("Bad tag in '%s': Expected one of %s" % (l, ', '.join([ o.slug for o in ExtResourceName.objects.all() ])))
211+
continue
212+
value = parts[1]
213+
try:
214+
validate_external_resource_value(name, value)
215+
except ValidationError as e:
216+
e.message += " : " + value
217+
errors.append(e)
218+
if errors:
219+
raise ValidationError(errors)
220+
return lines
221+
189222
def clean_delegates(self):
190223
if len(self.cleaned_data["delegates"]) > MAX_GROUP_DELEGATES:
191224
raise forms.ValidationError("At most %s delegates can be appointed at the same time, please remove %s delegates." % (

ietf/group/tests_info.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,46 @@ def test_edit_info(self):
634634
self.assertTrue(prefix+'@' in outbox[0]['To'])
635635
self.assertTrue(outbox[0].get_payload(decode=True).decode(str(outbox[0].get_charset())).startswith('Sec Retary'))
636636

637+
def test_edit_extresources(self):
638+
group = GroupFactory(acronym='mars',parent=GroupFactory(type_id='area'))
639+
CharterFactory(group=group)
640+
641+
url = urlreverse('ietf.group.views.edit', kwargs=dict(group_type=group.type_id, acronym=group.acronym, action="edit", field="resources"))
642+
login_testing_unauthorized(self, "secretary", url)
643+
644+
r = self.client.get(url)
645+
self.assertEqual(r.status_code,200)
646+
q = PyQuery(r.content)
647+
self.assertEqual(len(q('form textarea[id=id_resources]')),1)
648+
649+
badlines = (
650+
'github_repo https://github3.com/some/repo',
651+
'github_notify badaddr',
652+
'website /not/a/good/url'
653+
'notavalidtag blahblahblah'
654+
)
655+
656+
for line in badlines:
657+
r = self.client.post(url, dict(resources=line, submit="1"))
658+
self.assertEqual(r.status_code, 200)
659+
q = PyQuery(r.content)
660+
self.assertTrue(q('.alert-danger'))
661+
662+
goodlines = """
663+
github_repo https://github.com/some/repo Some display text
664+
github_notify notify@example.com
665+
github_username githubuser
666+
website http://example.com/http/is/fine
667+
"""
668+
669+
r = self.client.post(url, dict(resources=goodlines, submit="1"))
670+
self.assertEqual(r.status_code,302)
671+
group = Group.objects.get(acronym=group.acronym)
672+
self.assertEqual(group.latest_event(GroupEvent,type="info_changed").desc[:20], 'Resources changed to')
673+
self.assertIn('github_username githubuser', group.latest_event(GroupEvent,type="info_changed").desc)
674+
self.assertEqual(group.groupextresource_set.count(), 4)
675+
self.assertEqual(group.groupextresource_set.get(name__slug='github_repo').display_name, 'Some display text')
676+
637677

638678
def test_edit_field(self):
639679
group = GroupFactory(acronym="mars")

ietf/group/views.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,17 @@ def format_urls(urls, fs="\n"):
880880
res.append(u.url)
881881
return fs.join(res)
882882

883+
def format_resources(resources, fs="\n"):
884+
res = []
885+
for r in resources:
886+
if r.display_name:
887+
res.append("%s %s (%s)" % (r.name.slug, r.value, r.display_name.strip('()')))
888+
else:
889+
res.append("%s %s" % (r.name.slug, r.value))
890+
# 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.
891+
# Might be better to shift to a formset instead of parsing these lines.
892+
return fs.join(res)
893+
883894
def diff(attr, name):
884895
if field and attr != field:
885896
return
@@ -933,11 +944,6 @@ def diff(attr, name):
933944
else:
934945
save_group_in_history(group)
935946

936-
937-
## XXX Remove after testing
938-
# if action == "charter" and not group.charter: # make sure we have a charter
939-
# group.charter = get_or_create_initial_charter(group, group_type)
940-
941947
changes = []
942948

943949
# update the attributes, keeping track of what we're doing
@@ -1023,6 +1029,19 @@ def diff(attr, name):
10231029
url = GroupURL(url=m.group('url'), name='', group=group)
10241030
url.save()
10251031

1032+
if 'resources' in clean:
1033+
old_resources = sorted(format_resources(group.groupextresource_set.all()).splitlines())
1034+
new_resources = sorted(clean['resources'])
1035+
if old_resources != new_resources:
1036+
group.groupextresource_set.all().delete()
1037+
for u in new_resources:
1038+
parts = u.split(None, 2)
1039+
name = parts[0]
1040+
value = parts[1]
1041+
display_name = ' '.join(parts[2:]).strip('()')
1042+
group.groupextresource_set.create(value=value, name_id=name, display_name=display_name)
1043+
changes.append(('resources', new_resources, desc('Resources', ", ".join(new_resources), ", ".join(old_resources))))
1044+
10261045
group.time = datetime.datetime.now()
10271046

10281047
if changes and not new_group:
@@ -1075,6 +1094,7 @@ def diff(attr, name):
10751094
list_subscribe=group.list_subscribe if group.list_subscribe else None,
10761095
list_archive=group.list_archive if group.list_archive else None,
10771096
urls=format_urls(group.groupurl_set.all()),
1097+
resources=format_resources(group.groupextresource_set.all()),
10781098
closing_note = closing_note,
10791099
)
10801100

ietf/templates/group/group_about.html

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,36 @@
138138
</tr>
139139
{% endif %}
140140
{% endwith %}
141+
142+
{% with group.groupextresource_set.all as resources %}
143+
{% if resources or can_edit_group %}
144+
<tr>
145+
<td></td>
146+
<th>Additional Resources</th>
147+
<td class="edit">
148+
{% if can_edit_group %}
149+
<a class="btn btn-default btn-xs" href="{% url 'ietf.group.views.edit' acronym=group.acronym field='resources' %}">Edit</a>
150+
{% endif %}
151+
</td>
152+
<td>
153+
{% if resources %}
154+
<table class="col-md-12 col-sm-12 col-xs-12">
155+
<tbody>
156+
{% for resource in resources|dictsort:"display_name" %}
157+
{% if resource.name.type.slug == 'url' or resource.name.type.slug == 'email' %}
158+
<tr><td> - <a href="{{ resource.value }}" title="{{resource.name.name}}">{% firstof resource.display_name resource.name.name %}</a></td></tr>
159+
{# Maybe make how a resource displays itself a method on the class so templates aren't doing this switching #}
160+
{% else %}
161+
<tr><td> - <span title="{{resource.name.name}}">{% firstof resource.display_name resource.name.name %}: {{resource.value}}</span></td></tr>
162+
{% endif %}
163+
{% endfor %}
164+
</tbody>
165+
</table>
166+
{% endif %}
167+
</td>
168+
</tr>
169+
{% endif %}
170+
{% endwith %}
141171
</tbody>
142172

143173
<tbody class="meta">

0 commit comments

Comments
 (0)