Merged in ^/personal/henrik/6.21.1-biophoto@11313, with work from rjsparks@nostrum.com and henrik@levkowetz.com which provides support for profile biography and photo.
- Legacy-Id: 11314
This commit is contained in:
commit
9eacdbf2c6
|
@ -38,6 +38,7 @@ RUN apt-get update && apt-get install -qy \
|
|||
gawk \
|
||||
ipython \
|
||||
less \
|
||||
libjpeg8-dev \
|
||||
libmysqlclient-dev \
|
||||
libsvn1/wheezy-backports \
|
||||
libxml2-dev \
|
||||
|
|
|
@ -17,3 +17,11 @@ IDSUBMIT_IDNITS_BINARY = "/usr/local/bin/idnits"
|
|||
IDSUBMIT_REPOSITORY_PATH = "test/id/"
|
||||
IDSUBMIT_STAGING_PATH = "test/staging/"
|
||||
INTERNET_DRAFT_ARCHIVE_DIR = "test/archive/"
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
MEDIA_ROOT = BASE_DIR + '/media/'
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
PHOTOS_DIRNAME = 'photos'
|
||||
PHOTOS_DIR = MEDIA_ROOT + PHOTOS_DIRNAME
|
||||
|
|
292
ietf/bin/2016-05-25-collect-photos
Executable file
292
ietf/bin/2016-05-25-collect-photos
Executable file
|
@ -0,0 +1,292 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import os, re, sys, shutil, pathlib
|
||||
from collections import namedtuple
|
||||
from PIL import Image
|
||||
|
||||
# boilerplate
|
||||
basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
|
||||
sys.path = [ basedir ] + sys.path
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "ietf.settings")
|
||||
|
||||
import django
|
||||
django.setup()
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.text import slugify
|
||||
|
||||
import debug
|
||||
|
||||
from ietf.group.models import Role, Person
|
||||
from ietf.person.name import name_parts
|
||||
|
||||
old_images_dir = ''
|
||||
new_images_dir = settings.PHOTOS_DIR
|
||||
|
||||
if not os.path.exists(new_images_dir):
|
||||
print("New images directory does not exist: %s" % new_images_dir)
|
||||
sys.exit(1)
|
||||
|
||||
old_image_files = []
|
||||
for dir in settings.OLD_PHOTO_DIRS:
|
||||
if not os.path.exists(dir):
|
||||
print("Old images directory does not exist: %s" % dir)
|
||||
sys.exit(1)
|
||||
old_image_files += [ f for f in pathlib.Path(dir).iterdir() if f.is_file() and f.suffix.lower() in ['.jpg', '.jpeg', '.png'] ]
|
||||
|
||||
photo = namedtuple('photo', ['path', 'name', 'ext', 'width', 'height', 'time', 'file'])
|
||||
|
||||
old_images = []
|
||||
for f in old_image_files:
|
||||
path = str(f)
|
||||
img = Image.open(path)
|
||||
old_images.append(photo(path, f.stem.decode('utf8'), f.suffix, img.size[0], img.size[1], f.stat().st_mtime, f))
|
||||
|
||||
# Fix up some names:
|
||||
|
||||
def fix_missing_surnames(images):
|
||||
replacement = {
|
||||
"alissa": "alissa-cooper",
|
||||
"alissa1": "alissa-cooper",
|
||||
"andrei": "andrei-robachevsky",
|
||||
"bernard": "bernard-aboba",
|
||||
"danny": "danny-mcpherson",
|
||||
"danny1": "danny-mcpherson",
|
||||
"dthaler": "dave-thaler",
|
||||
"eliot-mug": "eliot-lear",
|
||||
"erik.nordmark-300": "erik-nordmark",
|
||||
"hannes": "hannes-tschofenig",
|
||||
"hildebrand": "joe-hildebrand",
|
||||
"housley": "russ-housley",
|
||||
"jariarkko": "jari-arkko",
|
||||
"joel": "joel-jaeggli",
|
||||
"joel1": "joel-jaeggli",
|
||||
"joel2": "joel-jaeggli",
|
||||
"jon": "jon-peterson",
|
||||
"kessens": "david-kessens",
|
||||
"klensin": "john-klensin",
|
||||
"lars": "lars-eggert",
|
||||
"lars1": "lars-eggert",
|
||||
"marc_blanchet": "marc-blanchet",
|
||||
"marcelo": "marcelo-bagnulo",
|
||||
"olaf": "olaf-kolkman",
|
||||
"olaf1": "olaf-kolkman",
|
||||
"ross": "ross-callon",
|
||||
"spencer": "spencer-dawkins",
|
||||
"spencer1": "spencer-dawkins",
|
||||
"vijay": "vijay-gurbani",
|
||||
"xing": "xing-li",
|
||||
}
|
||||
|
||||
for i in range(len(images)):
|
||||
img = images[i]
|
||||
name = re.sub('-[0-9]+x[0-9]+', '', img.name)
|
||||
if '/iab/' in img.path and name in replacement:
|
||||
name = replacement[name]
|
||||
images[i] = photo(img.path, name, img.ext, img.width, img.height, img.time, img.file)
|
||||
|
||||
|
||||
fix_missing_surnames(old_images)
|
||||
|
||||
interesting_persons = set(Person.objects.all())
|
||||
|
||||
name_alias = {
|
||||
u"andy": [u"andrew", ],
|
||||
u"ben": [u"benjamin", ],
|
||||
u"bill": [u"william", ],
|
||||
u"bob": [u"robert", ],
|
||||
u"chris": [u"christopher", u"christian"],
|
||||
u"dan": [u"daniel", ],
|
||||
u"dave": [u"david", ],
|
||||
u"dick": [u"richard", ],
|
||||
u"fred": [u"alfred", ],
|
||||
u"geoff": [u"geoffrey", ],
|
||||
u"jake": [u"jacob", ],
|
||||
u"jerry": [u"gerald", ],
|
||||
u"jim": [u"james", ],
|
||||
u"joe": [u"joseph", ],
|
||||
u"jon": [u"jonathan", ],
|
||||
u"mike": [u"michael", ],
|
||||
u"ned": [u"edward", ],
|
||||
u"pete": [u"peter", ],
|
||||
u"ron": [u"ronald", ],
|
||||
u"russ": [u"russel", ],
|
||||
u"steve": [u"stephen", ],
|
||||
u"ted": [u"edward", ],
|
||||
u"terry": [u"terence", ],
|
||||
u"tom": [u"thomas", ],
|
||||
u"wes": [u"wesley", ],
|
||||
u"will": [u"william", ],
|
||||
|
||||
u"beth": [u"elizabeth", ],
|
||||
u"liz": [u"elizabeth", ],
|
||||
u"lynn": [u"carolyn", ],
|
||||
u"pat": [u"patricia", u"patrick", ],
|
||||
u"sue": [u"susan", ],
|
||||
}
|
||||
# Add lookups from long to short, from the initial set
|
||||
for key,value in name_alias.items():
|
||||
for item in value:
|
||||
if item in name_alias:
|
||||
name_alias[item] += [ key ];
|
||||
else:
|
||||
name_alias[item] = [ key ];
|
||||
|
||||
exceptions = {
|
||||
'Aboba' : 'aboba-bernard',
|
||||
'Bernardos' : 'cano-carlos',
|
||||
'Bormann' : 'bormann-carsten',
|
||||
'Hinden' : 'hinden-bob',
|
||||
'Hutton' : 'hutton-andy',
|
||||
'Narten' : 'narten-thomas', # but there's no picture of him
|
||||
'O\'Donoghue' : 'odonoghue-karen',
|
||||
'Przygienda' : 'przygienda-antoni',
|
||||
'Salowey' : 'salowey-joe',
|
||||
'Gunter Van de Velde' : 'vandevelde-gunter',
|
||||
'Eric Vyncke' : 'vynke-eric',
|
||||
'Zuniga' : 'zuniga-carlos-juan',
|
||||
'Zhen Cao' : 'zhen-cao',
|
||||
'Jamal Hadi Salim': 'hadi-salim-jamal',
|
||||
}
|
||||
|
||||
# Manually copied Bo Burman and Thubert Pascal from wg/photos/
|
||||
# Manually copied Victor Pascual (main image, not thumb) from wg/
|
||||
# Manually copied Eric Vync?ke (main image, not thumb) from wg/photos/
|
||||
# Manually copied Danial King (main image, not thumb) from wg/photos/
|
||||
# Manually copied the thumb (not labelled as such) for Tianran Zhou as both the main and thumb image from wg/photos/
|
||||
|
||||
processed_files = []
|
||||
|
||||
for person in sorted(list(interesting_persons),key=lambda x:x.last_name()+x.ascii):
|
||||
substr_pattern = None
|
||||
for exception in exceptions:
|
||||
if exception in person.ascii:
|
||||
substr_pattern = exceptions[exception]
|
||||
break
|
||||
if not person.ascii.strip():
|
||||
print(" Setting person.ascii for %s" % person.name)
|
||||
person.ascii = person.name.encode('ascii', errors='replace').decode('ascii')
|
||||
|
||||
_, first, _, last, _ = person.ascii_parts()
|
||||
first = first.lower()
|
||||
last = last. lower()
|
||||
if not substr_pattern:
|
||||
substr_pattern = slugify("%s %s" % (last, first))
|
||||
|
||||
if first in ['', '<>'] or last in ['', '<>']:
|
||||
continue
|
||||
|
||||
#debug.show('1, substr_pattern')
|
||||
|
||||
candidates = [x for x in old_images if x.name.lower().startswith(substr_pattern)]
|
||||
# Also check the reverse the name order (necessary for Deng Hui, for instance)
|
||||
substr_pattern = slugify("%s %s" % (first, last))
|
||||
#debug.show('2, substr_pattern')
|
||||
prev_len = len(candidates)
|
||||
candidates += [x for x in old_images if x.name.lower().startswith(substr_pattern)]
|
||||
if prev_len < len(candidates) :
|
||||
print(" Found match with '%s %s' for '%s %s'" % (last, first, first, last, ))
|
||||
# If no joy, try a short name
|
||||
if first in name_alias:
|
||||
prev_len = len(candidates)
|
||||
for alias in name_alias[first]:
|
||||
substr_pattern = slugify("%s %s" % (last, alias))
|
||||
#debug.show('3, substr_pattern')
|
||||
candidates += [x for x in old_images if x.name.lower().startswith(substr_pattern)]
|
||||
if prev_len < len(candidates):
|
||||
print(" Found match with '%s %s' for '%s %s'" % (alias, last, first, last, ))
|
||||
|
||||
|
||||
# # If still no joy, try with Person.plain_name() (necessary for Donald Eastlake)
|
||||
# if not candidates:
|
||||
# prefix, first, middle, last, suffix = person.name_parts()
|
||||
# name_parts = person.plain_name().lower().split()
|
||||
#
|
||||
# substr_pattern = u'-'.join(name_parts[-1:]+name_parts[0:1])
|
||||
# candidates = [x for x in old_images if x.name.lower().startswith(substr_pattern)]
|
||||
# # If no joy, try a short name
|
||||
# if not candidates and first in name_alias:
|
||||
# prev_len = len(candidates)
|
||||
# for alias in name_alias[first]:
|
||||
# substr_pattern = u'-'.join(name_parts[-1:]+[alias])
|
||||
# candidates += [x for x in old_images if x.name.lower().startswith(substr_pattern)]
|
||||
# if prev_len < len(candidates) :
|
||||
# print(" Used '%s %s' instead of '%s %s'" % (alias, last, first, last, ))
|
||||
|
||||
# # Fixup for other exceptional cases
|
||||
# if person.ascii=="David Oran":
|
||||
# candidates = ['oran-dave-th.jpg','oran-david.jpg']
|
||||
#
|
||||
# if person.ascii=="Susan Hares":
|
||||
# candidates = ['hares-sue-th.jpg','hares-susan.JPG']
|
||||
#
|
||||
# if person.ascii=="Mahesh Jethanandani":
|
||||
# candidates = ['Mahesh-Jethanandani-th.jpg','Jethanandani-Mahesh.jpg']
|
||||
|
||||
processed_files += [ c.path for c in candidates ]
|
||||
|
||||
# We now have a list of candidate photos.
|
||||
# * Consider anything less than 200x200 a thumbnail
|
||||
# * For the full photo, sort by size (width) and time
|
||||
# * For the thumbnail:
|
||||
# - first look for a square photo less than 200x200
|
||||
# - if none found, then for the first in the sorted list less than 200x200
|
||||
# - if none found, then the smallest photo
|
||||
if candidates:
|
||||
candidates.sort(key=lambda x: "%04d-%d" % (x.width, x.time))
|
||||
iesg_cand = [ c for c in candidates if '/iesg/' in c.path ]
|
||||
iab_cand = [ c for c in candidates if '/iab/' in c.path ]
|
||||
if iesg_cand:
|
||||
full = iesg_cand[-1]
|
||||
thumb = iesg_cand[-1]
|
||||
elif iab_cand:
|
||||
full = iab_cand[-1]
|
||||
thumb = iab_cand[0]
|
||||
else:
|
||||
full = candidates[-1]
|
||||
thumbs = [ c for c in candidates if c.width==c.height and c.width <= 200 ]
|
||||
if not thumbs:
|
||||
thumbs = [ c for c in candidates if c.width==c.height ]
|
||||
if not thumbs:
|
||||
thumbs = [ c for c in candidates if c.width <= 200 ]
|
||||
if not thumbs:
|
||||
thumbs = candidates[:1]
|
||||
thumb = thumbs[-1]
|
||||
candidates = [ thumb, full ]
|
||||
|
||||
# At this point we either have no candidates or two. If two, the first will be the thumb
|
||||
|
||||
def copy(old, new):
|
||||
if not os.path.exists(new):
|
||||
print("Copying "+old+" to "+new)
|
||||
shutil.copy(old, new)
|
||||
shutil.copystat(old, new)
|
||||
|
||||
assert(len(candidates) in [0,2])
|
||||
if len(candidates)==2:
|
||||
thumb, full = candidates
|
||||
|
||||
new_name = person.photo_name(thumb=False)+full.ext.lower()
|
||||
new_thumb_name = person.photo_name(thumb=True)+thumb.ext.lower()
|
||||
|
||||
copy( full.path, os.path.join(new_images_dir,new_name) )
|
||||
|
||||
#
|
||||
copy( thumb.path, os.path.join(new_images_dir,new_thumb_name) )
|
||||
|
||||
|
||||
print("")
|
||||
not_processed = 0
|
||||
for file in old_image_files:
|
||||
if ( file.is_file()
|
||||
and not file.suffix.lower() in ['.txt', '.lck', '.html',]
|
||||
and not file.name.startswith('index.')
|
||||
and not file.name.startswith('milestoneupdate')
|
||||
and not file.name.startswith('nopicture')
|
||||
and not file.name.startswith('robots.txt')
|
||||
):
|
||||
if not str(file).decode('utf8') in processed_files:
|
||||
not_processed += 1
|
||||
print(u"Not processed: "+str(file).decode('utf8'))
|
||||
print("")
|
||||
print("Not processed: %s files" % not_processed)
|
|
@ -389,10 +389,10 @@ class Document(DocumentInfo):
|
|||
else:
|
||||
filename = self.external_url
|
||||
if meeting.type_id == 'ietf':
|
||||
url = '%sproceedings/%s/%s/%s' % (settings.MEDIA_URL,meeting.number,self.type_id,filename)
|
||||
url = '%sproceedings/%s/%s/%s' % (settings.IETF_HOST_URL,meeting.number,self.type_id,filename)
|
||||
elif meeting.type_id == 'interim':
|
||||
url = "%sproceedings/interim/%s/%s/%s/%s" % (
|
||||
settings.MEDIA_URL,
|
||||
settings.IETF_HOST_URL,
|
||||
meeting.date.strftime('%Y/%m/%d'),
|
||||
session.group.acronym,
|
||||
self.type_id,
|
||||
|
|
|
@ -19,7 +19,7 @@ from django.utils.html import escape
|
|||
from django.template.defaultfilters import urlize
|
||||
|
||||
from ietf.doc.models import Document, DocAlias, DocEvent, State
|
||||
from ietf.group.models import Group, GroupEvent, GroupMilestone, GroupStateTransitions
|
||||
from ietf.group.models import Group, GroupEvent, GroupMilestone, GroupStateTransitions, Role
|
||||
from ietf.group.utils import save_group_in_history, setup_default_community_list_for_group
|
||||
from ietf.name.models import DocTagName, GroupStateName, GroupTypeName
|
||||
from ietf.person.models import Person, Email
|
||||
|
@ -350,6 +350,33 @@ class GroupPagesTests(TestCase):
|
|||
self.assertTrue(de.desc in unicontent(r))
|
||||
|
||||
|
||||
def test_chair_photos(self):
|
||||
make_test_data()
|
||||
url = urlreverse("ietf.group.views.chair_photos", kwargs={'group_type':'wg'})
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
chairs = Role.objects.filter(group__type='wg', group__state='active', name_id='chair')
|
||||
self.assertEqual(len(q('div.photo-thumbnail img')), chairs.count())
|
||||
|
||||
def test_wg_photos(self):
|
||||
make_test_data()
|
||||
url = urlreverse("ietf.group.views.group_photos", kwargs={'group_type':'wg', 'acronym':'mars'})
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
roles = Role.objects.filter(group__acronym='mars')
|
||||
self.assertEqual(len(q('div.photo-thumbnail img')), roles.count())
|
||||
|
||||
def test_group_photos(self):
|
||||
make_test_data()
|
||||
url = urlreverse("ietf.group.views.group_photos", kwargs={'acronym':'iab'})
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
roles = Role.objects.filter(group__acronym='iab')
|
||||
self.assertEqual(len(q('div.photo-thumbnail img')), roles.count())
|
||||
|
||||
class GroupEditTests(TestCase):
|
||||
def setUp(self):
|
||||
self.charter_dir = os.path.abspath("tmp-charter-dir")
|
||||
|
|
|
@ -18,6 +18,7 @@ urlpatterns = patterns('',
|
|||
(r'^chartering/create/$', RedirectView.as_view(url='/group/chartering/create/%(group_type)s/')),
|
||||
(r'^bofs/$', views.bofs),
|
||||
(r'^email-aliases/$', 'ietf.group.views.email_aliases'),
|
||||
(r'^bofs/create/$', views_edit.edit, {'action': "create"}, "bof_create"),
|
||||
(r'^bofs/create/$', views_edit.edit, {'action': "create", "group_state":"bof"}, "bof_create"),
|
||||
(r'^photos/$', views.chair_photos),
|
||||
(r'^(?P<acronym>[a-zA-Z0-9-._]+)/', include('ietf.group.urls_info_details')),
|
||||
)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from django.conf.urls import patterns, url
|
||||
from django.views.generic import RedirectView
|
||||
import views
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^$', 'ietf.group.views.group_home', None, "group_home"),
|
||||
|
@ -28,5 +29,6 @@ urlpatterns = patterns('',
|
|||
(r'^materials/new/$', 'ietf.doc.views_material.choose_material_type'),
|
||||
(r'^materials/new/(?P<doc_type>[\w-]+)/$', 'ietf.doc.views_material.edit_material', { 'action': "new" }, "group_new_material"),
|
||||
(r'^archives/$', 'ietf.group.views.derived_archives'),
|
||||
(r'^photos/$', views.group_photos),
|
||||
url(r'^email-aliases/$', RedirectView.as_view(pattern_name='ietf.group.views.email',permanent=False),name='old_group_email_aliases'),
|
||||
)
|
||||
|
|
|
@ -347,24 +347,15 @@ def construct_group_menu_context(request, group, selected, group_type, others):
|
|||
entries.append(("Materials", urlreverse("ietf.group.views.materials", kwargs=kwargs)))
|
||||
if group.type_id in ('rg','wg','team'):
|
||||
entries.append(("Meetings", urlreverse("ietf.group.views.meetings", kwargs=kwargs)))
|
||||
entries.append(("Email expansions", urlreverse("ietf.group.views.email", kwargs=kwargs)))
|
||||
entries.append(("History", urlreverse("ietf.group.views.history", kwargs=kwargs)))
|
||||
|
||||
entries.append(("Photos", urlreverse("ietf.group.views.group_photos", kwargs=kwargs)))
|
||||
entries.append(("Email expansions", urlreverse("ietf.group.views.email", kwargs=kwargs)))
|
||||
if group.list_archive.startswith("http:") or group.list_archive.startswith("https:") or group.list_archive.startswith("ftp:"):
|
||||
if 'mailarchive.ietf.org' in group.list_archive:
|
||||
entries.append(("List archive", urlreverse("ietf.group.views.derived_archives", kwargs=kwargs)))
|
||||
entries.append(("list archive", urlreverse("ietf.group.views.derived_archives", kwargs=kwargs)))
|
||||
else:
|
||||
entries.append((mark_safe("List archive »"), group.list_archive))
|
||||
|
||||
if group.features.has_documents:
|
||||
kwargs["output_type"] = "svg"
|
||||
entries.append((mark_safe("Dependency graph »"), urlreverse("ietf.group.views.dependencies", kwargs=kwargs)))
|
||||
del kwargs["output_type"]
|
||||
|
||||
if group.has_tools_page():
|
||||
entries.append((mark_safe("Tools page »"), "https://tools.ietf.org/%s/%s/" % (group.type_id, group.acronym)))
|
||||
|
||||
|
||||
# actions
|
||||
actions = []
|
||||
|
||||
|
@ -870,3 +861,39 @@ def derived_archives(request, acronym=None, group_type=None):
|
|||
'group':group,
|
||||
'list_acronym':list_acronym,
|
||||
}))
|
||||
|
||||
def chair_photos(request, group_type=None):
|
||||
roles = sorted(Role.objects.filter(group__type=group_type, group__state='active', name_id='chair'),key=lambda x: x.person.last_name()+x.person.name+x.group.acronym)
|
||||
for role in roles:
|
||||
role.last_initial = role.person.last_name()[0]
|
||||
return render(request, 'group/all_photos.html', {'group_type': group_type, 'role': 'Chair', 'roles': roles })
|
||||
|
||||
def reorder_roles(roles, role_names):
|
||||
list = []
|
||||
for name in role_names:
|
||||
list += [ r for r in roles if r.name_id == name ]
|
||||
list += [ r for r in roles if not r in list ]
|
||||
return list
|
||||
|
||||
def group_photos(request, group_type=None, acronym=None):
|
||||
group = get_object_or_404(Group, acronym=acronym)
|
||||
roles = sorted(Role.objects.filter(group__acronym=acronym),key=lambda x: x.name.name+x.person.last_name())
|
||||
|
||||
if group.type_id in ['wg', 'rg', ]:
|
||||
roles = reorder_roles(roles, ['chair', 'secr'])
|
||||
elif group.type_id in ['nomcom', ]:
|
||||
roles = reorder_roles(roles, ['chair', 'member', 'advisor', ])
|
||||
elif group.type_id in ['team', ]:
|
||||
roles = reorder_roles(roles, ['chair', 'member', 'matman', ])
|
||||
elif group.type_id in ['sdo', ]:
|
||||
roles = reorder_roles(roles, ['liaiman', ])
|
||||
else:
|
||||
pass
|
||||
for role in roles:
|
||||
role.last_initial = role.person.last_name()[0]
|
||||
return render(request, 'group/group_photos.html',
|
||||
construct_group_menu_context(request, group, "photos", group_type, {
|
||||
'group_type': group_type,
|
||||
'roles': roles,
|
||||
'group':group }))
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ from pyquery import PyQuery
|
|||
|
||||
from ietf.doc.models import DocEvent, BallotDocEvent, BallotPositionDocEvent, TelechatDocEvent
|
||||
from ietf.doc.models import Document, DocAlias, State, RelatedDocument
|
||||
from ietf.group.models import Group, GroupMilestone
|
||||
from ietf.group.models import Group, GroupMilestone, Role
|
||||
from ietf.iesg.agenda import get_agenda_date, agenda_data
|
||||
from ietf.iesg.models import TelechatDate
|
||||
from ietf.name.models import StreamName
|
||||
|
@ -66,6 +66,14 @@ class IESGTests(TestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue(draft.name in unicontent(r))
|
||||
|
||||
def test_photos(self):
|
||||
url = urlreverse("ietf.iesg.views.photos")
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
ads = Role.objects.filter(group__type='area', group__state='active', name_id='ad')
|
||||
self.assertEqual(len(q('div.photo-thumbnail img')), ads.count())
|
||||
|
||||
class IESGAgendaTests(TestCase):
|
||||
def setUp(self):
|
||||
make_test_data()
|
||||
|
|
|
@ -53,4 +53,5 @@ urlpatterns = patterns('',
|
|||
(r'^agenda/telechat-(?:(?P<date>\d{4}-\d{2}-\d{2})-)?docs.tgz', "ietf.iesg.views.telechat_docs_tarfile"),
|
||||
(r'^discusses/$', "ietf.iesg.views.discusses"),
|
||||
(r'^milestones/$', "ietf.iesg.views.milestones_needing_review"),
|
||||
(r'^photos/$', "ietf.iesg.views.photos"),
|
||||
)
|
||||
|
|
|
@ -54,7 +54,7 @@ from django.contrib.sites.models import Site
|
|||
|
||||
from ietf.doc.models import Document, TelechatDocEvent, LastCallDocEvent, ConsensusDocEvent, DocEvent, IESG_BALLOT_ACTIVE_STATES
|
||||
from ietf.doc.utils import update_telechat, augment_events_with_revision
|
||||
from ietf.group.models import GroupMilestone
|
||||
from ietf.group.models import GroupMilestone, Role
|
||||
from ietf.iesg.agenda import agenda_data, agenda_sections, fill_in_agenda_docs, get_agenda_date
|
||||
from ietf.iesg.models import TelechatDate
|
||||
from ietf.iesg.utils import telechat_page_count
|
||||
|
@ -474,3 +474,10 @@ def milestones_needing_review(request):
|
|||
),
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def photos(request):
|
||||
roles = sorted(Role.objects.filter(group__type='area', group__state='active', name_id='ad'),key=lambda x: "" if x.group.acronym=="gen" else x.group.acronym)
|
||||
for role in roles:
|
||||
role.last_initial = role.person.last_name()[0]
|
||||
return render(request, 'iesg/photos.html', {'group_type': 'IESG', 'role': '', 'roles': roles })
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ from django.contrib.auth.models import User
|
|||
from django.utils.html import mark_safe
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.person.models import Person, Email
|
||||
|
||||
|
||||
|
@ -45,16 +47,30 @@ def ascii_cleaner(supposedly_ascii):
|
|||
raise forms.ValidationError("Please only enter ASCII characters.")
|
||||
return supposedly_ascii
|
||||
|
||||
class PersonForm(ModelForm):
|
||||
class Meta:
|
||||
model = Person
|
||||
exclude = ('time', 'user')
|
||||
def get_person_form(*args, **kwargs):
|
||||
|
||||
def clean_ascii(self):
|
||||
return ascii_cleaner(self.cleaned_data.get("ascii") or u"")
|
||||
exclude_list = ['time', 'user', 'photo_thumb', ]
|
||||
|
||||
def clean_ascii_short(self):
|
||||
return ascii_cleaner(self.cleaned_data.get("ascii_short") or u"")
|
||||
person = kwargs['instance']
|
||||
roles = person.role_set.all()
|
||||
if not roles:
|
||||
exclude_list += ['biography', 'photo', ]
|
||||
|
||||
class PersonForm(ModelForm):
|
||||
class Meta:
|
||||
model = Person
|
||||
exclude = exclude_list
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ModelForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean_ascii(self):
|
||||
return ascii_cleaner(self.cleaned_data.get("ascii") or u"")
|
||||
|
||||
def clean_ascii_short(self):
|
||||
return ascii_cleaner(self.cleaned_data.get("ascii_short") or u"")
|
||||
|
||||
return PersonForm(*args, **kwargs)
|
||||
|
||||
|
||||
class NewEmailForm(forms.Form):
|
||||
|
|
|
@ -44,7 +44,7 @@ from django.contrib.auth.models import User
|
|||
|
||||
from ietf.group.models import Role
|
||||
from ietf.ietfauth.forms import RegistrationForm, PasswordForm, ResetPasswordForm, TestEmailForm
|
||||
from ietf.ietfauth.forms import PersonForm, RoleEmailForm, NewEmailForm
|
||||
from ietf.ietfauth.forms import get_person_form, RoleEmailForm, NewEmailForm
|
||||
from ietf.ietfauth.htpasswd import update_htpasswd_file
|
||||
from ietf.person.models import Person, Email, Alias
|
||||
from ietf.utils.mail import send_mail
|
||||
|
@ -175,7 +175,7 @@ def profile(request):
|
|||
new_email_forms = []
|
||||
|
||||
if request.method == 'POST':
|
||||
person_form = PersonForm(request.POST, instance=person)
|
||||
person_form = get_person_form(request.POST, instance=person)
|
||||
for r in roles:
|
||||
r.email_form = RoleEmailForm(r, request.POST, prefix="role_%s" % r.pk)
|
||||
|
||||
|
@ -224,7 +224,7 @@ def profile(request):
|
|||
|
||||
# Make sure the alias table contains any new and/or old names.
|
||||
existing_aliases = set(Alias.objects.filter(person=person).values_list("name", flat=True))
|
||||
curr_names = set(x for x in [updated_person.name, updated_person.ascii, updated_person.ascii_short] if x)
|
||||
curr_names = set(x for x in [updated_person.name, updated_person.ascii, updated_person.ascii_short, updated_person.plain_name(), ] if x)
|
||||
new_aliases = curr_names - existing_aliases
|
||||
for name in new_aliases:
|
||||
Alias.objects.create(person=updated_person, name=name)
|
||||
|
@ -236,7 +236,7 @@ def profile(request):
|
|||
for r in roles:
|
||||
r.email_form = RoleEmailForm(r, prefix="role_%s" % r.pk)
|
||||
|
||||
person_form = PersonForm(instance=person)
|
||||
person_form = get_person_form(instance=person)
|
||||
|
||||
return render(request, 'registration/edit_profile.html', {
|
||||
'user': request.user,
|
||||
|
|
|
@ -4,7 +4,6 @@ import os
|
|||
from django.db import models
|
||||
from django.db.models.signals import post_delete
|
||||
from django.conf import settings
|
||||
from django.core.files.storage import FileSystemStorage
|
||||
from django.contrib.auth.models import User
|
||||
from django.template.loader import render_to_string
|
||||
from django.template.defaultfilters import linebreaks
|
||||
|
@ -22,6 +21,8 @@ from ietf.nomcom.utils import (initialize_templates_for_group,
|
|||
initialize_requirements_for_position,
|
||||
delete_nomcom_templates)
|
||||
|
||||
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
|
||||
|
||||
|
||||
def upload_path_handler(instance, filename):
|
||||
return os.path.join(instance.group.acronym, 'public.cert')
|
||||
|
@ -32,14 +33,6 @@ class ReminderDates(models.Model):
|
|||
nomcom = models.ForeignKey('NomCom')
|
||||
|
||||
|
||||
class NoLocationMigrationFileSystemStorage(FileSystemStorage):
|
||||
|
||||
def deconstruct(obj):
|
||||
path, args, kwargs = FileSystemStorage.deconstruct(obj)
|
||||
kwargs["location"] = None
|
||||
return (path, args, kwargs)
|
||||
|
||||
|
||||
class NomCom(models.Model):
|
||||
public_key = models.FileField(storage=NoLocationMigrationFileSystemStorage(location=settings.NOMCOM_PUBLIC_KEYS_DIR),
|
||||
upload_to=upload_path_handler, blank=True, null=True)
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import os
|
||||
import factory
|
||||
import faker
|
||||
|
||||
import shutil
|
||||
from unidecode import unidecode
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from ietf.person.models import Person, Alias, Email
|
||||
|
||||
fake = faker.Factory.create()
|
||||
|
@ -30,6 +33,11 @@ class PersonFactory(factory.DjangoModelFactory):
|
|||
name = factory.LazyAttribute(lambda p: '%s %s'%(p.user.first_name,p.user.last_name))
|
||||
ascii = factory.LazyAttribute(lambda p: unidecode(p.name))
|
||||
|
||||
class Params:
|
||||
with_bio = factory.Trait(
|
||||
biography = u"\n\n".join(fake.paragraphs()),
|
||||
)
|
||||
|
||||
@factory.post_generation
|
||||
def default_aliases(self, create, extracted, **kwargs):
|
||||
make_alias = getattr(AliasFactory, 'create' if create else 'build')
|
||||
|
@ -41,6 +49,22 @@ class PersonFactory(factory.DjangoModelFactory):
|
|||
make_email = getattr(EmailFactory, 'create' if create else 'build')
|
||||
make_email(person=self,address=self.user.email)
|
||||
|
||||
@factory.post_generation
|
||||
def default_photo(self, create, extracted, **kwargs):
|
||||
import atexit
|
||||
if self.biography:
|
||||
photo_name = self.photo_name()
|
||||
media_name = u"%s/%s.jpg" % (settings.PHOTOS_DIRNAME, photo_name)
|
||||
self.photo = media_name
|
||||
self.photo_thumb = media_name
|
||||
photosrc = os.path.join(settings.TEST_DATA_DIR, "profile-default.jpg")
|
||||
photodst = os.path.join(settings.PHOTOS_DIR, photo_name + '.jpg')
|
||||
if not os.path.exists(photodst):
|
||||
shutil.copy(photosrc, photodst)
|
||||
def delete_file(file):
|
||||
os.unlink(file)
|
||||
atexit.register(delete_file, photodst)
|
||||
|
||||
class AliasFactory(factory.DjangoModelFactory):
|
||||
class Meta:
|
||||
model = Alias
|
||||
|
|
26
ietf/person/migrations/0008_add_biography_field.py
Normal file
26
ietf/person/migrations/0008_add_biography_field.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('person', '0007_auto_20160520_0304'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='person',
|
||||
name='biography',
|
||||
field=models.TextField(help_text=b'Short biography for use on leadership pages.', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='personhistory',
|
||||
name='biography',
|
||||
field=models.TextField(help_text=b'Short biography for use on leadership pages.', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
82
ietf/person/migrations/0009_populate_biography.py
Normal file
82
ietf/person/migrations/0009_populate_biography.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
bios = {
|
||||
|
||||
'Jari Arkko' : 'Jari Arkko is an Expert on Internet Architecture with Ericsson Research in Jorvas, Finland. At the IETF, he has served six years as one of the Internet Area Directors in the Internet Engineering Steering Group (IESG). He has published 32 RFCs, including specifications for Mobile IPv6, EAP-AKA, Diameter, SEND, and various IPv6 related documents. He has previously served as a chair of three IETF working groups, and has created and terminated over a dozen of working groups at the IETF in his Area Director role. Jari also serves as a chair of the Technical Advisory Board for the IP Smart Objects Alliance (IPSO) and works in a number of research projects at Ericsson. In the past, Jari has worked in the implementation of routers, VPN software, testing tools, modem banks, cellular network nodes, AAA systems, compilers, and AI systems. He received his Licentiate\'s degree from Helsinki University of Technology in 1996. Jari\'s main interests in the Internet include architecture, IPv6, small implementations, the Internet of Things, social media, Internet governance, and cutting through hype that often surrounds some aspects of our technology. He likes to build and and use the technology that he works with. For instance, he moved to an IPv6-only network in 2010 and builds smart home networks as a hobby. He frequently communicates with his laundry on Facebook.',
|
||||
|
||||
'Ralph Droms' : 'Dr. Ralph Droms is a Cisco Distinguished Engineer in the office of the Enterprise Networking CTO. At Cisco, he heads up a research project in the application of ICN to sensor and actuator networks. Dr. Droms is also working on highly scalable DNS service discovery. Dr. Droms organized the IETF working group that designed DHCP in 1989 and has been active in the IETF in several roles ever since. He is an author of more than 20 RFCs, including many of the core DHCP specifications. Dr. Droms chaired the dhc WG until 2009, when he was selected to be an Internet Area Director in the IESG. In addition to serving on the IAB, Dr. Droms is currently co-chair of the dnssd WG and technical advisor to the 6lo WG. Dr. Droms was also an editor for the IPv6 requirements in the CableLabs DOCSIS 3.0 specification and contributed to the ZigBee Alliance ZigBee-IP specification. Prior to joining Cisco in 2000, Dr. Droms was a member of the computer science department faculty at Bucknell University and co-director of the Computer Center at Bucknell. He has also been a member of the computer science faculty at Pennsylvania State University, and was on the research staff at both IBM and Burroughs (Unisys). Dr. Droms is a co-author of "The DHCP Handbook". His PhD is in computer science from Purdue University.',
|
||||
|
||||
'Ted Hardie' : 'Ted Hardie currently works for Google, putting networks, protocols, and people together in new and optimal ways. Ted first worked in the Internet field in 1988 when he joined the operations staff of the SRI NIC. He later became the technical lead for the NASA NIC, part of the NASA Science Internet project. After leaving NASA, he joined Equinix as its initial Director of Engineering before taking on the role of Director of Research and Development. He was an early-stage executive at Nominum before joining Qualcomm R & D. While he was Qualcomm\'s Director of Internet and Wireless, he served the Internet community as a member of the Internet Architecture Board and as an Applications Area Director for the IETF. He served as Trustee of the Internet Society from 2007 to 2010, and as its Treasurer in 2008 to 2010, while Managing Director of Panasonic\'s Silicon Valley Wireless Research Lab. Dr. Hardie received his bachelor\'s degree from Yale and his doctorate from Stanford. He has been a Fulbright Fellow and a Yale-China Fellow, both in Hong Kong.',
|
||||
|
||||
'Joe Hildebrand' : 'Joe Hildebrand is a Cisco Distinguished Engineer in the Corporate Strategic Innovation Group, which builds corporate-scale technology strategies for Cisco. Previously, he ran architecture for WebEx, was the CTO at Jabber Inc., was the Chief Architect at a custom software development company, built battlefield messaging systems, and engineered robots and their control systems for safety-critical applications. Joe has co-chaired several IETF working groups (including XMPP, HyBi, and webpush), serves on the RFC Series Oversight Committee (RSOC), and has a deep interest in protocols designed for use by typical application developers. He received a B.S. in Mechanical Engineering from Virginia Tech in 1992.',
|
||||
|
||||
'Russ Housley' : 'Russ Housley has worked in the computer and network security field since 1982, and he founded Vigil Security, LLC in September 2002. Russ began serving as the IETF Chair in March 2007. His security research and standards interests include security protocols, certificate management, cryptographic key distribution, and high assurance design and development practices. Prior to accepting the IETF Chair position, Russ served as the Security Area Director, and prior to that he chaired the Secure MIME (S/MIME) Working Group. Russ was editor for several cornerstone Internet PKI standards (including RFC 3280). In November 2004, Russ was recognized by the IEEE 802.11 working group for his contributions to IEEE 802.11i-2004, which fixes the severe security shortcoming of the Wired Equivalent Privacy (WEP). Russ received his B.S. in computer science from Virginia Tech in 1982, and he received his M.S. in computer science from George Mason University in 1992.',
|
||||
|
||||
'Lee Howard' : 'Lee Howard is the Director of Network Technology for Time Warner Cable, where he leads efforts in evolving technologies, and the company\'s deployment of IPv6. His team includes network measurement, tools, and security. In addition to his IETF work, he has contributed to or presented at CableLabs, SCTE, NANOG, and every RIR. Previous work has included experience at enterprise networks, application hosting, and large and small ISPs. He has served on the ARIN Board of Trustees and the NRO NC.',
|
||||
|
||||
'Erik Nordmark' : 'Erik Nordmark works on networking software at Arista based in California, USA. He has been active in the IETF since the early 1990-ies, as key contributor to IPv6 standards,co-chair in Mobile IP and TRILL, and as an Internet Area Director. His interest is in expanding the reach and capability of the core Internet standards to datacenters, virtualization, and towards low-powered devices, by providing architectures and standards that are robust across a large range of scales. Erik holds a Technical Licentiate Degree from Uppsala University and a Master of Science from Stanford University.',
|
||||
|
||||
'Robert Sparks' : 'Robert Sparks is a member of the RFC Series Oversight Committee and the IAOC\'s Tools Development and Technology Management Committees. He is a co-chair of the STIR working group, and is a past-chair of the SIMPLE and GEOPRIV working groups. Robert was an Area Director for the Real-time Applications and Infrastructure area from 2009-2013. He is a co-author of the core SIP specification and several of its updates and extensions, and has focused on improving the level of interoperability of SIP implementations by coordinating the SIPit interoperability events. He is also an active open source contributor. Robert is a Senior Principal Member of Technical Staff at Oracle. Before joining Oracle, he was a Principal Engineer at Tekelec, the VP of Research and Development at Estacado Systems, CTO at Xten Networks (now CounterPath), and has held management and research positions at dynamicsoft, Lucent, Worldcom and Texas A&M University. For over 15 years, Robert has focused on designing and developing real-time IP communications systems. Robert has a BSc in Computer Science and a MSc in Mathematics from Texas A&M University.',
|
||||
|
||||
'Andrew Sullivan' : 'Andrew Sullivan is Director of DNS Engineering at Dyn, an Infrastructure as a Service company based in Manchester, New Hampshire, USA. He has been active in the IETF since 2005, and served as co-chair of the DNSEXT and SPFBIS working groups. His main areas of network specialization are the DNS and internationalization. Andrew holds a BA from the University of Ottawa and an MA from McMaster University, both in philosophy.',
|
||||
|
||||
'Dave Thaler': 'Dave Thaler is a Software Architect in the Windows Networking and Devices division at Microsoft. Prior to joining Microsoft in 1998, he was a routing developer at Merit Network. Since then, he has been responsible for multicast, IPv6, network diagnostics, and peer-to-peer efforts within Windows Networking, and also led the TCP/IP team during the design of the new TCP/IP stack in Windows Vista. Dave has been active in the IETF since 1995 and has authored over 20 RFCs, covering IPv6, multicast, MIBs, etc. He is also a member of the MIB Doctors group, and previously served as co-chair of the MALLOC WG. Dave holds a Ph.D in Computer Science from the University of Michigan. Website: http://research.microsoft.com/users/dthaler',
|
||||
|
||||
'Martin Thomson' : 'Martin Thomson is an engineer at Mozilla. There he works on open standards in both the IETF and W3C. His recent work includes HTTP/2 and Web Push, and he is a core contributor to HTTP, TLS, and WebRTC. He previously worked at Microsoft, Commscope and Nortel on system architecture. Technical interests are privacy, security, and the messy interface where standardized protocols are applied to real problems.',
|
||||
|
||||
'Brian Trammell' : 'Brian Trammell is a Senior Researcher at the CSG at the Swiss Federal Institute of Technology (ETH) Zurich. His primary focus is on network monitoring and measurement, specifically on performance measurement, security monitoring, measurement tools, and privacy issues in measurement and management. Active in the IETF since 2005, he\'s co-authored 15 RFCs in the Security and Operations/Management areas, and co-chairs the IP Performance Metrics working group. Prior to his work with CSG, he was Engineering Technical Lead at the CERT Network Situational Awareness group, and a veteran of a variety of short-lived Internet start-ups. He earned a BS in Computer Science from Georgia Tech in 2000.',
|
||||
|
||||
'Suzanne Woolf' : 'Suzanne is an independent consultant specializing in Internet infrastructure operations and policy. Her experience includes carrier network operations, DNS administration and root name server operations, IP address and DNS policy, infrastructure protocol development and implementation, and open source software engineering management in related areas. Her long-term background in technology and policy with the USC Information Sciences Institute, Internet Systems Consortium, ICANN, and current consulting clients have left her fascinated with the problems of technology at Internet scale, committed to building more open Internet, and warily interested in internet governance. Her primary technical interests include DNS and other naming systems, basic access and connectivity issues such as IPv4-IPv6 co-existence and the transition to IPv6, and supporting the growth of open systems in an increasingly compartmentalized and fragmented network.',
|
||||
|
||||
'Lars Eggert' : 'Lars Eggert is Technical Director for Networking in NetApp’s Advanced Technology Group, based in Munich, Germany. In addition, Lars is an Adjunct Professor at Aalto University, Finland’s premier technical university. He pursues his scientific research interests in diverse areas of Internet technology, including architecture, end-to-end protocols, virtualization, measurements and resource scheduling, through collaborative research projects with leading universities and research labs, in part supported by DARPA, the NSF or the EU. Lars has also been leading the standardization efforts of many related topics as a steering group member of the IETF, and he currently chairs the IRTF, the IETF’s research arm. He is a senior member of the ACM and the IEEE, and serves on the program and organization committees of many academic conferences and workshops, such as ACM SIGCOMM and IEEE Infocom. Lars received his Ph.D. in Computer Science from the University of Southern California (USC) in 2003. Before joining NetApp in 2011, he was a Principal Scientist at Nokia Research Center in Helsinki, Finland and one of Nokia’s most senior technology experts, serving on the corporation’s CTO Technology Council. Before that, he was a senior researcher at NEC Laboratories.',
|
||||
|
||||
'Ben Campbell' : 'Ben Campbell currently serves as a Principal Engineer at Oracle Communications, and as an Area Director for the Real-time Applications and Infrastructure (RAI) area of the IETF. He previously served as a chair for the DART, XMPP, and SIMPLE working groups.\nBen has worked in the IETF since 2000, primarily focused on real-time communication protocols. He co-authored several RFCs in the area, including RFC 3428 and RFC 4975. Prior to joining Oracle, he worked in the Tekelec CTO Team, and was a founding partner at Estacado Systems. When not reading internet-drafts, Ben enjoys sailing and middle-eastern percussion.',
|
||||
|
||||
'Alissa Cooper' : 'Alissa Cooper is a Distinguished Engineer at Cisco Systems, where she is responsible for driving privacy and policy strategy within the company\'s portfolio of real-time collaboration products. She currently serves as Real-Time Applications and Infrastructure (RAI) area director within the Internet Engineering Task Force (IETF). Previously, Alissa served as the Chief Computer Scientist at the Center for Democracy and Technology, where she was a leading public interest advocate and technologist on issues related to privacy, net neutrality, and technical standards. Alissa holds a PhD from the Oxford Internet Institute and MS and BS degrees in computer science from Stanford University.',
|
||||
|
||||
'Alexey Melnikov' : 'Alexey Melnikov is currently co-director of the IETF Applications and Real-Time Area and is the Internet Messaging Team Lead at Isode. In his spare time he also tries to help maintain Cyrus SASL. Alexey is the author or co-author of 29+ published RFCs related to electronic mail and application layer security. In the past he co-chaired the Sieve, Kitten and Usefor IETF Working Groups. Since 1998 his areas of interest have included IMAP, email filtering using Sieve, mobile network optimizations of application protocols, application protocol and format design, real-time collaboration and security frameworks for providing authentication and data integrity/confidentiality. Alexey received a bachelor\'s degree with honors in computer science and mathematics from Moscow State University.',
|
||||
|
||||
'Suresh Krishnan' : 'Suresh Krishnan works as a Distinguished Engineer at Ericsson where his main areas of work are in 5G wireless networks, network simplification, software defined networks and M2M. He has a Bachelor\'s degree in Electrical Engineering from the University of Madras in India and a Masters degree in Electrical Engineering from Concordia University in Canada. He has chaired the dna, intarea, and the sofwire working groups in the IETF, the mobopts research group in the IRTF and has authored more than 30 RFCs across multiple IETF areas.',
|
||||
|
||||
'Terry Manderson' : 'Terry is the Director of DNS Engineering at ICANN, he is responsible for L-root (one of the 13 root servers) and manages the talented team that keeps it humming along with the infrastructure supporting the portfolio of ICANN organisational domains. Terry also serves on the board of AusNOG, the Australian Network Operators Group. For almost all of his career he has held operationally focused roles and, while Terry has a number academic parchments on the wall, he prefers to see tangible links between concepts and actually deployability. In the research realm Terry is most interested in advancements in Internet networking (home, enterprise, global) and the behaviours of large scale network services and topologies, and the impact they have on the end user.\nTerry lives in Brisbane, Australia, with his wife Lauren, two daughters Phoenix and Caitlyn, and their highly trained attack guinea pigs. He gets his kicks from refereeing ice hockey and has an unhealthy fascination with motorcycles, especially Italian made sports bikes. Asking about what happened to the BMW motorcycles in Maastricht (IETF78) is unwise as the memory and pain is yet to fade.',
|
||||
|
||||
'Benoit Claise' : 'Benoit Claise is a Cisco Distinguished Engineer at Cisco Systems, working as an architect for embedded management and device instrumentation. Areas of passion & expertise include Internet traffic monitoring, accounting, performance, fault management, configuration management, deep packet inspection, and energy management. Benoit has been working in the IETF since 2001, mainly in the Operations and Management Area, with more than 30 RFCs in the domain of IP Flow Information eXport - IPFIX, Packet SAMPling - PSAMP, IP Performance Metrics - IPPM, Performance Metrics at Other Layer - PMOL, and Energy MANagment - EMAN. IETF WG: he currently serves as the IETF Area Director for Operations and Management, focusing on YANG-related activities.\n These days, Benoit focuses on configuration management, network automation, and data-model driven management. From a technology point of view, this means YANG as THE data model language, standard YANG data models, NETCONF and RESTCONF as the protocols, etc.\n Benoit is the author of the ciscopress book "Network Management: Accounting and Performance Strategies".',
|
||||
|
||||
'Joel Jaeggli' : 'Joel Jaeggli is a Network Architect at Zynga. Prior to serving as IETF Operations and Management Area director, he co-chaired the v6ops and opsec working groups.',
|
||||
|
||||
'Alia Atlas' : 'Alia K. Atlas has 15 years of experience in the routing area. She started with research at BBN, built and designed routers at Avici Systems, worked on data-center fabrics and management at Google, did network modeling and planning at British Telecom, and works on routing architecture and technologies at Juniper Networks. In the IETF, she was the co-chair of the RTGWG and I2RS working groups. She is an author of RFC 4090, RFC 5286, RFC 5440, RFC 5443, and RFC 5837. Knowing that "bad things happen to good networks", she has a technical interest in resiliency and fast-reroute in routing. Alia has a PhD in Computer Science from Boston University and a B.S. in Electrical Engineering from MIT. Alia works in the Routing Architecture and Technology group at Juniper Networks.',
|
||||
|
||||
'Deborah Brungard' : 'Deborah Brungard is a Lead Member of Technical Staff in network architecture and service planning at AT&T. Her current work is on SDN and NFV. She has been with AT&T for more than 30 years, working in various areas, primarily on international services and network operations. Early in her career, she was part of the first group of AT&T expatriates working in The Netherlands. She has been involved in standards development for more than 15 years and has held various leadership positions in IETF, ITU-T and ANSI. She has been most active in the Routing Area, Co-Chair of both CCAMP and TEAS. She received her Master of Engineering in Electrical Engineering from Stevens Institute of Technology, Hoboken, NJ, USA. Her main passion is escaping in her Silver Bullet (Airstream - the Taj Mahal for campers).',
|
||||
|
||||
'Alvaro Retana' : 'Alvaro Retana is a Distinguished Engineer at Cisco Systems, where he works on Strategic Customer Enablement. He also chairs the IETF-LAC Task Force for LACNOG, with the objective of increasing the participation of people from Latin America and the Caribbean in the IETF. He has published 4 technical books and has been awarded multiple patents by the US Patent and Trademark Office. Alvaro\'s current interests include Software Defined Networking, energy efficiency, infrastructure security, network complexity and other related topics.\n Alvaro has been participating in the IETF since 1998, mainly in the Routing Area. He previously co-chaired Routing Area Working Group (rtgwg) and the Source Packet Routing in Networking WG (spring), and has co-authored several documents on routing technology. Alvaro currently serves as Routing Area Director.',
|
||||
|
||||
'Stephen Farrell' : 'Stephen Farrell is a research fellow in CONNECT, a Science Foundation Ireland research institute and the school of Computer Science and Statistics at Trinity College Dublin, where he teaches and researches on security and delay/disruption-tolerant networking (DTN), and in 2006 co-authored the first book on the latter topic. He is a co-founder of Tolerant Networks Limited, a TCD campus company. Stephen has been a security area director since 2011.\nThe main funding that supports Stephen in his role as area director comes from the CONNECT centre, with additional support from IE Domain Registry Limited (IEDR - the .ie ccTLD) and Google.',
|
||||
|
||||
'Kathleen Moriarty' : 'Serving as the IETF Security Area Director, Kathleen Moriarty is also the Global Lead Security Architect with the EMC Office of the CTO working on technology strategy and standards. Kathleen has been the primary author of multiple published standards and actively contributes to security standards activity in the IETF. Previously, as the Practice Manager for security consulting at EMC, Kathleen was responsible for oversight of key projects, and development of security programs, in addition to serving as the acting CISO of a global investment banking firm. Kathleen has also been the head of IT Security at MIT Lincoln Laboratory and the Director of Information Security at FactSet Research Systems. Kathleen holds a Masters of Science degree in Computer Science from Rensselaer Polytechnic Institute.',
|
||||
|
||||
'Spencer Dawkins' : 'Spencer Dawkins is Senior Standards Manager at Huawei Technologies (USA) in Plano, Texas by day, and hand-drummer at IETF plenaries by night. Spencer began participating in the Internet Engineering Task Force in 1996, served as co-chair for the MARTINI, MEDIACTRL, and SIPCLF working groups in the Realtime Applications and Infrastructure Area, and as co-chair for the PILC working group in the Transport Area. Spencer is a recovering IETF process wonk, having served on the General Area Directorate and on the author team for RFC 3774, and as editor for several revisions to the NomCom process specification, and also served on the General Area Review Team from its formation in 2004 until he was selected for the IAB in 2010. Spencer also serves as Technical Director for the SIP Forum.\n Prior to joining Huawei, Spencer engineered GPRS and VoIP network monitor products for Inet Technologies/Tektronix, did product architecture for protocol accelerators at a start-up and for next-generation SONET switches at Fujitsu Network Communications, and served in a variety of roles at Nortel Networks, where he started out as a business systems computer programmer, but fast-talked his way into networking product development after reading Andrew Tanenbaum\'s "Computer Networks", which changed Spencer’s life. Spencer holds BBA and MS degrees from the University of North Texas, has a lovely wife named Shirley, and volunteers with early-teenaged kids, where the weirdness keeps him young.',
|
||||
|
||||
'Mirja Kühlewind' : 'Mirja Kühlewind is senior researcher at the Networked Systems Group of the Computer Engineering and Networks Laboratory at ETH Zurich. She received here PhD in 2015 from the University of Stuttgart where she worked on protocol design and TCP congestion control at the Institute of Communication Networks and Computer Engineering (IKR). Her research interests additionally include Internet measurements with a focus on middlebox impairments and cooperation. Further, Mirja is the project coordinator of a new EU research project in this area: Measurement and Architecture for a Middleboxed Internet (MAMI). Mirja was selected as IETF Transport Area Director in 2016; prior to that, she was co-chair of the IETF\'s RTP Media Congestion Avoidance Techniques (rmcat) and TCP increased security (tcpinc) working groups. She also co-chairs the Measurement and Analysis for Protocols research group (maprg) in the IRTF.' ,
|
||||
|
||||
}
|
||||
|
||||
|
||||
def add_biography(apps, schema_editor):
|
||||
Person = apps.get_model('person','Person')
|
||||
for name in bios.keys():
|
||||
person = Person.objects.get(name=name)
|
||||
person.biography = bios[name]
|
||||
person.save()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('person', '0008_add_biography_field'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(add_biography, None)
|
||||
]
|
39
ietf/person/migrations/0010_add_photo_fields.py
Normal file
39
ietf/person/migrations/0010_add_photo_fields.py
Normal file
|
@ -0,0 +1,39 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import ietf.utils.storage
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('person', '0009_populate_biography'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='person',
|
||||
name='photo',
|
||||
field=models.ImageField(storage=ietf.utils.storage.NoLocationMigrationFileSystemStorage(location=None), upload_to=b'photos/', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='person',
|
||||
name='photo_thumb',
|
||||
field=models.ImageField(storage=ietf.utils.storage.NoLocationMigrationFileSystemStorage(location=None), upload_to=b'photos/', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='personhistory',
|
||||
name='photo',
|
||||
field=models.ImageField(storage=ietf.utils.storage.NoLocationMigrationFileSystemStorage(location=None), upload_to=b'photos/', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='personhistory',
|
||||
name='photo_thumb',
|
||||
field=models.ImageField(storage=ietf.utils.storage.NoLocationMigrationFileSystemStorage(location=None), upload_to=b'photos/', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
57
ietf/person/migrations/0011_populate_photos.py
Normal file
57
ietf/person/migrations/0011_populate_photos.py
Normal file
|
@ -0,0 +1,57 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
|
||||
from hashids import Hashids
|
||||
|
||||
from django.db import migrations
|
||||
from django.conf import settings
|
||||
from django.utils.text import slugify
|
||||
|
||||
from ietf.person.name import name_parts
|
||||
|
||||
def photo_name(person,thumb=False):
|
||||
hasher = Hashids(salt='Person photo name salt',min_length=5)
|
||||
_, first, _, last, _ = name_parts(person.ascii)
|
||||
return '%s-%s%s' % ( slugify("%s %s" % (first, last)), hasher.encode(person.id), '-th' if thumb else '' )
|
||||
|
||||
def forward(apps,schema_editor):
|
||||
Person = apps.get_model('person','Person')
|
||||
images_dir = settings.PHOTOS_DIR
|
||||
image_filenames = []
|
||||
for (dirpath, dirnames, filenames) in os.walk(images_dir):
|
||||
image_filenames.extend(filenames)
|
||||
break # Only interested in the files in the top directory
|
||||
image_basenames = [os.path.splitext(name)[0] for name in image_filenames]
|
||||
for person in Person.objects.all():
|
||||
if not person.name.strip():
|
||||
continue
|
||||
dirty = False
|
||||
if photo_name(person,thumb=False) in image_basenames:
|
||||
person.photo = os.path.join(settings.PHOTOS_DIRNAME, image_filenames[image_basenames.index(photo_name(person,thumb=False))])
|
||||
dirty = True
|
||||
if photo_name(person,thumb=True) in image_basenames:
|
||||
person.photo_thumb = os.path.join(settings.PHOTOS_DIRNAME, image_filenames[image_basenames.index(photo_name(person,thumb=True))])
|
||||
dirty = True
|
||||
if dirty:
|
||||
person.save()
|
||||
|
||||
def reverse(apps, schema_editor):
|
||||
Person = apps.get_model('person','Person')
|
||||
for person in Person.objects.filter(photo__gt=''):
|
||||
person.photo = None
|
||||
person.save()
|
||||
for person in Person.objects.filter(photo_thumb__gt=''):
|
||||
person.photo_thumb = None
|
||||
person.save()
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('person', '0010_add_photo_fields'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(forward,reverse)
|
||||
]
|
51
ietf/person/migrations/0012_auto_20160606_0823.py
Normal file
51
ietf/person/migrations/0012_auto_20160606_0823.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import ietf.utils.storage
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('person', '0011_populate_photos'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='person',
|
||||
name='biography',
|
||||
field=models.TextField(help_text=b'Short biography for use on leadership pages. Use plain text or reStructuredText markup.', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='person',
|
||||
name='photo',
|
||||
field=models.ImageField(default=None, storage=ietf.utils.storage.NoLocationMigrationFileSystemStorage(location=None), upload_to=b'photos', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='person',
|
||||
name='photo_thumb',
|
||||
field=models.ImageField(default=None, storage=ietf.utils.storage.NoLocationMigrationFileSystemStorage(location=None), upload_to=b'photos', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='personhistory',
|
||||
name='biography',
|
||||
field=models.TextField(help_text=b'Short biography for use on leadership pages. Use plain text or reStructuredText markup.', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='personhistory',
|
||||
name='photo',
|
||||
field=models.ImageField(default=None, storage=ietf.utils.storage.NoLocationMigrationFileSystemStorage(location=None), upload_to=b'photos', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='personhistory',
|
||||
name='photo_thumb',
|
||||
field=models.ImageField(default=None, storage=ietf.utils.storage.NoLocationMigrationFileSystemStorage(location=None), upload_to=b'photos', blank=True),
|
||||
preserve_default=True,
|
||||
),
|
||||
]
|
|
@ -2,15 +2,19 @@
|
|||
|
||||
import datetime
|
||||
from urlparse import urljoin
|
||||
from hashids import Hashids
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.text import slugify
|
||||
|
||||
|
||||
from ietf.person.name import name_parts, initials
|
||||
from ietf.utils.mail import send_mail_preformatted
|
||||
from ietf.utils.storage import NoLocationMigrationFileSystemStorage
|
||||
|
||||
class PersonInfo(models.Model):
|
||||
time = models.DateTimeField(default=datetime.datetime.now) # When this Person record entered the system
|
||||
|
@ -23,6 +27,9 @@ class PersonInfo(models.Model):
|
|||
ascii_short = models.CharField("Abbreviated Name (ASCII)", max_length=32, null=True, blank=True, help_text="Example: A. Nonymous. Fill in this with initials and surname only if taking the initials and surname of the ASCII name above produces an incorrect initials-only form. (Blank is OK).")
|
||||
affiliation = models.CharField(max_length=255, blank=True, help_text="Employer, university, sponsor, etc.")
|
||||
address = models.TextField(max_length=255, blank=True, help_text="Postal mailing address.")
|
||||
biography = models.TextField(blank=True, help_text="Short biography for use on leadership pages. Use plain text or reStructuredText markup.")
|
||||
photo = models.ImageField(storage=NoLocationMigrationFileSystemStorage(location=settings.PHOTOS_DIR),upload_to=settings.PHOTOS_DIRNAME,blank=True, default=None)
|
||||
photo_thumb = models.ImageField(storage=NoLocationMigrationFileSystemStorage(location=settings.PHOTOS_DIR),upload_to=settings.PHOTOS_DIRNAME,blank=True, default=None)
|
||||
|
||||
def __unicode__(self):
|
||||
return self.plain_name()
|
||||
|
@ -37,8 +44,6 @@ class PersonInfo(models.Model):
|
|||
prefix, first, middle, last, suffix = self.ascii_parts()
|
||||
return (first and first[0]+"." or "")+(middle or "")+" "+last+(suffix and " "+suffix or "")
|
||||
def plain_name(self):
|
||||
if self.ascii_short:
|
||||
return self.ascii_short
|
||||
prefix, first, middle, last, suffix = name_parts(self.name)
|
||||
return u" ".join([first, last])
|
||||
def initials(self):
|
||||
|
@ -84,6 +89,12 @@ class PersonInfo(models.Model):
|
|||
def full_name_as_key(self):
|
||||
# this is mostly a remnant from the old views, needed in the menu
|
||||
return self.plain_name().lower().replace(" ", ".")
|
||||
|
||||
def photo_name(self,thumb=False):
|
||||
hasher = Hashids(salt='Person photo name salt',min_length=5)
|
||||
_, first, _, last, _ = name_parts(self.ascii)
|
||||
return u'%s-%s%s' % ( slugify(u"%s %s" % (first, last)), hasher.encode(self.id), '-th' if thumb else '' )
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
|
|
@ -3,7 +3,10 @@ import re
|
|||
def name_parts(name):
|
||||
prefix, first, middle, last, suffix = "", "", "", "", ""
|
||||
|
||||
# if we got a name on the form "Hello There (Foo Bar)", get rid of
|
||||
if not name.strip():
|
||||
return prefix, first, middle, last, suffix
|
||||
|
||||
# if we got a name on the form "Some Name (Foo Bar)", get rid of
|
||||
# the paranthesized part
|
||||
name_with_paren_match = re.search("^([^(]+)\s*\(.*\)$", name)
|
||||
if name_with_paren_match:
|
||||
|
@ -13,7 +16,7 @@ def name_parts(name):
|
|||
if len(parts) > 2 and parts[0] in ["M", "M.", "Sri", ] and "." not in parts[1]:
|
||||
prefix = parts[0];
|
||||
parts = parts[1:]
|
||||
if parts[0] in ["Mr", "Mr.", "Mrs", "Mrs.", "Ms", "Ms.", "Miss", "Dr.", "Doctor", "Prof", "Prof.", "Professor", "Sir", "Lady", "Dame", ]:
|
||||
if len(parts) > 1 and parts[0] in ["Mr", "Mr.", "Mrs", "Mrs.", "Ms", "Ms.", "Miss", "Dr", "Dr.", "Doctor", "Prof", "Prof.", "Professor", "Sir", "Lady", "Dame", ]:
|
||||
prefix = parts[0];
|
||||
parts = parts[1:]
|
||||
if len(parts) > 2:
|
||||
|
@ -52,4 +55,4 @@ if __name__ == "__main__":
|
|||
name = " ".join(sys.argv[1:])
|
||||
print name_parts(name)
|
||||
print initials(name)
|
||||
|
||||
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
import json
|
||||
from pyquery import PyQuery
|
||||
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.person.factories import EmailFactory,PersonFactory
|
||||
from ietf.utils.test_utils import TestCase
|
||||
from ietf.utils.test_data import make_test_data
|
||||
|
||||
from ietf.person.factories import EmailFactory,PersonFactory
|
||||
|
||||
class PersonTests(TestCase):
|
||||
|
||||
def test_ajax_search_emails(self):
|
||||
draft = make_test_data()
|
||||
person = draft.ad
|
||||
|
@ -24,3 +27,24 @@ class PersonTests(TestCase):
|
|||
EmailFactory(person=person,primary=False,active=True)
|
||||
EmailFactory(person=person,primary=False,active=False)
|
||||
self.assertTrue(primary.address in person.formatted_email())
|
||||
|
||||
def test_profile(self):
|
||||
person = PersonFactory(with_bio=True)
|
||||
|
||||
self.assertTrue(person.photo is not None)
|
||||
self.assertTrue(person.photo.name is not None)
|
||||
|
||||
url = urlreverse("ietf.person.views.profile", kwargs={ "email_or_name": person.plain_name()})
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertIn(person.photo_name(), r.content)
|
||||
q = PyQuery(r.content)
|
||||
self.assertIn("Photo of %s"%person, q("div.bio-text img.bio-photo").attr("alt"))
|
||||
|
||||
bio_text = q("div.bio-text").text()
|
||||
self.assertIsNotNone(bio_text)
|
||||
|
||||
photo_url = q("div.bio-text img.bio-photo").attr("src")
|
||||
r = self.client.get(photo_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from django.conf.urls import patterns
|
||||
from ietf.person import ajax
|
||||
from ietf.person import views, ajax
|
||||
|
||||
urlpatterns = patterns('',
|
||||
(r'^search/(?P<model_name>(person|email))/$', "ietf.person.views.ajax_select2_search", None, 'ajax_select2_search_person_email'),
|
||||
(r'^(?P<personid>[a-z0-9]+).json$', ajax.person_json),
|
||||
(ur'^(?P<email_or_name>[\w\s]+)', views.profile),
|
||||
)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from django.http import HttpResponse
|
||||
from django.db.models import Q
|
||||
|
||||
from ietf.person.models import Email, Person
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponse
|
||||
from django.shortcuts import render, get_object_or_404
|
||||
|
||||
from ietf.person.models import Email, Person, Alias
|
||||
from ietf.person.fields import select2_id_name_json
|
||||
|
||||
def ajax_select2_search(request, model_name):
|
||||
|
@ -47,3 +49,11 @@ def ajax_select2_search(request, model_name):
|
|||
objs = objs.distinct()[page:page + 10]
|
||||
|
||||
return HttpResponse(select2_id_name_json(objs), content_type='application/json')
|
||||
|
||||
def profile(request, email_or_name):
|
||||
person = None
|
||||
if '@' in email_or_name:
|
||||
person = get_object_or_404(Email, address=email_or_name).person
|
||||
else:
|
||||
person = get_object_or_404(Alias, name=email_or_name).person
|
||||
return render(request, 'person/profile.html', {'person': person})
|
||||
|
|
|
@ -52,7 +52,7 @@ class InterimMeeting(Meeting):
|
|||
'''
|
||||
if os.path.exists(self.get_proceedings_path()):
|
||||
url = "%sproceedings/interim/%s/%s/proceedings.html" % (
|
||||
settings.MEDIA_URL,
|
||||
settings.IETF_HOST_URL,
|
||||
self.date.strftime('%Y/%m/%d'),
|
||||
self.group().acronym)
|
||||
return url
|
||||
|
|
|
@ -269,10 +269,10 @@ def create_proceedings(meeting, group, is_final=False):
|
|||
|
||||
meeting_root = get_upload_root(meeting)
|
||||
if meeting.type.slug == 'ietf':
|
||||
url_root = "%sproceedings/%s/" % (settings.MEDIA_URL,meeting.number)
|
||||
url_root = "%sproceedings/%s/" % (settings.IETF_HOST_URL,meeting.number)
|
||||
else:
|
||||
url_root = "%sproceedings/interim/%s/%s/" % (
|
||||
settings.MEDIA_URL,
|
||||
settings.IETF_HOST_URL,
|
||||
meeting.date.strftime('%Y/%m/%d'),
|
||||
group.acronym)
|
||||
|
||||
|
@ -407,7 +407,7 @@ def gen_areas(context):
|
|||
|
||||
# append proceedings URL
|
||||
for group in gmet + gnot:
|
||||
group.proceedings_url = "%sproceedings/%s/%s.html" % (settings.MEDIA_URL,meeting.number,group.acronym)
|
||||
group.proceedings_url = "%sproceedings/%s/%s.html" % (settings.IETF_HOST_URL,meeting.number,group.acronym)
|
||||
|
||||
for (counter,area) in enumerate(context['areas'], start=1):
|
||||
groups_met = {'wg':filter(lambda a: a.parent==area and a.state.slug not in ('bof','bof-conc') and a.type_id=='wg',gmet),
|
||||
|
@ -584,7 +584,7 @@ def gen_research(context):
|
|||
|
||||
# append proceedings URL
|
||||
for group in groups:
|
||||
group.proceedings_url = "%sproceedings/%s/%s.html" % (settings.MEDIA_URL,meeting.number,group.acronym)
|
||||
group.proceedings_url = "%sproceedings/%s/%s.html" % (settings.IETF_HOST_URL,meeting.number,group.acronym)
|
||||
|
||||
html = render_to_response('proceedings/rg_irtf.html',{
|
||||
'meeting': meeting,
|
||||
|
|
|
@ -36,13 +36,13 @@ def get_proceedings_path(meeting,group):
|
|||
|
||||
def get_proceedings_url(meeting,group=None):
|
||||
if meeting.type_id == 'ietf':
|
||||
url = "%sproceedings/%s/" % (settings.MEDIA_URL,meeting.number)
|
||||
url = "%sproceedings/%s/" % (settings.IETF_HOST_URL,meeting.number)
|
||||
if group:
|
||||
url = url + "%s.html" % group.acronym
|
||||
|
||||
elif meeting.type_id == 'interim':
|
||||
url = "%sproceedings/interim/%s/%s/proceedings.html" % (
|
||||
settings.MEDIA_URL,
|
||||
settings.IETF_HOST_URL,
|
||||
meeting.date.strftime('%Y/%m/%d'),
|
||||
group.acronym)
|
||||
return url
|
||||
|
|
|
@ -100,9 +100,27 @@ USE_I18N = False
|
|||
|
||||
USE_TZ = False
|
||||
|
||||
MEDIA_URL = 'https://www.ietf.org/'
|
||||
IETF_ID_URL = MEDIA_URL + 'id/'
|
||||
IETF_ID_ARCHIVE_URL = MEDIA_URL + 'archive/id/'
|
||||
MEDIA_ROOT = '/a/www/www6s/lib/dt/media/'
|
||||
MEDIA_URL = 'https://www.ietf.org/lib/dt/media/'
|
||||
PHOTOS_DIRNAME = 'photo'
|
||||
PHOTOS_DIR = MEDIA_ROOT + PHOTOS_DIRNAME
|
||||
|
||||
OLD_PHOTO_DIRS = [
|
||||
'/a/www/www6/wg/images',
|
||||
'/a/www/www6/iesg/bio/photo',
|
||||
'/a/www/iab/wp-content/IAB-uploads/2010/10/',
|
||||
'/a/www/iab/wp-content/IAB-uploads/2011/05/',
|
||||
'/a/www/iab/wp-content/IAB-uploads/2014/02/',
|
||||
'/a/www/iab/wp-content/IAB-uploads/2015/02/',
|
||||
'/a/www/iab/wp-content/IAB-uploads/2015/03/',
|
||||
'/a/www/iab/wp-content/IAB-uploads/2015/06/',
|
||||
'/a/www/iab/wp-content/IAB-uploads/2015/08/',
|
||||
'/a/www/iab/wp-content/IAB-uploads/2016/03/',
|
||||
]
|
||||
|
||||
IETF_HOST_URL = 'https://www.ietf.org/'
|
||||
IETF_ID_URL = IETF_HOST_URL + 'id/'
|
||||
IETF_ID_ARCHIVE_URL = IETF_HOST_URL + 'archive/id/'
|
||||
|
||||
|
||||
# Absolute path to the directory static files should be collected to.
|
||||
|
@ -253,6 +271,7 @@ INSTALLED_APPS = (
|
|||
'form_utils',
|
||||
'tastypie',
|
||||
'widget_tweaks',
|
||||
'django_markup',
|
||||
# IETF apps
|
||||
'ietf.api',
|
||||
'ietf.community',
|
||||
|
@ -582,17 +601,19 @@ SELENIUM_TESTS_ONLY = False
|
|||
DRAFT_ALIAS_DOMAIN = IETF_DOMAIN
|
||||
GROUP_ALIAS_DOMAIN = IETF_DOMAIN
|
||||
|
||||
TEST_DATA_DIR = os.path.abspath(BASE_DIR + "/../test/data")
|
||||
|
||||
# Path to the email alias lists. Used by ietf.utils.aliases
|
||||
DRAFT_ALIASES_PATH = os.path.abspath(BASE_DIR + "/../test/data/draft-aliases")
|
||||
DRAFT_VIRTUAL_PATH = os.path.abspath(BASE_DIR + "/../test/data/draft-virtual")
|
||||
DRAFT_ALIASES_PATH = os.path.join(TEST_DATA_DIR + "draft-aliases")
|
||||
DRAFT_VIRTUAL_PATH = os.path.join(TEST_DATA_DIR + "draft-virtual")
|
||||
|
||||
# Set debug apps in DEV_APPS settings_local
|
||||
DEV_APPS = ()
|
||||
DEV_MIDDLEWARE_CLASSES = ()
|
||||
DRAFT_VIRTUAL_DOMAIN = "virtual.ietf.org"
|
||||
|
||||
GROUP_ALIASES_PATH = os.path.abspath(BASE_DIR + "/../test/data/group-aliases")
|
||||
GROUP_VIRTUAL_PATH = os.path.abspath(BASE_DIR + "/../test/data/group-virtual")
|
||||
GROUP_ALIASES_PATH = os.path.join(TEST_DATA_DIR + "group-aliases")
|
||||
GROUP_VIRTUAL_PATH = os.path.join(TEST_DATA_DIR + "group-virtual")
|
||||
GROUP_VIRTUAL_DOMAIN = "virtual.ietf.org"
|
||||
|
||||
POSTCONFIRM_PATH = "/a/postconfirm/wrapper"
|
||||
|
@ -616,9 +637,18 @@ TRAC_SVN_URL_PATTERN = "https://svn.ietf.org/svn/group/%s/"
|
|||
# against the following list of regex expressions with re.search(pat, addr):
|
||||
EXLUDED_PERSONAL_EMAIL_REGEX_PATTERNS = ["@ietf.org$"]
|
||||
|
||||
# Email addresses people attempt to set for their account will be checked
|
||||
# against the following list of regex expressions with re.search(pat, addr):
|
||||
EXLUDED_PERSONAL_EMAIL_REGEX_PATTERNS = ["@ietf.org$"]
|
||||
MARKUP_SETTINGS = {
|
||||
'restructuredtext': {
|
||||
'settings_overrides': {
|
||||
'initial_header_level': 3,
|
||||
'doctitle_xform': False,
|
||||
'footnote_references': 'superscript',
|
||||
'trim_footnote_reference_space': True,
|
||||
'default_reference_context': 'view',
|
||||
'link_base': ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Put the production SECRET_KEY in settings_local.py, and also any other
|
||||
# sensitive or site-specific changes. DO NOT commit settings_local.py to svn.
|
||||
|
@ -645,3 +675,4 @@ if SERVER_MODE != 'production':
|
|||
if 'SECRET_KEY' not in locals():
|
||||
SECRET_KEY = 'PDwXboUq!=hPjnrtG2=ge#N$Dwy+wn@uivrugwpic8mxyPfHka'
|
||||
ALLOWED_HOSTS = ['*',]
|
||||
|
||||
|
|
|
@ -459,3 +459,21 @@ label#list-feeds {
|
|||
.email-subscription button[type=submit] {
|
||||
margin-left: 3em;
|
||||
}
|
||||
|
||||
.photo-name {
|
||||
height: 3em;
|
||||
}
|
||||
|
||||
.bio-text {
|
||||
max-width: 85ex;
|
||||
}
|
||||
.bio-photo {
|
||||
float: left;
|
||||
margin: 0.3em 1em 0.5em 0.1em;
|
||||
max-width: 200px;
|
||||
}
|
||||
|
||||
.nav-tabs > li > a {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
<h2>Active research groups</h2>
|
||||
<a class="btn btn-default" href="{% url "ietf.group.views.all_status" %}">Status Reports</a>
|
||||
<a class="btn btn-default" href="{% url "ietf.group.views.chair_photos" group_type="rg" %}">Chair Photos</a>
|
||||
|
||||
<table class="table table-striped table-condensed tablesorter">
|
||||
<thead>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
<a class="btn btn-default" href="{% url "ietf.group.views.concluded_groups" %}">Concluded WGs</a>
|
||||
<a class="btn btn-default" href="https://www.ietf.org/dyn/wg/charter/history/">Historic charters</a>
|
||||
<a class="btn btn-default" href="{% url "ietf.group.views.all_status" %}">Status Reports</a>
|
||||
<a class="btn btn-default" href="{% url "ietf.group.views.chair_photos" group_type="wg" %}">Chair Photos</a>
|
||||
|
||||
{% for area in areas %}
|
||||
<h2 class="anchor-target" id="{{area.acronym}}">{{ area.name }} ({{ area.acronym }})</h2>
|
||||
|
|
66
ietf/templates/group/all_photos.html
Normal file
66
ietf/templates/group/all_photos.html
Normal file
|
@ -0,0 +1,66 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin staticfiles %}
|
||||
|
||||
{% block morecss %}
|
||||
.well { max-width: 150px;}
|
||||
{% endblock %}
|
||||
|
||||
{% block bodyAttrs %}data-spy="scroll" data-target="#affix"{% endblock %}
|
||||
|
||||
{% block title %}Chair Photos{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
<h1>{{ group_type | upper }} {{ role }} Photos</h1>
|
||||
|
||||
{% regroup roles by last_initial as alphabet_blocks %}
|
||||
<div class="col-md-11">
|
||||
{% for letter in alphabet_blocks %}
|
||||
<div class="row anchor-target" id="{{letter.grouper}}">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{{letter.grouper}}</div>
|
||||
<div class="panel-body">
|
||||
<ul class="list-inline">
|
||||
{% regroup letter.list by person as person_groups %}
|
||||
{% for person_with_groups in person_groups %}
|
||||
<li>
|
||||
<div class="well photo-thumbnail">
|
||||
{% if person_with_groups.grouper.photo %}<a href="{% url 'ietf.person.views.profile' email_or_name=person_with_groups.grouper.plain_name %}">{% endif %}
|
||||
<div>
|
||||
{% if person_with_groups.grouper.photo_thumb %}
|
||||
<img width=100 src="{{person_with_groups.grouper.photo_thumb.url}}" alt="Photo of {{person_with_groups.grouper.name}}"/>
|
||||
{% else %}
|
||||
<img width=100 src="{{ MEDIA_URL }}photos/nopictureavailable.jpg" alt="No picture available"/>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="photo-name">
|
||||
<strong>{{person_with_groups.grouper.plain_name}}</strong>
|
||||
</div>
|
||||
{% if person_with_groups.grouper.photo %}</a>{% endif %}
|
||||
<div class="photo-role-list">
|
||||
{% for role in person_with_groups.list %}
|
||||
<a href="{% url 'ietf.group.views.group_home' acronym=role.group.acronym %}">{{role.group.acronym}}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 hidden-print bs-docs-sidebar" id="affix">
|
||||
<ul class="nav nav-pills nav-stacked small fixed" data-spy="affix">
|
||||
{% for letter in alphabet_blocks %}
|
||||
<li><a href="#{{letter.grouper}}">{{letter.grouper}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -12,76 +12,87 @@
|
|||
|
||||
<table class="table table-condensed">
|
||||
<thead><tr><th colspan="3"></th></tr></thead>
|
||||
<tbody class="meta">
|
||||
<tr>
|
||||
<th>{{ group.type.name }}</th>
|
||||
<th>Name</th>
|
||||
<td>{{ group.name }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>Acronym</th>
|
||||
<td>{{ group.acronym }}</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td></td>
|
||||
{% if group.parent and group.parent.type_id == "area" %}
|
||||
<th>{{ group.parent.type.name }}</th>
|
||||
<td>{{ group.parent.name }} ({{ group.parent.acronym }})</td>
|
||||
{% else %}
|
||||
<th></th><td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>State</th>
|
||||
<td>
|
||||
{{ group.state.name }}
|
||||
{% if requested_close %}
|
||||
<div class="label label-info">In the process of being closed</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% if group.features.has_chartering_process %}
|
||||
<tbody class="meta">
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>Charter</th>
|
||||
<td>
|
||||
{% if group.charter %}
|
||||
<a href="{% url "doc_view" name=group.charter.name %}">{{ group.charter.name }}-{{ group.charter.rev }}</a>
|
||||
<span class="label label-info">{{ group.charter.get_state.name }}</span>
|
||||
{% else %}
|
||||
(None)
|
||||
{% if user|has_role:"Area Director,Secretariat" %}
|
||||
<a class="btn btn-warning btn-xs" href="{% url "ietf.group.views_edit.submit_initial_charter" group_type=group.type_id acronym=group.acronym %}">Submit charter</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<th>{{ group.type.name }}</th>
|
||||
<th>Name</th>
|
||||
<td>{{ group.name }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if can_provide_status_update or status_update %}
|
||||
<tr id='status_update'>
|
||||
<td></td>
|
||||
<th>Status Update</th>
|
||||
<td>
|
||||
{% if status_update %}
|
||||
(last changed {{status_update.time|date:"Y-m-d"}})
|
||||
{% else %}
|
||||
(None)
|
||||
{% endif %}
|
||||
<a class="btn btn-default btn-xs" href="{% url "ietf.group.views.group_about_status" acronym=group.acronym %}">Show</a>
|
||||
{% if can_provide_status_update %}
|
||||
<a class="btn btn-default btn-xs" href="{% url "ietf.group.views.group_about_status_edit" acronym=group.acronym %}">Edit</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>Acronym</th>
|
||||
<td>{{ group.acronym }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
<tr>
|
||||
<td></td>
|
||||
{% if group.parent and group.parent.type_id == "area" %}
|
||||
<th>{{ group.parent.type.name }}</th>
|
||||
<td>{{ group.parent.name }} ({{ group.parent.acronym }})</td>
|
||||
{% else %}
|
||||
<th></th><td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>State</th>
|
||||
<td>
|
||||
{{ group.state.name }}
|
||||
{% if requested_close %}
|
||||
<div class="label label-info">In the process of being closed</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
{% if group.features.has_chartering_process %}
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>Charter</th>
|
||||
<td>
|
||||
{% if group.charter %}
|
||||
<a href="{% url "doc_view" name=group.charter.name %}">{{ group.charter.name }}-{{ group.charter.rev }}</a>
|
||||
<span class="label label-info">{{ group.charter.get_state.name }}</span>
|
||||
{% else %}
|
||||
(None)
|
||||
{% if user|has_role:"Area Director,Secretariat" %}
|
||||
<a class="btn btn-warning btn-xs" href="{% url "ietf.group.views_edit.submit_initial_charter" group_type=group.type_id acronym=group.acronym %}">Submit charter</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if can_provide_status_update or status_update %}
|
||||
<tr id='status_update'>
|
||||
<td></td>
|
||||
<th>Status Update</th>
|
||||
<td>
|
||||
{% if status_update %}
|
||||
(last changed {{status_update.time|date:"Y-m-d"}})
|
||||
{% else %}
|
||||
(None)
|
||||
{% endif %}
|
||||
<a class="btn btn-default btn-xs" href="{% url "ietf.group.views.group_about_status" acronym=group.acronym %}">Show</a>
|
||||
{% if can_provide_status_update %}
|
||||
<a class="btn btn-default btn-xs" href="{% url "ietf.group.views.group_about_status_edit" acronym=group.acronym %}">Edit</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% if group.features.has_documents %}
|
||||
<tr id='dependency_graph'>
|
||||
<td></td>
|
||||
<th>Dependencies</th>
|
||||
<td>
|
||||
<a href="{% url 'ietf.group.views.dependencies' group_type=group.type_id acronym=group.acronym output_type='svg' %}">
|
||||
Document dependency graph (SVG)
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% with group.groupurl_set.all as urls %}
|
||||
{% if urls %}
|
||||
|
@ -89,6 +100,10 @@
|
|||
<td></td>
|
||||
<th>More info</th>
|
||||
<td>
|
||||
{% if group.has_tools_page %}
|
||||
<a href="https://tools.ietf.org/{{ group.type_id }}/{{ group.acronym }}/">Tools page</a>
|
||||
{% if urls %}<br>{% endif %}
|
||||
{% endif %}
|
||||
{% for url in urls %}
|
||||
<a href="{{ url.url }}">{% firstof url.name url.url %}</a>{% if not forloop.last %}<br>{% endif %}
|
||||
{% endfor %}
|
||||
|
@ -97,28 +112,29 @@
|
|||
{% endif %}
|
||||
{% endwith %}
|
||||
</tbody>
|
||||
|
||||
<tbody class="meta">
|
||||
{% for slug, label, roles in group.personnel %}
|
||||
<tr>
|
||||
{% if forloop.first %}
|
||||
<th>Personnel</th>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
<th>{{ label }}</th>
|
||||
<td>
|
||||
{% for r in roles %}
|
||||
<span class="fa fa-envelope-o"></span>
|
||||
<a href="mailto:{{ r.email.address }}">{{ r.person.plain_name }}</a>
|
||||
<br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% for slug, label, roles in group.personnel %}
|
||||
<tr>
|
||||
{% if forloop.first %}
|
||||
<th>Personnel</th>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
<th>{{ label }}</th>
|
||||
<td>
|
||||
|
||||
|
||||
{% for r in roles %}
|
||||
<span class="fa fa-envelope-o"></span>
|
||||
<a href="mailto:{{ r.email.address }}">{{ r.person.plain_name }}</a>
|
||||
<br>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
|
||||
{% if group.list_email %}
|
||||
<tbody class="meta">
|
||||
<tr>
|
||||
|
|
61
ietf/templates/group/group_photos.html
Normal file
61
ietf/templates/group/group_photos.html
Normal file
|
@ -0,0 +1,61 @@
|
|||
{% extends "group/group_base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin staticfiles %}
|
||||
|
||||
{% block morecss %}
|
||||
.well { max-width: 150px;}
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}{{ group }} ({{group.acronym}}) Photos{% endblock %}
|
||||
|
||||
{% block bodyAttrs %}data-spy="scroll" data-target="#affix"{% endblock %}
|
||||
|
||||
{% block group_content %}
|
||||
{% origin %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
<h1>{{ group }} ({{group.acronym}}) Photos</h1>
|
||||
|
||||
{% regroup roles by name as role_groups %}
|
||||
<div class="col-md-10">
|
||||
{% for role_name in role_groups %}
|
||||
<div class="row anchor-target" id="{{role_name.grouper|urlencode}}">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{{role_name.grouper}}{{role_name.list|pluralize}}</div>
|
||||
<div class="panel-body">
|
||||
<ul class="list-inline">
|
||||
{% regroup role_name.list by person as person_groups %}
|
||||
{% for person_with_groups in person_groups %}
|
||||
<li>
|
||||
{% if person_with_groups.grouper.photo %}<a href="{% url 'ietf.person.views.profile' email_or_name=person_with_groups.grouper.plain_name %}">{% endif %}
|
||||
<div class="well photo-thumbnail">
|
||||
<div>
|
||||
{% if person_with_groups.grouper.photo_thumb %}
|
||||
<img width=100 src="{{person_with_groups.grouper.photo_thumb.url}}" alt="Photo of {{person_with_groups.grouper.name}}" />
|
||||
{% else %}
|
||||
<img width=100 src="{{ MEDIA_URL }}photos/nopictureavailable.jpg" alt="No picture available"/>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div >
|
||||
<strong>{{person_with_groups.grouper.plain_name}}</strong>
|
||||
</div>
|
||||
</div>
|
||||
{% if person_with_groups.grouper.photo %}</a>{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 hidden-print bs-docs-sidebar" id="affix">
|
||||
<ul class="nav nav-pills nav-stacked small fixed" data-spy="affix">
|
||||
{% for role_name in role_groups %}
|
||||
<li><a href="#{{role_name.grouper|urlencode}}">{{role_name.grouper}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
|
@ -20,15 +20,15 @@
|
|||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
|
||||
<h1>IESG agenda: {{ date }} </h1>
|
||||
|
||||
<p class="buttonlist">
|
||||
<a class="btn btn-default" role="button" href="/iesg/agenda/documents/">Documents on future agendas</a>
|
||||
<a class="btn btn-default" role="button" href="/iesg/discusses/">DISCUSS positions</a>
|
||||
</p>
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class="active"><a href="/iesg/agenda/">IESG Agenda</a></li>
|
||||
<li class=" "><a href="/iesg/agenda/documents/">Documents on future agendas</a></li>
|
||||
<li class=" "><a href="/iesg/discusses/">DISCUSS positions</a></li>
|
||||
<li class=" "><a href="{% url "ietf.iesg.views.photos" %}">IESG Photos</a></li>
|
||||
</ul>
|
||||
|
||||
{% for num, section in sections %}
|
||||
|
||||
|
@ -130,6 +130,4 @@
|
|||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
|
@ -22,6 +22,15 @@
|
|||
{% origin %}
|
||||
<h1>Documents on future IESG telechat agendas</h1>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class=" "><a href="/iesg/agenda/">IESG Agenda</a></li>
|
||||
<li class="active"><a href="/iesg/agenda/documents/">Documents on future agendas</a></li>
|
||||
<li class=" "><a href="/iesg/discusses/">DISCUSS positions</a></li>
|
||||
<li class=" "><a href="{% url "ietf.iesg.views.photos" %}">IESG Photos</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
{% if user|has_role:"Secretariat" %}
|
||||
<form class="form-inline" method="post">
|
||||
{% csrf_token %}
|
||||
|
|
|
@ -15,6 +15,15 @@
|
|||
{% origin %}
|
||||
<h1>IESG discuss positions</h1>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li class=" "><a href="/iesg/agenda/">IESG Agenda</a></li>
|
||||
<li class=" "><a href="/iesg/agenda/documents/">Documents on future agendas</a></li>
|
||||
<li class="active"><a href="/iesg/discusses/">DISCUSS positions</a></li>
|
||||
<li class=" "><a href="{% url "ietf.iesg.views.photos" %}">IESG Photos</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
|
||||
{% if user|has_role:"Area Director" %}
|
||||
<p class="btn-group" data-toggle="buttons">
|
||||
<label class="btn btn-default active discuss">
|
||||
|
|
74
ietf/templates/iesg/photos.html
Normal file
74
ietf/templates/iesg/photos.html
Normal file
|
@ -0,0 +1,74 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin staticfiles %}
|
||||
{% load ietf_filters %}
|
||||
|
||||
{% block morecss %}
|
||||
.well { max-width: 150px;}
|
||||
.nav-tabs { position: relative; z-index: 100; }
|
||||
.anchor-target { position: relative; z-index: 0; }
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}{{ group_type | upper }} {{ role }} Photos{% endblock %}
|
||||
|
||||
{% block bodyAttrs %}data-spy="scroll" data-target="#affix"{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
|
||||
<h1>{{ group_type | upper }} {{ role }} Photos</h1>
|
||||
|
||||
<ul class="nav nav-tabs" role="tablist">
|
||||
<li ><a href="/iesg/agenda/documents/">IESG Agenda</a></li>
|
||||
<li ><a href="/iesg/agenda/documents/">Documents on future agendas</a></li>
|
||||
<li ><a href="/iesg/discusses/">DISCUSS positions</a></li>
|
||||
<li class="active"><a href="{% url "ietf.iesg.views.photos" %}">IESG Photos</a></li>
|
||||
</ul>
|
||||
|
||||
{% regroup roles by group.acronym as alphabet_blocks %}
|
||||
<div class="col-md-11" >
|
||||
{% for letter in alphabet_blocks %}
|
||||
<div class="row anchor-target" id="{{letter.grouper}}">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">{{letter.grouper}}</div>
|
||||
<div class="panel-body">
|
||||
<ul class="list-inline">
|
||||
{% regroup letter.list by person as person_groups %}
|
||||
{% for person_with_groups in person_groups %}
|
||||
<li>
|
||||
{% if person_with_groups.grouper.photo %}<a href="{% url 'ietf.person.views.profile' email_or_name=person_with_groups.grouper.plain_name %}">{% endif %}
|
||||
<div class="well photo-thumbnail">
|
||||
<div>
|
||||
{% if person_with_groups.grouper.photo_thumb %}
|
||||
<img width=100 src="{{person_with_groups.grouper.photo_thumb.url}}" alt="Photo of {{person_with_groups.grouper.name}}"/>
|
||||
{% else %}
|
||||
<img width=100 src="{{ MEDIA_URL }}photos/nopictureavailable.jpg" alt="No photo available"/>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="photo-name">
|
||||
<strong>{{person_with_groups.grouper.plain_name}}</strong>
|
||||
</div>
|
||||
<div class="photo-role-list">
|
||||
{% for role in person_with_groups.list %}
|
||||
<a href="{% url 'ietf.group.views.group_home' acronym=role.group.acronym %}">{{role.group.acronym}}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% if person_with_groups.grouper.photo %}</a>{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-1 hidden-print bs-docs-sidebar" id="affix">
|
||||
<ul class="nav nav-pills nav-stacked small fixed" data-spy="affix">
|
||||
{% for letter in alphabet_blocks %}
|
||||
<li><a href="#{{letter.grouper}}">{{letter.grouper}}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock %}
|
24
ietf/templates/person/profile.html
Normal file
24
ietf/templates/person/profile.html
Normal file
|
@ -0,0 +1,24 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load markup_tags %}
|
||||
|
||||
{% block title %}Profile for {{ person }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% origin %}
|
||||
<h1>{{ person.name }}</h1>
|
||||
|
||||
<div class="bio-text">
|
||||
{% if person.photo %}
|
||||
<a href="{{person.photo.url}}">
|
||||
<img class="bio-photo" src="{{ person.photo.url }}" alt="Photo of {{ person }}" />
|
||||
</a>
|
||||
{% else %}
|
||||
<img class="bio-photo" src="{{ MEDIA_URL }}photos/nopictureavailable.jpg" alt="No photo available"/>
|
||||
{% endif %}
|
||||
{{ person.biography | apply_markup:"restructuredtext" }}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
22
ietf/urls.py
22
ietf/urls.py
|
@ -2,10 +2,13 @@
|
|||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import patterns, include
|
||||
from django.conf.urls.static import static
|
||||
from django.contrib import admin
|
||||
from django.views.generic import TemplateView
|
||||
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
|
||||
|
||||
import debug # pyflakes:ignore
|
||||
|
||||
from ietf.liaisons.sitemaps import LiaisonMap
|
||||
from ietf.ipr.sitemaps import IPRMap
|
||||
from ietf import api
|
||||
|
@ -75,22 +78,15 @@ for n,a in api._api_list:
|
|||
|
||||
# This is needed to serve files during testing
|
||||
if settings.SERVER_MODE in ('development', 'test'):
|
||||
urlpatterns += ( staticfiles_urlpatterns()
|
||||
+ patterns('',
|
||||
save_debug = settings.DEBUG
|
||||
settings.DEBUG = True
|
||||
urlpatterns += staticfiles_urlpatterns()
|
||||
urlpatterns += patterns('',
|
||||
(r'^_test500/$', lambda x: None),
|
||||
(r'^environment/$', 'ietf.help.views.environment'),
|
||||
## maybe preserve some static legacy URLs ?
|
||||
(r'^(?P<path>(?:images|css|js)/.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_ROOT+'ietf/'}),
|
||||
)
|
||||
)
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
settings.DEBUG = save_debug
|
||||
|
||||
# This is needed to serve files which are not handled by collectstatic :
|
||||
# if settings.SERVER_MODE in ('development', 'test'):
|
||||
# urlpatterns += patterns('',
|
||||
# (r'^(?P<path>(?:images|css|js|test|static)/.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_LOCAL}),
|
||||
# (r'^(?P<path>admin/(?:img|css|js)/.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_LOCAL}),
|
||||
# (r'^(?P<path>secretariat/(img|css|js)/.*)$', 'django.views.static.serve', {'document_root': settings.STATIC_LOCAL}),
|
||||
# (r'^(?P<path>robots\.txt)$', 'django.views.static.serve', {'document_root': settings.STATIC_LOCAL+"dev/"}),
|
||||
# (r'^_test500/$', lambda x: None),
|
||||
# (r'^environment/$', 'ietf.help.views.environment'),
|
||||
# )
|
||||
|
|
8
ietf/utils/storage.py
Normal file
8
ietf/utils/storage.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from django.core.files.storage import FileSystemStorage
|
||||
|
||||
class NoLocationMigrationFileSystemStorage(FileSystemStorage):
|
||||
|
||||
def deconstruct(obj):
|
||||
path, args, kwargs = FileSystemStorage.deconstruct(obj)
|
||||
kwargs["location"] = None
|
||||
return (path, args, kwargs)
|
|
@ -7,19 +7,22 @@ decorator>=3.4.0
|
|||
defusedxml>=0.4.1 # for TastyPie when ussing xml; not a declared dependency
|
||||
Django>=1.7.10,<1.8
|
||||
django-bootstrap3>=5.1.1,<7.0.0 # django-bootstrap 7.0 requires django 1.8
|
||||
django-markup>=1.1
|
||||
django-tastypie>=0.13.1
|
||||
django-widget-tweaks>=1.3
|
||||
docutils>=0.12
|
||||
factory-boy>=2.6.0
|
||||
# fake-factory==0.5.3 # from factory-boy
|
||||
hashids>=1.1.0
|
||||
html5lib>=0.90
|
||||
jsonfield>=1.0.3 # for SubmissionCheck. This is https://github.com/bradjasper/django-jsonfield/.
|
||||
#lxml>=3.4.0 # from PyQuery;
|
||||
mimeparse>=0.1.3 # from TastyPie
|
||||
MySQL-python>=1.2.5
|
||||
pathlib>=1.0
|
||||
pyang>=1.6
|
||||
Pillow>=3.0
|
||||
pip>=6.0
|
||||
pyang>=1.6
|
||||
pyflakes>=0.8.1
|
||||
pyquery>=1.2.5
|
||||
python-dateutil>=2.2
|
||||
|
|
BIN
test/data/profile-default.jpg
Normal file
BIN
test/data/profile-default.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Loading…
Reference in a new issue