| OLD | NEW |
| 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 datetime | 30 import datetime |
| 31 | 31 |
| 32 # AppEngine imports | 32 # AppEngine imports |
| 33 from google.appengine.api import mail | 33 from google.appengine.api import mail |
| 34 from google.appengine.api import users | 34 from google.appengine.api import users |
| 35 from google.appengine.api import urlfetch | 35 from google.appengine.api import urlfetch |
| 36 from google.appengine.ext import db | 36 from google.appengine.ext import db |
| 37 from google.appengine.ext.db import djangoforms | 37 from google.appengine.ext.db import djangoforms |
| 38 | 38 |
| 39 # DeadlineExceededError can live in two different places | 39 # DeadlineExceededError can live in two different places |
| 40 # TODO(guido): simplify once this is fixed. | 40 # TODO(guido): simplify once this is fixed. |
| 41 try: | 41 try: |
| 42 # When deployed | 42 # When deployed |
| 43 from google.appengine.runtime import DeadlineExceededError | 43 from google.appengine.runtime import DeadlineExceededError |
| 44 except ImportError: | 44 except ImportError: |
| 45 # In the development server | 45 # In the development server |
| 46 from google.appengine.runtime.apiproxy_errors import DeadlineExceededError | 46 from google.appengine.runtime.apiproxy_errors import DeadlineExceededError |
| 47 | 47 |
| 48 # Django imports | 48 # Django imports |
| 49 # TODO(guido): Don't import classes/functions directly. | 49 # TODO(guido): Don't import classes/functions directly. |
| 50 from django import newforms as forms | 50 from django import newforms as forms |
| (...skipping 393 matching lines...) Show 10 above Show 10 below |
| 444 issue = models.Issue.get_by_id(issue_id) | 444 issue = models.Issue.get_by_id(issue_id) |
| 445 if issue is None: | 445 if issue is None: |
| 446 form.errors['issue'] = ['No issue exists with that id (%s)' % | 446 form.errors['issue'] = ['No issue exists with that id (%s)' % |
| 447 issue_id] | 447 issue_id] |
| 448 else: | 448 else: |
| 449 if request.user != issue.owner: | 449 if request.user != issue.owner: |
| 450 form.errors['user'] = ['You (%s) don\'t own this issue (%s)' % | 450 form.errors['user'] = ['You (%s) don\'t own this issue (%s)' % |
| 451 (request.user, issue_id)] | 451 (request.user, issue_id)] |
| 452 issue = None | 452 issue = None |
| 453 else: | 453 else: |
| 454 if not add_patchset_from_form(request, issue, form, 'subject'): | 454 if not add_patchset_from_form(request, issue, form, 'subject'): |
| 455 issue = None | 455 issue = None |
| 456 else: | 456 else: |
| 457 action = 'created' | 457 action = 'created' |
| 458 issue = _make_new(request, form) | 458 issue = _make_new(request, form) |
| 459 if issue is None: | 459 if issue is None: |
| 460 msg = 'Issue creation errors:\n%s' % repr(form.errors) | 460 msg = 'Issue creation errors:\n%s' % repr(form.errors) |
| 461 else: | 461 else: |
| 462 msg = ('Issue %s. URL: %s' % | 462 msg = ('Issue %s. URL: %s' % |
| 463 (action, | 463 (action, |
| 464 request.build_absolute_uri('/%s' % issue.key().id()))) | 464 request.build_absolute_uri('/%s' % issue.key().id()))) |
| 465 return HttpResponse(msg, content_type='text/plain') | 465 return HttpResponse(msg, content_type='text/plain') |
| 466 | 466 |
| 467 | 467 |
| 468 class EmptyPatchSet(Exception): | 468 class EmptyPatchSet(Exception): |
| 469 """Exception used inside _make_new() to break out of the transaction.""" | 469 """Exception used inside _make_new() to break out of the transaction.""" |
| 470 | 470 |
| 471 | 471 |
| 472 def _make_new(request, form): | 472 def _make_new(request, form): |
| 473 """Helper for new(). | 473 """Helper for new(). |
| 474 | 474 |
| 475 Return a valid Issue, or None. | 475 Return a valid Issue, or None. |
| 476 """ | 476 """ |
| 477 if not form.is_valid(): | 477 if not form.is_valid(): |
| 478 return None | 478 return None |
| 479 | 479 |
| 480 data_url = _get_data_url(form) | 480 data_url = _get_data_url(form) |
| 481 if data_url is None: | 481 if data_url is None: |
| 482 return None | 482 return None |
| 483 data, url = data_url | 483 data, url = data_url |
| 484 | 484 |
| 485 reviewers = _get_reviewers(form) | 485 reviewers = _get_reviewers(form) |
| 486 if reviewers is None: | 486 if reviewers is None: |
| 487 return None | 487 return None |
| 488 | 488 |
| 489 base = form.get_base() | 489 base = form.get_base() |
| 490 if base is None: | 490 if base is None: |
| 491 return None | 491 return None |
| 492 | 492 |
| 493 def txn(): | 493 def txn(): |
| 494 #print base |
| 495 #print data |
| 496 #logging.info(base) |
| 497 #logging.info(data) |
| 494 issue = models.Issue(subject=form.cleaned_data['subject'], | 498 issue = models.Issue(subject=form.cleaned_data['subject'], |
| 495 description=form.cleaned_data['description'], | 499 description=form.cleaned_data['description'], |
| 496 base=base, | 500 base=base, |
| 497 reviewers=reviewers, | 501 reviewers=reviewers, |
| 498 owner=request.user) | 502 owner=request.user) |
| 499 issue.put() | 503 issue.put() |
| 500 | 504 |
| 501 patchset = models.PatchSet(issue=issue, data=data, url=url, | 505 patchset = models.PatchSet(issue=issue, data=data, url=url, |
| 502 base=base, owner=request.user, parent=issue) | 506 base=base, owner=request.user, parent=issue) |
| 503 patchset.put() | 507 patchset.put() |
| 504 | 508 |
| 505 patches = engine.ParsePatchSet(patchset) | 509 patches = engine.ParsePatchSet(patchset) |
| 506 if not patches: | 510 if not patches: |
| 507 raise EmptyPatchSet # Abort the transaction | 511 raise EmptyPatchSet # Abort the transaction |
| 508 db.put(patches) | 512 db.put(patches) |
| 509 return issue | 513 return issue |
| 510 | 514 |
| 511 try: | 515 try: |
| 512 return db.run_in_transaction(txn) | 516 return db.run_in_transaction(txn) |
| 513 except EmptyPatchSet: | 517 except EmptyPatchSet: |
| 514 errkey = url and 'url' or 'data' | 518 errkey = url and 'url' or 'data' |
| 515 form.errors[errkey] = ['Patch set contains no recognizable patches'] | 519 form.errors[errkey] = ['Patch set contains no recognizable patches'] |
| 516 return None | 520 return None |
| 517 | 521 |
| 518 | 522 |
| 519 def _get_data_url(form): | 523 def _get_data_url(form): |
| 520 """Helper for _make_new() above and add() below.""" | 524 """Helper for _make_new() above and add() below.""" |
| 521 cleaned_data = form.cleaned_data | 525 cleaned_data = form.cleaned_data |
| 522 | 526 |
| 523 data = cleaned_data['data'] | 527 data = cleaned_data['data'] |
| 524 url = cleaned_data.get('url') | 528 url = cleaned_data.get('url') |
| 525 if not (data or url): | 529 if not (data or url): |
| 526 form.errors['data'] = ['You must specify a URL or upload a file'] | 530 form.errors['data'] = ['You must specify a URL or upload a file'] |
| 527 return None | 531 return None |
| 528 if data and url: | 532 if data and url: |
| 529 form.errors['data'] = ['You must specify either a URL or upload a file ' | 533 form.errors['data'] = ['You must specify either a URL or upload a file ' |
| 530 'but not both'] | 534 'but not both'] |
| 531 return None | 535 return None |
| 532 | 536 |
| 533 if data is not None: | 537 if data is not None: |
| 534 data = db.Blob(data.content) | 538 data = db.Blob(data.content) |
| 535 url = None | 539 url = None |
| 536 else: | 540 else: |
| 537 assert url | 541 assert url |
| 538 try: | 542 try: |
| 539 fetch_result = urlfetch.fetch(url) | 543 fetch_result = urlfetch.fetch(url) |
| 540 except Exception, err: | 544 except Exception, err: |
| 541 form.errors['url'] = [str(err)] | 545 form.errors['url'] = [str(err)] |
| 542 return None | 546 return None |
| 543 if fetch_result.status_code != 200: | 547 if fetch_result.status_code != 200: |
| (...skipping 678 matching lines...) Show 10 above Show 10 below |
| 1222 return respond(request, 'branch_edit.html', | 1226 return respond(request, 'branch_edit.html', |
| 1223 {'branch': branch, 'form': form}) | 1227 {'branch': branch, 'form': form}) |
| 1224 branch.put() | 1228 branch.put() |
| 1225 return HttpResponseRedirect('/repos') | 1229 return HttpResponseRedirect('/repos') |
| 1226 | 1230 |
| 1227 | 1231 |
| 1228 @login_required | 1232 @login_required |
| 1229 def branch_delete(request, branch_id): | 1233 def branch_delete(request, branch_id): |
| 1230 """/branch_delete/<branch> - Delete a Branch record.""" | 1234 """/branch_delete/<branch> - Delete a Branch record.""" |
| 1231 branch = models.Branch.get_by_id(int(branch_id)) | 1235 branch = models.Branch.get_by_id(int(branch_id)) |
| 1232 if branch.owner != request.user: | 1236 if branch.owner != request.user: |
| 1233 return HttpResponseForbidden('You do not own this branch') | 1237 return HttpResponseForbidden('You do not own this branch') |
| 1234 repo = branch.repo | 1238 repo = branch.repo |
| 1235 branch.delete() | 1239 branch.delete() |
| 1236 num_branches = models.Branch.gql('WHERE repo = :1', repo).count() | 1240 num_branches = models.Branch.gql('WHERE repo = :1', repo).count() |
| 1237 if not num_branches: | 1241 if not num_branches: |
| 1238 # Even if we don't own the repository? Yes, I think so! Empty | 1242 # Even if we don't own the repository? Yes, I think so! Empty |
| 1239 # repositories have no representation on screen. | 1243 # repositories have no representation on screen. |
| 1240 repo.delete() | 1244 repo.delete() |
| 1241 return HttpResponseRedirect('/repos') | 1245 return HttpResponseRedirect('/repos') |
| 1242 | 1246 |
| 1243 | 1247 |
| 1244 ### User Profiles ### | 1248 ### User Profiles ### |
| 1245 | 1249 |
| 1246 @login_required | 1250 @login_required |
| 1247 def settings(request): | 1251 def settings(request): |
| 1248 account = models.Account.get_account_for_user(request.user) | 1252 account = models.Account.get_account_for_user(request.user) |
| 1249 if request.method != 'POST': | 1253 if request.method != 'POST': |
| 1250 nickname = account.nickname | 1254 nickname = account.nickname |
| 1251 form = SettingsForm(initial={'nickname': nickname}) | 1255 form = SettingsForm(initial={'nickname': nickname}) |
| 1252 return respond(request, 'settings.html', {'form': form}) | 1256 return respond(request, 'settings.html', {'form': form}) |
| 1253 form = SettingsForm(request.POST) | 1257 form = SettingsForm(request.POST) |
| 1254 if form.is_valid(): | 1258 if form.is_valid(): |
| 1255 nickname = form.cleaned_data['nickname'].strip() | 1259 nickname = form.cleaned_data['nickname'].strip() |
| 1256 if not nickname: | 1260 if not nickname: |
| 1257 form.errors['nickname'] = ['Your nickname cannot be empty.'] | 1261 form.errors['nickname'] = ['Your nickname cannot be empty.'] |
| 1258 elif '@' in nickname: | 1262 elif '@' in nickname: |
| 1259 form.errors['nickname'] = ['Your nickname cannot contain "@".'] | 1263 form.errors['nickname'] = ['Your nickname cannot contain "@".'] |
| 1260 elif ',' in nickname: | 1264 elif ',' in nickname: |
| 1261 form.errors['nickname'] = ['Your nickname cannot contain ",".'] | 1265 form.errors['nickname'] = ['Your nickname cannot contain ",".'] |
| 1262 else: | 1266 else: |
| 1263 accounts = models.Account.get_accounts_for_nickname(nickname) | 1267 accounts = models.Account.get_accounts_for_nickname(nickname) |
| 1264 if nickname != account.nickname and accounts: | 1268 if nickname != account.nickname and accounts: |
| 1265 form.errors['nickname'] = ['This nickname is already in use.'] | 1269 form.errors['nickname'] = ['This nickname is already in use.'] |
| 1266 else: | 1270 else: |
| 1267 account.nickname = nickname | 1271 account.nickname = nickname |
| 1268 account.put() | 1272 account.put() |
| 1269 if not form.is_valid(): | 1273 if not form.is_valid(): |
| 1270 return respond(request, 'settings.html', {'form': form}) | 1274 return respond(request, 'settings.html', {'form': form}) |
| 1271 return HttpResponseRedirect('/settings') | 1275 return HttpResponseRedirect('/settings') |
| OLD | NEW |