| LEFT | RIGHT |
|---|---|
| 1 # Copyright 2008 Google Inc. | 1 # Copyright 2008 Google Inc. |
| 2 # | 2 # |
| 3 # Licensed under the Apache License, Version 2.0 (the "License"); | 3 # Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 # you may not use this file except in compliance with the License. | 4 # you may not use this file except in compliance with the License. |
| 5 # You may obtain a copy of the License at | 5 # You may obtain a copy of the License at |
| 6 # | 6 # |
| 7 # http://www.apache.org/licenses/LICENSE-2.0 | 7 # http://www.apache.org/licenses/LICENSE-2.0 |
| 8 # | 8 # |
| 9 # Unless required by applicable law or agreed to in writing, software | 9 # Unless required by applicable law or agreed to in writing, software |
| 10 # distributed under the License is distributed on an "AS IS" BASIS, | 10 # distributed under the License is distributed on an "AS IS" BASIS, |
| 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 # See the License for the specific language governing permissions and | 12 # See the License for the specific language governing permissions and |
| 13 # limitations under the License. | 13 # limitations under the License. |
| 14 | 14 |
| 15 """Views for Rietveld. | 15 """Views for Rietveld. |
| 16 | 16 |
| 17 This requires Django 0.97.pre. | 17 This requires Django 0.97.pre. |
| 18 """ | 18 """ |
| 19 | 19 |
| 20 | 20 |
| 21 ### Imports ### | 21 ### Imports ### |
| 22 | 22 |
| 23 | 23 |
| 24 # Python imports | 24 # Python imports |
| 25 import os | 25 import os |
| 26 import cgi | 26 import cgi |
| 27 import random | 27 import random |
| 28 import logging | 28 import logging |
| 29 import binascii | 29 import binascii |
| 30 import sets | |
|
GvR
2008/05/11 19:20:49
This is Python 2.5, it has a built-in set type. (E
| |
| 30 | 31 |
| 31 # AppEngine imports | 32 # AppEngine imports |
| 32 from google.appengine.api import mail | 33 from google.appengine.api import mail |
| 33 from google.appengine.api import users | 34 from google.appengine.api import users |
| 34 from google.appengine.api import urlfetch | 35 from google.appengine.api import urlfetch |
| 35 from google.appengine.ext import db | 36 from google.appengine.ext import db |
| 36 from google.appengine.ext.db import djangoforms | 37 from google.appengine.ext.db import djangoforms |
| 37 | 38 |
| 38 # DeadlineExceededError can live in two different places | 39 # DeadlineExceededError can live in two different places |
| 39 # TODO(guido): simplify once this is fixed. | 40 # TODO(guido): simplify once this is fixed. |
| (...skipping 139 matching lines...) Show 10 above Show 10 below | |
| 179 max_length=1000, | 180 max_length=1000, |
| 180 widget=forms.TextInput(attrs={'size': 60})) | 181 widget=forms.TextInput(attrs={'size': 60})) |
| 181 send_mail = forms.BooleanField() | 182 send_mail = forms.BooleanField() |
| 182 message = forms.CharField(required=False, | 183 message = forms.CharField(required=False, |
| 183 max_length=10000, | 184 max_length=10000, |
| 184 widget=forms.Textarea(attrs={'cols': 60})) | 185 widget=forms.Textarea(attrs={'cols': 60})) |
| 185 | 186 |
| 186 | 187 |
| 187 class MiniPublishForm(forms.Form): | 188 class MiniPublishForm(forms.Form): |
| 188 | 189 |
| 190 send_mail = forms.BooleanField() | |
| 189 reviewers = forms.CharField(required=False, | 191 reviewers = forms.CharField(required=False, |
| 190 max_length=1000, | 192 max_length=1000, |
| 191 widget=forms.TextInput(attrs={'size': 60})) | 193 widget=forms.TextInput(attrs={'size': 60})) |
| 192 send_mail = forms.BooleanField() | |
| 193 message = forms.CharField(required=False, | 194 message = forms.CharField(required=False, |
| 194 max_length=10000, | 195 max_length=10000, |
| 195 widget=forms.Textarea(attrs={'cols': 60})) | 196 widget=forms.Textarea(attrs={'cols': 60})) |
| 196 | 197 |
| 197 | 198 |
| 198 class SettingsForm(forms.Form): | 199 class SettingsForm(forms.Form): |
| 199 | 200 |
| 200 nickname = forms.CharField(max_length=30) | 201 nickname = forms.CharField(max_length=30) |
| 201 | 202 |
| 202 | 203 |
| (...skipping 360 matching lines...) Show 10 above Show 10 below | |
| 563 errkey = url and 'url' or 'data' | 564 errkey = url and 'url' or 'data' |
| 564 form.errors[errkey] = ['Patch set contains no recognizable patches'] | 565 form.errors[errkey] = ['Patch set contains no recognizable patches'] |
| 565 return False | 566 return False |
| 566 db.put(patches) | 567 db.put(patches) |
| 567 issue.put() # To update last modified time | 568 issue.put() # To update last modified time |
| 568 return True | 569 return True |
| 569 | 570 |
| 570 | 571 |
| 571 def _get_reviewers(form): | 572 def _get_reviewers(form): |
| 572 """Helper to return the list of reviewers, or None for error.""" | 573 """Helper to return the list of reviewers, or None for error.""" |
| 573 reviewers = [] | 574 reviewers = sets.Set() |
| 574 raw_reviewers = form.cleaned_data.get('reviewers') | 575 raw_reviewers = form.cleaned_data.get('reviewers') |
| 575 if raw_reviewers: | 576 if raw_reviewers: |
| 576 for reviewer in raw_reviewers.split(','): | 577 for reviewer in raw_reviewers.split(','): |
| 577 reviewer = reviewer.strip() | 578 reviewer = reviewer.strip() |
| 578 if reviewer and reviewer not in reviewers: | 579 if reviewer: |
| 579 try: | 580 try: |
| 580 reviewer = db.Email(reviewer) | 581 reviewer = db.Email(reviewer) |
| 581 if reviewer.count('@') != 1: | 582 if reviewer.count('@') != 1: |
| 582 raise db.BadValueError('Invalid email address: %s' % reviewer) | 583 raise db.BadValueError('Invalid email address: %s' % reviewer) |
| 583 head, tail = reviewer.split('@') | 584 head, tail = reviewer.split('@') |
| 584 if '.' not in tail: | 585 if '.' not in tail: |
| 585 raise db.BadValueError('Invalid email address: %s' % reviewer) | 586 raise db.BadValueError('Invalid email address: %s' % reviewer) |
| 586 except db.BadValueError, err: | 587 except db.BadValueError, err: |
| 587 form.errors['reviewers'] = [unicode(err)] | 588 form.errors['reviewers'] = [unicode(err)] |
| 588 return None | 589 return None |
| 589 reviewers.append(reviewer) | 590 reviewers.add(reviewer) |
| 590 return reviewers | 591 return list(reviewers) |
|
GvR
2008/05/11 19:20:49
I'd prefer to keep reviewers in the order that the
| |
| 591 | 592 |
| 592 | 593 |
| 593 | 594 |
| 594 @issue_required | 595 @issue_required |
| 595 def show(request, form=AddForm()): | 596 def show(request, form=AddForm()): |
| 596 """/<issue> - Show an issue.""" | 597 """/<issue> - Show an issue.""" |
| 597 issue = request.issue | 598 issue = request.issue |
| 598 patchsets = list(issue.patchset_set.order('created')) | 599 patchsets = list(issue.patchset_set.order('created')) |
| 599 issue.draft_count = 0 | 600 issue.draft_count = 0 |
| 600 issue.comment_count = 0 | 601 issue.comment_count = 0 |
| (...skipping 337 matching lines...) Show 10 above Show 10 below | |
| 938 @issue_required | 939 @issue_required |
| 939 @login_required | 940 @login_required |
| 940 def publish(request): | 941 def publish(request): |
| 941 """ /<issue>/publish - Publish draft comments and send mail.""" | 942 """ /<issue>/publish - Publish draft comments and send mail.""" |
| 942 issue = request.issue | 943 issue = request.issue |
| 943 if request.user == issue.owner: | 944 if request.user == issue.owner: |
| 944 form_class = PublishForm | 945 form_class = PublishForm |
| 945 else: | 946 else: |
| 946 form_class = MiniPublishForm | 947 form_class = MiniPublishForm |
| 947 if request.method != 'POST': | 948 if request.method != 'POST': |
| 948 reviewers = issue.reviewers[:] | 949 if request.user != issue.owner and (not request.user.email() |
| 949 if request.user != issue.owner and (request.user.email() | 950 in issue.reviewers): |
|
GvR
2008/05/11 19:20:49
You can use 'not in' for the latter test.
| |
| 950 not in issue.reviewers): | 951 issue.reviewers.append(request.user.email()) |
|
GvR
2008/05/11 19:20:49
I wouldn't assign to issue.reviewers here, since y
| |
| 951 reviewers.append(request.user.email()) | |
| 952 form = form_class(initial={'subject': issue.subject, | 952 form = form_class(initial={'subject': issue.subject, |
| 953 'reviewers': ', '.join(reviewers), | 953 'reviewers': ', '.join(issue.reviewers), |
| 954 'send_mail': True, | 954 'send_mail': True, |
| 955 }) | 955 }) |
| 956 return respond(request, 'publish.html', {'form': form, 'issue': issue}) | 956 return respond(request, 'publish.html', {'form': form, 'issue': issue}) |
| 957 | 957 |
| 958 form = form_class(request.POST) | 958 form = form_class(request.POST) |
| 959 if form.is_valid(): | 959 if form.is_valid(): |
| 960 reviewers = _get_reviewers(form) | 960 reviewers = _get_reviewers(form) |
| 961 if not form.is_valid(): | 961 if not form.is_valid(): |
| 962 return respond(request, 'publish.html', {'form': form, 'issue': issue}) | 962 return respond(request, 'publish.html', {'form': form, 'issue': issue}) |
| 963 tbd = [] # List of things to put() after all is said and done | 963 tbd = [] # List of things to put() after all is said and done |
| (...skipping 39 matching lines...) Show 10 above Show 10 below | |
| 1003 if comments: | 1003 if comments: |
| 1004 logging.warn('Publishing %d comments', len(comments)) | 1004 logging.warn('Publishing %d comments', len(comments)) |
| 1005 # Decide who should receive mail | 1005 # Decide who should receive mail |
| 1006 my_email = db.Email(request.user.email()) | 1006 my_email = db.Email(request.user.email()) |
| 1007 addressees = [db.Email(issue.owner.email())] + issue.reviewers | 1007 addressees = [db.Email(issue.owner.email())] + issue.reviewers |
| 1008 if my_email in addressees: | 1008 if my_email in addressees: |
| 1009 everyone = addressees[:] | 1009 everyone = addressees[:] |
| 1010 if len(addressees) > 1: # Keep it if sending only to yourself | 1010 if len(addressees) > 1: # Keep it if sending only to yourself |
| 1011 addressees.remove(my_email) | 1011 addressees.remove(my_email) |
| 1012 else: | 1012 else: |
| 1013 everyone = addressees | 1013 everyone = addressees + [my_email] |
| 1014 details = _get_draft_details(request, comments) | 1014 details = _get_draft_details(request, comments) |
| 1015 text = ((message.strip() + '\n\n' + details.strip())).strip() | 1015 text = ((message.strip() + '\n\n' + details.strip())).strip() |
| 1016 msg = models.Message(issue=issue, | 1016 msg = models.Message(issue=issue, |
| 1017 subject=issue.subject, | 1017 subject=issue.subject, |
| 1018 sender=my_email, | 1018 sender=my_email, |
| 1019 recipients=everyone, | 1019 recipients=everyone, |
| 1020 text=db.Text(text), | 1020 text=db.Text(text), |
| 1021 parent=issue) | 1021 parent=issue) |
| 1022 tbd.append(msg) | 1022 tbd.append(msg) |
| 1023 | 1023 |
| (...skipping 220 matching lines...) Show 10 above Show 10 below | |
| 1244 else: | 1244 else: |
| 1245 accounts = models.Account.get_accounts_for_nickname(nickname) | 1245 accounts = models.Account.get_accounts_for_nickname(nickname) |
| 1246 if nickname != account.nickname and accounts: | 1246 if nickname != account.nickname and accounts: |
| 1247 form.errors['nickname'] = ['This nickname is already in use.'] | 1247 form.errors['nickname'] = ['This nickname is already in use.'] |
| 1248 else: | 1248 else: |
| 1249 account.nickname = nickname | 1249 account.nickname = nickname |
| 1250 account.put() | 1250 account.put() |
| 1251 if not form.is_valid(): | 1251 if not form.is_valid(): |
| 1252 return respond(request, 'settings.html', {'form': form}) | 1252 return respond(request, 'settings.html', {'form': form}) |
| 1253 return HttpResponseRedirect('/settings') | 1253 return HttpResponseRedirect('/settings') |
| LEFT | RIGHT |