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