##// END OF EJS Templates
show flags, and desc sort pull request based on created_date ref #765
marcink -
r3389:1c4505e3 beta
parent child Browse files
Show More
@@ -1,544 +1,548
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 import pkg_resources
29 import pkg_resources
30 import platform
30 import platform
31
31
32 from sqlalchemy import func
32 from sqlalchemy import func
33 from formencode import htmlfill
33 from formencode import htmlfill
34 from pylons import request, session, tmpl_context as c, url, config
34 from pylons import request, session, tmpl_context as c, url, config
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, NotAnonymous, HasPermissionAny,\
40 HasPermissionAnyDecorator, NotAnonymous, HasPermissionAny,\
41 HasReposGroupPermissionAll, HasReposGroupPermissionAny
41 HasReposGroupPermissionAll, HasReposGroupPermissionAny
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.celerylib import tasks, run_task
43 from rhodecode.lib.celerylib import tasks, run_task
44 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
44 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
45 set_rhodecode_config, repo_name_slug, check_git_version
45 set_rhodecode_config, repo_name_slug, check_git_version
46 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
46 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
47 RhodeCodeSetting, PullRequest, PullRequestReviewers
47 RhodeCodeSetting, PullRequest, PullRequestReviewers
48 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
49 ApplicationUiSettingsForm, ApplicationVisualisationForm
49 ApplicationUiSettingsForm, ApplicationVisualisationForm
50 from rhodecode.model.scm import ScmModel, GroupList
50 from rhodecode.model.scm import ScmModel, GroupList
51 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
52 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.repo import RepoModel
53 from rhodecode.model.db import User
53 from rhodecode.model.db import User
54 from rhodecode.model.notification import EmailNotificationModel
54 from rhodecode.model.notification import EmailNotificationModel
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56 from rhodecode.lib.utils2 import str2bool, safe_unicode
56 from rhodecode.lib.utils2 import str2bool, safe_unicode
57 from rhodecode.lib.compat import json
57 from rhodecode.lib.compat import json
58 from webob.exc import HTTPForbidden
58 from webob.exc import HTTPForbidden
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 class SettingsController(BaseController):
62 class SettingsController(BaseController):
63 """REST Controller styled on the Atom Publishing Protocol"""
63 """REST Controller styled on the Atom Publishing Protocol"""
64 # To properly map this controller, ensure your config/routing.py
64 # To properly map this controller, ensure your config/routing.py
65 # file has a resource setup:
65 # file has a resource setup:
66 # map.resource('setting', 'settings', controller='admin/settings',
66 # map.resource('setting', 'settings', controller='admin/settings',
67 # path_prefix='/admin', name_prefix='admin_')
67 # path_prefix='/admin', name_prefix='admin_')
68
68
69 @LoginRequired()
69 @LoginRequired()
70 def __before__(self):
70 def __before__(self):
71 c.admin_user = session.get('admin_user')
71 c.admin_user = session.get('admin_user')
72 c.admin_username = session.get('admin_username')
72 c.admin_username = session.get('admin_username')
73 c.modules = sorted([(p.project_name, p.version)
73 c.modules = sorted([(p.project_name, p.version)
74 for p in pkg_resources.working_set]
74 for p in pkg_resources.working_set]
75 + [('git', check_git_version())],
75 + [('git', check_git_version())],
76 key=lambda k: k[0].lower())
76 key=lambda k: k[0].lower())
77 c.py_version = platform.python_version()
77 c.py_version = platform.python_version()
78 c.platform = platform.platform()
78 c.platform = platform.platform()
79 super(SettingsController, self).__before__()
79 super(SettingsController, self).__before__()
80
80
81 @HasPermissionAllDecorator('hg.admin')
81 @HasPermissionAllDecorator('hg.admin')
82 def index(self, format='html'):
82 def index(self, format='html'):
83 """GET /admin/settings: All items in the collection"""
83 """GET /admin/settings: All items in the collection"""
84 # url('admin_settings')
84 # url('admin_settings')
85
85
86 defaults = RhodeCodeSetting.get_app_settings()
86 defaults = RhodeCodeSetting.get_app_settings()
87 defaults.update(self._get_hg_ui_settings())
87 defaults.update(self._get_hg_ui_settings())
88
88
89 return htmlfill.render(
89 return htmlfill.render(
90 render('admin/settings/settings.html'),
90 render('admin/settings/settings.html'),
91 defaults=defaults,
91 defaults=defaults,
92 encoding="UTF-8",
92 encoding="UTF-8",
93 force_defaults=False
93 force_defaults=False
94 )
94 )
95
95
96 @HasPermissionAllDecorator('hg.admin')
96 @HasPermissionAllDecorator('hg.admin')
97 def create(self):
97 def create(self):
98 """POST /admin/settings: Create a new item"""
98 """POST /admin/settings: Create a new item"""
99 # url('admin_settings')
99 # url('admin_settings')
100
100
101 @HasPermissionAllDecorator('hg.admin')
101 @HasPermissionAllDecorator('hg.admin')
102 def new(self, format='html'):
102 def new(self, format='html'):
103 """GET /admin/settings/new: Form to create a new item"""
103 """GET /admin/settings/new: Form to create a new item"""
104 # url('admin_new_setting')
104 # url('admin_new_setting')
105
105
106 @HasPermissionAllDecorator('hg.admin')
106 @HasPermissionAllDecorator('hg.admin')
107 def update(self, setting_id):
107 def update(self, setting_id):
108 """PUT /admin/settings/setting_id: Update an existing item"""
108 """PUT /admin/settings/setting_id: Update an existing item"""
109 # Forms posted to this method should contain a hidden field:
109 # Forms posted to this method should contain a hidden field:
110 # <input type="hidden" name="_method" value="PUT" />
110 # <input type="hidden" name="_method" value="PUT" />
111 # Or using helpers:
111 # Or using helpers:
112 # h.form(url('admin_setting', setting_id=ID),
112 # h.form(url('admin_setting', setting_id=ID),
113 # method='put')
113 # method='put')
114 # url('admin_setting', setting_id=ID)
114 # url('admin_setting', setting_id=ID)
115
115
116 if setting_id == 'mapping':
116 if setting_id == 'mapping':
117 rm_obsolete = request.POST.get('destroy', False)
117 rm_obsolete = request.POST.get('destroy', False)
118 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
118 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
119 initial = ScmModel().repo_scan()
119 initial = ScmModel().repo_scan()
120 log.debug('invalidating all repositories')
120 log.debug('invalidating all repositories')
121 for repo_name in initial.keys():
121 for repo_name in initial.keys():
122 invalidate_cache('get_repo_cached_%s' % repo_name)
122 invalidate_cache('get_repo_cached_%s' % repo_name)
123
123
124 added, removed = repo2db_mapper(initial, rm_obsolete)
124 added, removed = repo2db_mapper(initial, rm_obsolete)
125 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
125 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
126 h.flash(_('Repositories successfully '
126 h.flash(_('Repositories successfully '
127 'rescanned added: %s ; removed: %s') %
127 'rescanned added: %s ; removed: %s') %
128 (_repr(added), _repr(removed)),
128 (_repr(added), _repr(removed)),
129 category='success')
129 category='success')
130
130
131 if setting_id == 'whoosh':
131 if setting_id == 'whoosh':
132 repo_location = self._get_hg_ui_settings()['paths_root_path']
132 repo_location = self._get_hg_ui_settings()['paths_root_path']
133 full_index = request.POST.get('full_index', False)
133 full_index = request.POST.get('full_index', False)
134 run_task(tasks.whoosh_index, repo_location, full_index)
134 run_task(tasks.whoosh_index, repo_location, full_index)
135 h.flash(_('Whoosh reindex task scheduled'), category='success')
135 h.flash(_('Whoosh reindex task scheduled'), category='success')
136
136
137 if setting_id == 'global':
137 if setting_id == 'global':
138
138
139 application_form = ApplicationSettingsForm()()
139 application_form = ApplicationSettingsForm()()
140 try:
140 try:
141 form_result = application_form.to_python(dict(request.POST))
141 form_result = application_form.to_python(dict(request.POST))
142 except formencode.Invalid, errors:
142 except formencode.Invalid, errors:
143 return htmlfill.render(
143 return htmlfill.render(
144 render('admin/settings/settings.html'),
144 render('admin/settings/settings.html'),
145 defaults=errors.value,
145 defaults=errors.value,
146 errors=errors.error_dict or {},
146 errors=errors.error_dict or {},
147 prefix_error=False,
147 prefix_error=False,
148 encoding="UTF-8"
148 encoding="UTF-8"
149 )
149 )
150
150
151 try:
151 try:
152 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
152 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
153 sett1.app_settings_value = form_result['rhodecode_title']
153 sett1.app_settings_value = form_result['rhodecode_title']
154 Session().add(sett1)
154 Session().add(sett1)
155
155
156 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
156 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
157 sett2.app_settings_value = form_result['rhodecode_realm']
157 sett2.app_settings_value = form_result['rhodecode_realm']
158 Session().add(sett2)
158 Session().add(sett2)
159
159
160 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
160 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
161 sett3.app_settings_value = form_result['rhodecode_ga_code']
161 sett3.app_settings_value = form_result['rhodecode_ga_code']
162 Session().add(sett3)
162 Session().add(sett3)
163
163
164 Session().commit()
164 Session().commit()
165 set_rhodecode_config(config)
165 set_rhodecode_config(config)
166 h.flash(_('Updated application settings'), category='success')
166 h.flash(_('Updated application settings'), category='success')
167
167
168 except Exception:
168 except Exception:
169 log.error(traceback.format_exc())
169 log.error(traceback.format_exc())
170 h.flash(_('error occurred during updating '
170 h.flash(_('error occurred during updating '
171 'application settings'),
171 'application settings'),
172 category='error')
172 category='error')
173
173
174 if setting_id == 'visual':
174 if setting_id == 'visual':
175
175
176 application_form = ApplicationVisualisationForm()()
176 application_form = ApplicationVisualisationForm()()
177 try:
177 try:
178 form_result = application_form.to_python(dict(request.POST))
178 form_result = application_form.to_python(dict(request.POST))
179 except formencode.Invalid, errors:
179 except formencode.Invalid, errors:
180 return htmlfill.render(
180 return htmlfill.render(
181 render('admin/settings/settings.html'),
181 render('admin/settings/settings.html'),
182 defaults=errors.value,
182 defaults=errors.value,
183 errors=errors.error_dict or {},
183 errors=errors.error_dict or {},
184 prefix_error=False,
184 prefix_error=False,
185 encoding="UTF-8"
185 encoding="UTF-8"
186 )
186 )
187
187
188 try:
188 try:
189 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
189 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
190 sett1.app_settings_value = \
190 sett1.app_settings_value = \
191 form_result['rhodecode_show_public_icon']
191 form_result['rhodecode_show_public_icon']
192 Session().add(sett1)
192 Session().add(sett1)
193
193
194 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
194 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
195 sett2.app_settings_value = \
195 sett2.app_settings_value = \
196 form_result['rhodecode_show_private_icon']
196 form_result['rhodecode_show_private_icon']
197 Session().add(sett2)
197 Session().add(sett2)
198
198
199 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
199 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
200 sett3.app_settings_value = \
200 sett3.app_settings_value = \
201 form_result['rhodecode_stylify_metatags']
201 form_result['rhodecode_stylify_metatags']
202 Session().add(sett3)
202 Session().add(sett3)
203
203
204 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
204 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
205 sett4.app_settings_value = \
205 sett4.app_settings_value = \
206 form_result['rhodecode_lightweight_dashboard']
206 form_result['rhodecode_lightweight_dashboard']
207 Session().add(sett4)
207 Session().add(sett4)
208
208
209 sett4 = RhodeCodeSetting.get_by_name_or_create('repository_fields')
209 sett4 = RhodeCodeSetting.get_by_name_or_create('repository_fields')
210 sett4.app_settings_value = \
210 sett4.app_settings_value = \
211 form_result['rhodecode_repository_fields']
211 form_result['rhodecode_repository_fields']
212 Session().add(sett4)
212 Session().add(sett4)
213
213
214 Session().commit()
214 Session().commit()
215 set_rhodecode_config(config)
215 set_rhodecode_config(config)
216 h.flash(_('Updated visualisation settings'),
216 h.flash(_('Updated visualisation settings'),
217 category='success')
217 category='success')
218
218
219 except Exception:
219 except Exception:
220 log.error(traceback.format_exc())
220 log.error(traceback.format_exc())
221 h.flash(_('error occurred during updating '
221 h.flash(_('error occurred during updating '
222 'visualisation settings'),
222 'visualisation settings'),
223 category='error')
223 category='error')
224
224
225 if setting_id == 'vcs':
225 if setting_id == 'vcs':
226 application_form = ApplicationUiSettingsForm()()
226 application_form = ApplicationUiSettingsForm()()
227 try:
227 try:
228 form_result = application_form.to_python(dict(request.POST))
228 form_result = application_form.to_python(dict(request.POST))
229 except formencode.Invalid, errors:
229 except formencode.Invalid, errors:
230 return htmlfill.render(
230 return htmlfill.render(
231 render('admin/settings/settings.html'),
231 render('admin/settings/settings.html'),
232 defaults=errors.value,
232 defaults=errors.value,
233 errors=errors.error_dict or {},
233 errors=errors.error_dict or {},
234 prefix_error=False,
234 prefix_error=False,
235 encoding="UTF-8"
235 encoding="UTF-8"
236 )
236 )
237
237
238 try:
238 try:
239 # fix namespaces for hooks and extensions
239 # fix namespaces for hooks and extensions
240 _f = lambda s: s.replace('.', '_')
240 _f = lambda s: s.replace('.', '_')
241
241
242 sett = RhodeCodeUi.get_by_key('push_ssl')
242 sett = RhodeCodeUi.get_by_key('push_ssl')
243 sett.ui_value = form_result['web_push_ssl']
243 sett.ui_value = form_result['web_push_ssl']
244 Session().add(sett)
244 Session().add(sett)
245
245
246 sett = RhodeCodeUi.get_by_key('/')
246 sett = RhodeCodeUi.get_by_key('/')
247 sett.ui_value = form_result['paths_root_path']
247 sett.ui_value = form_result['paths_root_path']
248 Session().add(sett)
248 Session().add(sett)
249
249
250 #HOOKS
250 #HOOKS
251 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
251 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
252 sett.ui_active = form_result[_f('hooks_%s' %
252 sett.ui_active = form_result[_f('hooks_%s' %
253 RhodeCodeUi.HOOK_UPDATE)]
253 RhodeCodeUi.HOOK_UPDATE)]
254 Session().add(sett)
254 Session().add(sett)
255
255
256 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
256 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
257 sett.ui_active = form_result[_f('hooks_%s' %
257 sett.ui_active = form_result[_f('hooks_%s' %
258 RhodeCodeUi.HOOK_REPO_SIZE)]
258 RhodeCodeUi.HOOK_REPO_SIZE)]
259 Session().add(sett)
259 Session().add(sett)
260
260
261 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
261 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
262 sett.ui_active = form_result[_f('hooks_%s' %
262 sett.ui_active = form_result[_f('hooks_%s' %
263 RhodeCodeUi.HOOK_PUSH)]
263 RhodeCodeUi.HOOK_PUSH)]
264 Session().add(sett)
264 Session().add(sett)
265
265
266 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
266 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
267 sett.ui_active = form_result[_f('hooks_%s' %
267 sett.ui_active = form_result[_f('hooks_%s' %
268 RhodeCodeUi.HOOK_PULL)]
268 RhodeCodeUi.HOOK_PULL)]
269
269
270 Session().add(sett)
270 Session().add(sett)
271
271
272 ## EXTENSIONS
272 ## EXTENSIONS
273 sett = RhodeCodeUi.get_by_key('largefiles')
273 sett = RhodeCodeUi.get_by_key('largefiles')
274 if not sett:
274 if not sett:
275 #make one if it's not there !
275 #make one if it's not there !
276 sett = RhodeCodeUi()
276 sett = RhodeCodeUi()
277 sett.ui_key = 'largefiles'
277 sett.ui_key = 'largefiles'
278 sett.ui_section = 'extensions'
278 sett.ui_section = 'extensions'
279 sett.ui_active = form_result[_f('extensions_largefiles')]
279 sett.ui_active = form_result[_f('extensions_largefiles')]
280 Session().add(sett)
280 Session().add(sett)
281
281
282 sett = RhodeCodeUi.get_by_key('hgsubversion')
282 sett = RhodeCodeUi.get_by_key('hgsubversion')
283 if not sett:
283 if not sett:
284 #make one if it's not there !
284 #make one if it's not there !
285 sett = RhodeCodeUi()
285 sett = RhodeCodeUi()
286 sett.ui_key = 'hgsubversion'
286 sett.ui_key = 'hgsubversion'
287 sett.ui_section = 'extensions'
287 sett.ui_section = 'extensions'
288
288
289 sett.ui_active = form_result[_f('extensions_hgsubversion')]
289 sett.ui_active = form_result[_f('extensions_hgsubversion')]
290 Session().add(sett)
290 Session().add(sett)
291
291
292 # sett = RhodeCodeUi.get_by_key('hggit')
292 # sett = RhodeCodeUi.get_by_key('hggit')
293 # if not sett:
293 # if not sett:
294 # #make one if it's not there !
294 # #make one if it's not there !
295 # sett = RhodeCodeUi()
295 # sett = RhodeCodeUi()
296 # sett.ui_key = 'hggit'
296 # sett.ui_key = 'hggit'
297 # sett.ui_section = 'extensions'
297 # sett.ui_section = 'extensions'
298 #
298 #
299 # sett.ui_active = form_result[_f('extensions_hggit')]
299 # sett.ui_active = form_result[_f('extensions_hggit')]
300 # Session().add(sett)
300 # Session().add(sett)
301
301
302 Session().commit()
302 Session().commit()
303
303
304 h.flash(_('Updated VCS settings'), category='success')
304 h.flash(_('Updated VCS settings'), category='success')
305
305
306 except Exception:
306 except Exception:
307 log.error(traceback.format_exc())
307 log.error(traceback.format_exc())
308 h.flash(_('error occurred during updating '
308 h.flash(_('error occurred during updating '
309 'application settings'), category='error')
309 'application settings'), category='error')
310
310
311 if setting_id == 'hooks':
311 if setting_id == 'hooks':
312 ui_key = request.POST.get('new_hook_ui_key')
312 ui_key = request.POST.get('new_hook_ui_key')
313 ui_value = request.POST.get('new_hook_ui_value')
313 ui_value = request.POST.get('new_hook_ui_value')
314 try:
314 try:
315
315
316 if ui_value and ui_key:
316 if ui_value and ui_key:
317 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
317 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
318 h.flash(_('Added new hook'),
318 h.flash(_('Added new hook'),
319 category='success')
319 category='success')
320
320
321 # check for edits
321 # check for edits
322 update = False
322 update = False
323 _d = request.POST.dict_of_lists()
323 _d = request.POST.dict_of_lists()
324 for k, v in zip(_d.get('hook_ui_key', []),
324 for k, v in zip(_d.get('hook_ui_key', []),
325 _d.get('hook_ui_value_new', [])):
325 _d.get('hook_ui_value_new', [])):
326 RhodeCodeUi.create_or_update_hook(k, v)
326 RhodeCodeUi.create_or_update_hook(k, v)
327 update = True
327 update = True
328
328
329 if update:
329 if update:
330 h.flash(_('Updated hooks'), category='success')
330 h.flash(_('Updated hooks'), category='success')
331 Session().commit()
331 Session().commit()
332 except Exception:
332 except Exception:
333 log.error(traceback.format_exc())
333 log.error(traceback.format_exc())
334 h.flash(_('error occurred during hook creation'),
334 h.flash(_('error occurred during hook creation'),
335 category='error')
335 category='error')
336
336
337 return redirect(url('admin_edit_setting', setting_id='hooks'))
337 return redirect(url('admin_edit_setting', setting_id='hooks'))
338
338
339 if setting_id == 'email':
339 if setting_id == 'email':
340 test_email = request.POST.get('test_email')
340 test_email = request.POST.get('test_email')
341 test_email_subj = 'RhodeCode TestEmail'
341 test_email_subj = 'RhodeCode TestEmail'
342 test_email_body = 'RhodeCode Email test'
342 test_email_body = 'RhodeCode Email test'
343
343
344 test_email_html_body = EmailNotificationModel()\
344 test_email_html_body = EmailNotificationModel()\
345 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
345 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
346 body=test_email_body)
346 body=test_email_body)
347
347
348 recipients = [test_email] if test_email else None
348 recipients = [test_email] if test_email else None
349
349
350 run_task(tasks.send_email, recipients, test_email_subj,
350 run_task(tasks.send_email, recipients, test_email_subj,
351 test_email_body, test_email_html_body)
351 test_email_body, test_email_html_body)
352
352
353 h.flash(_('Email task created'), category='success')
353 h.flash(_('Email task created'), category='success')
354 return redirect(url('admin_settings'))
354 return redirect(url('admin_settings'))
355
355
356 @HasPermissionAllDecorator('hg.admin')
356 @HasPermissionAllDecorator('hg.admin')
357 def delete(self, setting_id):
357 def delete(self, setting_id):
358 """DELETE /admin/settings/setting_id: Delete an existing item"""
358 """DELETE /admin/settings/setting_id: Delete an existing item"""
359 # Forms posted to this method should contain a hidden field:
359 # Forms posted to this method should contain a hidden field:
360 # <input type="hidden" name="_method" value="DELETE" />
360 # <input type="hidden" name="_method" value="DELETE" />
361 # Or using helpers:
361 # Or using helpers:
362 # h.form(url('admin_setting', setting_id=ID),
362 # h.form(url('admin_setting', setting_id=ID),
363 # method='delete')
363 # method='delete')
364 # url('admin_setting', setting_id=ID)
364 # url('admin_setting', setting_id=ID)
365 if setting_id == 'hooks':
365 if setting_id == 'hooks':
366 hook_id = request.POST.get('hook_id')
366 hook_id = request.POST.get('hook_id')
367 RhodeCodeUi.delete(hook_id)
367 RhodeCodeUi.delete(hook_id)
368 Session().commit()
368 Session().commit()
369
369
370 @HasPermissionAllDecorator('hg.admin')
370 @HasPermissionAllDecorator('hg.admin')
371 def show(self, setting_id, format='html'):
371 def show(self, setting_id, format='html'):
372 """
372 """
373 GET /admin/settings/setting_id: Show a specific item"""
373 GET /admin/settings/setting_id: Show a specific item"""
374 # url('admin_setting', setting_id=ID)
374 # url('admin_setting', setting_id=ID)
375
375
376 @HasPermissionAllDecorator('hg.admin')
376 @HasPermissionAllDecorator('hg.admin')
377 def edit(self, setting_id, format='html'):
377 def edit(self, setting_id, format='html'):
378 """
378 """
379 GET /admin/settings/setting_id/edit: Form to
379 GET /admin/settings/setting_id/edit: Form to
380 edit an existing item"""
380 edit an existing item"""
381 # url('admin_edit_setting', setting_id=ID)
381 # url('admin_edit_setting', setting_id=ID)
382
382
383 c.hooks = RhodeCodeUi.get_builtin_hooks()
383 c.hooks = RhodeCodeUi.get_builtin_hooks()
384 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
384 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
385
385
386 return htmlfill.render(
386 return htmlfill.render(
387 render('admin/settings/hooks.html'),
387 render('admin/settings/hooks.html'),
388 defaults={},
388 defaults={},
389 encoding="UTF-8",
389 encoding="UTF-8",
390 force_defaults=False
390 force_defaults=False
391 )
391 )
392
392
393 def _load_my_repos_data(self):
393 def _load_my_repos_data(self):
394 repos_list = Session().query(Repository)\
394 repos_list = Session().query(Repository)\
395 .filter(Repository.user_id ==
395 .filter(Repository.user_id ==
396 self.rhodecode_user.user_id)\
396 self.rhodecode_user.user_id)\
397 .order_by(func.lower(Repository.repo_name)).all()
397 .order_by(func.lower(Repository.repo_name)).all()
398
398
399 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
399 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
400 admin=True)
400 admin=True)
401 #json used to render the grid
401 #json used to render the grid
402 return json.dumps(repos_data)
402 return json.dumps(repos_data)
403
403
404 @NotAnonymous()
404 @NotAnonymous()
405 def my_account(self):
405 def my_account(self):
406 """
406 """
407 GET /_admin/my_account Displays info about my account
407 GET /_admin/my_account Displays info about my account
408 """
408 """
409 # url('admin_settings_my_account')
409 # url('admin_settings_my_account')
410
410
411 c.user = User.get(self.rhodecode_user.user_id)
411 c.user = User.get(self.rhodecode_user.user_id)
412 c.ldap_dn = c.user.ldap_dn
412 c.ldap_dn = c.user.ldap_dn
413
413
414 if c.user.username == 'default':
414 if c.user.username == 'default':
415 h.flash(_("You can't edit this user since it's"
415 h.flash(_("You can't edit this user since it's"
416 " crucial for entire application"), category='warning')
416 " crucial for entire application"), category='warning')
417 return redirect(url('users'))
417 return redirect(url('users'))
418
418
419 #json used to render the grid
419 #json used to render the grid
420 c.data = self._load_my_repos_data()
420 c.data = self._load_my_repos_data()
421
421
422 defaults = c.user.get_dict()
422 defaults = c.user.get_dict()
423
423
424 c.form = htmlfill.render(
424 c.form = htmlfill.render(
425 render('admin/users/user_edit_my_account_form.html'),
425 render('admin/users/user_edit_my_account_form.html'),
426 defaults=defaults,
426 defaults=defaults,
427 encoding="UTF-8",
427 encoding="UTF-8",
428 force_defaults=False
428 force_defaults=False
429 )
429 )
430 return render('admin/users/user_edit_my_account.html')
430 return render('admin/users/user_edit_my_account.html')
431
431
432 @NotAnonymous()
432 @NotAnonymous()
433 def my_account_update(self):
433 def my_account_update(self):
434 """PUT /_admin/my_account_update: Update an existing item"""
434 """PUT /_admin/my_account_update: Update an existing item"""
435 # Forms posted to this method should contain a hidden field:
435 # Forms posted to this method should contain a hidden field:
436 # <input type="hidden" name="_method" value="PUT" />
436 # <input type="hidden" name="_method" value="PUT" />
437 # Or using helpers:
437 # Or using helpers:
438 # h.form(url('admin_settings_my_account_update'),
438 # h.form(url('admin_settings_my_account_update'),
439 # method='put')
439 # method='put')
440 # url('admin_settings_my_account_update', id=ID)
440 # url('admin_settings_my_account_update', id=ID)
441 uid = self.rhodecode_user.user_id
441 uid = self.rhodecode_user.user_id
442 c.user = User.get(self.rhodecode_user.user_id)
442 c.user = User.get(self.rhodecode_user.user_id)
443 c.ldap_dn = c.user.ldap_dn
443 c.ldap_dn = c.user.ldap_dn
444 email = self.rhodecode_user.email
444 email = self.rhodecode_user.email
445 _form = UserForm(edit=True,
445 _form = UserForm(edit=True,
446 old_data={'user_id': uid, 'email': email})()
446 old_data={'user_id': uid, 'email': email})()
447 form_result = {}
447 form_result = {}
448 try:
448 try:
449 form_result = _form.to_python(dict(request.POST))
449 form_result = _form.to_python(dict(request.POST))
450 skip_attrs = ['admin', 'active'] # skip attr for my account
450 skip_attrs = ['admin', 'active'] # skip attr for my account
451 if c.ldap_dn:
451 if c.ldap_dn:
452 #forbid updating username for ldap accounts
452 #forbid updating username for ldap accounts
453 skip_attrs.append('username')
453 skip_attrs.append('username')
454 UserModel().update(uid, form_result, skip_attrs=skip_attrs)
454 UserModel().update(uid, form_result, skip_attrs=skip_attrs)
455 h.flash(_('Your account was updated successfully'),
455 h.flash(_('Your account was updated successfully'),
456 category='success')
456 category='success')
457 Session().commit()
457 Session().commit()
458 except formencode.Invalid, errors:
458 except formencode.Invalid, errors:
459 #json used to render the grid
459 #json used to render the grid
460 c.data = self._load_my_repos_data()
460 c.data = self._load_my_repos_data()
461 c.form = htmlfill.render(
461 c.form = htmlfill.render(
462 render('admin/users/user_edit_my_account_form.html'),
462 render('admin/users/user_edit_my_account_form.html'),
463 defaults=errors.value,
463 defaults=errors.value,
464 errors=errors.error_dict or {},
464 errors=errors.error_dict or {},
465 prefix_error=False,
465 prefix_error=False,
466 encoding="UTF-8")
466 encoding="UTF-8")
467 return render('admin/users/user_edit_my_account.html')
467 return render('admin/users/user_edit_my_account.html')
468 except Exception:
468 except Exception:
469 log.error(traceback.format_exc())
469 log.error(traceback.format_exc())
470 h.flash(_('error occurred during update of user %s') \
470 h.flash(_('error occurred during update of user %s') \
471 % form_result.get('username'), category='error')
471 % form_result.get('username'), category='error')
472
472
473 return redirect(url('my_account'))
473 return redirect(url('my_account'))
474
474
475 @NotAnonymous()
475 @NotAnonymous()
476 def my_account_my_pullrequests(self):
476 def my_account_my_pullrequests(self):
477 c.my_pull_requests = PullRequest.query()\
477 c.my_pull_requests = PullRequest.query()\
478 .filter(PullRequest.user_id ==
478 .filter(PullRequest.user_id ==
479 self.rhodecode_user.user_id)\
479 self.rhodecode_user.user_id)\
480 .order_by(PullRequest.created_on.desc())\
480 .all()
481 .all()
481 c.participate_in_pull_requests = \
482
483 c.participate_in_pull_requests = sorted(
482 [x.pull_request for x in PullRequestReviewers.query()\
484 [x.pull_request for x in PullRequestReviewers.query()\
483 .filter(PullRequestReviewers.user_id ==
485 .filter(PullRequestReviewers.user_id ==
484 self.rhodecode_user.user_id)\
486 self.rhodecode_user.user_id)\
485 .all()]
487 .all()],
488 key=lambda o: o.created_on, reverse=True)
489
486 return render('admin/users/user_edit_my_account_pullrequests.html')
490 return render('admin/users/user_edit_my_account_pullrequests.html')
487
491
488 @NotAnonymous()
492 @NotAnonymous()
489 def create_repository(self):
493 def create_repository(self):
490 """GET /_admin/create_repository: Form to create a new item"""
494 """GET /_admin/create_repository: Form to create a new item"""
491 new_repo = request.GET.get('repo', '')
495 new_repo = request.GET.get('repo', '')
492 parent_group = request.GET.get('parent_group')
496 parent_group = request.GET.get('parent_group')
493 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
497 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
494 #you're not super admin nor have global create permissions,
498 #you're not super admin nor have global create permissions,
495 #but maybe you have at least write permission to a parent group ?
499 #but maybe you have at least write permission to a parent group ?
496 _gr = RepoGroup.get(parent_group)
500 _gr = RepoGroup.get(parent_group)
497 gr_name = _gr.group_name if _gr else None
501 gr_name = _gr.group_name if _gr else None
498 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
502 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
499 raise HTTPForbidden
503 raise HTTPForbidden
500
504
501 acl_groups = GroupList(RepoGroup.query().all(),
505 acl_groups = GroupList(RepoGroup.query().all(),
502 perm_set=['group.write', 'group.admin'])
506 perm_set=['group.write', 'group.admin'])
503 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
507 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
504 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
508 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
505 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
509 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
506
510
507 c.new_repo = repo_name_slug(new_repo)
511 c.new_repo = repo_name_slug(new_repo)
508
512
509 ## apply the defaults from defaults page
513 ## apply the defaults from defaults page
510 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
514 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
511 if parent_group:
515 if parent_group:
512 defaults.update({'repo_group': parent_group})
516 defaults.update({'repo_group': parent_group})
513
517
514 return htmlfill.render(
518 return htmlfill.render(
515 render('admin/repos/repo_add.html'),
519 render('admin/repos/repo_add.html'),
516 defaults=defaults,
520 defaults=defaults,
517 errors={},
521 errors={},
518 prefix_error=False,
522 prefix_error=False,
519 encoding="UTF-8"
523 encoding="UTF-8"
520 )
524 )
521
525
522 def _get_hg_ui_settings(self):
526 def _get_hg_ui_settings(self):
523 ret = RhodeCodeUi.query().all()
527 ret = RhodeCodeUi.query().all()
524
528
525 if not ret:
529 if not ret:
526 raise Exception('Could not get application ui settings !')
530 raise Exception('Could not get application ui settings !')
527 settings = {}
531 settings = {}
528 for each in ret:
532 for each in ret:
529 k = each.ui_key
533 k = each.ui_key
530 v = each.ui_value
534 v = each.ui_value
531 if k == '/':
535 if k == '/':
532 k = 'root_path'
536 k = 'root_path'
533
537
534 if k == 'push_ssl':
538 if k == 'push_ssl':
535 v = str2bool(v)
539 v = str2bool(v)
536
540
537 if k.find('.') != -1:
541 if k.find('.') != -1:
538 k = k.replace('.', '_')
542 k = k.replace('.', '_')
539
543
540 if each.ui_section in ['hooks', 'extensions']:
544 if each.ui_section in ['hooks', 'extensions']:
541 v = each.ui_active
545 v = each.ui_active
542
546
543 settings[each.ui_section + '_' + k] = v
547 settings[each.ui_section + '_' + k] = v
544 return settings
548 return settings
@@ -1,257 +1,258
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.pull_request
3 rhodecode.model.pull_request
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 pull request model for RhodeCode
6 pull request model for RhodeCode
7
7
8 :created_on: Jun 6, 2012
8 :created_on: Jun 6, 2012
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import datetime
27 import datetime
28 import re
28 import re
29
29
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31
31
32 from rhodecode.model.meta import Session
32 from rhodecode.model.meta import Session
33 from rhodecode.lib import helpers as h, unionrepo
33 from rhodecode.lib import helpers as h, unionrepo
34 from rhodecode.model import BaseModel
34 from rhodecode.model import BaseModel
35 from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification,\
35 from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification,\
36 ChangesetStatus
36 ChangesetStatus
37 from rhodecode.model.notification import NotificationModel
37 from rhodecode.model.notification import NotificationModel
38 from rhodecode.lib.utils2 import safe_unicode
38 from rhodecode.lib.utils2 import safe_unicode
39
39
40 from rhodecode.lib.vcs.utils.hgcompat import scmutil
40 from rhodecode.lib.vcs.utils.hgcompat import scmutil
41 from rhodecode.lib.vcs.utils import safe_str
41 from rhodecode.lib.vcs.utils import safe_str
42 from rhodecode.lib.vcs.backends.base import EmptyChangeset
42 from rhodecode.lib.vcs.backends.base import EmptyChangeset
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class PullRequestModel(BaseModel):
47 class PullRequestModel(BaseModel):
48
48
49 cls = PullRequest
49 cls = PullRequest
50
50
51 def __get_pull_request(self, pull_request):
51 def __get_pull_request(self, pull_request):
52 return self._get_instance(PullRequest, pull_request)
52 return self._get_instance(PullRequest, pull_request)
53
53
54 def get_all(self, repo):
54 def get_all(self, repo):
55 repo = self._get_repo(repo)
55 repo = self._get_repo(repo)
56 return PullRequest.query()\
56 return PullRequest.query()\
57 .filter(PullRequest.other_repo == repo)\
57 .filter(PullRequest.other_repo == repo)\
58 .order_by(PullRequest.created_on)\
58 .order_by(PullRequest.created_on.desc())\
59 .all()
59 .all()
60
60
61 def create(self, created_by, org_repo, org_ref, other_repo, other_ref,
61 def create(self, created_by, org_repo, org_ref, other_repo, other_ref,
62 revisions, reviewers, title, description=None):
62 revisions, reviewers, title, description=None):
63 from rhodecode.model.changeset_status import ChangesetStatusModel
63 from rhodecode.model.changeset_status import ChangesetStatusModel
64
64
65 created_by_user = self._get_user(created_by)
65 created_by_user = self._get_user(created_by)
66 org_repo = self._get_repo(org_repo)
66 org_repo = self._get_repo(org_repo)
67 other_repo = self._get_repo(other_repo)
67 other_repo = self._get_repo(other_repo)
68
68
69 new = PullRequest()
69 new = PullRequest()
70 new.org_repo = org_repo
70 new.org_repo = org_repo
71 new.org_ref = org_ref
71 new.org_ref = org_ref
72 new.other_repo = other_repo
72 new.other_repo = other_repo
73 new.other_ref = other_ref
73 new.other_ref = other_ref
74 new.revisions = revisions
74 new.revisions = revisions
75 new.title = title
75 new.title = title
76 new.description = description
76 new.description = description
77 new.author = created_by_user
77 new.author = created_by_user
78 self.sa.add(new)
78 self.sa.add(new)
79 Session().flush()
79 Session().flush()
80 #members
80 #members
81 for member in reviewers:
81 for member in set(reviewers):
82 _usr = self._get_user(member)
82 _usr = self._get_user(member)
83 reviewer = PullRequestReviewers(_usr, new)
83 reviewer = PullRequestReviewers(_usr, new)
84 self.sa.add(reviewer)
84 self.sa.add(reviewer)
85
85
86 #reset state to under-review
86 #reset state to under-review
87 ChangesetStatusModel().set_status(
87 ChangesetStatusModel().set_status(
88 repo=org_repo,
88 repo=org_repo,
89 status=ChangesetStatus.STATUS_UNDER_REVIEW,
89 status=ChangesetStatus.STATUS_UNDER_REVIEW,
90 user=created_by_user,
90 user=created_by_user,
91 pull_request=new
91 pull_request=new
92 )
92 )
93
93
94 #notification to reviewers
94 #notification to reviewers
95 notif = NotificationModel()
95 notif = NotificationModel()
96
96
97 pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name,
97 pr_url = h.url('pullrequest_show', repo_name=other_repo.repo_name,
98 pull_request_id=new.pull_request_id,
98 pull_request_id=new.pull_request_id,
99 qualified=True,
99 qualified=True,
100 )
100 )
101 subject = safe_unicode(
101 subject = safe_unicode(
102 h.link_to(
102 h.link_to(
103 _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \
103 _('%(user)s wants you to review pull request #%(pr_id)s: %(pr_title)s') % \
104 {'user': created_by_user.username,
104 {'user': created_by_user.username,
105 'pr_title': new.title,
105 'pr_title': new.title,
106 'pr_id': new.pull_request_id},
106 'pr_id': new.pull_request_id},
107 pr_url
107 pr_url
108 )
108 )
109 )
109 )
110 body = description
110 body = description
111 kwargs = {
111 kwargs = {
112 'pr_title': title,
112 'pr_title': title,
113 'pr_user_created': h.person(created_by_user.email),
113 'pr_user_created': h.person(created_by_user.email),
114 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name,
114 'pr_repo_url': h.url('summary_home', repo_name=other_repo.repo_name,
115 qualified=True,),
115 qualified=True,),
116 'pr_url': pr_url,
116 'pr_url': pr_url,
117 'pr_revisions': revisions
117 'pr_revisions': revisions
118 }
118 }
119
119 notif.create(created_by=created_by_user, subject=subject, body=body,
120 notif.create(created_by=created_by_user, subject=subject, body=body,
120 recipients=reviewers,
121 recipients=reviewers,
121 type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs)
122 type_=Notification.TYPE_PULL_REQUEST, email_kwargs=kwargs)
122 return new
123 return new
123
124
124 def update_reviewers(self, pull_request, reviewers_ids):
125 def update_reviewers(self, pull_request, reviewers_ids):
125 reviewers_ids = set(reviewers_ids)
126 reviewers_ids = set(reviewers_ids)
126 pull_request = self.__get_pull_request(pull_request)
127 pull_request = self.__get_pull_request(pull_request)
127 current_reviewers = PullRequestReviewers.query()\
128 current_reviewers = PullRequestReviewers.query()\
128 .filter(PullRequestReviewers.pull_request==
129 .filter(PullRequestReviewers.pull_request==
129 pull_request)\
130 pull_request)\
130 .all()
131 .all()
131 current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
132 current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
132
133
133 to_add = reviewers_ids.difference(current_reviewers_ids)
134 to_add = reviewers_ids.difference(current_reviewers_ids)
134 to_remove = current_reviewers_ids.difference(reviewers_ids)
135 to_remove = current_reviewers_ids.difference(reviewers_ids)
135
136
136 log.debug("Adding %s reviewers" % to_add)
137 log.debug("Adding %s reviewers" % to_add)
137 log.debug("Removing %s reviewers" % to_remove)
138 log.debug("Removing %s reviewers" % to_remove)
138
139
139 for uid in to_add:
140 for uid in to_add:
140 _usr = self._get_user(uid)
141 _usr = self._get_user(uid)
141 reviewer = PullRequestReviewers(_usr, pull_request)
142 reviewer = PullRequestReviewers(_usr, pull_request)
142 self.sa.add(reviewer)
143 self.sa.add(reviewer)
143
144
144 for uid in to_remove:
145 for uid in to_remove:
145 reviewer = PullRequestReviewers.query()\
146 reviewer = PullRequestReviewers.query()\
146 .filter(PullRequestReviewers.user_id==uid,
147 .filter(PullRequestReviewers.user_id==uid,
147 PullRequestReviewers.pull_request==pull_request)\
148 PullRequestReviewers.pull_request==pull_request)\
148 .scalar()
149 .scalar()
149 if reviewer:
150 if reviewer:
150 self.sa.delete(reviewer)
151 self.sa.delete(reviewer)
151
152
152 def delete(self, pull_request):
153 def delete(self, pull_request):
153 pull_request = self.__get_pull_request(pull_request)
154 pull_request = self.__get_pull_request(pull_request)
154 Session().delete(pull_request)
155 Session().delete(pull_request)
155
156
156 def close_pull_request(self, pull_request):
157 def close_pull_request(self, pull_request):
157 pull_request = self.__get_pull_request(pull_request)
158 pull_request = self.__get_pull_request(pull_request)
158 pull_request.status = PullRequest.STATUS_CLOSED
159 pull_request.status = PullRequest.STATUS_CLOSED
159 pull_request.updated_on = datetime.datetime.now()
160 pull_request.updated_on = datetime.datetime.now()
160 self.sa.add(pull_request)
161 self.sa.add(pull_request)
161
162
162 def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref):
163 def _get_changesets(self, alias, org_repo, org_ref, other_repo, other_ref):
163 """
164 """
164 Returns a list of changesets that can be merged from org_repo@org_ref
165 Returns a list of changesets that can be merged from org_repo@org_ref
165 to other_repo@other_ref ... and the ancestor that would be used for merge
166 to other_repo@other_ref ... and the ancestor that would be used for merge
166
167
167 :param org_repo:
168 :param org_repo:
168 :param org_ref:
169 :param org_ref:
169 :param other_repo:
170 :param other_repo:
170 :param other_ref:
171 :param other_ref:
171 :param tmp:
172 :param tmp:
172 """
173 """
173
174
174 ancestor = None
175 ancestor = None
175
176
176 if alias == 'hg':
177 if alias == 'hg':
177 # lookup up the exact node id
178 # lookup up the exact node id
178 _revset_predicates = {
179 _revset_predicates = {
179 'branch': 'branch',
180 'branch': 'branch',
180 'book': 'bookmark',
181 'book': 'bookmark',
181 'tag': 'tag',
182 'tag': 'tag',
182 'rev': 'id',
183 'rev': 'id',
183 }
184 }
184
185
185 org_rev_spec = "%s('%s')" % (_revset_predicates[org_ref[0]],
186 org_rev_spec = "%s('%s')" % (_revset_predicates[org_ref[0]],
186 safe_str(org_ref[1]))
187 safe_str(org_ref[1]))
187 if org_ref[1] == EmptyChangeset().raw_id:
188 if org_ref[1] == EmptyChangeset().raw_id:
188 org_rev = org_ref[1]
189 org_rev = org_ref[1]
189 else:
190 else:
190 org_rev = org_repo._repo[scmutil.revrange(org_repo._repo,
191 org_rev = org_repo._repo[scmutil.revrange(org_repo._repo,
191 [org_rev_spec])[-1]]
192 [org_rev_spec])[-1]]
192 other_rev_spec = "%s('%s')" % (_revset_predicates[other_ref[0]],
193 other_rev_spec = "%s('%s')" % (_revset_predicates[other_ref[0]],
193 safe_str(other_ref[1]))
194 safe_str(other_ref[1]))
194 if other_ref[1] == EmptyChangeset().raw_id:
195 if other_ref[1] == EmptyChangeset().raw_id:
195 other_rev = other_ref[1]
196 other_rev = other_ref[1]
196 else:
197 else:
197 other_rev = other_repo._repo[scmutil.revrange(other_repo._repo,
198 other_rev = other_repo._repo[scmutil.revrange(other_repo._repo,
198 [other_rev_spec])[-1]]
199 [other_rev_spec])[-1]]
199
200
200 #case two independent repos
201 #case two independent repos
201 if org_repo != other_repo:
202 if org_repo != other_repo:
202 hgrepo = unionrepo.unionrepository(other_repo.baseui,
203 hgrepo = unionrepo.unionrepository(other_repo.baseui,
203 other_repo.path,
204 other_repo.path,
204 org_repo.path)
205 org_repo.path)
205 # all the changesets we are looking for will be in other_repo,
206 # all the changesets we are looking for will be in other_repo,
206 # so rev numbers from hgrepo can be used in other_repo
207 # so rev numbers from hgrepo can be used in other_repo
207
208
208 #no remote compare do it on the same repository
209 #no remote compare do it on the same repository
209 else:
210 else:
210 hgrepo = other_repo._repo
211 hgrepo = other_repo._repo
211
212
212 revs = ["ancestors(id('%s')) and not ancestors(id('%s'))" %
213 revs = ["ancestors(id('%s')) and not ancestors(id('%s'))" %
213 (other_rev, org_rev)]
214 (other_rev, org_rev)]
214 changesets = [other_repo.get_changeset(cs)
215 changesets = [other_repo.get_changeset(cs)
215 for cs in scmutil.revrange(hgrepo, revs)]
216 for cs in scmutil.revrange(hgrepo, revs)]
216
217
217 if org_repo != other_repo:
218 if org_repo != other_repo:
218 ancestors = scmutil.revrange(hgrepo,
219 ancestors = scmutil.revrange(hgrepo,
219 ["ancestor(id('%s'), id('%s'))" % (org_rev, other_rev)])
220 ["ancestor(id('%s'), id('%s'))" % (org_rev, other_rev)])
220 if len(ancestors) == 1:
221 if len(ancestors) == 1:
221 ancestor = hgrepo[ancestors[0]].hex()
222 ancestor = hgrepo[ancestors[0]].hex()
222
223
223 elif alias == 'git':
224 elif alias == 'git':
224 assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos
225 assert org_repo == other_repo, (org_repo, other_repo) # no git support for different repos
225 so, se = org_repo.run_git_command(
226 so, se = org_repo.run_git_command(
226 'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1],
227 'log --reverse --pretty="format: %%H" -s -p %s..%s' % (org_ref[1],
227 other_ref[1])
228 other_ref[1])
228 )
229 )
229 changesets = [org_repo.get_changeset(cs)
230 changesets = [org_repo.get_changeset(cs)
230 for cs in re.findall(r'[0-9a-fA-F]{40}', so)]
231 for cs in re.findall(r'[0-9a-fA-F]{40}', so)]
231
232
232 return changesets, ancestor
233 return changesets, ancestor
233
234
234 def get_compare_data(self, org_repo, org_ref, other_repo, other_ref):
235 def get_compare_data(self, org_repo, org_ref, other_repo, other_ref):
235 """
236 """
236 Returns incoming changesets for mercurial repositories
237 Returns incoming changesets for mercurial repositories
237
238
238 :param org_repo:
239 :param org_repo:
239 :param org_ref:
240 :param org_ref:
240 :param other_repo:
241 :param other_repo:
241 :param other_ref:
242 :param other_ref:
242 """
243 """
243
244
244 if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)):
245 if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)):
245 raise Exception('org_ref must be a two element list/tuple')
246 raise Exception('org_ref must be a two element list/tuple')
246
247
247 if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)):
248 if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)):
248 raise Exception('other_ref must be a two element list/tuple')
249 raise Exception('other_ref must be a two element list/tuple')
249
250
250 org_repo_scm = org_repo.scm_instance
251 org_repo_scm = org_repo.scm_instance
251 other_repo_scm = other_repo.scm_instance
252 other_repo_scm = other_repo.scm_instance
252
253
253 alias = org_repo.scm_instance.alias
254 alias = org_repo.scm_instance.alias
254 cs_ranges, ancestor = self._get_changesets(alias,
255 cs_ranges, ancestor = self._get_changesets(alias,
255 org_repo_scm, org_ref,
256 org_repo_scm, org_ref,
256 other_repo_scm, other_ref)
257 other_repo_scm, other_ref)
257 return cs_ranges, ancestor
258 return cs_ranges, ancestor
@@ -1,41 +1,49
1
1
2 <div class="pullrequests_section_head">${_('Opened by me')}</div>
2 <div class="pullrequests_section_head">${_('Opened by me')}</div>
3 <ul>
3 <ul>
4 %if c.my_pull_requests:
4 %if c.my_pull_requests:
5 %for pull_request in c.my_pull_requests:
5 %for pull_request in c.my_pull_requests:
6 <li>
6 <li>
7 <div style="height: 12px">
7 <div style="height: 12px">
8 <div style="float:left">
8 <div style="float:left">
9 %if pull_request.is_closed():
10 <img src="${h.url('/images/icons/lock_go.png')}" title="${_('Closed')}"/>
11 %endif
12 <img src="${h.url('/images/icons/flag_status_%s.png' % str(pull_request.last_review_status))}" />
9 <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
13 <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
10 ${_('Pull request #%s opened on %s') % (pull_request.pull_request_id, h.fmt_date(pull_request.created_on))}
14 ${_('Pull request #%s opened on %s') % (pull_request.pull_request_id, h.fmt_date(pull_request.created_on))}
11 </a>
15 </a>
12 </div>
16 </div>
13 <div style="float:left;margin-top: -5px">
17 <div style="float:left;margin-top: -5px">
14 ${h.form(url('pullrequest_delete', repo_name=pull_request.other_repo.repo_name, pull_request_id=pull_request.pull_request_id),method='delete')}
18 ${h.form(url('pullrequest_delete', repo_name=pull_request.other_repo.repo_name, pull_request_id=pull_request.pull_request_id),method='delete')}
15 ${h.submit('remove_%s' % pull_request.pull_request_id,'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
19 ${h.submit('remove_%s' % pull_request.pull_request_id,'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
16 ${h.end_form()}
20 ${h.end_form()}
17 </div>
21 </div>
18 </div>
22 </div>
19 </li>
23 </li>
20 %endfor
24 %endfor
21 %else:
25 %else:
22 <li><span class="empty_data">${_('Nothing here yet')}</span></li>
26 <li><span class="empty_data">${_('Nothing here yet')}</span></li>
23 %endif
27 %endif
24 </ul>
28 </ul>
25
29
26 <div class="pullrequests_section_head" style="clear:both">${_('I participate in')}</div>
30 <div class="pullrequests_section_head" style="clear:both">${_('I participate in')}</div>
27 <ul>
31 <ul>
28 %if c.participate_in_pull_requests:
32 %if c.participate_in_pull_requests:
29 %for pull_request in c.participate_in_pull_requests:
33 %for pull_request in c.participate_in_pull_requests:
30 <li>
34 <li>
31 <div style="height: 12px">
35 <div style="height: 12px">
36 %if pull_request.is_closed():
37 <img src="${h.url('/images/icons/lock_go.png')}" title="${_('Closed')}"/>
38 %endif
39 <img src="${h.url('/images/icons/flag_status_%s.png' % str(pull_request.last_review_status))}" />
32 <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
40 <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
33 ${_('Pull request #%s opened by %s on %s') % (pull_request.pull_request_id, pull_request.author.full_name, h.fmt_date(pull_request.created_on))}
41 ${_('Pull request #%s opened by %s on %s') % (pull_request.pull_request_id, pull_request.author.full_name, h.fmt_date(pull_request.created_on))}
34 </a>
42 </a>
35 </div>
43 </div>
36 </li>
44 </li>
37 %endfor
45 %endfor
38 %else:
46 %else:
39 <li><span class="empty_data">${_('Nothing here yet')}</span></li>
47 <li><span class="empty_data">${_('Nothing here yet')}</span></li>
40 %endif
48 %endif
41 </ul>
49 </ul>
General Comments 0
You need to be logged in to leave comments. Login now