Added 'Additional URLs' for documents, the same way we have them for groups.
This could be used to point to a document source repository, to extracted yang module files, document wikis, and other relevant resources. - Legacy-Id: 14166
This commit is contained in:
parent
34c32e1b71
commit
92d425fd9b
|
@ -7,7 +7,7 @@ from models import (StateType, State, RelatedDocument, DocumentAuthor, Document,
|
||||||
DocHistoryAuthor, DocHistory, DocAlias, DocReminder, DocEvent, NewRevisionDocEvent,
|
DocHistoryAuthor, DocHistory, DocAlias, DocReminder, DocEvent, NewRevisionDocEvent,
|
||||||
StateDocEvent, ConsensusDocEvent, BallotType, BallotDocEvent, WriteupDocEvent, LastCallDocEvent,
|
StateDocEvent, ConsensusDocEvent, BallotType, BallotDocEvent, WriteupDocEvent, LastCallDocEvent,
|
||||||
TelechatDocEvent, BallotPositionDocEvent, ReviewRequestDocEvent, InitialReviewDocEvent,
|
TelechatDocEvent, BallotPositionDocEvent, ReviewRequestDocEvent, InitialReviewDocEvent,
|
||||||
AddedMessageEvent, SubmissionDocEvent, DeletedEvent, EditedAuthorsDocEvent, )
|
AddedMessageEvent, SubmissionDocEvent, DeletedEvent, EditedAuthorsDocEvent, DocumentURL)
|
||||||
|
|
||||||
|
|
||||||
from ietf.doc.utils import get_state_types
|
from ietf.doc.utils import get_state_types
|
||||||
|
@ -211,3 +211,7 @@ class BallotPositionDocEventAdmin(DocEventAdmin):
|
||||||
raw_id_fields = ["doc", "by", "ad", "ballot"]
|
raw_id_fields = ["doc", "by", "ad", "ballot"]
|
||||||
admin.site.register(BallotPositionDocEvent, BallotPositionDocEventAdmin)
|
admin.site.register(BallotPositionDocEvent, BallotPositionDocEventAdmin)
|
||||||
|
|
||||||
|
class DocumentUrlAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ['id', 'doc', 'tag', 'url', 'desc', ]
|
||||||
|
raw_id_fields = ['doc', ]
|
||||||
|
admin.site.register(DocumentURL, DocumentUrlAdmin)
|
||||||
|
|
|
@ -20,7 +20,8 @@ import debug # pyflakes:ignore
|
||||||
|
|
||||||
from ietf.group.models import Group
|
from ietf.group.models import Group
|
||||||
from ietf.name.models import ( DocTypeName, DocTagName, StreamName, IntendedStdLevelName, StdLevelName,
|
from ietf.name.models import ( DocTypeName, DocTagName, StreamName, IntendedStdLevelName, StdLevelName,
|
||||||
DocRelationshipName, DocReminderTypeName, BallotPositionName, ReviewRequestStateName, FormalLanguageName )
|
DocRelationshipName, DocReminderTypeName, BallotPositionName, ReviewRequestStateName, FormalLanguageName,
|
||||||
|
DocUrlTagName)
|
||||||
from ietf.person.models import Email, Person
|
from ietf.person.models import Email, Person
|
||||||
from ietf.utils import log
|
from ietf.utils import log
|
||||||
from ietf.utils.admin import admin_link
|
from ietf.utils.admin import admin_link
|
||||||
|
@ -777,9 +778,14 @@ class Document(DocumentInfo):
|
||||||
stream=self.stream, group=self.group)
|
stream=self.stream, group=self.group)
|
||||||
|
|
||||||
return dh
|
return dh
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DocumentURL(models.Model):
|
||||||
|
doc = models.ForeignKey(Document)
|
||||||
|
tag = models.ForeignKey(DocUrlTagName)
|
||||||
|
desc = models.CharField(max_length=255, default='', blank=True)
|
||||||
|
url = models.URLField()
|
||||||
|
|
||||||
class RelatedDocHistory(models.Model):
|
class RelatedDocHistory(models.Model):
|
||||||
source = models.ForeignKey('DocHistory')
|
source = models.ForeignKey('DocHistory')
|
||||||
target = models.ForeignKey('DocAlias', related_name="reversely_related_document_history_set")
|
target = models.ForeignKey('DocAlias', related_name="reversely_related_document_history_set")
|
||||||
|
|
|
@ -12,7 +12,7 @@ from ietf.doc.models import (BallotType, DeletedEvent, StateType, State, Documen
|
||||||
TelechatDocEvent, DocReminder, LastCallDocEvent, NewRevisionDocEvent, WriteupDocEvent,
|
TelechatDocEvent, DocReminder, LastCallDocEvent, NewRevisionDocEvent, WriteupDocEvent,
|
||||||
InitialReviewDocEvent, DocHistoryAuthor, BallotDocEvent, RelatedDocument,
|
InitialReviewDocEvent, DocHistoryAuthor, BallotDocEvent, RelatedDocument,
|
||||||
RelatedDocHistory, BallotPositionDocEvent, AddedMessageEvent, SubmissionDocEvent,
|
RelatedDocHistory, BallotPositionDocEvent, AddedMessageEvent, SubmissionDocEvent,
|
||||||
ReviewRequestDocEvent, EditedAuthorsDocEvent)
|
ReviewRequestDocEvent, EditedAuthorsDocEvent, DocumentURL)
|
||||||
|
|
||||||
from ietf.name.resources import BallotPositionNameResource, DocTypeNameResource
|
from ietf.name.resources import BallotPositionNameResource, DocTypeNameResource
|
||||||
class BallotTypeResource(ModelResource):
|
class BallotTypeResource(ModelResource):
|
||||||
|
@ -629,3 +629,22 @@ class EditedAuthorsDocEventResource(ModelResource):
|
||||||
"docevent_ptr": ALL_WITH_RELATIONS,
|
"docevent_ptr": ALL_WITH_RELATIONS,
|
||||||
}
|
}
|
||||||
api.doc.register(EditedAuthorsDocEventResource())
|
api.doc.register(EditedAuthorsDocEventResource())
|
||||||
|
|
||||||
|
|
||||||
|
from ietf.name.resources import DocUrlTagNameResource
|
||||||
|
class DocumentURLResource(ModelResource):
|
||||||
|
doc = ToOneField(DocumentResource, 'doc')
|
||||||
|
tag = ToOneField(DocUrlTagNameResource, 'tag')
|
||||||
|
class Meta:
|
||||||
|
queryset = DocumentURL.objects.all()
|
||||||
|
serializer = api.Serializer()
|
||||||
|
cache = SimpleCache()
|
||||||
|
#resource_name = 'documenturl'
|
||||||
|
filtering = {
|
||||||
|
"id": ALL,
|
||||||
|
"desc": ALL,
|
||||||
|
"url": ALL,
|
||||||
|
"doc": ALL_WITH_RELATIONS,
|
||||||
|
"tag": ALL_WITH_RELATIONS,
|
||||||
|
}
|
||||||
|
api.doc.register(DocumentURLResource())
|
||||||
|
|
|
@ -765,6 +765,11 @@ class ExpireLastCallTests(TestCase):
|
||||||
self.assertTrue('draft-ietf-mars-test@' in outbox[-1]['To'])
|
self.assertTrue('draft-ietf-mars-test@' in outbox[-1]['To'])
|
||||||
|
|
||||||
class IndividualInfoFormsTests(TestCase):
|
class IndividualInfoFormsTests(TestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.doc = make_test_data()
|
||||||
|
self.docname = self.doc.name
|
||||||
|
|
||||||
def test_doc_change_stream(self):
|
def test_doc_change_stream(self):
|
||||||
url = urlreverse('ietf.doc.views_draft.change_stream', kwargs=dict(name=self.docname))
|
url = urlreverse('ietf.doc.views_draft.change_stream', kwargs=dict(name=self.docname))
|
||||||
login_testing_unauthorized(self, "secretary", url)
|
login_testing_unauthorized(self, "secretary", url)
|
||||||
|
@ -1050,11 +1055,24 @@ class IndividualInfoFormsTests(TestCase):
|
||||||
q = PyQuery(r.content)
|
q = PyQuery(r.content)
|
||||||
self.assertTrue(q('textarea')[0].text.strip().startswith("As required by RFC 4858"))
|
self.assertTrue(q('textarea')[0].text.strip().startswith("As required by RFC 4858"))
|
||||||
|
|
||||||
def setUp(self):
|
def test_doc_change_document_urls(self):
|
||||||
make_test_data()
|
url = urlreverse('ietf.doc.views_draft.edit_document_urls', kwargs=dict(name=self.docname))
|
||||||
self.docname='draft-ietf-mars-test'
|
|
||||||
|
# get
|
||||||
|
login_testing_unauthorized(self, "secretary", url)
|
||||||
|
|
||||||
|
r = self.client.get(url)
|
||||||
|
self.assertEqual(r.status_code,200)
|
||||||
|
q = PyQuery(r.content)
|
||||||
|
self.assertEqual(len(q('form textarea[id=id_urls]')),1)
|
||||||
|
|
||||||
|
# direct edit
|
||||||
|
r = self.client.post(url, dict(urls='wiki https://wiki.org/ Wiki\nrepository https://repository.org/ Repo\n', submit="1"))
|
||||||
|
self.assertEqual(r.status_code,302)
|
||||||
self.doc = Document.objects.get(name=self.docname)
|
self.doc = Document.objects.get(name=self.docname)
|
||||||
|
self.assertTrue(self.doc.latest_event(DocEvent,type="changed_document").desc.startswith('Changed document URLs'))
|
||||||
|
self.assertIn('wiki https://wiki.org/', self.doc.latest_event(DocEvent,type="changed_document").desc)
|
||||||
|
self.assertIn('https://wiki.org/', [ u.url for u in self.doc.documenturl_set.all() ])
|
||||||
|
|
||||||
class SubmitToIesgTests(TestCase):
|
class SubmitToIesgTests(TestCase):
|
||||||
def test_verify_permissions(self):
|
def test_verify_permissions(self):
|
||||||
|
|
|
@ -118,7 +118,8 @@ urlpatterns = [
|
||||||
url(r'^%(name)s/edit/approvaltext/$' % settings.URL_REGEXPS, views_ballot.ballot_approvaltext),
|
url(r'^%(name)s/edit/approvaltext/$' % settings.URL_REGEXPS, views_ballot.ballot_approvaltext),
|
||||||
url(r'^%(name)s/edit/approveballot/$' % settings.URL_REGEXPS, views_ballot.approve_ballot),
|
url(r'^%(name)s/edit/approveballot/$' % settings.URL_REGEXPS, views_ballot.approve_ballot),
|
||||||
url(r'^%(name)s/edit/makelastcall/$' % settings.URL_REGEXPS, views_ballot.make_last_call),
|
url(r'^%(name)s/edit/makelastcall/$' % settings.URL_REGEXPS, views_ballot.make_last_call),
|
||||||
|
url(r'^%(name)s/edit/urls/$' % settings.URL_REGEXPS, views_draft.edit_document_urls),
|
||||||
|
|
||||||
url(r'^help/state/(?P<type>[\w-]+)/$', views_help.state_help),
|
url(r'^help/state/(?P<type>[\w-]+)/$', views_help.state_help),
|
||||||
url(r'^help/relationships/$', views_help.relationship_help),
|
url(r'^help/relationships/$', views_help.relationship_help),
|
||||||
url(r'^help/relationships/(?P<subset>\w+)/$', views_help.relationship_help),
|
url(r'^help/relationships/(?P<subset>\w+)/$', views_help.relationship_help),
|
||||||
|
|
|
@ -3,15 +3,17 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.conf import settings
|
||||||
|
from django.contrib import messages
|
||||||
|
from django.contrib.auth.decorators import login_required
|
||||||
|
from django.core.exceptions import ValidationError, ObjectDoesNotExist
|
||||||
|
from django.core.validators import URLValidator
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.http import HttpResponseRedirect, HttpResponseForbidden, Http404
|
from django.http import HttpResponseRedirect, HttpResponseForbidden, Http404
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.conf import settings
|
|
||||||
from django.forms.utils import ErrorList
|
from django.forms.utils import ErrorList
|
||||||
from django.contrib.auth.decorators import login_required
|
|
||||||
from django.template.defaultfilters import pluralize
|
from django.template.defaultfilters import pluralize
|
||||||
from django.contrib import messages
|
|
||||||
|
|
||||||
import debug # pyflakes:ignore
|
import debug # pyflakes:ignore
|
||||||
|
|
||||||
|
@ -33,7 +35,7 @@ from ietf.iesg.models import TelechatDate
|
||||||
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream, user_is_person
|
from ietf.ietfauth.utils import has_role, is_authorized_in_doc_stream, user_is_person
|
||||||
from ietf.ietfauth.utils import role_required
|
from ietf.ietfauth.utils import role_required
|
||||||
from ietf.message.models import Message
|
from ietf.message.models import Message
|
||||||
from ietf.name.models import IntendedStdLevelName, DocTagName, StreamName
|
from ietf.name.models import IntendedStdLevelName, DocTagName, StreamName, DocUrlTagName
|
||||||
from ietf.person.fields import SearchableEmailField
|
from ietf.person.fields import SearchableEmailField
|
||||||
from ietf.person.models import Person, Email
|
from ietf.person.models import Person, Email
|
||||||
from ietf.utils.mail import send_mail, send_mail_message
|
from ietf.utils.mail import send_mail, send_mail_message
|
||||||
|
@ -1110,14 +1112,89 @@ def edit_consensus(request, name):
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
class PublicationForm(forms.Form):
|
def edit_document_urls(request, name):
|
||||||
subject = forms.CharField(max_length=200, required=True)
|
class DocumentUrlForm(forms.Form):
|
||||||
body = forms.CharField(widget=forms.Textarea, required=True, strip=False)
|
urls = forms.CharField(widget=forms.Textarea, label="Additional URLs", required=False,
|
||||||
|
help_text=("Format: 'tag https://site/path (Optional description)'."
|
||||||
|
" Separate multiple entries with newline. Prefer HTTPS URLs where possible.") )
|
||||||
|
|
||||||
|
def clean_urls(self):
|
||||||
|
lines = [x.strip() for x in self.cleaned_data["urls"].splitlines() if x.strip()]
|
||||||
|
url_validator = URLValidator()
|
||||||
|
for l in lines:
|
||||||
|
errors = []
|
||||||
|
parts = l.split()
|
||||||
|
if len(parts) == 1:
|
||||||
|
errors.append("Too few fields: Expected at least url and tag: '%s'" % l)
|
||||||
|
elif len(parts) >= 2:
|
||||||
|
tag = parts[0]
|
||||||
|
url = parts[1]
|
||||||
|
try:
|
||||||
|
url_validator(url)
|
||||||
|
except ValidationError as e:
|
||||||
|
errors.append(e)
|
||||||
|
try:
|
||||||
|
DocUrlTagName.objects.get(slug=tag)
|
||||||
|
except ObjectDoesNotExist:
|
||||||
|
errors.append("Bad tag in '%s': Expected one of %s" % (l, ', '.join([ o.slug for o in DocUrlTagName.objects.all() ])))
|
||||||
|
if errors:
|
||||||
|
raise ValidationError(errors)
|
||||||
|
return lines
|
||||||
|
|
||||||
|
def format_urls(urls, fs="\n"):
|
||||||
|
res = []
|
||||||
|
for u in urls:
|
||||||
|
if u.desc:
|
||||||
|
res.append(u"%s %s (%s)" % (u.tag.slug, u.url, u.desc.strip('()')))
|
||||||
|
else:
|
||||||
|
res.append(u"%s %s" % (u.tag.slug, u.url))
|
||||||
|
return fs.join(res)
|
||||||
|
|
||||||
|
doc = get_object_or_404(Document, name=name)
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
old_urls = format_urls(doc.documenturl_set.all())
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = DocumentUrlForm(request.POST)
|
||||||
|
if form.is_valid():
|
||||||
|
old_urls = sorted(old_urls.splitlines())
|
||||||
|
new_urls = sorted(form.cleaned_data['urls'])
|
||||||
|
if old_urls != new_urls:
|
||||||
|
doc.documenturl_set.all().delete()
|
||||||
|
for u in new_urls:
|
||||||
|
parts = u.split(None, 2)
|
||||||
|
tag = parts[0]
|
||||||
|
url = parts[1]
|
||||||
|
desc = ' '.join(parts[2:]).strip('()')
|
||||||
|
doc.documenturl_set.create(url=url, tag_id=tag, desc=desc)
|
||||||
|
new_urls = format_urls(doc.documenturl_set.all())
|
||||||
|
e = DocEvent(doc=doc, rev=doc.rev, by=request.user.person, type='changed_document')
|
||||||
|
e.desc = "Changed document URLs from:\n\n%s\n\nto:\n\n%s" % (old_urls, new_urls)
|
||||||
|
e.save()
|
||||||
|
doc.save_with_history([e])
|
||||||
|
messages.success(request,"Document URLs updated.")
|
||||||
|
else:
|
||||||
|
messages.info(request,"No change in Document URLs.")
|
||||||
|
return redirect('ietf.doc.views_doc.document_main', name=doc.name)
|
||||||
|
else:
|
||||||
|
form = DocumentUrlForm(initial={'urls': old_urls, })
|
||||||
|
|
||||||
|
info = "Valid tags:<br><br> %s" % ', '.join([ o.slug for o in DocUrlTagName.objects.all() ])
|
||||||
|
title = "Additional document URLs"
|
||||||
|
return render(request, 'doc/edit_field.html',dict(doc=doc, form=form, title=title, info=info) )
|
||||||
|
|
||||||
def request_publication(request, name):
|
def request_publication(request, name):
|
||||||
"""Request publication by RFC Editor for a document which hasn't
|
"""Request publication by RFC Editor for a document which hasn't
|
||||||
been through the IESG ballot process."""
|
been through the IESG ballot process."""
|
||||||
|
|
||||||
|
class PublicationForm(forms.Form):
|
||||||
|
subject = forms.CharField(max_length=200, required=True)
|
||||||
|
body = forms.CharField(widget=forms.Textarea, required=True, strip=False)
|
||||||
|
|
||||||
doc = get_object_or_404(Document, type="draft", name=name, stream__in=("iab", "ise", "irtf"))
|
doc = get_object_or_404(Document, type="draft", name=name, stream__in=("iab", "ise", "irtf"))
|
||||||
|
|
||||||
if not is_authorized_in_doc_stream(request.user, doc):
|
if not is_authorized_in_doc_stream(request.user, doc):
|
||||||
|
|
|
@ -8,7 +8,8 @@ from ietf.name.models import (
|
||||||
IprLicenseTypeName, LiaisonStatementEventTypeName, LiaisonStatementPurposeName,
|
IprLicenseTypeName, LiaisonStatementEventTypeName, LiaisonStatementPurposeName,
|
||||||
LiaisonStatementState, LiaisonStatementTagName, MeetingTypeName, NomineePositionStateName,
|
LiaisonStatementState, LiaisonStatementTagName, MeetingTypeName, NomineePositionStateName,
|
||||||
ReviewRequestStateName, ReviewResultName, ReviewTypeName, RoleName, RoomResourceName,
|
ReviewRequestStateName, ReviewResultName, ReviewTypeName, RoleName, RoomResourceName,
|
||||||
SessionStatusName, StdLevelName, StreamName, TimeSlotTypeName, TopicAudienceName, )
|
SessionStatusName, StdLevelName, StreamName, TimeSlotTypeName, TopicAudienceName,
|
||||||
|
DocUrlTagName)
|
||||||
|
|
||||||
from ietf.stats.models import CountryAlias
|
from ietf.stats.models import CountryAlias
|
||||||
|
|
||||||
|
@ -75,3 +76,4 @@ admin.site.register(StdLevelName, NameAdmin)
|
||||||
admin.site.register(StreamName, NameAdmin)
|
admin.site.register(StreamName, NameAdmin)
|
||||||
admin.site.register(TimeSlotTypeName, NameAdmin)
|
admin.site.register(TimeSlotTypeName, NameAdmin)
|
||||||
admin.site.register(TopicAudienceName, NameAdmin)
|
admin.site.register(TopicAudienceName, NameAdmin)
|
||||||
|
admin.site.register(DocUrlTagName, NameAdmin)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -107,6 +107,9 @@ class CountryName(NameModel):
|
||||||
"Afghanistan, Aaland Islands, Albania, ..."
|
"Afghanistan, Aaland Islands, Albania, ..."
|
||||||
continent = models.ForeignKey(ContinentName)
|
continent = models.ForeignKey(ContinentName)
|
||||||
in_eu = models.BooleanField(verbose_name="In EU", default=False)
|
in_eu = models.BooleanField(verbose_name="In EU", default=False)
|
||||||
|
|
||||||
class ImportantDateName(NameModel):
|
class ImportantDateName(NameModel):
|
||||||
|
"Registration Opens, Scheduling Opens, ID Cutoff, ..."
|
||||||
default_offset_days = models.SmallIntegerField()
|
default_offset_days = models.SmallIntegerField()
|
||||||
|
class DocUrlTagName(NameModel):
|
||||||
|
"Repository, Wiki, Issue Tracker, ..."
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,9 @@ from ietf.name.models import (TimeSlotTypeName, GroupStateName, DocTagName, Inte
|
||||||
IprEventTypeName, GroupMilestoneStateName, SessionStatusName, DocReminderTypeName,
|
IprEventTypeName, GroupMilestoneStateName, SessionStatusName, DocReminderTypeName,
|
||||||
ConstraintName, MeetingTypeName, DocRelationshipName, RoomResourceName, IprLicenseTypeName,
|
ConstraintName, MeetingTypeName, DocRelationshipName, RoomResourceName, IprLicenseTypeName,
|
||||||
LiaisonStatementTagName, FeedbackTypeName, LiaisonStatementState, StreamName,
|
LiaisonStatementTagName, FeedbackTypeName, LiaisonStatementState, StreamName,
|
||||||
BallotPositionName, DBTemplateTypeName, NomineePositionStateName,
|
BallotPositionName, DBTemplateTypeName, NomineePositionStateName, ReviewRequestStateName,
|
||||||
ReviewRequestStateName, ReviewTypeName, ReviewResultName,
|
ReviewTypeName, ReviewResultName, TopicAudienceName, FormalLanguageName, ContinentName,
|
||||||
TopicAudienceName, FormalLanguageName, ContinentName, CountryName, ImportantDateName)
|
CountryName, ImportantDateName, DocUrlTagName)
|
||||||
|
|
||||||
|
|
||||||
class TimeSlotTypeNameResource(ModelResource):
|
class TimeSlotTypeNameResource(ModelResource):
|
||||||
|
@ -536,3 +536,19 @@ class ImportantDateNameResource(ModelResource):
|
||||||
"default_offset_days": ALL,
|
"default_offset_days": ALL,
|
||||||
}
|
}
|
||||||
api.name.register(ImportantDateNameResource())
|
api.name.register(ImportantDateNameResource())
|
||||||
|
|
||||||
|
|
||||||
|
class DocUrlTagNameResource(ModelResource):
|
||||||
|
class Meta:
|
||||||
|
queryset = DocUrlTagName.objects.all()
|
||||||
|
serializer = api.Serializer()
|
||||||
|
cache = SimpleCache()
|
||||||
|
#resource_name = 'docurltagname'
|
||||||
|
filtering = {
|
||||||
|
"slug": ALL,
|
||||||
|
"name": ALL,
|
||||||
|
"desc": ALL,
|
||||||
|
"used": ALL,
|
||||||
|
"order": ALL,
|
||||||
|
}
|
||||||
|
api.name.register(DocUrlTagNameResource())
|
||||||
|
|
|
@ -240,6 +240,32 @@
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% with doc.documenturl_set.all as urls %}
|
||||||
|
{% if urls or can_edit_stream_info %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<th>Additional URLs</th>
|
||||||
|
<td class="edit">
|
||||||
|
{% if can_edit_stream_info %}
|
||||||
|
<a class="btn btn-default btn-xs" href="{% url 'ietf.doc.views_draft.edit_document_urls' name=doc.name %}">Edit</a>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if urls %}
|
||||||
|
<table class="col-md-12 col-sm-12 col-xs-12">
|
||||||
|
<tbody>
|
||||||
|
{% for url in urls %}
|
||||||
|
<tr><td> - <a href="{{ url.url }}">{% firstof url.desc url.tag.name %}</a></td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
<tbody class="meta">
|
<tbody class="meta">
|
||||||
<tr>
|
<tr>
|
||||||
|
|
|
@ -114,7 +114,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% with group.groupurl_set.all as urls %}
|
{% with group.groupurl_set.all as urls %}
|
||||||
{% if urls %}
|
{% if urls or can_edit_group %}
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<th>Additional URLs</th>
|
<th>Additional URLs</th>
|
||||||
|
@ -124,9 +124,15 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{% for url in urls %}
|
{% if urls %}
|
||||||
<a href="{{ url.url }}">{% firstof url.name url.url %}</a>{% if not forloop.last %}<br>{% endif %}
|
<table>
|
||||||
{% endfor %}
|
<tbody>
|
||||||
|
{% for url in urls %}
|
||||||
|
<tr><td> - <a href="{{ url.url }}">{% firstof url.name url.url %}</a></td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
Loading…
Reference in a new issue