Merge lp:~mhall119/loco-team-portal/686268 into lp:loco-team-portal

Proposed by Michael Hall
Status: Merged
Approved by: Chris Johnston
Approved revision: 369
Merged at revision: 369
Proposed branch: lp:~mhall119/loco-team-portal/686268
Merge into: lp:loco-team-portal
Diff against target: 718 lines (+469/-33)
14 files modified
loco_directory/common/views.py (+10/-0)
loco_directory/media/css/newstyle.css (+26/-0)
loco_directory/meetings/forms.py (+49/-17)
loco_directory/meetings/models.py (+24/-6)
loco_directory/meetings/templatetags/recurse.py (+82/-0)
loco_directory/meetings/urls.py (+3/-0)
loco_directory/meetings/views.py (+119/-4)
loco_directory/services/urls.py (+1/-0)
loco_directory/services/views.py (+4/-1)
loco_directory/templates/meetings/agenda_item_delete_confirm.html (+43/-0)
loco_directory/templates/meetings/agenda_item_new.html (+36/-0)
loco_directory/templates/meetings/agenda_item_update.html (+37/-0)
loco_directory/templates/meetings/team_meeting_detail_agenda.inc.html (+19/-5)
loco_directory/templates/site_search.html (+16/-0)
To merge this branch: bzr merge lp:~mhall119/loco-team-portal/686268
Reviewer Review Type Date Requested Status
Chris Johnston Needs Fixing
Review via email: mp+47162@code.launchpad.net

Description of the change

Added forms for adding/updating agenda items, as well as a template tag for recursively displaying them, and css to make it all look nice.

To post a comment you must log in.
Revision history for this message
Dave Walker (davewalker) wrote :

Visual review looks good, but does it not require a migration script for the model changes?

Revision history for this message
Michael Hall (mhall119) wrote :

The migration script was part of a previous merge, this is just adding a front-end to it

lp:~mhall119/loco-team-portal/686268 updated
366. By Chris Johnston

Fixes bug 639772. Props mhall119

Revision history for this message
Chris Johnston (cjohnston) wrote :

IMO, the owner should be assigned automatically.

The time still shows up as "13:53:09.032017" - it really only needs HH:MM

Can we have a mouseover on the agenda item link say something like "Update <agenda item>" (i.e. when you hover over "Agenda item 1" in: Agenda item 1 - Chris Johnston @ 2011-01-23 13:53 it would read "Update Agenda item 1")

What do you think about indenting sub items a little more?

I also think that there should be some sort of end (if there isn't already, I've gone down 4 levels) to the subitems.

review: Needs Fixing
Revision history for this message
Michael Hall (mhall119) wrote :

The owner is defaulted to the logged in user, but it's changeable. There have been times were I've added an item for someone else, or someone else has added one for me.

Agreed on the time, I'll fix that.

How much more indentation do you think it needs?

It would be more work to limit the depth of the tree than it would be to leave it up to the user. Anything over 3 levels deep will only get a point bullet though, rather than a number or letter.

lp:~mhall119/loco-team-portal/686268 updated
367. By Michael Hall

Show appropriate timezone and localtimes on events and meetings, merge from Chris Johnston

368. By Michael Hall

Add link to meeting chair's lp profile, merge from Chris Johnston

369. By Michael Hall

Merge from trunk

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'loco_directory/common/views.py'
2--- loco_directory/common/views.py 2010-11-30 12:54:39 +0000
3+++ loco_directory/common/views.py 2011-01-24 02:52:15 +0000
4@@ -68,6 +68,7 @@
5 global_events = []
6 team_events = []
7 venues = []
8+ meetings = []
9 q = None
10 form = SiteSearchForm(data=request.GET)
11 if form.is_valid():
12@@ -77,11 +78,13 @@
13 global_events = search_global_events(q)
14 team_events = search_team_events(q)
15 venues = search_venues(q)
16+ meetings = search_meetings(q)
17 context = {
18 'teams': teams,
19 'global_events': global_events,
20 'team_events': team_events,
21 'venues': venues,
22+ 'meetings': meetings,
23 'q': q,
24 'colcycle' : simple_iterator('col_left', 'col_right'),
25 }
26@@ -114,5 +117,12 @@
27 venue_list = Venue.objects.filter(Q(name__icontains=q) | Q(country__name__icontains=q) | Q(city__icontains=q) | Q(address__icontains=q)).order_by('name').distinct()
28 return venue_list
29
30+def search_meetings(q):
31+ from meetings.models import TeamMeeting
32+ from django.db.models import Q
33+ meeting_list = TeamMeeting.objects.next_meetings()
34+ meeting_list = meeting_list.filter(Q(name__icontains=q) | Q(teams__name__icontains=q) | Q(agenda__title__icontains=q)).distinct()
35+ return meeting_list
36+
37
38
39
40=== modified file 'loco_directory/media/css/newstyle.css'
41--- loco_directory/media/css/newstyle.css 2011-01-02 18:45:45 +0000
42+++ loco_directory/media/css/newstyle.css 2011-01-24 02:52:15 +0000
43@@ -434,3 +434,29 @@
44 .attendee-mugshot {
45 vertical-align: middle;
46 }
47+
48+.agenda-list {
49+ list-style-type: decimal;
50+}
51+
52+.agenda-list .agenda-list {
53+ margin-left: 20px;
54+ list-style-type: lower-alpha;
55+}
56+
57+.agenda-list .agenda-list .agenda-list {
58+ list-style-type: lower-roman;
59+}
60+
61+.agenda-list .agenda-list .agenda-list .agenda-list {
62+ list-style-type: circle;
63+}
64+
65+.agenda-title {
66+ font-weight: bold;
67+}
68+
69+.agenda-description {
70+ margin-left: 30px;
71+ font-size: 0.9em;
72+}
73
74=== modified file 'loco_directory/meetings/forms.py'
75--- loco_directory/meetings/forms.py 2011-01-24 02:44:41 +0000
76+++ loco_directory/meetings/forms.py 2011-01-24 02:52:15 +0000
77@@ -4,13 +4,23 @@
78 from django.utils.translation import ugettext as _
79 from django.core.urlresolvers import reverse
80
81-from models import BaseMeeting, TeamMeeting
82+from models import BaseMeeting, TeamMeeting, AgendaItem
83 from common.forms import RenderableMixin
84 from userprofiles.models import UserProfile
85
86 import pytz
87 import urllib
88
89+def grouped_user_list(teams):
90+ other_members, team_members = [], []
91+ for profile in UserProfile.objects.filter(user__groups__name__in=teams):
92+ team_members.append((profile.id, str(profile)))
93+ for profile in UserProfile.objects.all().exclude(pk__in=[m[0] for m in team_members]):
94+ other_members.append((profile.id, str(profile)))
95+
96+ return [('', '---------'),
97+ (_('Team members'), team_members),
98+ (_('Other users'), other_members)]
99
100 class BaseMeetingForm(forms.ModelForm, RenderableMixin):
101 """
102@@ -60,22 +70,10 @@
103 def __init__(self, teams=None, *args, **kargs):
104 super(TeamMeetingForm, self).__init__(*args, **kargs)
105 if teams:
106- self.fields['chair'].choices = self.grouped_user_list(teams)
107- self.fields['channel'].initial = teams[0].irc_chan
108- elif self.instance:
109- self.fields['chair'].choices = self.grouped_user_list(self.instance.teams.iterator())
110+ self.fields['chair'].choices = grouped_user_list(teams)
111+ elif self.instance.teams:
112+ self.fields['chair'].choices = grouped_user_list([team.lp_name for team in self.instance.teams.iterator()])
113
114- def grouped_user_list(self, teams):
115- print dir(teams)
116- other_members, team_members = [], []
117- for profile in UserProfile.objects.filter(user__groups__name__in=teams):
118- team_members.append((profile.id, str(profile)))
119- for profile in UserProfile.objects.all().exclude(user__groups__name__in=teams):
120- other_members.append((profile.id, str(profile)))
121- return [('', '---------'),
122- (_('Team members'), team_members),
123- (_('Other users'), other_members)]
124-
125 def save(self):
126 start_date = self.cleaned_data['date_begin']
127 self.instance.logs = 'http://irclogs.ubuntu.com/%(date)s/%(channel)s.html#t%(time)s' % {'date': start_date.strftime('%Y/%m/%d'),
128@@ -83,4 +81,38 @@
129 'time': start_date.strftime('%H:%M')}
130 return super(TeamMeetingForm, self).save()
131
132-
133+class AgendaItemForm(forms.ModelForm, RenderableMixin):
134+
135+ class Meta:
136+ model = AgendaItem
137+ exclude = ('meeting','created_date')
138+
139+ def __init__(self, *args, **kargs):
140+ super(AgendaItemForm, self).__init__(*args, **kargs)
141+ parent_choices = AgendaItem.objects.filter(meeting=self.instance.meeting).exclude(pk=self.instance.id)
142+ self.fields['parent'].choices = [('', '')]+[(item.id, unicode(item)) for item in parent_choices if item not in self.instance.descendents]
143+ if self.instance:
144+ meeting = self.instance.meeting.teammeeting
145+ self.fields['owner'].choices = grouped_user_list(meeting.teams.iterator())
146+
147+
148+class AgendaItemFormSet(forms.models.BaseInlineFormSet):
149+
150+ def __init__(self, *args, **kargs):
151+ super(AgendaItemFormSet, self).__init__(*args, **kargs)
152+ self.node_tree = {}
153+
154+ def add_fields(self, form, index):
155+ super(AgendaItemFormSet, self).add_fields(form, index)
156+ self.fields['node_id'] = IntegerField(label=_(u'Node'),
157+ initial=index,
158+ required=False)
159+ self.node_tree[form.instance.id] = index
160+ self.fields['parent_node'] = IntegerField(label=_(u'Parent Node'),
161+ initial=self.node_tree.get(form.instance.parent, None),
162+ required=False)
163+
164+ def save(self):
165+ pass
166+ def as_tree(self):
167+ pass
168
169=== modified file 'loco_directory/meetings/models.py'
170--- loco_directory/meetings/models.py 2011-01-10 16:08:59 +0000
171+++ loco_directory/meetings/models.py 2011-01-24 02:52:15 +0000
172@@ -92,6 +92,9 @@
173
174 class AgendaItemManager(models.Manager):
175
176+ def top(self):
177+ return self.filter(parent__isnull=True).order_by('order')
178+
179 def as_tree(self):
180 cache = {}
181 tree = []
182@@ -109,27 +112,42 @@
183 class AgendaItem(models.Model):
184
185 class Meta:
186- ordering = ('parent__id', 'order')
187+ ordering = ('parent__id', 'order', 'created_date')
188
189 meeting = models.ForeignKey(BaseMeeting, verbose_name=_('Meeting'), related_name='agenda', help_text=_('meeting during which this agenda item is to be discussed'))
190- parent = models.ForeignKey('self', verbose_name=_('Parent Agenda Item'), related_name='children', help_text=_('agenda item that contains this item'), blank=True, null=True)
191- order = models.PositiveIntegerField(verbose_name=_('Order'), help_text=_('index number of where this item falls in the agenda'))
192+ title = models.CharField(verbose_name=_('Title'), max_length = 150, help_text=_('descriptive name for this item'))
193 owner = models.ForeignKey(UserProfile, verbose_name=_('Owner'), help_text=_('person proposing or responsible for this item'))
194 created_date = models.DateTimeField(verbose_name=_('Created Date'), auto_now_add=True, help_text=_('timestamp of when this item was created'))
195- title = models.CharField(verbose_name=_('Title'), max_length = 150, help_text=_('descriptive name for this item'))
196 description = models.TextField(verbose_name=_('Description'), help_text=_('detailed description of this item'), blank=True, null=True)
197+ parent = models.ForeignKey('self', verbose_name=_('Parent Agenda Item'), related_name='children', help_text=_('agenda item that contains this item'), blank=True, null=True)
198+ order = models.PositiveIntegerField(verbose_name=_('Order'), help_text=_('index number of where this item falls in the agenda'), default=1)
199 log = models.URLField(verbose_name=_('Log URL'), max_length=200, verify_exists=False, help_text=_('URL to this item\'s discussion'), blank=True, null=True)
200
201 objects = AgendaItemManager()
202
203+ def get_descendents(self):
204+ descendents = []
205+ if self.id is None:
206+ return descendents
207+ for child in self.children.all():
208+ descendents.append(child)
209+ descendents.extend(child.descendents)
210+ return descendents
211+ descendents = property(get_descendents)
212+
213 def get_sig(self):
214- return '<a href="http://launchpad.net/~%s">%s</a> %s' % (self.owner.user.username, self.owner.realname, self.created_date)
215+ return '<a href="http://launchpad.net/~%s" target="launchpaduser">%s</a> %s' % (self.owner.user.username, self.owner.realname, self.created_date)
216
217 sig = property(get_sig)
218
219+ def save(self, *args, **kargs):
220+ if not self.created_date:
221+ self.created_date = datetime.datetime.now()
222+ return super(AgendaItem, self).save(*args, **kargs)
223+
224 def __unicode__(self):
225 if self.parent is None:
226- return '%s: %s' % (self.meeting, self.title)
227+ return '%s' % self.title
228 else:
229 return '%s->%s' % (self.parent, self.title)
230
231
232=== added directory 'loco_directory/meetings/templatetags'
233=== added file 'loco_directory/meetings/templatetags/__init__.py'
234=== added file 'loco_directory/meetings/templatetags/recurse.py'
235--- loco_directory/meetings/templatetags/recurse.py 1970-01-01 00:00:00 +0000
236+++ loco_directory/meetings/templatetags/recurse.py 2011-01-24 02:52:15 +0000
237@@ -0,0 +1,82 @@
238+###############################################################################
239+# Recurse template tag for Django v1.1
240+# Copyright (C) 2008 Lucas Murray
241+# http://www.undefinedfire.com
242+#
243+# This program is free software: you can redistribute it and/or modify
244+# it under the terms of the GNU Lesser General Public License as published
245+# by the Free Software Foundation, either version 3 of the License, or
246+# (at your option) any later version.
247+#
248+# This program is distributed in the hope that it will be useful,
249+# but WITHOUT ANY WARRANTY; without even the implied warranty of
250+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
251+# GNU Lesser General Public License for more details.
252+###############################################################################
253+
254+from django import template
255+
256+register = template.Library()
257+
258+class RecurseNode(template.Node):
259+ def __init__(self, var, name, child, nodeList):
260+ self.var = var
261+ self.name = name
262+ self.child = child
263+ self.nodeList = nodeList
264+
265+ def __repr__(self):
266+ return '<RecurseNode>'
267+
268+ def renderCallback(self, context, vals, level):
269+ output = []
270+ try:
271+ if len(vals):
272+ pass
273+ except:
274+ vals = [vals]
275+ if len(vals):
276+ if 'loop' in self.nodeList:
277+ output.append(self.nodeList['loop'].render(context))
278+ for val in vals:
279+ context.push()
280+ context['level'] = level
281+ context[self.name] = val
282+ if 'child' in self.nodeList:
283+ output.append(self.nodeList['child'].render(context))
284+ child = self.child.resolve(context)
285+ if child:
286+ output.append(self.renderCallback(context, child, level + 1))
287+ if 'endloop' in self.nodeList:
288+ output.append(self.nodeList['endloop'].render(context))
289+ else:
290+ output.append(self.nodeList['endrecurse'].render(context))
291+ context.pop()
292+ if 'endloop' in self.nodeList:
293+ output.append(self.nodeList['endrecurse'].render(context))
294+ return ''.join(output)
295+
296+ def render(self, context):
297+ vals = self.var.resolve(context)
298+ output = self.renderCallback(context, vals, 1)
299+ return output
300+
301+def do_recurse(parser, token):
302+ bits = list(token.split_contents())
303+ if len(bits) != 6 and bits[2] != 'with' and bits[4] != 'as':
304+ raise template.TemplateSyntaxError, "Invalid tag syxtax expected '{% recurse [childVar] with [parents] as [parent] %}'"
305+ child = parser.compile_filter(bits[1])
306+ var = parser.compile_filter(bits[3])
307+ name = bits[5]
308+
309+ nodeList = {}
310+ while len(nodeList) < 4:
311+ temp = parser.parse(('child','loop','endloop','endrecurse'))
312+ tag = parser.tokens[0].contents
313+ nodeList[tag] = temp
314+ parser.delete_first_token()
315+ if tag == 'endrecurse':
316+ break
317+
318+ return RecurseNode(var, name, child, nodeList)
319+do_recurse = register.tag('recurse', do_recurse)
320\ No newline at end of file
321
322=== modified file 'loco_directory/meetings/urls.py'
323--- loco_directory/meetings/urls.py 2010-12-02 09:12:53 +0000
324+++ loco_directory/meetings/urls.py 2011-01-24 02:52:15 +0000
325@@ -16,4 +16,7 @@
326 url(r'^team/(?P<team_slug>[a-zA-Z0-9\-\.\+?]+)/ical/$', 'meetings.views.team_meeting_list_ical', name='team-meeting-list-ical'),
327 url(r'^team/add/$', 'meetings.views.team_meeting_select', name='team-meeting-select'),
328
329+ url(r'^team/(?P<team_meeting_id>\d+)/agenda/(?P<agenda_item_id>\d+)/delete/$', 'meetings.views.agenda_item_delete', name='agenda-item-delete'),
330+ url(r'^team/(?P<team_meeting_id>\d+)/agenda/(?P<agenda_item_id>\d+)/update/$', 'meetings.views.agenda_item_update', name='agenda-item-update'),
331+ url(r'^team/(?P<team_meeting_id>\d+)/agenda/add/$', 'meetings.views.agenda_item_new', name='agenda-item-new'),
332 )
333
334=== modified file 'loco_directory/meetings/views.py'
335--- loco_directory/meetings/views.py 2011-01-18 20:44:47 +0000
336+++ loco_directory/meetings/views.py 2011-01-24 02:52:15 +0000
337@@ -6,15 +6,16 @@
338 from django.utils.translation import ugettext as _
339 from django.core.urlresolvers import reverse
340
341-from meetings.models import TeamMeeting
342+from meetings.models import TeamMeeting, AgendaItem
343 from teams.models import Team
344
345-from forms import TeamMeetingForm
346+from forms import TeamMeetingForm, AgendaItemForm
347 from django.db.models import Q
348
349 from common.utils import redirect, simple_iterator
350 from common import launchpad
351
352+from userprofiles.models import UserProfile
353 import datetime
354
355 def meeting_list(request):
356@@ -172,16 +173,18 @@
357 is_on_lc = launchpad.is_user_on_loco_council(request.user)
358 is_member = launchpad.is_team_member(request.user, team_object)
359
360+ team_meeting_object = TeamMeeting(chair=team_object.owner_profile,
361+ channel=team_object.irc_chan)
362 if is_on_lc or is_member:
363 if request.method == 'POST':
364- form = TeamMeetingForm(data=request.POST, teams=[team_object])
365+ form = TeamMeetingForm(data=request.POST, teams=[team_object], instance=team_meeting_object)
366 if form.is_valid():
367 team_meeting = form.save()
368 team_meeting.teams.add(team_object)
369 team_meeting_id = team_meeting.id
370 return HttpResponseRedirect(reverse('team-meeting-detail', kwargs={'team_meeting_id': team_meeting_id}))
371 else:
372- form = TeamMeetingForm(teams=[team_object])
373+ form = TeamMeetingForm(teams=[team_object], instance=team_meeting_object)
374
375 context = {
376 'team_object': team_object,
377@@ -262,3 +265,115 @@
378 else:
379 request.user.message_set.create(message=_('You can not update this team meeting. You are not member of the team or on the LoCo Council.'))
380 return redirect( team_meeting_object )
381+
382+@login_required
383+def agenda_item_new(request, team_meeting_id):
384+ """
385+ new agenda item
386+ """
387+ team_meeting_object = get_object_or_404(TeamMeeting, pk=team_meeting_id)
388+ try:
389+ user = UserProfile.objects.get(user=request.user)
390+ agenda_item_object = AgendaItem(meeting=team_meeting_object,
391+ owner=user,
392+ created_date=datetime.datetime.now())
393+ except UserProfile.DoesNotExist:
394+ agenda_item_object = AgendaItem(meeting=team_meeting_object,
395+ created_date=datetime.datetime.now())
396+ is_member = False
397+ for team in team_meeting_object.teams.all():
398+ if launchpad.is_team_member(request.user, team):
399+ is_member = True
400+ break
401+ is_on_lc = launchpad.is_user_on_loco_council(request.user)
402+
403+ if is_on_lc or is_member:
404+ if request.method == 'POST':
405+ form = AgendaItemForm(data=request.POST, instance=agenda_item_object)
406+ if form.is_valid():
407+ agenda_item_object = form.save()
408+ request.user.message_set.create(message=_('Meeting agenda updated.'))
409+ return redirect( team_meeting_object )
410+ else:
411+ form = AgendaItemForm(instance=agenda_item_object)
412+
413+ context = {
414+ 'team_meeting_object': team_meeting_object,
415+ 'form': form,
416+ }
417+ return render_to_response('meetings/agenda_item_new.html',
418+ context, RequestContext(request))
419+ else:
420+ # XXX: Once we move to a new ACL system, this needs fixing.
421+ request.user.message_set.create(message=_('You can not add a new agenda item for this team meeting. You are not member of the team or on the LoCo Council.'))
422+ return redirect( team_meeting_object )
423+
424+@login_required
425+def agenda_item_update(request, team_meeting_id, agenda_item_id):
426+ """
427+ update agenda item
428+ """
429+ team_meeting_object = get_object_or_404(TeamMeeting, pk=team_meeting_id)
430+ agenda_item_object = get_object_or_404(AgendaItem, pk=agenda_item_id)
431+ #check if user is admin or owner of a team
432+ is_member = False
433+ for team in team_meeting_object.teams.all():
434+ if launchpad.is_team_member(request.user, team):
435+ is_member = True
436+ break
437+
438+ is_on_lc = launchpad.is_user_on_loco_council(request.user)
439+
440+ if is_on_lc or is_member:
441+ if request.method == 'POST':
442+ form = AgendaItemForm(data=request.POST, instance=agenda_item_object)
443+ if form.is_valid():
444+ form.save()
445+ request.user.message_set.create(message=_('Meeting agenda updated.'))
446+ return redirect( team_meeting_object )
447+ else:
448+ form = AgendaItemForm(instance=agenda_item_object)
449+
450+ context = {
451+ 'team_meeting_object': team_meeting_object,
452+ 'agenda_item_object': agenda_item_object,
453+ 'form': form,
454+ }
455+ return render_to_response('meetings/agenda_item_update.html',
456+ context, RequestContext(request))
457+ else:
458+ request.user.message_set.create(message=_('You can not update this team meeting agenda. You are not member of the team or on the LoCo Council.'))
459+ return redirect( team_meeting_object )
460+
461+@login_required
462+def agenda_item_delete(request, team_meeting_id, agenda_item_id):
463+ """
464+ delete an agenda item
465+ """
466+ team_meeting_object = get_object_or_404(TeamMeeting, pk=team_meeting_id)
467+ agenda_item_object = get_object_or_404(AgendaItem, pk=agenda_item_id)
468+ #check if user is admin or owner of a team
469+ is_member = False
470+ for team in team_meeting_object.teams.all():
471+ if launchpad.is_team_member(request.user, team):
472+ is_member = True
473+ break
474+
475+ is_on_lc = launchpad.is_user_on_loco_council(request.user)
476+
477+ if is_on_lc or is_member:
478+ if request.method == 'POST':
479+ agenda_item_object.delete()
480+ request.user.message_set.create(message=_('Agenda Item removed.'))
481+ return redirect( team_meeting_object )
482+ else:
483+ context = {
484+ 'team_meeting_object': team_meeting_object,
485+ 'agenda_item_object': agenda_item_object}
486+ return render_to_response('meetings/agenda_item_delete_confirm.html', context, RequestContext(request))
487+
488+ else:
489+ request.user.message_set.create(message=_('You can not remove this agenda item. You are not admin/owner of the Launchpad team or on the LoCo Council.'))
490+ return redirect( team_meeting_object )
491+
492+
493
494=== modified file 'loco_directory/services/urls.py'
495--- loco_directory/services/urls.py 2010-12-02 07:34:24 +0000
496+++ loco_directory/services/urls.py 2011-01-24 02:52:15 +0000
497@@ -12,6 +12,7 @@
498 url(r'^continents/(.*)$', 'services.views.continent_service', name='continent_service'),
499 url(r'^events/(.*)$', 'services.views.team_event_service', name='team_event_service'),
500 url(r'^meeting/(.*)$', 'services.views.meeting_service', name='meeting_service'),
501+ url(r'^agenda/(.*)$', 'services.views.meeting_agenda_service', name='meeting_agenda_service'),
502 url(r'^global/(.*)$', 'services.views.global_event_service', name='global_event_service'),
503 url(r'^comments/(.*)$', 'services.views.event_comment_service', name='event_comment_service'),
504 url(r'^attendees/(.*)$', 'services.views.event_attendee_service', name='event_attendee_service'),
505
506=== modified file 'loco_directory/services/views.py'
507--- loco_directory/services/views.py 2010-12-19 19:20:44 +0000
508+++ loco_directory/services/views.py 2011-01-24 02:52:15 +0000
509@@ -1,6 +1,6 @@
510 from teams.models import Team, Continent, Country, Language
511 from events.models import GlobalEvent, TeamEvent, TeamEventComment, Attendee
512-from meetings.models import TeamMeeting
513+from meetings.models import TeamMeeting, AgendaItem
514 from venues.models import Venue
515 from userprofiles.models import UserProfile
516 from django.contrib.auth.models import User, Group
517@@ -22,6 +22,9 @@
518 def meeting_service(request, url):
519 return model_service(TeamMeeting, request, url)
520
521+def meeting_agenda_service(request, url):
522+ return model_service(AgendaItem, request, url)
523+
524 def global_event_service(request, url):
525 return model_service(GlobalEvent, request, url)
526
527
528=== added file 'loco_directory/templates/meetings/agenda_item_delete_confirm.html'
529--- loco_directory/templates/meetings/agenda_item_delete_confirm.html 1970-01-01 00:00:00 +0000
530+++ loco_directory/templates/meetings/agenda_item_delete_confirm.html 2011-01-24 02:52:15 +0000
531@@ -0,0 +1,43 @@
532+{% extends "base.html" %}
533+{% load i18n %}
534+{% load recurse %}
535+
536+{% block title %}{% trans "Delete Agenda Item" %} | {% trans "Ubuntu LoCo Team Directory" %} {% endblock %}
537+
538+{% block sub_nav_links %}
539+<a class="sub-nav-item" href="{% url team-meeting-detail team_meeting_object.id %}">{% trans "Back to Meeting Details" %}</a>
540+{% endblock %}
541+
542+{% block extrahead %}{{ block.super }}
543+{{form.media}}
544+{% endblock %}
545+
546+{% block content %}
547+<article class="main-content">
548+<h2>{% trans "Delete Agenda Item" %}</h2>
549+
550+{% if agenda_item_object.children.all %}
551+<p>
552+Deleting this Agenda Item will also delete the following Agenda Items:
553+</p>
554+{% recurse item.children.all with agenda_item_object.children.all as item %}
555+ <ol class="agenda-list">
556+ {% loop %}
557+ <li class="agenda-item">
558+ <a class="agenda-title" title="{% trans 'Update Agenda Item:' %} {{item.title}}" href="{% url agenda-item-update team_meeting_object.id item.id %}">{{ item.title }}</a>
559+ - <a class="agenda-sig" target="launchpaduser" href="https://launchpad.net/~{{ item.owner.user.username}}">{{ item.owner.realname }}</a>
560+ @ {{ item.created_date|date:"D, d N Y H:i T" }}
561+ </li>
562+ {% child %}
563+ {% endloop %}
564+ </ol>
565+{% endrecurse %}
566+{% endif %}
567+
568+<p>{% blocktrans with agenda_item_object.title as itemname %}Do you really want to delete the Agenda Item: {{itemname}}?{% endblocktrans %}</p>
569+
570+<form action="." method="post">
571+ <p><input type="submit" value="Submit" /> <a href="{{team_meeting_object.get_absolute_url}}">{% trans "Cancel" %}</a></p>
572+</form>
573+</article>
574+{% endblock %}
575
576=== added file 'loco_directory/templates/meetings/agenda_item_new.html'
577--- loco_directory/templates/meetings/agenda_item_new.html 1970-01-01 00:00:00 +0000
578+++ loco_directory/templates/meetings/agenda_item_new.html 2011-01-24 02:52:15 +0000
579@@ -0,0 +1,36 @@
580+{% extends "base.html" %}
581+{% load i18n %}
582+
583+{% block title %}{% trans "New Agenda Item" %} | {% trans "Ubuntu LoCo Team Directory" %} {% endblock %}
584+
585+{% block sub_nav_links %}
586+<a class="sub-nav-item" href="{% url team-meeting-detail team_meeting_object.id %}">{% trans "Back to Meeting Details" %}</a>
587+{% endblock %}
588+
589+{% block extrahead %}{{ block.super }}
590+{{form.media}}
591+{% endblock %}
592+
593+{% block extrafooter %}
594+<script type="text/javascript"><!--
595+$(document).ready(function(){
596+ $('span[rel*=help]').colorTip({color:'orange'});
597+});
598+--></script>
599+{% endblock %}
600+
601+{% block content %}
602+<article class="main-content">
603+<h2>{% trans "Add new Agenda Item for " %}{{ team_meeting_object.name}}</h2>
604+<form action="." method="post">
605+ <div class="form" style="width:auto;">
606+ {{ form.as_template }}
607+ <div>
608+ {% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %}
609+ <input type="submit" name="submit" value="{% trans "Submit" %}" class="submit-button" />
610+ </div>
611+ </div>
612+</form>
613+</article>
614+
615+{% endblock %}
616
617=== added file 'loco_directory/templates/meetings/agenda_item_update.html'
618--- loco_directory/templates/meetings/agenda_item_update.html 1970-01-01 00:00:00 +0000
619+++ loco_directory/templates/meetings/agenda_item_update.html 2011-01-24 02:52:15 +0000
620@@ -0,0 +1,37 @@
621+{% extends "base.html" %}
622+{% load i18n admin_modify adminmedia %}
623+
624+{% block title %}{% trans "Update Agenda Item" %} | {% trans "Ubuntu LoCo Team Directory" %} {% endblock %}
625+
626+{% block sub_nav_links %}
627+<a class="sub-nav-item" href="{% url team-meeting-detail team_meeting_object.id %}">{% trans "Back to Meeting Details" %}</a>
628+<a class="sub-nav-item" href="{% url agenda-item-delete team_meeting_object.id agenda_item_object.id %}">{% trans "Delete Agenda Item" %}</a>
629+{% endblock %}
630+
631+{% block extrahead %}{{ block.super }}
632+{{form.media}}
633+{% endblock %}
634+
635+{% block extrafooter %}
636+<script type="text/javascript"><!--
637+$(document).ready(function(){
638+ $('span[rel*=help]').colorTip({color:'orange'});
639+});
640+--></script>
641+{% endblock %}
642+
643+{% block content %}
644+<article class="main-content">
645+<h2>{% trans "Update Agenda Item" %}</h2>
646+<form action="." method="post">
647+ <div class="form" style="width:auto;">
648+ {{ form.as_template }}
649+ <div>
650+ {% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %}
651+ <input type="submit" name="submit" value="{% trans "Submit" %}" class="submit-button" />
652+ </div>
653+ </div>
654+</form>
655+</article>
656+
657+{% endblock %}
658
659=== modified file 'loco_directory/templates/meetings/team_meeting_detail_agenda.inc.html'
660--- loco_directory/templates/meetings/team_meeting_detail_agenda.inc.html 2010-12-16 13:01:35 +0000
661+++ loco_directory/templates/meetings/team_meeting_detail_agenda.inc.html 2011-01-24 02:52:15 +0000
662@@ -1,10 +1,24 @@
663 {% load i18n %}
664+{% load recurse %}
665+ <p>
666+ <a href="{% url agenda-item-new team_meeting_object.id %}">{% trans 'Add Agenda Item' %}</a>
667+ </p>
668 {% if team_meeting_object.agenda %}
669-<ol class="agenda-list">
670-{% for item in team_meeting_object.agenda.as_tree %}
671-{{item.as_ol|safe}}
672-{% endfor %}
673-</ol>
674+ {% recurse item.children.all with team_meeting_object.agenda.top as item %}
675+ <ol class="agenda-list">
676+ {% loop %}
677+ <li class="agenda-item">
678+ <a class="agenda-title" title="{% trans 'Update Agenda Item:' %} {{item.title}}" href="{% url agenda-item-update team_meeting_object.id item.id %}">{{ item.title }}</a>
679+ - <a class="agenda-sig" target="launchpaduser" href="https://launchpad.net/~{{ item.owner.user.username}}">{{ item.owner.realname }}</a>
680+ @ {{ item.created_date|date:"D, d N Y H:i T" }}
681+ {% if item.description %}
682+ <div class="agenda-description">{{ item.description|linebreaks }}</div>
683+ {% endif %}
684+ </li>
685+ {% child %}
686+ {% endloop %}
687+ </ol>
688+ {% endrecurse %}
689 {% else %}
690 <p>{% trans "There are currently no items on the agenda." %}</p>
691 {% endif %}
692
693=== modified file 'loco_directory/templates/site_search.html'
694--- loco_directory/templates/site_search.html 2010-11-20 18:33:39 +0000
695+++ loco_directory/templates/site_search.html 2011-01-24 02:52:15 +0000
696@@ -57,6 +57,22 @@
697 <hr class="divide" />
698 {% endif %}
699
700+{% if meetings %}
701+<article class="main-content">
702+ <h2>{% trans "Meetings" %}</h2>
703+ {{colcycle.reset}}
704+ <ul>
705+ {% for meeting in meetings %}
706+ <li class="{{colcycle.next}}"><a href="{{ meeting.get_absolute_url }}">{{ meeting.name }}</a>
707+ {{ meeting.date_begin|date:"D, d N Y H:i T" }}
708+ - {% for team in meeting.teams.all %}<a title="{% trans "more information about this team" %}" href="{{ team.get_absolute_url }}">{{ team.name }}</a>{% endfor %}
709+ </li>
710+ {% endfor %}
711+ </ul>
712+</article>
713+<hr class="divide" />
714+{% endif %}
715+
716 {% else %}
717 <article class="main-content alone">
718 <center>

Subscribers

People subscribed via source and target branches