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, |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
71 from oauth2client.appengine import _safe_html | 71 from oauth2client.appengine import _safe_html |
72 from oauth2client.appengine import CredentialsNDBModel | 72 from oauth2client.appengine import CredentialsNDBModel |
73 from oauth2client.appengine import StorageByKeyName | 73 from oauth2client.appengine import StorageByKeyName |
74 from oauth2client.appengine import xsrf_secret_key | 74 from oauth2client.appengine import xsrf_secret_key |
75 from oauth2client.client import AccessTokenRefreshError | 75 from oauth2client.client import AccessTokenRefreshError |
76 from oauth2client.client import OAuth2WebServerFlow | 76 from oauth2client.client import OAuth2WebServerFlow |
77 from oauth2client import xsrfutil | 77 from oauth2client import xsrfutil |
78 | 78 |
79 from codereview import auth_utils | 79 from codereview import auth_utils |
80 from codereview import buildbucket | 80 from codereview import buildbucket |
| 81 from codereview import dependency_utils |
81 from codereview import engine | 82 from codereview import engine |
82 from codereview import library | 83 from codereview import library |
83 from codereview import models | 84 from codereview import models |
84 from codereview import models_chromium | 85 from codereview import models_chromium |
85 from codereview import net | 86 from codereview import net |
86 from codereview import notify_xmpp | 87 from codereview import notify_xmpp |
87 from codereview import patching | 88 from codereview import patching |
88 from codereview import utils | 89 from codereview import utils |
89 from codereview.common import IS_DEV | 90 from codereview.common import IS_DEV |
90 from codereview.exceptions import FetchError | 91 from codereview.exceptions import FetchError |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
174 class UploadForm(forms.Form): | 175 class UploadForm(forms.Form): |
175 | 176 |
176 subject = forms.CharField(max_length=MAX_SUBJECT) | 177 subject = forms.CharField(max_length=MAX_SUBJECT) |
177 description = forms.CharField(max_length=MAX_DESCRIPTION, required=False) | 178 description = forms.CharField(max_length=MAX_DESCRIPTION, required=False) |
178 project = forms.CharField(required=False) | 179 project = forms.CharField(required=False) |
179 content_upload = forms.BooleanField(required=False) | 180 content_upload = forms.BooleanField(required=False) |
180 separate_patches = forms.BooleanField(required=False) | 181 separate_patches = forms.BooleanField(required=False) |
181 base = forms.CharField(max_length=MAX_URL, required=False) | 182 base = forms.CharField(max_length=MAX_URL, required=False) |
182 target_ref = forms.CharField(max_length=MAX_URL, required=False) | 183 target_ref = forms.CharField(max_length=MAX_URL, required=False) |
183 cq_dry_run = forms.BooleanField(required=False) | 184 cq_dry_run = forms.BooleanField(required=False) |
| 185 depends_on_patchset = forms.CharField(required=False) |
184 data = forms.FileField(required=False) | 186 data = forms.FileField(required=False) |
185 issue = forms.IntegerField(required=False) | 187 issue = forms.IntegerField(required=False) |
186 reviewers = forms.CharField(max_length=MAX_REVIEWERS, required=False) | 188 reviewers = forms.CharField(max_length=MAX_REVIEWERS, required=False) |
187 cc = forms.CharField(max_length=MAX_CC, required=False) | 189 cc = forms.CharField(max_length=MAX_CC, required=False) |
188 private = forms.BooleanField(required=False, initial=False) | 190 private = forms.BooleanField(required=False, initial=False) |
189 send_mail = forms.BooleanField(required=False) | 191 send_mail = forms.BooleanField(required=False) |
190 base_hashes = forms.CharField(required=False) | 192 base_hashes = forms.CharField(required=False) |
191 commit = forms.BooleanField(required=False) | 193 commit = forms.BooleanField(required=False) |
192 repo_guid = forms.CharField(required=False, max_length=MAX_URL) | 194 repo_guid = forms.CharField(required=False, max_length=MAX_URL) |
193 | 195 |
(...skipping 857 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1051 return HttpResponseBadRequest( | 1053 return HttpResponseBadRequest( |
1052 'Only the issue owner can edit patchset titles') | 1054 'Only the issue owner can edit patchset titles') |
1053 | 1055 |
1054 patchset = request.patchset | 1056 patchset = request.patchset |
1055 patchset.message = request.POST.get('patchset_title') | 1057 patchset.message = request.POST.get('patchset_title') |
1056 patchset.put() | 1058 patchset.put() |
1057 | 1059 |
1058 return HttpResponse('OK', content_type='text/plain') | 1060 return HttpResponse('OK', content_type='text/plain') |
1059 | 1061 |
1060 | 1062 |
| 1063 @deco.access_control_allow_origin_star |
| 1064 @deco.require_methods('POST') |
| 1065 @deco.patchset_required |
| 1066 @deco.json_response |
| 1067 def get_depends_on_patchset(request): |
| 1068 """/<issue>/get_depends_on_patchset- The patchset this patchset depends on.""" |
| 1069 response = {} |
| 1070 if request.patchset.depends_on_patchset: |
| 1071 # Verify that the depended upon issue is not closed. |
| 1072 tokens = dependency_utils.get_dependency_tokens( |
| 1073 request.patchset.depends_on_patchset) |
| 1074 depends_on_issue = models.Issue.get_by_id(int(tokens[0])) |
| 1075 if depends_on_issue and not depends_on_issue.closed: |
| 1076 response = { |
| 1077 'issue': tokens[0], |
| 1078 'patchset': tokens[1], |
| 1079 } |
| 1080 return response |
| 1081 |
| 1082 |
1061 @deco.admin_required | 1083 @deco.admin_required |
1062 @deco.user_key_required | 1084 @deco.user_key_required |
1063 @deco.xsrf_required | 1085 @deco.xsrf_required |
1064 def block_user(request): | 1086 def block_user(request): |
1065 """/user/<user>/block - Blocks a specific user.""" | 1087 """/user/<user>/block - Blocks a specific user.""" |
1066 account = models.Account.get_account_for_user(request.user_to_show) | 1088 account = models.Account.get_account_for_user(request.user_to_show) |
1067 if request.method == 'POST': | 1089 if request.method == 'POST': |
1068 form = BlockForm(request.POST) | 1090 form = BlockForm(request.POST) |
1069 if form.is_valid(): | 1091 if form.is_valid(): |
1070 account.blocked = form.cleaned_data['blocked'] | 1092 account.blocked = form.cleaned_data['blocked'] |
(...skipping 393 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1464 issue.put() | 1486 issue.put() |
1465 | 1487 |
1466 if cq_dry_run: | 1488 if cq_dry_run: |
1467 commit_checked_msg = 'The CQ bit was checked by %s to run a CQ dry run' % ( | 1489 commit_checked_msg = 'The CQ bit was checked by %s to run a CQ dry run' % ( |
1468 cq_dry_run_triggered_by) | 1490 cq_dry_run_triggered_by) |
1469 make_message(request, issue, commit_checked_msg, send_mail=False, | 1491 make_message(request, issue, commit_checked_msg, send_mail=False, |
1470 auto_generated=True).put() | 1492 auto_generated=True).put() |
1471 | 1493 |
1472 first_ps_id, _ = models.PatchSet.allocate_ids(1, parent=issue.key) | 1494 first_ps_id, _ = models.PatchSet.allocate_ids(1, parent=issue.key) |
1473 ps_key = ndb.Key(models.PatchSet, first_ps_id, parent=issue.key) | 1495 ps_key = ndb.Key(models.PatchSet, first_ps_id, parent=issue.key) |
| 1496 depends_on_patchset = ( |
| 1497 dependency_utils.mark_as_dependent_and_get_dependency_str( |
| 1498 form.cleaned_data.get('depends_on_patchset'), issue.key.id(), |
| 1499 ps_key.id())) |
1474 patchset = models.PatchSet( | 1500 patchset = models.PatchSet( |
1475 issue_key=issue.key, data=data, url=url, key=ps_key) | 1501 issue_key=issue.key, data=data, url=url, key=ps_key, |
| 1502 depends_on_patchset=depends_on_patchset) |
1476 patchset.put() | 1503 patchset.put() |
1477 | 1504 |
1478 if not separate_patches: | 1505 if not separate_patches: |
1479 try: | 1506 try: |
1480 patches = engine.ParsePatchSet(patchset) | 1507 patches = engine.ParsePatchSet(patchset) |
1481 except Exception: | 1508 except Exception: |
1482 # catch all exceptions happening in engine.ParsePatchSet, | 1509 # catch all exceptions happening in engine.ParsePatchSet, |
1483 # engine.SplitPatch. With malformed diffs a variety of exceptions could | 1510 # engine.SplitPatch. With malformed diffs a variety of exceptions could |
1484 # happen there. | 1511 # happen there. |
1485 logging.exception('Exception during patch parsing') | 1512 logging.exception('Exception during patch parsing') |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1559 account = models.Account.get_account_for_user(request.user) | 1586 account = models.Account.get_account_for_user(request.user) |
1560 if account.blocked: | 1587 if account.blocked: |
1561 return None | 1588 return None |
1562 if not issue.edit_allowed: | 1589 if not issue.edit_allowed: |
1563 # This check is done at each call site but check again as a safety measure. | 1590 # This check is done at each call site but check again as a safety measure. |
1564 return None | 1591 return None |
1565 data, url, separate_patches = data_url | 1592 data, url, separate_patches = data_url |
1566 message = form.cleaned_data[message_key] | 1593 message = form.cleaned_data[message_key] |
1567 first_id, _ = models.PatchSet.allocate_ids(1, parent=issue.key) | 1594 first_id, _ = models.PatchSet.allocate_ids(1, parent=issue.key) |
1568 ps_key = ndb.Key(models.PatchSet, first_id, parent=issue.key) | 1595 ps_key = ndb.Key(models.PatchSet, first_id, parent=issue.key) |
| 1596 |
| 1597 depends_on_patchset = ( |
| 1598 dependency_utils.mark_as_dependent_and_get_dependency_str( |
| 1599 form.cleaned_data.get('depends_on_patchset'), issue.key.id(), |
| 1600 ps_key.id())) |
1569 patchset = models.PatchSet( | 1601 patchset = models.PatchSet( |
1570 issue_key=issue.key, message=message, data=data, url=url, key=ps_key) | 1602 issue_key=issue.key, message=message, data=data, url=url, key=ps_key, |
| 1603 depends_on_patchset=depends_on_patchset) |
1571 patchset.put() | 1604 patchset.put() |
1572 | 1605 |
1573 if not separate_patches: | 1606 if not separate_patches: |
1574 try: | 1607 try: |
1575 patches = engine.ParsePatchSet(patchset) | 1608 patches = engine.ParsePatchSet(patchset) |
1576 except Exception: | 1609 except Exception: |
1577 logging.exception('Exception during patchset parsing') | 1610 logging.exception('Exception during patchset parsing') |
1578 patches = [] | 1611 patches = [] |
1579 if not patches: | 1612 if not patches: |
1580 patchset.key.delete() | 1613 patchset.key.delete() |
(...skipping 502 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2083 logging.info("Updating %d patches", len(patches)) | 2116 logging.info("Updating %d patches", len(patches)) |
2084 ndb.put_multi(patches) | 2117 ndb.put_multi(patches) |
2085 | 2118 |
2086 | 2119 |
2087 @deco.require_methods('POST') | 2120 @deco.require_methods('POST') |
2088 @deco.issue_editor_required | 2121 @deco.issue_editor_required |
2089 @deco.xsrf_required | 2122 @deco.xsrf_required |
2090 def delete(request): | 2123 def delete(request): |
2091 """/<issue>/delete - Delete an issue. There is no way back.""" | 2124 """/<issue>/delete - Delete an issue. There is no way back.""" |
2092 issue = request.issue | 2125 issue = request.issue |
| 2126 # Update the dependents to remove dependency on this issue. |
| 2127 dependency_utils.remove_dependencies_from_all_patchsets(issue) |
| 2128 # Remove this issue as a dependent. |
| 2129 dependency_utils.remove_all_patchsets_as_dependents(issue) |
| 2130 |
2093 tbd = [issue] | 2131 tbd = [issue] |
2094 for cls in [models.PatchSet, models.Patch, models.Comment, | 2132 for cls in [models.PatchSet, models.Patch, models.Comment, |
2095 models.Message, models.Content, models.TryJobResult]: | 2133 models.Message, models.Content, models.TryJobResult]: |
2096 tbd += cls.query(ancestor=issue.key) | 2134 tbd += cls.query(ancestor=issue.key) |
2097 ndb.delete_multi(entity.key for entity in tbd) | 2135 ndb.delete_multi(entity.key for entity in tbd) |
2098 return HttpResponseRedirect(reverse(mine)) | 2136 return HttpResponseRedirect(reverse(mine)) |
2099 | 2137 |
2100 | 2138 |
2101 @deco.require_methods('POST') | 2139 @deco.require_methods('POST') |
2102 @deco.patchset_editor_required | 2140 @deco.patchset_editor_required |
2103 @deco.xsrf_required | 2141 @deco.xsrf_required |
2104 def delete_patchset(request): | 2142 def delete_patchset(request): |
2105 """/<issue>/patch/<patchset>/delete - Delete a patchset. | 2143 """/<issue>/patch/<patchset>/delete - Delete a patchset. |
2106 | 2144 |
2107 There is no way back. | 2145 There is no way back. |
2108 """ | 2146 """ |
2109 # Log patchset deletion. | 2147 # Log patchset deletion. |
2110 patchset_num = 0 | 2148 patchset_num = 0 |
2111 for patchset in list(request.issue.patchsets): | 2149 for patchset in list(request.issue.patchsets): |
2112 patchset_num += 1 | 2150 patchset_num += 1 |
2113 if patchset.key.id() == request.patchset.key.id(): | 2151 if patchset.key.id() == request.patchset.key.id(): |
2114 break | 2152 break |
2115 delete_msg = 'Patchset #%s (id:%s) has been deleted' % ( | 2153 delete_msg = 'Patchset #%s (id:%s) has been deleted' % ( |
2116 patchset_num, request.patchset.key.id()) | 2154 patchset_num, request.patchset.key.id()) |
2117 make_message(request, request.issue, delete_msg, send_mail=False, | 2155 make_message(request, request.issue, delete_msg, send_mail=False, |
2118 auto_generated=True).put() | 2156 auto_generated=True).put() |
| 2157 # Update all dependents of this patchset. |
| 2158 dependency_utils.remove_dependencies(request.patchset) |
| 2159 # Remove this patchset as a dependent. |
| 2160 dependency_utils.remove_as_dependent(request.patchset) |
2119 # Delete the patchset. | 2161 # Delete the patchset. |
2120 request.patchset.nuke() | 2162 request.patchset.nuke() |
2121 return HttpResponseRedirect(reverse(show, args=[request.issue.key.id()])) | 2163 return HttpResponseRedirect(reverse(show, args=[request.issue.key.id()])) |
2122 | 2164 |
2123 | 2165 |
2124 @deco.require_methods('POST') | 2166 @deco.require_methods('POST') |
2125 @deco.issue_editor_required | 2167 @deco.issue_editor_required |
2126 @deco.xsrf_required | 2168 @deco.xsrf_required |
2127 def close(request): | 2169 def close(request): |
2128 """/<issue>/close - Close an issue.""" | 2170 """/<issue>/close - Close an issue.""" |
(...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2396 values = { | 2438 values = { |
2397 'patchset': patchset.key.id(), | 2439 'patchset': patchset.key.id(), |
2398 'issue': issue.key.id(), | 2440 'issue': issue.key.id(), |
2399 'owner': library.get_nickname(issue.owner, True, request), | 2441 'owner': library.get_nickname(issue.owner, True, request), |
2400 'owner_email': issue.owner.email(), | 2442 'owner_email': issue.owner.email(), |
2401 'message': patchset.message, | 2443 'message': patchset.message, |
2402 'url': patchset.url, | 2444 'url': patchset.url, |
2403 'created': str(patchset.created), | 2445 'created': str(patchset.created), |
2404 'modified': str(patchset.modified), | 2446 'modified': str(patchset.modified), |
2405 'num_comments': patchset.num_comments, | 2447 'num_comments': patchset.num_comments, |
| 2448 'depends_on_patchset': patchset.depends_on_patchset, |
| 2449 'dependent_patchsets': patchset.dependent_patchsets, |
2406 'files': {}, | 2450 'files': {}, |
2407 } | 2451 } |
2408 if (try_jobs): | 2452 if (try_jobs): |
2409 values['try_job_results'] = [ | 2453 values['try_job_results'] = [ |
2410 t.to_dict() for t in patchset.try_job_results] | 2454 t.to_dict() for t in patchset.try_job_results] |
2411 | 2455 |
2412 all_no_base_file_keys_future = models.Content.query( | 2456 all_no_base_file_keys_future = models.Content.query( |
2413 models.Content.file_too_large == True, | 2457 models.Content.file_too_large == True, |
2414 ancestor=patchset.key).fetch_async(10000, keys_only=True, batch_size=1000) | 2458 ancestor=patchset.key).fetch_async(10000, keys_only=True, batch_size=1000) |
2415 patches_future = models.Patch.query(ancestor=patchset.key).fetch_async( | 2459 patches_future = models.Patch.query(ancestor=patchset.key).fetch_async( |
(...skipping 3083 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
5499 return HttpResponseNotFound() | 5543 return HttpResponseNotFound() |
5500 tops = [] | 5544 tops = [] |
5501 shame = [] | 5545 shame = [] |
5502 for i in data: | 5546 for i in data: |
5503 if i.score == models.AccountStatsBase.NULL_SCORE: | 5547 if i.score == models.AccountStatsBase.NULL_SCORE: |
5504 shame.append(i) | 5548 shame.append(i) |
5505 else: | 5549 else: |
5506 tops.append(i) | 5550 tops.append(i) |
5507 return respond( | 5551 return respond( |
5508 request, 'leaderboard.html', {'tops': tops, 'shame': shame, 'when': when}) | 5552 request, 'leaderboard.html', {'tops': tops, 'shame': shame, 'when': when}) |
OLD | NEW |