Merge lp:~cjohnston/summit/add-user-profiles into lp:summit
- add-user-profiles
- Merge into trunk
Status: | Work in progress | ||||
---|---|---|---|---|---|
Proposed branch: | lp:~cjohnston/summit/add-user-profiles | ||||
Merge into: | lp:summit | ||||
Diff against target: |
604 lines (+489/-2) 15 files modified
summit/common/forms.py (+40/-0) summit/common/launchpad.py (+27/-0) summit/schedule/models/summitmodel.py (+1/-1) summit/schedule/tests.py (+3/-1) summit/settings.py (+1/-0) summit/urls.py (+5/-0) summit/userprofiles/admin.py (+7/-0) summit/userprofiles/forms.py (+25/-0) summit/userprofiles/management/commands/update-profiles.py (+39/-0) summit/userprofiles/migrations/0001_add_user_profiles.py (+93/-0) summit/userprofiles/models.py (+82/-0) summit/userprofiles/templates/userprofiles/profile.html (+43/-0) summit/userprofiles/templates/userprofiles/profile_update.html (+41/-0) summit/userprofiles/tests.py (+16/-0) summit/userprofiles/views.py (+66/-0) |
||||
To merge this branch: | bzr merge lp:~cjohnston/summit/add-user-profiles | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Summit Hackers | Pending | ||
Review via email: mp+85194@code.launchpad.net |
Commit message
Description of the change
- 233. By Chris Johnston
-
[r=nigelbabu] Adds a sponsorship reviews state to summit so that links only show up when needed.
- 234. By Chris Johnston
-
[r=nigelbabu] Removes videographer and adds video fields.
- 235. By Chris Johnston
-
[r=nigelbabu] Adds track page with info about tracks.
- 236. By Chris Johnston
-
[r=chrisjohnston] Fixes token problems
- 237. By Данило Шеган
-
[r=chrisjohnston] Changes meeting name to 100 chars
- 238. By Chris Johnston
-
[r=nigelbabu,
danilo] Adds manager and scheduler user roles to summit - 239. By Michael Hall
-
[r=chrisjohnston] Adds private meeting urls for sharing access via emailed links
- 240. By Chris Johnston
-
[r=mhall119] Adds the ability for admins to add participants to a meeting
- 241. By Michael Hall
-
[r=nigelbabu] Sort attendees by user.username first, and summit second, to allow easier searching
- 242. By Chris Johnston
-
Fixes plenaries showing up on wrong days
- 243. By Michael Hall
-
[r=chrisjohnston] Removes obsolete tests and fix others
- 244. By Michael Hall
-
[r=chrisjohnston] Adds links to quickly add or remove yourself as a participant in a meeting.
- 245. By Chris Johnston
-
[r=mhall119] Adds ability for track leads, managers and schedulers to create private meetings from the UI
- 246. By Michael Hall
-
[r=chrisjohnston] Adds a new display for daily schedules that is more convenient for viewing on a desktop
- 247. By Michael Hall
-
[r=chrisjohnston] Add icecast urls to the new schedule
- 248. By Chris Johnston
-
[r=mhall119] This removes the images from the meeting fields on the widescreen page as well as changing the text to direct to the meeting page.
- 249. By Michael Hall
-
[r=chrisjohnston] Removed Linaro hacks
- 250. By Michael Hall
-
[r=chrisjohnston] Hide plenary room from room list unless the user can edit the schedule
- 251. By Chris Johnston
-
[r=mhall119] Adds can change agenda to create and edit PM
- 252. By Michael Hall
-
[r=chrisjohnston] Display time headers in 24-hour time, add end time
- 253. By Michael Hall
-
[r=chrisjohnston] Add management command to import summit schedule data.
- 254. By Chris Johnston
-
[r=james-w] Moves templates to common app
- 255. By Chris Johnston
-
[r=james-w] Remove remaining i18n stuff
- 256. By Chris Johnston
-
[r=james-w] Fixes spelling issue
- 257. By Michael Hall
-
[r=chrisjohnston] Fix localtime conversion issues
- 258. By Michael Hall
-
[r=chrisjohnston] Fix wide schedule links and tests
- 259. By Chris Johnston
-
[r=mhall119] Renames the app to "The Summit Scheduler" and adds/fixes page titles across the board.
- 260. By Chris Johnston
-
[r=mhall119] This removes the rest of the hacks for having multiple plenaries at the same time which was breaking the widescreen and daily displays.
- 261. By Chris Johnston
-
Starts adding social media to summit by adding opengraph
- 262. By Данило Шеган
-
Do not crash when multiple agendas are returned for the same time slot (eg. due to multiple plenary rooms)
- 263. By Chris Johnston
-
Add lunch and plenary labels to the new schedule
- 264. By Chris Johnston
-
Adds twitter hash tag to the summit model, displays twidenash on the main summit page
- 265. By Michael Hall
-
force available times on test rooms so that they will be on the schedule when they should be
- 266. By Chris Johnston
-
Version 1.0.0 Release
- 267. By Chris Johnston
-
Adds fixes from mhall119
- 268. By Chris Johnston
-
Adds url fix from danilos
- 269. By Chris Johnston
-
Release version 1.0.1
- 270. By Chris Johnston
-
[r=danilo] Removes "Private:" from the schedule list if there are no private rooms
- 271. By Данило Шеган
-
[r=chrisjohnston] Makes it possible for the creator of a private meeting to add required participants.
- 272. By Chris Johnston
-
[r=danilo] Removes "Lead:" if there is no track lead set
- 273. By Chris Johnston
-
[r=danilo] Removes empty subnav from meeting pages
- 274. By Chris Johnston
-
[r=chrisjohnsto
n,danilo] Removes the requirement to set participants for a private meeting, adds help text. - 275. By Chris Johnston
-
Version 1.0.2 release
- 276. By Michael Hall
-
[r=chrisjohnston] A quick and dirty monkey patching to give Users a sane order, added users real name in addition to their launchpad id, on meeting page also added real name in addition to launchpad id for participants.
- 277. By Michael Hall
-
[r=chrisjohnston] Fixes reverse lookup errors on meetings with no name and past summits page
- 278. By Chris Johnston
-
[r=mhall119] Makes the leads display on the track display smaller
- 279. By Chris Johnston
-
Allows etherpad url to be defined in the summit model
- 280. By Chris Johnston
-
Version 1.0.3 release
- 281. By Stuart Langridge
-
[r=mhall119] Custom styles for devices less than 480px wide; tiny bit of JS to override the hover stuff and toggle the details div on and off; one extra class added as a convenience hook.
- 282. By Chris Johnston
-
[r=alanbell,
mhall119] Fixes the issue where etherpad SSO breaks summit. - 283. By Stuart Langridge
-
[r=mhall119] More mobile tweaks!
- 284. By Michael Hall
-
[r=chrisjohnston] Replaces jorge with mhall119 as email contact.
- 285. By Michael Hall
-
[r=chrisjohnston] Show multi-slot meetings in subsequent slots in the new schedule. Show track names in normal display, but not in mobile, on the new schedule
- 286. By Chris Johnston
-
Version 1.0.4 release
- 287. By Chris Johnston
-
[r=mhall119] Removes an empty if statement
- 288. By Chris Johnston
-
[r=chrisjohnsto
n,mhall119] Changes the links to match the links on connect when viewed at summit.linaro.org - 289. By Chris Johnston
-
[r=danilo] Adds custom QR codes for displaying links to different apps per summit
- 290. By Chris Johnston
-
[r=danilo] Adds a link to the blueprint (if there is on) in the sub-nav of the meeting page
- 291. By Chris Johnston
-
Makes it to where links on a page viewed _popup=1 contain _popup=1 in their url
- 292. By Chris Johnston
-
Makes the meeting page open up in a new window on s.l.o agenda page
- 293. By Chris Johnston
-
Version 1.0.5 release
- 294. By Michael Hall
-
[r=chrisjohnston] * Removes broken javascript for "Hide talks that aren't for me"
* Fixes schedule layout on the summit page to avoid excessive scrolling - 295. By Michael Hall
-
[r=chrisjohnston] Limit the list of attendees the participant inline to those for the same summit as the meeting being viewed
- 296. By Chris Johnston
-
Displays everything on the user page
- 297. By Chris Johnston
-
Adds if around mugshot
- 298. By Chris Johnston
-
update
Unmerged revisions
- 298. By Chris Johnston
-
update
- 297. By Chris Johnston
-
Adds if around mugshot
- 296. By Chris Johnston
-
Displays everything on the user page
Preview Diff
1 | === added file 'summit/common/forms.py' |
2 | --- summit/common/forms.py 1970-01-01 00:00:00 +0000 |
3 | +++ summit/common/forms.py 2012-02-07 17:38:18 +0000 |
4 | @@ -0,0 +1,40 @@ |
5 | +from django.template import Context, loader |
6 | +from django import forms |
7 | + |
8 | +# Taken from http://djangosnippets.org/snippets/1732/ |
9 | +class RenderableMixin(object): |
10 | + """ |
11 | + Mixin to render forms from a predefined template |
12 | + """ |
13 | + |
14 | + @property |
15 | + def form_class_name(self): |
16 | + return '.'.join([self.__module__, self.__class__.__name__.lower()]) |
17 | + |
18 | + def as_template(self): |
19 | + """ |
20 | + Renders a form from a template |
21 | + """ |
22 | + self.template_name = self.__class__.__name__.lower() |
23 | + |
24 | + if not getattr(self, 'tpl', None): |
25 | + self.tpl = loader.get_template('form.html') |
26 | + |
27 | + context_dict = dict( |
28 | + non_field_errors=self.non_field_errors(), |
29 | + fields=[ forms.forms.BoundField(self, field, name) for name, field in self.fields.iteritems()], |
30 | + errors=self.errors, |
31 | + data=self.data, |
32 | + form=self, |
33 | + ) |
34 | + |
35 | + if getattr(self, 'initial', None): |
36 | + context_dict.update(dict(initial=self.initial)) |
37 | + if getattr(self, 'instance', None): |
38 | + context_dict.update(dict(instance=self.instance)) |
39 | + if getattr(self, 'cleaned_data', None): |
40 | + context_dict.update(dict(cleaned_data=self.cleaned_data)) |
41 | + |
42 | + return self.tpl.render( |
43 | + Context(context_dict) |
44 | + ) |
45 | |
46 | === renamed file 'summit/common/forms.py' => 'summit/common/forms.py.moved' |
47 | === renamed file 'summit/schedule/launchpad.py' => 'summit/common/launchpad.py' |
48 | --- summit/schedule/launchpad.py 2012-01-23 01:18:55 +0000 |
49 | +++ summit/common/launchpad.py 2012-02-07 17:38:18 +0000 |
50 | @@ -71,3 +71,30 @@ |
51 | openid_assoc.save() |
52 | return True |
53 | return False |
54 | + |
55 | +def get_mugshot_url(lp, identity): |
56 | + # Not ideal, but until LP #336943 |
57 | + # or similar, we are in a hard spot. |
58 | + # When this bug is fixed, this can be made cleaner. |
59 | + try: |
60 | + lp.people[identity].mugshot.open() |
61 | + return lp.people[identity].mugshot_link or "https://api.launchpad.net/1.0/~%s/mugshot" % (identity) |
62 | + except HTTPError: |
63 | + # 404 or some other issue that means we should default to False |
64 | + return "https://api.launchpad.net/1.0/ubuntu/mugshot" |
65 | + |
66 | +def get_user_timezone(username, lp=None): |
67 | + timezone = 'UTC' |
68 | + if username is None: |
69 | + return timezone |
70 | + |
71 | + if not lp: |
72 | + lp = lp_login() |
73 | + if not lp: |
74 | + return timezone |
75 | + try: |
76 | + lp_user = lp.people[username] |
77 | + timezone = lp_user.timezone |
78 | + except: |
79 | + pass |
80 | + return timezone |
81 | |
82 | === modified file 'summit/schedule/models/summitmodel.py' |
83 | --- summit/schedule/models/summitmodel.py 2012-02-02 15:27:54 +0000 |
84 | +++ summit/schedule/models/summitmodel.py 2012-02-07 17:38:18 +0000 |
85 | @@ -33,7 +33,7 @@ |
86 | from django.contrib.auth.models import User |
87 | |
88 | from summit.schedule.fields import NameField |
89 | -from summit.schedule import launchpad |
90 | +from summit.common import launchpad |
91 | |
92 | __all__ = ( |
93 | 'Summit', |
94 | |
95 | === modified file 'summit/schedule/tests.py' |
96 | --- summit/schedule/tests.py 2012-01-25 08:32:48 +0000 |
97 | +++ summit/schedule/tests.py 2012-02-07 17:38:18 +0000 |
98 | @@ -33,8 +33,10 @@ |
99 | from summit.schedule.fields import NameField |
100 | |
101 | from summit.schedule.models import * |
102 | + |
103 | from summit.schedule.render import Schedule |
104 | -from summit.schedule import launchpad |
105 | + |
106 | +from summit.common import launchpad |
107 | |
108 | # Monkey-patch our NameField into the types of fields that the factory |
109 | # understands. This is simpler than trying to subclass the Mommy |
110 | |
111 | === modified file 'summit/settings.py' |
112 | --- summit/settings.py 2012-02-02 14:16:45 +0000 |
113 | +++ summit/settings.py 2012-02-07 17:38:18 +0000 |
114 | @@ -153,6 +153,7 @@ |
115 | 'summit.schedule', |
116 | 'summit.sponsor', |
117 | 'summit.common', |
118 | + 'summit.userprofiles', |
119 | 'south', |
120 | ] |
121 | |
122 | |
123 | === modified file 'summit/urls.py' |
124 | --- summit/urls.py 2012-01-23 01:18:55 +0000 |
125 | +++ summit/urls.py 2012-02-07 17:38:18 +0000 |
126 | @@ -88,6 +88,11 @@ |
127 | (r'^(?P<summit_name>[\w-]+)/track/(?P<track_slug>[%+\.\w-]+).ical$', 'track_ical'), |
128 | ) |
129 | |
130 | +urlpatterns += patterns('summit.userprofiles.views', |
131 | + (r'^u/(?P<username>[%+\.\w-]+)/update$', 'update_profile'), |
132 | + (r'^u/(?P<username>[%+\.\w-]+)', 'user_profile'), |
133 | + ) |
134 | + |
135 | if settings.DEBUG or settings.SERVE_STATIC: |
136 | urlpatterns += patterns('', |
137 | (r'^media/(?P<path>.*)$', 'django.views.static.serve', |
138 | |
139 | === added directory 'summit/userprofiles' |
140 | === added file 'summit/userprofiles/__init__.py' |
141 | === added file 'summit/userprofiles/admin.py' |
142 | --- summit/userprofiles/admin.py 1970-01-01 00:00:00 +0000 |
143 | +++ summit/userprofiles/admin.py 2012-02-07 17:38:18 +0000 |
144 | @@ -0,0 +1,7 @@ |
145 | +from django.contrib import admin |
146 | +from userprofiles.models import UserProfile |
147 | + |
148 | +class UserProfileAdmin(admin.ModelAdmin): |
149 | + search_fields = ('realname',) |
150 | + |
151 | +admin.site.register(UserProfile, UserProfileAdmin) |
152 | |
153 | === added file 'summit/userprofiles/forms.py' |
154 | --- summit/userprofiles/forms.py 1970-01-01 00:00:00 +0000 |
155 | +++ summit/userprofiles/forms.py 2012-02-07 17:38:18 +0000 |
156 | @@ -0,0 +1,25 @@ |
157 | +# Ubuntu Developer Summit web application |
158 | +# Copyright (C) 2008, 2009, 2010 Canonical Ltd |
159 | +# |
160 | +# This program is free software: you can redistribute it and/or modify |
161 | +# it under the terms of the GNU Affero General Public License as |
162 | +# published by the Free Software Foundation, either version 3 of the |
163 | +# License, or (at your option) any later version. |
164 | +# |
165 | +# This program is distributed in the hope that it will be useful, |
166 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
167 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
168 | +# GNU Affero General Public License for more details. |
169 | +# |
170 | +# You should have received a copy of the GNU Affero General Public License |
171 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
172 | + |
173 | +from django import forms |
174 | + |
175 | +from common.forms import RenderableMixin |
176 | +from summit.userprofiles.models import UserProfile |
177 | + |
178 | +class UpdateProfileForm(forms.ModelForm, RenderableMixin): |
179 | + class Meta: |
180 | + model = UserProfile |
181 | + exclude = ('user', 'tz') |
182 | |
183 | === added directory 'summit/userprofiles/management' |
184 | === added file 'summit/userprofiles/management/__init__.py' |
185 | === added directory 'summit/userprofiles/management/commands' |
186 | === added file 'summit/userprofiles/management/commands/__init__.py' |
187 | === added file 'summit/userprofiles/management/commands/update-profiles.py' |
188 | --- summit/userprofiles/management/commands/update-profiles.py 1970-01-01 00:00:00 +0000 |
189 | +++ summit/userprofiles/management/commands/update-profiles.py 2012-02-07 17:38:18 +0000 |
190 | @@ -0,0 +1,39 @@ |
191 | +#!/usr/bin/python |
192 | +# -*- coding: utf-8 -*- |
193 | + |
194 | +from django.core.management.base import NoArgsCommand |
195 | + |
196 | +from common import launchpad |
197 | +from django.contrib.auth.models import User, Group |
198 | +from userprofiles.models import UserProfile, create_profile |
199 | + |
200 | +from datetime import datetime |
201 | +import sys |
202 | + |
203 | +class Command(NoArgsCommand): |
204 | + help = "Updates user profiles with information from Launchpad." |
205 | + |
206 | + def handle_noargs(self, **options): |
207 | + lp = launchpad.lp_login() |
208 | + if not lp: |
209 | + sys.exit(1) |
210 | + USER_BLACKLIST = (u"root", u"admin") |
211 | + |
212 | + summit_users = User.objects.all() |
213 | + |
214 | + for summit_user in summit_users: |
215 | + if not summit_user.username in USER_BLACKLIST: |
216 | + profile, created = UserProfile.objects.get_or_create(user=summit_user) |
217 | + try: |
218 | + lp_user = lp.people[summit_user.username] |
219 | + profile.tz = lp_user.time_zone or "UTC" |
220 | + profile.realname = lp_user.display_name or user.username |
221 | + if lp_user.logo_link.startswith("https://") : |
222 | + profile.mugshot = lp_user.logo_link |
223 | + else: |
224 | + profile.mugshot = "https://launchpad.net/@@/person-logo" |
225 | + profile.save() |
226 | + except Exception, e: |
227 | + print "Error updating %s" % summit_user |
228 | + print e |
229 | + pass |
230 | |
231 | === added directory 'summit/userprofiles/migrations' |
232 | === added file 'summit/userprofiles/migrations/0001_add_user_profiles.py' |
233 | --- summit/userprofiles/migrations/0001_add_user_profiles.py 1970-01-01 00:00:00 +0000 |
234 | +++ summit/userprofiles/migrations/0001_add_user_profiles.py 2012-02-07 17:38:18 +0000 |
235 | @@ -0,0 +1,93 @@ |
236 | +# encoding: utf-8 |
237 | +import datetime |
238 | +from south.db import db |
239 | +from south.v2 import SchemaMigration |
240 | +from django.db import models |
241 | + |
242 | +class Migration(SchemaMigration): |
243 | + |
244 | + def forwards(self, orm): |
245 | + |
246 | + # Adding model 'UserProfile' |
247 | + db.create_table('userprofiles_userprofile', ( |
248 | + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), |
249 | + ('user', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['auth.User'], unique=True)), |
250 | + ('realname', self.gf('django.db.models.fields.CharField')(max_length=150, blank=True)), |
251 | + ('tz', self.gf('django.db.models.fields.CharField')(default='UTC', max_length=32)), |
252 | + ('mugshot', self.gf('django.db.models.fields.URLField')(max_length=150, null=True, blank=True)), |
253 | + ('blog', self.gf('django.db.models.fields.URLField')(max_length=200, null=True, blank=True)), |
254 | + ('twitter', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
255 | + ('identica', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
256 | + ('picasa', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
257 | + ('flickr', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
258 | + ('facebook', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
259 | + ('irc', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
260 | + ('aim', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
261 | + ('xmpp', self.gf('django.db.models.fields.CharField')(max_length=32, null=True, blank=True)), |
262 | + )) |
263 | + db.send_create_signal('userprofiles', ['UserProfile']) |
264 | + |
265 | + |
266 | + def backwards(self, orm): |
267 | + |
268 | + # Deleting model 'UserProfile' |
269 | + db.delete_table('userprofiles_userprofile') |
270 | + |
271 | + |
272 | + models = { |
273 | + 'auth.group': { |
274 | + 'Meta': {'object_name': 'Group'}, |
275 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
276 | + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), |
277 | + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) |
278 | + }, |
279 | + 'auth.permission': { |
280 | + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, |
281 | + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
282 | + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), |
283 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
284 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) |
285 | + }, |
286 | + 'auth.user': { |
287 | + 'Meta': {'object_name': 'User'}, |
288 | + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
289 | + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), |
290 | + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
291 | + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), |
292 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
293 | + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), |
294 | + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
295 | + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), |
296 | + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), |
297 | + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), |
298 | + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), |
299 | + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), |
300 | + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) |
301 | + }, |
302 | + 'contenttypes.contenttype': { |
303 | + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, |
304 | + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
305 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
306 | + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), |
307 | + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) |
308 | + }, |
309 | + 'userprofiles.userprofile': { |
310 | + 'Meta': {'ordering': "('user__username',)", 'object_name': 'UserProfile'}, |
311 | + 'aim': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
312 | + 'blog': ('django.db.models.fields.URLField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), |
313 | + 'facebook': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
314 | + 'flickr': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
315 | + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), |
316 | + 'identica': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
317 | + 'irc': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
318 | + 'mugshot': ('django.db.models.fields.URLField', [], {'max_length': '150', 'null': 'True', 'blank': 'True'}), |
319 | + 'picasa': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
320 | + 'realname': ('django.db.models.fields.CharField', [], {'max_length': '150', 'blank': 'True'}), |
321 | + 'twitter': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}), |
322 | + 'tz': ('django.db.models.fields.CharField', [], {'default': "'UTC'", 'max_length': '32'}), |
323 | + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}), |
324 | + 'xmpp': ('django.db.models.fields.CharField', [], {'max_length': '32', 'null': 'True', 'blank': 'True'}) |
325 | + } |
326 | + } |
327 | + |
328 | + complete_apps = ['userprofiles'] |
329 | |
330 | === added file 'summit/userprofiles/migrations/__init__.py' |
331 | === added file 'summit/userprofiles/models.py' |
332 | --- summit/userprofiles/models.py 1970-01-01 00:00:00 +0000 |
333 | +++ summit/userprofiles/models.py 2012-02-07 17:38:18 +0000 |
334 | @@ -0,0 +1,82 @@ |
335 | +from django.db import models |
336 | +from django.utils.translation import ugettext_lazy as _ |
337 | +from django.contrib.auth import models as auth_models |
338 | + |
339 | + |
340 | +import pytz |
341 | + |
342 | +class UserProfile(models.Model): |
343 | + " Store profile information about a user " |
344 | + |
345 | + user = models.OneToOneField(auth_models.User) |
346 | + realname = models.CharField(_("Real Name"), max_length=150, blank=True) |
347 | + tz = models.CharField(verbose_name=_('Timezone'), max_length=32, default='UTC') |
348 | + mugshot = models.URLField(verbose_name=_("Mugshot"), max_length=150, blank=True, verify_exists=False, null=True) |
349 | + blog = models.URLField(verbose_name=_('Blog URL'), blank=True, null=True, verify_exists=False) |
350 | + twitter = models.CharField(verbose_name=_('Twitter ID'), max_length=32, blank=True, null=True) |
351 | + identica = models.CharField(verbose_name=_('Identi.ca ID'), max_length=32, blank=True, null=True) |
352 | + picasa = models.CharField(verbose_name=_('Picasa ID'), max_length=32, blank=True, null=True) |
353 | + flickr = models.CharField(verbose_name=_('Flickr ID'), max_length=32, blank=True, null=True) |
354 | + facebook = models.CharField(verbose_name=_('Facebook ID'), max_length=32, blank=True, null=True) |
355 | + irc = models.CharField(verbose_name=_('IRC Nick'), max_length=32, blank=True, null=True) |
356 | + aim = models.CharField(verbose_name=_('AOL IM Nick'), max_length=32, blank=True, null=True) |
357 | + xmpp = models.CharField(verbose_name=_('XMPP IM Nick'), max_length=32, blank=True, null=True) |
358 | + |
359 | + class Meta: |
360 | + ordering = ('user__username',) |
361 | + |
362 | + def __unicode__(self): |
363 | + try: |
364 | + if self.realname: |
365 | + return "%s (%s)" % (self.user.username, self.realname) |
366 | + return "%s" % self.user.username |
367 | + except: |
368 | + return "Unknown Profile" |
369 | + |
370 | + def get_timezone(self): |
371 | + try: |
372 | + return pytz.timezone(self.tz) |
373 | + except: |
374 | + return pytz.utc |
375 | + timezone = property(get_timezone) |
376 | + |
377 | + def tolocaltime(self, dt): |
378 | + as_utc = pytz.utc.localize(dt) |
379 | + return as_utc.astimezone(self.timezone) |
380 | + |
381 | + def fromlocaltime(self, dt): |
382 | + local = self.timezone.localize(dt) |
383 | + return local.astimezone(pytz.utc) |
384 | + |
385 | +def _getUserProfile(self): |
386 | + if self.is_anonymous(): |
387 | + return UserProfile() |
388 | + |
389 | + profile, created = UserProfile.objects.get_or_create(user=self) |
390 | + |
391 | + if created: |
392 | + from common.launchpad import get_user_timezone |
393 | + profile.tz = get_user_timezone(self.username) |
394 | + profile.save() |
395 | + |
396 | + return profile |
397 | + |
398 | +def _getAnonProfile(self): |
399 | + return UserProfile() |
400 | + |
401 | +auth_models.User.profile = property(_getUserProfile) |
402 | +auth_models.AnonymousUser.profile = property(_getAnonProfile) |
403 | + |
404 | +def create_profile(username): |
405 | + user, created = auth_models.User.objects.get_or_create(username=username) |
406 | + if created: |
407 | + user.save() |
408 | + from common import launchpad |
409 | + launchpad.set_user_openid(user, force=True) |
410 | + profile, created = UserProfile.objects.get_or_create(user=user) |
411 | + if created: |
412 | + # set real name as username for now, |
413 | + # we get the name later on via cronjob |
414 | + profile.realname = username |
415 | + profile.save() |
416 | + return profile |
417 | |
418 | === added directory 'summit/userprofiles/templates' |
419 | === added directory 'summit/userprofiles/templates/userprofiles' |
420 | === added file 'summit/userprofiles/templates/userprofiles/profile.html' |
421 | --- summit/userprofiles/templates/userprofiles/profile.html 1970-01-01 00:00:00 +0000 |
422 | +++ summit/userprofiles/templates/userprofiles/profile.html 2012-02-07 17:38:18 +0000 |
423 | @@ -0,0 +1,43 @@ |
424 | +{% extends "base.html" %} |
425 | + |
426 | +{% block page_name %} |
427 | +{{ display_user.profile.realname }} |
428 | +{% endblock %} |
429 | +{% block sub_nav_links %} |
430 | +<a class="sub-nav-item" href="">Update</a> |
431 | +{% endblock %} |
432 | +{% block content %} |
433 | +<h3>User: {{ display_user.profile.realname }}</h3> |
434 | +{% if display_user.profile.mugshot %}<img src="{{ display_user.profile.mugshot }}" alt="{{ display_user.profile.realname }}" /><br />{% endif %} |
435 | +Launchpad ID: {{ display_user.profile.user }}<br /> |
436 | +{% if display_user.profile.tz %} |
437 | +Timezone: {{ display_user.profile.tz }}<br /> |
438 | +{% endif %} |
439 | +{% if display_user.profile.irc %} |
440 | +IRC Nick: {{ display_user.profile.irc }} <br /> |
441 | +{% endif %} |
442 | +{% if display_user.profile.blog %} |
443 | +Blog: <a href="{{ display_user.profile.blog }}">{{ display_user.profile.blog }}</a><br /> |
444 | +{% endif %} |
445 | +{% if display_user.profile.twitter %} |
446 | +Twitter: <a href="http://twitter.com/{{ display_user.profile.twitter }}">{{ display_user.profile.twitter }}</a><br /> |
447 | +{% endif %} |
448 | +{% if display_user.profile.facebook %} |
449 | +Facebook: <a href="http://facebook.com/{{ display_user.profile.facebook }}">{{ display_user.profile.facebook }}</a><br /> |
450 | +{% endif %} |
451 | +{% if display_user.profile.identia %} |
452 | +Identi.ca: {{ display_user.profile.identica }}<br /> |
453 | +{% endif %} |
454 | +{% if display_user.profile.picasa %} |
455 | +Picasa: {{ display_user.profile.picasa }}<br /> |
456 | +{% endif %} |
457 | +{% if display_user.profile.flickr %} |
458 | +Flickr: {{ display_user.profile.flickr }}<br /> |
459 | +{% endif %} |
460 | +{% if display_user.profile.aim %} |
461 | +AOL IM: {{ display_user.profile.aim }}<br /> |
462 | +{% endif %} |
463 | +{% if display_user.profile.xmpp %} |
464 | +XMPP: {{ display_user.profile.xmpp }} |
465 | +{% endif %} |
466 | +{% endblock %} |
467 | |
468 | === added file 'summit/userprofiles/templates/userprofiles/profile_update.html' |
469 | --- summit/userprofiles/templates/userprofiles/profile_update.html 1970-01-01 00:00:00 +0000 |
470 | +++ summit/userprofiles/templates/userprofiles/profile_update.html 2012-02-07 17:38:18 +0000 |
471 | @@ -0,0 +1,41 @@ |
472 | +{% extends "base.html" %} |
473 | + |
474 | +{% block extrafooter %} |
475 | +<script type="text/javascript"><!-- |
476 | +$(document).ready(function(){ |
477 | + $('span[rel*=help]').colorTip({color:'orange'}); |
478 | +}); |
479 | +--></script> |
480 | +{% endblock %} |
481 | + |
482 | + |
483 | +{% block content %} |
484 | + |
485 | + <article id="form" class="main-content"> |
486 | + {% if form.errors %} |
487 | + <p style="color: red;"> |
488 | + Please correct the error{{ form.errors|pluralize }} below. |
489 | + </p> |
490 | + {% endif %} |
491 | + |
492 | + <form action="{{ request.path_info }}" method="POST"> |
493 | + <fieldset> |
494 | + <legend>User information</legend> |
495 | + <div> |
496 | + <div class="field"><label>Username:</label></div> |
497 | + <span class="extra">{{ display_user.profile.user }}</span> |
498 | + </div> |
499 | + <div> |
500 | + <div class="field"><label>Timezone:</label></div> |
501 | + <span class="extra">{{ display_user.profile.tz }}</span> |
502 | + </div> |
503 | + </fieldset> |
504 | + <fieldset> |
505 | + <legend>Update profile information below</legend> |
506 | + {{ form.as_template }} |
507 | + </fieldset> |
508 | + {% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %} |
509 | + <input type="submit" name="submit" value="Update!" class="submit-button" /> |
510 | + </form> |
511 | + </article> |
512 | +{% endblock %} |
513 | |
514 | === added file 'summit/userprofiles/tests.py' |
515 | --- summit/userprofiles/tests.py 1970-01-01 00:00:00 +0000 |
516 | +++ summit/userprofiles/tests.py 2012-02-07 17:38:18 +0000 |
517 | @@ -0,0 +1,16 @@ |
518 | +""" |
519 | +This file demonstrates writing tests using the unittest module. These will pass |
520 | +when you run "manage.py test". |
521 | + |
522 | +Replace this with more appropriate tests for your application. |
523 | +""" |
524 | + |
525 | +from django.test import TestCase |
526 | + |
527 | + |
528 | +class SimpleTest(TestCase): |
529 | + def test_basic_addition(self): |
530 | + """ |
531 | + Tests that 1 + 1 always equals 2. |
532 | + """ |
533 | + self.assertEqual(1 + 1, 2) |
534 | |
535 | === added file 'summit/userprofiles/views.py' |
536 | --- summit/userprofiles/views.py 1970-01-01 00:00:00 +0000 |
537 | +++ summit/userprofiles/views.py 2012-02-07 17:38:18 +0000 |
538 | @@ -0,0 +1,66 @@ |
539 | +# Ubuntu Developer Summit web application |
540 | +# Copyright (C) 2008, 2009, 2010 Canonical Ltd |
541 | +# |
542 | +# This program is free software: you can redistribute it and/or modify |
543 | +# it under the terms of the GNU Affero General Public License as |
544 | +# published by the Free Software Foundation, either version 3 of the |
545 | +# License, or (at your option) any later version. |
546 | +# |
547 | +# This program is distributed in the hope that it will be useful, |
548 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
549 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
550 | +# GNU Affero General Public License for more details. |
551 | +# |
552 | +# You should have received a copy of the GNU Affero General Public License |
553 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. |
554 | + |
555 | +from django.conf import settings |
556 | +from django.http import HttpResponse, HttpResponseRedirect |
557 | +from django.shortcuts import render_to_response, get_object_or_404 |
558 | +from django.template import RequestContext |
559 | +from django.contrib.auth.models import User |
560 | + |
561 | +from common.utils import redirect |
562 | + |
563 | +import forms |
564 | + |
565 | +from summit.userprofiles.models import UserProfile |
566 | + |
567 | +__all__ = ( |
568 | + 'user_profile', |
569 | +) |
570 | + |
571 | +def user_profile(request, username): |
572 | + user = get_object_or_404(User, username=username) |
573 | + |
574 | + context = { |
575 | + 'display_user': user, |
576 | + 'display_user_profile': user.profile, |
577 | + } |
578 | + return render_to_response("userprofiles/profile.html", context, |
579 | + context_instance=RequestContext(request)) |
580 | + |
581 | +def update_profile(request, username): |
582 | + user = get_object_or_404(User, username=username) |
583 | + |
584 | + if request.user.id != user.id: |
585 | + request.user.message_set.create(message='You are not allowed to make changes to this profile.') |
586 | + return redirect( user_profile, username ) |
587 | + |
588 | + if request.method == "POST": |
589 | + form = forms.UpdateProfileForm(data=request.POST, instance=user.profile) |
590 | + if form.is_valid(): |
591 | + form.save() |
592 | + request.user.message_set.create(message='User profile updated') |
593 | + return redirect( user_profile, username ) |
594 | + else: |
595 | + request.user.message_set.create(message='User profile could not be saved.') |
596 | + else: |
597 | + form = forms.UpdateProfileForm(instance=user.profile) |
598 | + |
599 | + |
600 | + return render_to_response('userprofiles/profile_update.html', |
601 | + {'form': form, |
602 | + 'display_user': user, |
603 | + 'display_user_profile': user.profile, |
604 | + }, RequestContext(request)) |