##// END OF EJS Templates
labs: enable by default. We want people to encourage using those
marcink -
r366:3565e711 default
parent child Browse files
Show More
@@ -1,881 +1,881 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2016 RhodeCode GmbH
3 # Copyright (C) 2013-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 Repositories controller for RhodeCode
23 Repositories controller for RhodeCode
24 """
24 """
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 import formencode
29 import formencode
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, tmpl_context as c, url
31 from pylons import request, tmpl_context as c, url
32 from pylons.controllers.util import redirect
32 from pylons.controllers.util import redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from webob.exc import HTTPForbidden, HTTPNotFound, HTTPBadRequest
34 from webob.exc import HTTPForbidden, HTTPNotFound, HTTPBadRequest
35
35
36 import rhodecode
36 import rhodecode
37 from rhodecode.lib import auth, helpers as h
37 from rhodecode.lib import auth, helpers as h
38 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
39 LoginRequired, HasPermissionAllDecorator,
39 LoginRequired, HasPermissionAllDecorator,
40 HasRepoPermissionAllDecorator, NotAnonymous, HasPermissionAny,
40 HasRepoPermissionAllDecorator, NotAnonymous, HasPermissionAny,
41 HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator)
41 HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator)
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.ext_json import json
44 from rhodecode.lib.exceptions import AttachedForksError
44 from rhodecode.lib.exceptions import AttachedForksError
45 from rhodecode.lib.utils import action_logger, repo_name_slug, jsonify
45 from rhodecode.lib.utils import action_logger, repo_name_slug, jsonify
46 from rhodecode.lib.utils2 import safe_int, str2bool
46 from rhodecode.lib.utils2 import safe_int, str2bool
47 from rhodecode.lib.vcs import RepositoryError
47 from rhodecode.lib.vcs import RepositoryError
48 from rhodecode.model.db import (
48 from rhodecode.model.db import (
49 User, Repository, UserFollowing, RepoGroup, RepositoryField)
49 User, Repository, UserFollowing, RepoGroup, RepositoryField)
50 from rhodecode.model.forms import (
50 from rhodecode.model.forms import (
51 RepoForm, RepoFieldForm, RepoPermsForm, RepoVcsSettingsForm,
51 RepoForm, RepoFieldForm, RepoPermsForm, RepoVcsSettingsForm,
52 IssueTrackerPatternsForm)
52 IssueTrackerPatternsForm)
53 from rhodecode.model.meta import Session
53 from rhodecode.model.meta import Session
54 from rhodecode.model.repo import RepoModel
54 from rhodecode.model.repo import RepoModel
55 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
55 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
56 from rhodecode.model.settings import (
56 from rhodecode.model.settings import (
57 SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel,
57 SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel,
58 SettingNotFound)
58 SettingNotFound)
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 class ReposController(BaseRepoController):
63 class ReposController(BaseRepoController):
64 """
64 """
65 REST Controller styled on the Atom Publishing Protocol"""
65 REST Controller styled on the Atom Publishing Protocol"""
66 # To properly map this controller, ensure your config/routing.py
66 # To properly map this controller, ensure your config/routing.py
67 # file has a resource setup:
67 # file has a resource setup:
68 # map.resource('repo', 'repos')
68 # map.resource('repo', 'repos')
69
69
70 @LoginRequired()
70 @LoginRequired()
71 def __before__(self):
71 def __before__(self):
72 super(ReposController, self).__before__()
72 super(ReposController, self).__before__()
73
73
74 def _load_repo(self, repo_name):
74 def _load_repo(self, repo_name):
75 repo_obj = Repository.get_by_repo_name(repo_name)
75 repo_obj = Repository.get_by_repo_name(repo_name)
76
76
77 if repo_obj is None:
77 if repo_obj is None:
78 h.not_mapped_error(repo_name)
78 h.not_mapped_error(repo_name)
79 return redirect(url('repos'))
79 return redirect(url('repos'))
80
80
81 return repo_obj
81 return repo_obj
82
82
83 def __load_defaults(self, repo=None):
83 def __load_defaults(self, repo=None):
84 acl_groups = RepoGroupList(RepoGroup.query().all(),
84 acl_groups = RepoGroupList(RepoGroup.query().all(),
85 perm_set=['group.write', 'group.admin'])
85 perm_set=['group.write', 'group.admin'])
86 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
86 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
87 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
87 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
88
88
89 # in case someone no longer have a group.write access to a repository
89 # in case someone no longer have a group.write access to a repository
90 # pre fill the list with this entry, we don't care if this is the same
90 # pre fill the list with this entry, we don't care if this is the same
91 # but it will allow saving repo data properly.
91 # but it will allow saving repo data properly.
92
92
93 repo_group = None
93 repo_group = None
94 if repo:
94 if repo:
95 repo_group = repo.group
95 repo_group = repo.group
96 if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices:
96 if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices:
97 c.repo_groups_choices.append(unicode(repo_group.group_id))
97 c.repo_groups_choices.append(unicode(repo_group.group_id))
98 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
98 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
99
99
100 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
100 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
101 c.landing_revs_choices = choices
101 c.landing_revs_choices = choices
102
102
103 def __load_data(self, repo_name=None):
103 def __load_data(self, repo_name=None):
104 """
104 """
105 Load defaults settings for edit, and update
105 Load defaults settings for edit, and update
106
106
107 :param repo_name:
107 :param repo_name:
108 """
108 """
109 c.repo_info = self._load_repo(repo_name)
109 c.repo_info = self._load_repo(repo_name)
110 self.__load_defaults(c.repo_info)
110 self.__load_defaults(c.repo_info)
111
111
112 # override defaults for exact repo info here git/hg etc
112 # override defaults for exact repo info here git/hg etc
113 if not c.repository_requirements_missing:
113 if not c.repository_requirements_missing:
114 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
114 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
115 c.repo_info)
115 c.repo_info)
116 c.landing_revs_choices = choices
116 c.landing_revs_choices = choices
117 defaults = RepoModel()._get_defaults(repo_name)
117 defaults = RepoModel()._get_defaults(repo_name)
118
118
119 return defaults
119 return defaults
120
120
121 def _log_creation_exception(self, e, repo_name):
121 def _log_creation_exception(self, e, repo_name):
122 reason = None
122 reason = None
123 if len(e.args) == 2:
123 if len(e.args) == 2:
124 reason = e.args[1]
124 reason = e.args[1]
125
125
126 if reason == 'INVALID_CERTIFICATE':
126 if reason == 'INVALID_CERTIFICATE':
127 log.exception(
127 log.exception(
128 'Exception creating a repository: invalid certificate')
128 'Exception creating a repository: invalid certificate')
129 msg = (_('Error creating repository %s: invalid certificate')
129 msg = (_('Error creating repository %s: invalid certificate')
130 % repo_name)
130 % repo_name)
131 else:
131 else:
132 log.exception("Exception creating a repository")
132 log.exception("Exception creating a repository")
133 msg = (_('Error creating repository %s')
133 msg = (_('Error creating repository %s')
134 % repo_name)
134 % repo_name)
135
135
136 return msg
136 return msg
137
137
138 @NotAnonymous()
138 @NotAnonymous()
139 def index(self, format='html'):
139 def index(self, format='html'):
140 """GET /repos: All items in the collection"""
140 """GET /repos: All items in the collection"""
141 # url('repos')
141 # url('repos')
142
142
143 repo_list = Repository.get_all_repos()
143 repo_list = Repository.get_all_repos()
144 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
144 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
145 repos_data = RepoModel().get_repos_as_dict(
145 repos_data = RepoModel().get_repos_as_dict(
146 repo_list=c.repo_list, admin=True, super_user_actions=True)
146 repo_list=c.repo_list, admin=True, super_user_actions=True)
147 # json used to render the grid
147 # json used to render the grid
148 c.data = json.dumps(repos_data)
148 c.data = json.dumps(repos_data)
149
149
150 return render('admin/repos/repos.html')
150 return render('admin/repos/repos.html')
151
151
152 # perms check inside
152 # perms check inside
153 @NotAnonymous()
153 @NotAnonymous()
154 @auth.CSRFRequired()
154 @auth.CSRFRequired()
155 def create(self):
155 def create(self):
156 """
156 """
157 POST /repos: Create a new item"""
157 POST /repos: Create a new item"""
158 # url('repos')
158 # url('repos')
159
159
160 self.__load_defaults()
160 self.__load_defaults()
161 form_result = {}
161 form_result = {}
162 task_id = None
162 task_id = None
163 try:
163 try:
164 # CanWriteToGroup validators checks permissions of this POST
164 # CanWriteToGroup validators checks permissions of this POST
165 form_result = RepoForm(repo_groups=c.repo_groups_choices,
165 form_result = RepoForm(repo_groups=c.repo_groups_choices,
166 landing_revs=c.landing_revs_choices)()\
166 landing_revs=c.landing_revs_choices)()\
167 .to_python(dict(request.POST))
167 .to_python(dict(request.POST))
168
168
169 # create is done sometimes async on celery, db transaction
169 # create is done sometimes async on celery, db transaction
170 # management is handled there.
170 # management is handled there.
171 task = RepoModel().create(form_result, c.rhodecode_user.user_id)
171 task = RepoModel().create(form_result, c.rhodecode_user.user_id)
172 from celery.result import BaseAsyncResult
172 from celery.result import BaseAsyncResult
173 if isinstance(task, BaseAsyncResult):
173 if isinstance(task, BaseAsyncResult):
174 task_id = task.task_id
174 task_id = task.task_id
175 except formencode.Invalid as errors:
175 except formencode.Invalid as errors:
176 c.personal_repo_group = RepoGroup.get_by_group_name(
176 c.personal_repo_group = RepoGroup.get_by_group_name(
177 c.rhodecode_user.username)
177 c.rhodecode_user.username)
178 return htmlfill.render(
178 return htmlfill.render(
179 render('admin/repos/repo_add.html'),
179 render('admin/repos/repo_add.html'),
180 defaults=errors.value,
180 defaults=errors.value,
181 errors=errors.error_dict or {},
181 errors=errors.error_dict or {},
182 prefix_error=False,
182 prefix_error=False,
183 encoding="UTF-8",
183 encoding="UTF-8",
184 force_defaults=False)
184 force_defaults=False)
185
185
186 except Exception as e:
186 except Exception as e:
187 msg = self._log_creation_exception(e, form_result.get('repo_name'))
187 msg = self._log_creation_exception(e, form_result.get('repo_name'))
188 h.flash(msg, category='error')
188 h.flash(msg, category='error')
189 return redirect(url('home'))
189 return redirect(url('home'))
190
190
191 return redirect(h.url('repo_creating_home',
191 return redirect(h.url('repo_creating_home',
192 repo_name=form_result['repo_name_full'],
192 repo_name=form_result['repo_name_full'],
193 task_id=task_id))
193 task_id=task_id))
194
194
195 # perms check inside
195 # perms check inside
196 @NotAnonymous()
196 @NotAnonymous()
197 def create_repository(self):
197 def create_repository(self):
198 """GET /_admin/create_repository: Form to create a new item"""
198 """GET /_admin/create_repository: Form to create a new item"""
199 new_repo = request.GET.get('repo', '')
199 new_repo = request.GET.get('repo', '')
200 parent_group = request.GET.get('parent_group')
200 parent_group = request.GET.get('parent_group')
201 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
201 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
202 # you're not super admin nor have global create permissions,
202 # you're not super admin nor have global create permissions,
203 # but maybe you have at least write permission to a parent group ?
203 # but maybe you have at least write permission to a parent group ?
204 _gr = RepoGroup.get(parent_group)
204 _gr = RepoGroup.get(parent_group)
205 gr_name = _gr.group_name if _gr else None
205 gr_name = _gr.group_name if _gr else None
206 # create repositories with write permission on group is set to true
206 # create repositories with write permission on group is set to true
207 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
207 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
208 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
208 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
209 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
209 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
210 if not (group_admin or (group_write and create_on_write)):
210 if not (group_admin or (group_write and create_on_write)):
211 raise HTTPForbidden
211 raise HTTPForbidden
212
212
213 acl_groups = RepoGroupList(RepoGroup.query().all(),
213 acl_groups = RepoGroupList(RepoGroup.query().all(),
214 perm_set=['group.write', 'group.admin'])
214 perm_set=['group.write', 'group.admin'])
215 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
215 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
216 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
216 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
217 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
217 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
218 c.personal_repo_group = RepoGroup.get_by_group_name(c.rhodecode_user.username)
218 c.personal_repo_group = RepoGroup.get_by_group_name(c.rhodecode_user.username)
219 c.new_repo = repo_name_slug(new_repo)
219 c.new_repo = repo_name_slug(new_repo)
220
220
221 ## apply the defaults from defaults page
221 ## apply the defaults from defaults page
222 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
222 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
223 # set checkbox to autochecked
223 # set checkbox to autochecked
224 defaults['repo_copy_permissions'] = True
224 defaults['repo_copy_permissions'] = True
225 if parent_group:
225 if parent_group:
226 defaults.update({'repo_group': parent_group})
226 defaults.update({'repo_group': parent_group})
227
227
228 return htmlfill.render(
228 return htmlfill.render(
229 render('admin/repos/repo_add.html'),
229 render('admin/repos/repo_add.html'),
230 defaults=defaults,
230 defaults=defaults,
231 errors={},
231 errors={},
232 prefix_error=False,
232 prefix_error=False,
233 encoding="UTF-8",
233 encoding="UTF-8",
234 force_defaults=False
234 force_defaults=False
235 )
235 )
236
236
237 @NotAnonymous()
237 @NotAnonymous()
238 def repo_creating(self, repo_name):
238 def repo_creating(self, repo_name):
239 c.repo = repo_name
239 c.repo = repo_name
240 c.task_id = request.GET.get('task_id')
240 c.task_id = request.GET.get('task_id')
241 if not c.repo:
241 if not c.repo:
242 raise HTTPNotFound()
242 raise HTTPNotFound()
243 return render('admin/repos/repo_creating.html')
243 return render('admin/repos/repo_creating.html')
244
244
245 @NotAnonymous()
245 @NotAnonymous()
246 @jsonify
246 @jsonify
247 def repo_check(self, repo_name):
247 def repo_check(self, repo_name):
248 c.repo = repo_name
248 c.repo = repo_name
249 task_id = request.GET.get('task_id')
249 task_id = request.GET.get('task_id')
250
250
251 if task_id and task_id not in ['None']:
251 if task_id and task_id not in ['None']:
252 import rhodecode
252 import rhodecode
253 from celery.result import AsyncResult
253 from celery.result import AsyncResult
254 if rhodecode.CELERY_ENABLED:
254 if rhodecode.CELERY_ENABLED:
255 task = AsyncResult(task_id)
255 task = AsyncResult(task_id)
256 if task.failed():
256 if task.failed():
257 msg = self._log_creation_exception(task.result, c.repo)
257 msg = self._log_creation_exception(task.result, c.repo)
258 h.flash(msg, category='error')
258 h.flash(msg, category='error')
259 return redirect(url('home'), code=501)
259 return redirect(url('home'), code=501)
260
260
261 repo = Repository.get_by_repo_name(repo_name)
261 repo = Repository.get_by_repo_name(repo_name)
262 if repo and repo.repo_state == Repository.STATE_CREATED:
262 if repo and repo.repo_state == Repository.STATE_CREATED:
263 if repo.clone_uri:
263 if repo.clone_uri:
264 clone_uri = repo.clone_uri_hidden
264 clone_uri = repo.clone_uri_hidden
265 h.flash(_('Created repository %s from %s')
265 h.flash(_('Created repository %s from %s')
266 % (repo.repo_name, clone_uri), category='success')
266 % (repo.repo_name, clone_uri), category='success')
267 else:
267 else:
268 repo_url = h.link_to(repo.repo_name,
268 repo_url = h.link_to(repo.repo_name,
269 h.url('summary_home',
269 h.url('summary_home',
270 repo_name=repo.repo_name))
270 repo_name=repo.repo_name))
271 fork = repo.fork
271 fork = repo.fork
272 if fork:
272 if fork:
273 fork_name = fork.repo_name
273 fork_name = fork.repo_name
274 h.flash(h.literal(_('Forked repository %s as %s')
274 h.flash(h.literal(_('Forked repository %s as %s')
275 % (fork_name, repo_url)), category='success')
275 % (fork_name, repo_url)), category='success')
276 else:
276 else:
277 h.flash(h.literal(_('Created repository %s') % repo_url),
277 h.flash(h.literal(_('Created repository %s') % repo_url),
278 category='success')
278 category='success')
279 return {'result': True}
279 return {'result': True}
280 return {'result': False}
280 return {'result': False}
281
281
282 @HasRepoPermissionAllDecorator('repository.admin')
282 @HasRepoPermissionAllDecorator('repository.admin')
283 @auth.CSRFRequired()
283 @auth.CSRFRequired()
284 def update(self, repo_name):
284 def update(self, repo_name):
285 """
285 """
286 PUT /repos/repo_name: Update an existing item"""
286 PUT /repos/repo_name: Update an existing item"""
287 # Forms posted to this method should contain a hidden field:
287 # Forms posted to this method should contain a hidden field:
288 # <input type="hidden" name="_method" value="PUT" />
288 # <input type="hidden" name="_method" value="PUT" />
289 # Or using helpers:
289 # Or using helpers:
290 # h.form(url('repo', repo_name=ID),
290 # h.form(url('repo', repo_name=ID),
291 # method='put')
291 # method='put')
292 # url('repo', repo_name=ID)
292 # url('repo', repo_name=ID)
293
293
294 self.__load_data(repo_name)
294 self.__load_data(repo_name)
295 c.active = 'settings'
295 c.active = 'settings'
296 c.repo_fields = RepositoryField.query()\
296 c.repo_fields = RepositoryField.query()\
297 .filter(RepositoryField.repository == c.repo_info).all()
297 .filter(RepositoryField.repository == c.repo_info).all()
298
298
299 repo_model = RepoModel()
299 repo_model = RepoModel()
300 changed_name = repo_name
300 changed_name = repo_name
301
301
302 # override the choices with extracted revisions !
302 # override the choices with extracted revisions !
303 c.personal_repo_group = RepoGroup.get_by_group_name(
303 c.personal_repo_group = RepoGroup.get_by_group_name(
304 c.rhodecode_user.username)
304 c.rhodecode_user.username)
305 repo = Repository.get_by_repo_name(repo_name)
305 repo = Repository.get_by_repo_name(repo_name)
306 old_data = {
306 old_data = {
307 'repo_name': repo_name,
307 'repo_name': repo_name,
308 'repo_group': repo.group.get_dict() if repo.group else {},
308 'repo_group': repo.group.get_dict() if repo.group else {},
309 'repo_type': repo.repo_type,
309 'repo_type': repo.repo_type,
310 }
310 }
311 _form = RepoForm(
311 _form = RepoForm(
312 edit=True, old_data=old_data, repo_groups=c.repo_groups_choices,
312 edit=True, old_data=old_data, repo_groups=c.repo_groups_choices,
313 landing_revs=c.landing_revs_choices, allow_disabled=True)()
313 landing_revs=c.landing_revs_choices, allow_disabled=True)()
314
314
315 try:
315 try:
316 form_result = _form.to_python(dict(request.POST))
316 form_result = _form.to_python(dict(request.POST))
317 repo = repo_model.update(repo_name, **form_result)
317 repo = repo_model.update(repo_name, **form_result)
318 ScmModel().mark_for_invalidation(repo_name)
318 ScmModel().mark_for_invalidation(repo_name)
319 h.flash(_('Repository %s updated successfully') % repo_name,
319 h.flash(_('Repository %s updated successfully') % repo_name,
320 category='success')
320 category='success')
321 changed_name = repo.repo_name
321 changed_name = repo.repo_name
322 action_logger(c.rhodecode_user, 'admin_updated_repo',
322 action_logger(c.rhodecode_user, 'admin_updated_repo',
323 changed_name, self.ip_addr, self.sa)
323 changed_name, self.ip_addr, self.sa)
324 Session().commit()
324 Session().commit()
325 except formencode.Invalid as errors:
325 except formencode.Invalid as errors:
326 defaults = self.__load_data(repo_name)
326 defaults = self.__load_data(repo_name)
327 defaults.update(errors.value)
327 defaults.update(errors.value)
328 return htmlfill.render(
328 return htmlfill.render(
329 render('admin/repos/repo_edit.html'),
329 render('admin/repos/repo_edit.html'),
330 defaults=defaults,
330 defaults=defaults,
331 errors=errors.error_dict or {},
331 errors=errors.error_dict or {},
332 prefix_error=False,
332 prefix_error=False,
333 encoding="UTF-8",
333 encoding="UTF-8",
334 force_defaults=False)
334 force_defaults=False)
335
335
336 except Exception:
336 except Exception:
337 log.exception("Exception during update of repository")
337 log.exception("Exception during update of repository")
338 h.flash(_('Error occurred during update of repository %s') \
338 h.flash(_('Error occurred during update of repository %s') \
339 % repo_name, category='error')
339 % repo_name, category='error')
340 return redirect(url('edit_repo', repo_name=changed_name))
340 return redirect(url('edit_repo', repo_name=changed_name))
341
341
342 @HasRepoPermissionAllDecorator('repository.admin')
342 @HasRepoPermissionAllDecorator('repository.admin')
343 @auth.CSRFRequired()
343 @auth.CSRFRequired()
344 def delete(self, repo_name):
344 def delete(self, repo_name):
345 """
345 """
346 DELETE /repos/repo_name: Delete an existing item"""
346 DELETE /repos/repo_name: Delete an existing item"""
347 # Forms posted to this method should contain a hidden field:
347 # Forms posted to this method should contain a hidden field:
348 # <input type="hidden" name="_method" value="DELETE" />
348 # <input type="hidden" name="_method" value="DELETE" />
349 # Or using helpers:
349 # Or using helpers:
350 # h.form(url('repo', repo_name=ID),
350 # h.form(url('repo', repo_name=ID),
351 # method='delete')
351 # method='delete')
352 # url('repo', repo_name=ID)
352 # url('repo', repo_name=ID)
353
353
354 repo_model = RepoModel()
354 repo_model = RepoModel()
355 repo = repo_model.get_by_repo_name(repo_name)
355 repo = repo_model.get_by_repo_name(repo_name)
356 if not repo:
356 if not repo:
357 h.not_mapped_error(repo_name)
357 h.not_mapped_error(repo_name)
358 return redirect(url('repos'))
358 return redirect(url('repos'))
359 try:
359 try:
360 _forks = repo.forks.count()
360 _forks = repo.forks.count()
361 handle_forks = None
361 handle_forks = None
362 if _forks and request.POST.get('forks'):
362 if _forks and request.POST.get('forks'):
363 do = request.POST['forks']
363 do = request.POST['forks']
364 if do == 'detach_forks':
364 if do == 'detach_forks':
365 handle_forks = 'detach'
365 handle_forks = 'detach'
366 h.flash(_('Detached %s forks') % _forks, category='success')
366 h.flash(_('Detached %s forks') % _forks, category='success')
367 elif do == 'delete_forks':
367 elif do == 'delete_forks':
368 handle_forks = 'delete'
368 handle_forks = 'delete'
369 h.flash(_('Deleted %s forks') % _forks, category='success')
369 h.flash(_('Deleted %s forks') % _forks, category='success')
370 repo_model.delete(repo, forks=handle_forks)
370 repo_model.delete(repo, forks=handle_forks)
371 action_logger(c.rhodecode_user, 'admin_deleted_repo',
371 action_logger(c.rhodecode_user, 'admin_deleted_repo',
372 repo_name, self.ip_addr, self.sa)
372 repo_name, self.ip_addr, self.sa)
373 ScmModel().mark_for_invalidation(repo_name)
373 ScmModel().mark_for_invalidation(repo_name)
374 h.flash(_('Deleted repository %s') % repo_name, category='success')
374 h.flash(_('Deleted repository %s') % repo_name, category='success')
375 Session().commit()
375 Session().commit()
376 except AttachedForksError:
376 except AttachedForksError:
377 h.flash(_('Cannot delete %s it still contains attached forks')
377 h.flash(_('Cannot delete %s it still contains attached forks')
378 % repo_name, category='warning')
378 % repo_name, category='warning')
379
379
380 except Exception:
380 except Exception:
381 log.exception("Exception during deletion of repository")
381 log.exception("Exception during deletion of repository")
382 h.flash(_('An error occurred during deletion of %s') % repo_name,
382 h.flash(_('An error occurred during deletion of %s') % repo_name,
383 category='error')
383 category='error')
384
384
385 return redirect(url('repos'))
385 return redirect(url('repos'))
386
386
387 @HasPermissionAllDecorator('hg.admin')
387 @HasPermissionAllDecorator('hg.admin')
388 def show(self, repo_name, format='html'):
388 def show(self, repo_name, format='html'):
389 """GET /repos/repo_name: Show a specific item"""
389 """GET /repos/repo_name: Show a specific item"""
390 # url('repo', repo_name=ID)
390 # url('repo', repo_name=ID)
391
391
392 @HasRepoPermissionAllDecorator('repository.admin')
392 @HasRepoPermissionAllDecorator('repository.admin')
393 def edit(self, repo_name):
393 def edit(self, repo_name):
394 """GET /repo_name/settings: Form to edit an existing item"""
394 """GET /repo_name/settings: Form to edit an existing item"""
395 # url('edit_repo', repo_name=ID)
395 # url('edit_repo', repo_name=ID)
396 defaults = self.__load_data(repo_name)
396 defaults = self.__load_data(repo_name)
397 if 'clone_uri' in defaults:
397 if 'clone_uri' in defaults:
398 del defaults['clone_uri']
398 del defaults['clone_uri']
399
399
400 c.repo_fields = RepositoryField.query()\
400 c.repo_fields = RepositoryField.query()\
401 .filter(RepositoryField.repository == c.repo_info).all()
401 .filter(RepositoryField.repository == c.repo_info).all()
402 c.personal_repo_group = RepoGroup.get_by_group_name(
402 c.personal_repo_group = RepoGroup.get_by_group_name(
403 c.rhodecode_user.username)
403 c.rhodecode_user.username)
404 c.active = 'settings'
404 c.active = 'settings'
405 return htmlfill.render(
405 return htmlfill.render(
406 render('admin/repos/repo_edit.html'),
406 render('admin/repos/repo_edit.html'),
407 defaults=defaults,
407 defaults=defaults,
408 encoding="UTF-8",
408 encoding="UTF-8",
409 force_defaults=False)
409 force_defaults=False)
410
410
411 @HasRepoPermissionAllDecorator('repository.admin')
411 @HasRepoPermissionAllDecorator('repository.admin')
412 def edit_permissions(self, repo_name):
412 def edit_permissions(self, repo_name):
413 """GET /repo_name/settings: Form to edit an existing item"""
413 """GET /repo_name/settings: Form to edit an existing item"""
414 # url('edit_repo', repo_name=ID)
414 # url('edit_repo', repo_name=ID)
415 c.repo_info = self._load_repo(repo_name)
415 c.repo_info = self._load_repo(repo_name)
416 c.active = 'permissions'
416 c.active = 'permissions'
417 defaults = RepoModel()._get_defaults(repo_name)
417 defaults = RepoModel()._get_defaults(repo_name)
418
418
419 return htmlfill.render(
419 return htmlfill.render(
420 render('admin/repos/repo_edit.html'),
420 render('admin/repos/repo_edit.html'),
421 defaults=defaults,
421 defaults=defaults,
422 encoding="UTF-8",
422 encoding="UTF-8",
423 force_defaults=False)
423 force_defaults=False)
424
424
425 @HasRepoPermissionAllDecorator('repository.admin')
425 @HasRepoPermissionAllDecorator('repository.admin')
426 @auth.CSRFRequired()
426 @auth.CSRFRequired()
427 def edit_permissions_update(self, repo_name):
427 def edit_permissions_update(self, repo_name):
428 form = RepoPermsForm()().to_python(request.POST)
428 form = RepoPermsForm()().to_python(request.POST)
429 RepoModel().update_permissions(repo_name,
429 RepoModel().update_permissions(repo_name,
430 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
430 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
431
431
432 #TODO: implement this
432 #TODO: implement this
433 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
433 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
434 # repo_name, self.ip_addr, self.sa)
434 # repo_name, self.ip_addr, self.sa)
435 Session().commit()
435 Session().commit()
436 h.flash(_('Repository permissions updated'), category='success')
436 h.flash(_('Repository permissions updated'), category='success')
437 return redirect(url('edit_repo_perms', repo_name=repo_name))
437 return redirect(url('edit_repo_perms', repo_name=repo_name))
438
438
439 @HasRepoPermissionAllDecorator('repository.admin')
439 @HasRepoPermissionAllDecorator('repository.admin')
440 def edit_fields(self, repo_name):
440 def edit_fields(self, repo_name):
441 """GET /repo_name/settings: Form to edit an existing item"""
441 """GET /repo_name/settings: Form to edit an existing item"""
442 # url('edit_repo', repo_name=ID)
442 # url('edit_repo', repo_name=ID)
443 c.repo_info = self._load_repo(repo_name)
443 c.repo_info = self._load_repo(repo_name)
444 c.repo_fields = RepositoryField.query()\
444 c.repo_fields = RepositoryField.query()\
445 .filter(RepositoryField.repository == c.repo_info).all()
445 .filter(RepositoryField.repository == c.repo_info).all()
446 c.active = 'fields'
446 c.active = 'fields'
447 if request.POST:
447 if request.POST:
448
448
449 return redirect(url('repo_edit_fields'))
449 return redirect(url('repo_edit_fields'))
450 return render('admin/repos/repo_edit.html')
450 return render('admin/repos/repo_edit.html')
451
451
452 @HasRepoPermissionAllDecorator('repository.admin')
452 @HasRepoPermissionAllDecorator('repository.admin')
453 @auth.CSRFRequired()
453 @auth.CSRFRequired()
454 def create_repo_field(self, repo_name):
454 def create_repo_field(self, repo_name):
455 try:
455 try:
456 form_result = RepoFieldForm()().to_python(dict(request.POST))
456 form_result = RepoFieldForm()().to_python(dict(request.POST))
457 RepoModel().add_repo_field(
457 RepoModel().add_repo_field(
458 repo_name, form_result['new_field_key'],
458 repo_name, form_result['new_field_key'],
459 field_type=form_result['new_field_type'],
459 field_type=form_result['new_field_type'],
460 field_value=form_result['new_field_value'],
460 field_value=form_result['new_field_value'],
461 field_label=form_result['new_field_label'],
461 field_label=form_result['new_field_label'],
462 field_desc=form_result['new_field_desc'])
462 field_desc=form_result['new_field_desc'])
463
463
464 Session().commit()
464 Session().commit()
465 except Exception as e:
465 except Exception as e:
466 log.exception("Exception creating field")
466 log.exception("Exception creating field")
467 msg = _('An error occurred during creation of field')
467 msg = _('An error occurred during creation of field')
468 if isinstance(e, formencode.Invalid):
468 if isinstance(e, formencode.Invalid):
469 msg += ". " + e.msg
469 msg += ". " + e.msg
470 h.flash(msg, category='error')
470 h.flash(msg, category='error')
471 return redirect(url('edit_repo_fields', repo_name=repo_name))
471 return redirect(url('edit_repo_fields', repo_name=repo_name))
472
472
473 @HasRepoPermissionAllDecorator('repository.admin')
473 @HasRepoPermissionAllDecorator('repository.admin')
474 @auth.CSRFRequired()
474 @auth.CSRFRequired()
475 def delete_repo_field(self, repo_name, field_id):
475 def delete_repo_field(self, repo_name, field_id):
476 field = RepositoryField.get_or_404(field_id)
476 field = RepositoryField.get_or_404(field_id)
477 try:
477 try:
478 RepoModel().delete_repo_field(repo_name, field.field_key)
478 RepoModel().delete_repo_field(repo_name, field.field_key)
479 Session().commit()
479 Session().commit()
480 except Exception as e:
480 except Exception as e:
481 log.exception("Exception during removal of field")
481 log.exception("Exception during removal of field")
482 msg = _('An error occurred during removal of field')
482 msg = _('An error occurred during removal of field')
483 h.flash(msg, category='error')
483 h.flash(msg, category='error')
484 return redirect(url('edit_repo_fields', repo_name=repo_name))
484 return redirect(url('edit_repo_fields', repo_name=repo_name))
485
485
486 @HasRepoPermissionAllDecorator('repository.admin')
486 @HasRepoPermissionAllDecorator('repository.admin')
487 def edit_advanced(self, repo_name):
487 def edit_advanced(self, repo_name):
488 """GET /repo_name/settings: Form to edit an existing item"""
488 """GET /repo_name/settings: Form to edit an existing item"""
489 # url('edit_repo', repo_name=ID)
489 # url('edit_repo', repo_name=ID)
490 c.repo_info = self._load_repo(repo_name)
490 c.repo_info = self._load_repo(repo_name)
491 c.default_user_id = User.get_default_user().user_id
491 c.default_user_id = User.get_default_user().user_id
492 c.in_public_journal = UserFollowing.query()\
492 c.in_public_journal = UserFollowing.query()\
493 .filter(UserFollowing.user_id == c.default_user_id)\
493 .filter(UserFollowing.user_id == c.default_user_id)\
494 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
494 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
495
495
496 c.active = 'advanced'
496 c.active = 'advanced'
497 c.has_origin_repo_read_perm = False
497 c.has_origin_repo_read_perm = False
498 if c.repo_info.fork:
498 if c.repo_info.fork:
499 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
499 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
500 'repository.write', 'repository.read', 'repository.admin')(
500 'repository.write', 'repository.read', 'repository.admin')(
501 c.repo_info.fork.repo_name, 'repo set as fork page')
501 c.repo_info.fork.repo_name, 'repo set as fork page')
502
502
503 if request.POST:
503 if request.POST:
504 return redirect(url('repo_edit_advanced'))
504 return redirect(url('repo_edit_advanced'))
505 return render('admin/repos/repo_edit.html')
505 return render('admin/repos/repo_edit.html')
506
506
507 @HasRepoPermissionAllDecorator('repository.admin')
507 @HasRepoPermissionAllDecorator('repository.admin')
508 @auth.CSRFRequired()
508 @auth.CSRFRequired()
509 def edit_advanced_journal(self, repo_name):
509 def edit_advanced_journal(self, repo_name):
510 """
510 """
511 Set's this repository to be visible in public journal,
511 Set's this repository to be visible in public journal,
512 in other words assing default user to follow this repo
512 in other words assing default user to follow this repo
513
513
514 :param repo_name:
514 :param repo_name:
515 """
515 """
516
516
517 try:
517 try:
518 repo_id = Repository.get_by_repo_name(repo_name).repo_id
518 repo_id = Repository.get_by_repo_name(repo_name).repo_id
519 user_id = User.get_default_user().user_id
519 user_id = User.get_default_user().user_id
520 self.scm_model.toggle_following_repo(repo_id, user_id)
520 self.scm_model.toggle_following_repo(repo_id, user_id)
521 h.flash(_('Updated repository visibility in public journal'),
521 h.flash(_('Updated repository visibility in public journal'),
522 category='success')
522 category='success')
523 Session().commit()
523 Session().commit()
524 except Exception:
524 except Exception:
525 h.flash(_('An error occurred during setting this'
525 h.flash(_('An error occurred during setting this'
526 ' repository in public journal'),
526 ' repository in public journal'),
527 category='error')
527 category='error')
528
528
529 return redirect(url('edit_repo_advanced', repo_name=repo_name))
529 return redirect(url('edit_repo_advanced', repo_name=repo_name))
530
530
531 @HasRepoPermissionAllDecorator('repository.admin')
531 @HasRepoPermissionAllDecorator('repository.admin')
532 @auth.CSRFRequired()
532 @auth.CSRFRequired()
533 def edit_advanced_fork(self, repo_name):
533 def edit_advanced_fork(self, repo_name):
534 """
534 """
535 Mark given repository as a fork of another
535 Mark given repository as a fork of another
536
536
537 :param repo_name:
537 :param repo_name:
538 """
538 """
539
539
540 new_fork_id = request.POST.get('id_fork_of')
540 new_fork_id = request.POST.get('id_fork_of')
541 try:
541 try:
542
542
543 if new_fork_id and not new_fork_id.isdigit():
543 if new_fork_id and not new_fork_id.isdigit():
544 log.error('Given fork id %s is not an INT', new_fork_id)
544 log.error('Given fork id %s is not an INT', new_fork_id)
545
545
546 fork_id = safe_int(new_fork_id)
546 fork_id = safe_int(new_fork_id)
547 repo = ScmModel().mark_as_fork(repo_name, fork_id,
547 repo = ScmModel().mark_as_fork(repo_name, fork_id,
548 c.rhodecode_user.username)
548 c.rhodecode_user.username)
549 fork = repo.fork.repo_name if repo.fork else _('Nothing')
549 fork = repo.fork.repo_name if repo.fork else _('Nothing')
550 Session().commit()
550 Session().commit()
551 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
551 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
552 category='success')
552 category='success')
553 except RepositoryError as e:
553 except RepositoryError as e:
554 log.exception("Repository Error occurred")
554 log.exception("Repository Error occurred")
555 h.flash(str(e), category='error')
555 h.flash(str(e), category='error')
556 except Exception as e:
556 except Exception as e:
557 log.exception("Exception while editing fork")
557 log.exception("Exception while editing fork")
558 h.flash(_('An error occurred during this operation'),
558 h.flash(_('An error occurred during this operation'),
559 category='error')
559 category='error')
560
560
561 return redirect(url('edit_repo_advanced', repo_name=repo_name))
561 return redirect(url('edit_repo_advanced', repo_name=repo_name))
562
562
563 @HasRepoPermissionAllDecorator('repository.admin')
563 @HasRepoPermissionAllDecorator('repository.admin')
564 @auth.CSRFRequired()
564 @auth.CSRFRequired()
565 def edit_advanced_locking(self, repo_name):
565 def edit_advanced_locking(self, repo_name):
566 """
566 """
567 Unlock repository when it is locked !
567 Unlock repository when it is locked !
568
568
569 :param repo_name:
569 :param repo_name:
570 """
570 """
571 try:
571 try:
572 repo = Repository.get_by_repo_name(repo_name)
572 repo = Repository.get_by_repo_name(repo_name)
573 if request.POST.get('set_lock'):
573 if request.POST.get('set_lock'):
574 Repository.lock(repo, c.rhodecode_user.user_id,
574 Repository.lock(repo, c.rhodecode_user.user_id,
575 lock_reason=Repository.LOCK_WEB)
575 lock_reason=Repository.LOCK_WEB)
576 h.flash(_('Locked repository'), category='success')
576 h.flash(_('Locked repository'), category='success')
577 elif request.POST.get('set_unlock'):
577 elif request.POST.get('set_unlock'):
578 Repository.unlock(repo)
578 Repository.unlock(repo)
579 h.flash(_('Unlocked repository'), category='success')
579 h.flash(_('Unlocked repository'), category='success')
580 except Exception as e:
580 except Exception as e:
581 log.exception("Exception during unlocking")
581 log.exception("Exception during unlocking")
582 h.flash(_('An error occurred during unlocking'),
582 h.flash(_('An error occurred during unlocking'),
583 category='error')
583 category='error')
584 return redirect(url('edit_repo_advanced', repo_name=repo_name))
584 return redirect(url('edit_repo_advanced', repo_name=repo_name))
585
585
586 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
586 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
587 @auth.CSRFRequired()
587 @auth.CSRFRequired()
588 def toggle_locking(self, repo_name):
588 def toggle_locking(self, repo_name):
589 """
589 """
590 Toggle locking of repository by simple GET call to url
590 Toggle locking of repository by simple GET call to url
591
591
592 :param repo_name:
592 :param repo_name:
593 """
593 """
594
594
595 try:
595 try:
596 repo = Repository.get_by_repo_name(repo_name)
596 repo = Repository.get_by_repo_name(repo_name)
597
597
598 if repo.enable_locking:
598 if repo.enable_locking:
599 if repo.locked[0]:
599 if repo.locked[0]:
600 Repository.unlock(repo)
600 Repository.unlock(repo)
601 action = _('Unlocked')
601 action = _('Unlocked')
602 else:
602 else:
603 Repository.lock(repo, c.rhodecode_user.user_id,
603 Repository.lock(repo, c.rhodecode_user.user_id,
604 lock_reason=Repository.LOCK_WEB)
604 lock_reason=Repository.LOCK_WEB)
605 action = _('Locked')
605 action = _('Locked')
606
606
607 h.flash(_('Repository has been %s') % action,
607 h.flash(_('Repository has been %s') % action,
608 category='success')
608 category='success')
609 except Exception:
609 except Exception:
610 log.exception("Exception during unlocking")
610 log.exception("Exception during unlocking")
611 h.flash(_('An error occurred during unlocking'),
611 h.flash(_('An error occurred during unlocking'),
612 category='error')
612 category='error')
613 return redirect(url('summary_home', repo_name=repo_name))
613 return redirect(url('summary_home', repo_name=repo_name))
614
614
615 @HasRepoPermissionAllDecorator('repository.admin')
615 @HasRepoPermissionAllDecorator('repository.admin')
616 @auth.CSRFRequired()
616 @auth.CSRFRequired()
617 def edit_caches(self, repo_name):
617 def edit_caches(self, repo_name):
618 """PUT /{repo_name}/settings/caches: invalidate the repo caches."""
618 """PUT /{repo_name}/settings/caches: invalidate the repo caches."""
619 try:
619 try:
620 ScmModel().mark_for_invalidation(repo_name, delete=True)
620 ScmModel().mark_for_invalidation(repo_name, delete=True)
621 Session().commit()
621 Session().commit()
622 h.flash(_('Cache invalidation successful'),
622 h.flash(_('Cache invalidation successful'),
623 category='success')
623 category='success')
624 except Exception:
624 except Exception:
625 log.exception("Exception during cache invalidation")
625 log.exception("Exception during cache invalidation")
626 h.flash(_('An error occurred during cache invalidation'),
626 h.flash(_('An error occurred during cache invalidation'),
627 category='error')
627 category='error')
628
628
629 return redirect(url('edit_repo_caches', repo_name=c.repo_name))
629 return redirect(url('edit_repo_caches', repo_name=c.repo_name))
630
630
631 @HasRepoPermissionAllDecorator('repository.admin')
631 @HasRepoPermissionAllDecorator('repository.admin')
632 def edit_caches_form(self, repo_name):
632 def edit_caches_form(self, repo_name):
633 """GET /repo_name/settings: Form to edit an existing item"""
633 """GET /repo_name/settings: Form to edit an existing item"""
634 # url('edit_repo', repo_name=ID)
634 # url('edit_repo', repo_name=ID)
635 c.repo_info = self._load_repo(repo_name)
635 c.repo_info = self._load_repo(repo_name)
636 c.active = 'caches'
636 c.active = 'caches'
637
637
638 return render('admin/repos/repo_edit.html')
638 return render('admin/repos/repo_edit.html')
639
639
640 @HasRepoPermissionAllDecorator('repository.admin')
640 @HasRepoPermissionAllDecorator('repository.admin')
641 @auth.CSRFRequired()
641 @auth.CSRFRequired()
642 def edit_remote(self, repo_name):
642 def edit_remote(self, repo_name):
643 """PUT /{repo_name}/settings/remote: edit the repo remote."""
643 """PUT /{repo_name}/settings/remote: edit the repo remote."""
644 try:
644 try:
645 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
645 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
646 h.flash(_('Pulled from remote location'), category='success')
646 h.flash(_('Pulled from remote location'), category='success')
647 except Exception:
647 except Exception:
648 log.exception("Exception during pull from remote")
648 log.exception("Exception during pull from remote")
649 h.flash(_('An error occurred during pull from remote location'),
649 h.flash(_('An error occurred during pull from remote location'),
650 category='error')
650 category='error')
651 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
651 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
652
652
653 @HasRepoPermissionAllDecorator('repository.admin')
653 @HasRepoPermissionAllDecorator('repository.admin')
654 def edit_remote_form(self, repo_name):
654 def edit_remote_form(self, repo_name):
655 """GET /repo_name/settings: Form to edit an existing item"""
655 """GET /repo_name/settings: Form to edit an existing item"""
656 # url('edit_repo', repo_name=ID)
656 # url('edit_repo', repo_name=ID)
657 c.repo_info = self._load_repo(repo_name)
657 c.repo_info = self._load_repo(repo_name)
658 c.active = 'remote'
658 c.active = 'remote'
659
659
660 return render('admin/repos/repo_edit.html')
660 return render('admin/repos/repo_edit.html')
661
661
662 @HasRepoPermissionAllDecorator('repository.admin')
662 @HasRepoPermissionAllDecorator('repository.admin')
663 @auth.CSRFRequired()
663 @auth.CSRFRequired()
664 def edit_statistics(self, repo_name):
664 def edit_statistics(self, repo_name):
665 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
665 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
666 try:
666 try:
667 RepoModel().delete_stats(repo_name)
667 RepoModel().delete_stats(repo_name)
668 Session().commit()
668 Session().commit()
669 except Exception as e:
669 except Exception as e:
670 log.error(traceback.format_exc())
670 log.error(traceback.format_exc())
671 h.flash(_('An error occurred during deletion of repository stats'),
671 h.flash(_('An error occurred during deletion of repository stats'),
672 category='error')
672 category='error')
673 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
673 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
674
674
675 @HasRepoPermissionAllDecorator('repository.admin')
675 @HasRepoPermissionAllDecorator('repository.admin')
676 def edit_statistics_form(self, repo_name):
676 def edit_statistics_form(self, repo_name):
677 """GET /repo_name/settings: Form to edit an existing item"""
677 """GET /repo_name/settings: Form to edit an existing item"""
678 # url('edit_repo', repo_name=ID)
678 # url('edit_repo', repo_name=ID)
679 c.repo_info = self._load_repo(repo_name)
679 c.repo_info = self._load_repo(repo_name)
680 repo = c.repo_info.scm_instance()
680 repo = c.repo_info.scm_instance()
681
681
682 if c.repo_info.stats:
682 if c.repo_info.stats:
683 # this is on what revision we ended up so we add +1 for count
683 # this is on what revision we ended up so we add +1 for count
684 last_rev = c.repo_info.stats.stat_on_revision + 1
684 last_rev = c.repo_info.stats.stat_on_revision + 1
685 else:
685 else:
686 last_rev = 0
686 last_rev = 0
687 c.stats_revision = last_rev
687 c.stats_revision = last_rev
688
688
689 c.repo_last_rev = repo.count()
689 c.repo_last_rev = repo.count()
690
690
691 if last_rev == 0 or c.repo_last_rev == 0:
691 if last_rev == 0 or c.repo_last_rev == 0:
692 c.stats_percentage = 0
692 c.stats_percentage = 0
693 else:
693 else:
694 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
694 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
695
695
696 c.active = 'statistics'
696 c.active = 'statistics'
697
697
698 return render('admin/repos/repo_edit.html')
698 return render('admin/repos/repo_edit.html')
699
699
700 @HasRepoPermissionAllDecorator('repository.admin')
700 @HasRepoPermissionAllDecorator('repository.admin')
701 @auth.CSRFRequired()
701 @auth.CSRFRequired()
702 def repo_issuetracker_test(self, repo_name):
702 def repo_issuetracker_test(self, repo_name):
703 if request.is_xhr:
703 if request.is_xhr:
704 return h.urlify_commit_message(
704 return h.urlify_commit_message(
705 request.POST.get('test_text', ''),
705 request.POST.get('test_text', ''),
706 repo_name)
706 repo_name)
707 else:
707 else:
708 raise HTTPBadRequest()
708 raise HTTPBadRequest()
709
709
710 @HasRepoPermissionAllDecorator('repository.admin')
710 @HasRepoPermissionAllDecorator('repository.admin')
711 @auth.CSRFRequired()
711 @auth.CSRFRequired()
712 def repo_issuetracker_delete(self, repo_name):
712 def repo_issuetracker_delete(self, repo_name):
713 uid = request.POST.get('uid')
713 uid = request.POST.get('uid')
714 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
714 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
715 try:
715 try:
716 repo_settings.delete_entries(uid)
716 repo_settings.delete_entries(uid)
717 except Exception:
717 except Exception:
718 h.flash(_('Error occurred during deleting issue tracker entry'),
718 h.flash(_('Error occurred during deleting issue tracker entry'),
719 category='error')
719 category='error')
720 else:
720 else:
721 h.flash(_('Removed issue tracker entry'), category='success')
721 h.flash(_('Removed issue tracker entry'), category='success')
722 return redirect(url('repo_settings_issuetracker',
722 return redirect(url('repo_settings_issuetracker',
723 repo_name=repo_name))
723 repo_name=repo_name))
724
724
725 def _update_patterns(self, form, repo_settings):
725 def _update_patterns(self, form, repo_settings):
726 for uid in form['delete_patterns']:
726 for uid in form['delete_patterns']:
727 repo_settings.delete_entries(uid)
727 repo_settings.delete_entries(uid)
728
728
729 for pattern in form['patterns']:
729 for pattern in form['patterns']:
730 for setting, value, type_ in pattern:
730 for setting, value, type_ in pattern:
731 sett = repo_settings.create_or_update_setting(
731 sett = repo_settings.create_or_update_setting(
732 setting, value, type_)
732 setting, value, type_)
733 Session().add(sett)
733 Session().add(sett)
734
734
735 Session().commit()
735 Session().commit()
736
736
737 @HasRepoPermissionAllDecorator('repository.admin')
737 @HasRepoPermissionAllDecorator('repository.admin')
738 @auth.CSRFRequired()
738 @auth.CSRFRequired()
739 def repo_issuetracker_save(self, repo_name):
739 def repo_issuetracker_save(self, repo_name):
740 # Save inheritance
740 # Save inheritance
741 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
741 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
742 inherited = (request.POST.get('inherit_global_issuetracker')
742 inherited = (request.POST.get('inherit_global_issuetracker')
743 == "inherited")
743 == "inherited")
744 repo_settings.inherit_global_settings = inherited
744 repo_settings.inherit_global_settings = inherited
745 Session().commit()
745 Session().commit()
746
746
747 form = IssueTrackerPatternsForm()().to_python(request.POST)
747 form = IssueTrackerPatternsForm()().to_python(request.POST)
748 if form:
748 if form:
749 self._update_patterns(form, repo_settings)
749 self._update_patterns(form, repo_settings)
750
750
751 h.flash(_('Updated issue tracker entries'), category='success')
751 h.flash(_('Updated issue tracker entries'), category='success')
752 return redirect(url('repo_settings_issuetracker',
752 return redirect(url('repo_settings_issuetracker',
753 repo_name=repo_name))
753 repo_name=repo_name))
754
754
755 @HasRepoPermissionAllDecorator('repository.admin')
755 @HasRepoPermissionAllDecorator('repository.admin')
756 def repo_issuetracker(self, repo_name):
756 def repo_issuetracker(self, repo_name):
757 """GET /admin/settings/issue-tracker: All items in the collection"""
757 """GET /admin/settings/issue-tracker: All items in the collection"""
758 c.active = 'issuetracker'
758 c.active = 'issuetracker'
759 c.data = 'data'
759 c.data = 'data'
760 c.repo_info = self._load_repo(repo_name)
760 c.repo_info = self._load_repo(repo_name)
761
761
762 repo = Repository.get_by_repo_name(repo_name)
762 repo = Repository.get_by_repo_name(repo_name)
763 c.settings_model = IssueTrackerSettingsModel(repo=repo)
763 c.settings_model = IssueTrackerSettingsModel(repo=repo)
764 c.global_patterns = c.settings_model.get_global_settings()
764 c.global_patterns = c.settings_model.get_global_settings()
765 c.repo_patterns = c.settings_model.get_repo_settings()
765 c.repo_patterns = c.settings_model.get_repo_settings()
766
766
767 return render('admin/repos/repo_edit.html')
767 return render('admin/repos/repo_edit.html')
768
768
769 @HasRepoPermissionAllDecorator('repository.admin')
769 @HasRepoPermissionAllDecorator('repository.admin')
770 def repo_settings_vcs(self, repo_name):
770 def repo_settings_vcs(self, repo_name):
771 """GET /{repo_name}/settings/vcs/: All items in the collection"""
771 """GET /{repo_name}/settings/vcs/: All items in the collection"""
772
772
773 model = VcsSettingsModel(repo=repo_name)
773 model = VcsSettingsModel(repo=repo_name)
774
774
775 c.active = 'vcs'
775 c.active = 'vcs'
776 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
776 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
777 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
777 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
778 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
778 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
779 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
779 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
780 c.repo_info = self._load_repo(repo_name)
780 c.repo_info = self._load_repo(repo_name)
781 defaults = self._vcs_form_defaults(repo_name)
781 defaults = self._vcs_form_defaults(repo_name)
782 c.inherit_global_settings = defaults['inherit_global_settings']
782 c.inherit_global_settings = defaults['inherit_global_settings']
783 c.labs_active = str2bool(
783 c.labs_active = str2bool(
784 rhodecode.CONFIG.get('labs_settings_active', 'false'))
784 rhodecode.CONFIG.get('labs_settings_active', 'true'))
785
785
786 return htmlfill.render(
786 return htmlfill.render(
787 render('admin/repos/repo_edit.html'),
787 render('admin/repos/repo_edit.html'),
788 defaults=defaults,
788 defaults=defaults,
789 encoding="UTF-8",
789 encoding="UTF-8",
790 force_defaults=False)
790 force_defaults=False)
791
791
792 @HasRepoPermissionAllDecorator('repository.admin')
792 @HasRepoPermissionAllDecorator('repository.admin')
793 @auth.CSRFRequired()
793 @auth.CSRFRequired()
794 def repo_settings_vcs_update(self, repo_name):
794 def repo_settings_vcs_update(self, repo_name):
795 """POST /{repo_name}/settings/vcs/: All items in the collection"""
795 """POST /{repo_name}/settings/vcs/: All items in the collection"""
796 c.active = 'vcs'
796 c.active = 'vcs'
797
797
798 model = VcsSettingsModel(repo=repo_name)
798 model = VcsSettingsModel(repo=repo_name)
799 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
799 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
800 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
800 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
801 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
801 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
802 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
802 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
803 c.repo_info = self._load_repo(repo_name)
803 c.repo_info = self._load_repo(repo_name)
804 defaults = self._vcs_form_defaults(repo_name)
804 defaults = self._vcs_form_defaults(repo_name)
805 c.inherit_global_settings = defaults['inherit_global_settings']
805 c.inherit_global_settings = defaults['inherit_global_settings']
806
806
807 application_form = RepoVcsSettingsForm(repo_name)()
807 application_form = RepoVcsSettingsForm(repo_name)()
808 try:
808 try:
809 form_result = application_form.to_python(dict(request.POST))
809 form_result = application_form.to_python(dict(request.POST))
810 except formencode.Invalid as errors:
810 except formencode.Invalid as errors:
811 h.flash(
811 h.flash(
812 _("Some form inputs contain invalid data."),
812 _("Some form inputs contain invalid data."),
813 category='error')
813 category='error')
814 return htmlfill.render(
814 return htmlfill.render(
815 render('admin/repos/repo_edit.html'),
815 render('admin/repos/repo_edit.html'),
816 defaults=errors.value,
816 defaults=errors.value,
817 errors=errors.error_dict or {},
817 errors=errors.error_dict or {},
818 prefix_error=False,
818 prefix_error=False,
819 encoding="UTF-8",
819 encoding="UTF-8",
820 force_defaults=False
820 force_defaults=False
821 )
821 )
822
822
823 try:
823 try:
824 inherit_global_settings = form_result['inherit_global_settings']
824 inherit_global_settings = form_result['inherit_global_settings']
825 model.create_or_update_repo_settings(
825 model.create_or_update_repo_settings(
826 form_result, inherit_global_settings=inherit_global_settings)
826 form_result, inherit_global_settings=inherit_global_settings)
827 except Exception:
827 except Exception:
828 log.exception("Exception while updating settings")
828 log.exception("Exception while updating settings")
829 h.flash(
829 h.flash(
830 _('Error occurred during updating repository VCS settings'),
830 _('Error occurred during updating repository VCS settings'),
831 category='error')
831 category='error')
832 else:
832 else:
833 Session().commit()
833 Session().commit()
834 h.flash(_('Updated VCS settings'), category='success')
834 h.flash(_('Updated VCS settings'), category='success')
835 return redirect(url('repo_vcs_settings', repo_name=repo_name))
835 return redirect(url('repo_vcs_settings', repo_name=repo_name))
836
836
837 return htmlfill.render(
837 return htmlfill.render(
838 render('admin/repos/repo_edit.html'),
838 render('admin/repos/repo_edit.html'),
839 defaults=self._vcs_form_defaults(repo_name),
839 defaults=self._vcs_form_defaults(repo_name),
840 encoding="UTF-8",
840 encoding="UTF-8",
841 force_defaults=False)
841 force_defaults=False)
842
842
843 @HasRepoPermissionAllDecorator('repository.admin')
843 @HasRepoPermissionAllDecorator('repository.admin')
844 @auth.CSRFRequired()
844 @auth.CSRFRequired()
845 @jsonify
845 @jsonify
846 def repo_delete_svn_pattern(self, repo_name):
846 def repo_delete_svn_pattern(self, repo_name):
847 if not request.is_xhr:
847 if not request.is_xhr:
848 return False
848 return False
849
849
850 delete_pattern_id = request.POST.get('delete_svn_pattern')
850 delete_pattern_id = request.POST.get('delete_svn_pattern')
851 model = VcsSettingsModel(repo=repo_name)
851 model = VcsSettingsModel(repo=repo_name)
852 try:
852 try:
853 model.delete_repo_svn_pattern(delete_pattern_id)
853 model.delete_repo_svn_pattern(delete_pattern_id)
854 except SettingNotFound:
854 except SettingNotFound:
855 raise HTTPBadRequest()
855 raise HTTPBadRequest()
856
856
857 Session().commit()
857 Session().commit()
858 return True
858 return True
859
859
860 def _vcs_form_defaults(self, repo_name):
860 def _vcs_form_defaults(self, repo_name):
861 model = VcsSettingsModel(repo=repo_name)
861 model = VcsSettingsModel(repo=repo_name)
862 global_defaults = model.get_global_settings()
862 global_defaults = model.get_global_settings()
863
863
864 repo_defaults = {}
864 repo_defaults = {}
865 repo_defaults.update(global_defaults)
865 repo_defaults.update(global_defaults)
866 repo_defaults.update(model.get_repo_settings())
866 repo_defaults.update(model.get_repo_settings())
867
867
868 global_defaults = {
868 global_defaults = {
869 '{}_inherited'.format(k): global_defaults[k]
869 '{}_inherited'.format(k): global_defaults[k]
870 for k in global_defaults}
870 for k in global_defaults}
871
871
872 defaults = {
872 defaults = {
873 'inherit_global_settings': model.inherit_global_settings
873 'inherit_global_settings': model.inherit_global_settings
874 }
874 }
875 defaults.update(global_defaults)
875 defaults.update(global_defaults)
876 defaults.update(repo_defaults)
876 defaults.update(repo_defaults)
877 defaults.update({
877 defaults.update({
878 'new_svn_branch': '',
878 'new_svn_branch': '',
879 'new_svn_tag': '',
879 'new_svn_tag': '',
880 })
880 })
881 return defaults
881 return defaults
@@ -1,806 +1,806 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 settings controller for rhodecode admin
23 settings controller for rhodecode admin
24 """
24 """
25
25
26 import collections
26 import collections
27 import logging
27 import logging
28 import urllib2
28 import urllib2
29
29
30 import datetime
30 import datetime
31 import formencode
31 import formencode
32 from formencode import htmlfill
32 from formencode import htmlfill
33 import packaging.version
33 import packaging.version
34 from pylons import request, tmpl_context as c, url, config
34 from pylons import request, tmpl_context as c, url, config
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36 from pylons.i18n.translation import _, lazy_ugettext
36 from pylons.i18n.translation import _, lazy_ugettext
37 from webob.exc import HTTPBadRequest
37 from webob.exc import HTTPBadRequest
38
38
39 import rhodecode
39 import rhodecode
40 from rhodecode.admin.navigation import navigation_list
40 from rhodecode.admin.navigation import navigation_list
41 from rhodecode.lib import auth
41 from rhodecode.lib import auth
42 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
43 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
43 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
44 from rhodecode.lib.base import BaseController, render
44 from rhodecode.lib.base import BaseController, render
45 from rhodecode.lib.celerylib import tasks, run_task
45 from rhodecode.lib.celerylib import tasks, run_task
46 from rhodecode.lib.utils import repo2db_mapper
46 from rhodecode.lib.utils import repo2db_mapper
47 from rhodecode.lib.utils2 import (
47 from rhodecode.lib.utils2 import (
48 str2bool, safe_unicode, AttributeDict, safe_int)
48 str2bool, safe_unicode, AttributeDict, safe_int)
49 from rhodecode.lib.compat import OrderedDict
49 from rhodecode.lib.compat import OrderedDict
50 from rhodecode.lib.ext_json import json
50 from rhodecode.lib.ext_json import json
51 from rhodecode.lib.utils import jsonify
51 from rhodecode.lib.utils import jsonify
52
52
53 from rhodecode.model.db import RhodeCodeUi, Repository
53 from rhodecode.model.db import RhodeCodeUi, Repository
54 from rhodecode.model.forms import ApplicationSettingsForm, \
54 from rhodecode.model.forms import ApplicationSettingsForm, \
55 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
55 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
56 LabsSettingsForm, IssueTrackerPatternsForm
56 LabsSettingsForm, IssueTrackerPatternsForm
57
57
58 from rhodecode.model.scm import ScmModel
58 from rhodecode.model.scm import ScmModel
59 from rhodecode.model.notification import EmailNotificationModel
59 from rhodecode.model.notification import EmailNotificationModel
60 from rhodecode.model.meta import Session
60 from rhodecode.model.meta import Session
61 from rhodecode.model.settings import (
61 from rhodecode.model.settings import (
62 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
62 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
63 SettingsModel)
63 SettingsModel)
64
64
65 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
65 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
66
66
67
67
68 log = logging.getLogger(__name__)
68 log = logging.getLogger(__name__)
69
69
70
70
71 class SettingsController(BaseController):
71 class SettingsController(BaseController):
72 """REST Controller styled on the Atom Publishing Protocol"""
72 """REST Controller styled on the Atom Publishing Protocol"""
73 # To properly map this controller, ensure your config/routing.py
73 # To properly map this controller, ensure your config/routing.py
74 # file has a resource setup:
74 # file has a resource setup:
75 # map.resource('setting', 'settings', controller='admin/settings',
75 # map.resource('setting', 'settings', controller='admin/settings',
76 # path_prefix='/admin', name_prefix='admin_')
76 # path_prefix='/admin', name_prefix='admin_')
77
77
78 @LoginRequired()
78 @LoginRequired()
79 def __before__(self):
79 def __before__(self):
80 super(SettingsController, self).__before__()
80 super(SettingsController, self).__before__()
81 c.labs_active = str2bool(
81 c.labs_active = str2bool(
82 rhodecode.CONFIG.get('labs_settings_active', 'false'))
82 rhodecode.CONFIG.get('labs_settings_active', 'true'))
83 c.navlist = navigation_list(request)
83 c.navlist = navigation_list(request)
84
84
85 def _get_hg_ui_settings(self):
85 def _get_hg_ui_settings(self):
86 ret = RhodeCodeUi.query().all()
86 ret = RhodeCodeUi.query().all()
87
87
88 if not ret:
88 if not ret:
89 raise Exception('Could not get application ui settings !')
89 raise Exception('Could not get application ui settings !')
90 settings = {}
90 settings = {}
91 for each in ret:
91 for each in ret:
92 k = each.ui_key
92 k = each.ui_key
93 v = each.ui_value
93 v = each.ui_value
94 if k == '/':
94 if k == '/':
95 k = 'root_path'
95 k = 'root_path'
96
96
97 if k in ['push_ssl', 'publish']:
97 if k in ['push_ssl', 'publish']:
98 v = str2bool(v)
98 v = str2bool(v)
99
99
100 if k.find('.') != -1:
100 if k.find('.') != -1:
101 k = k.replace('.', '_')
101 k = k.replace('.', '_')
102
102
103 if each.ui_section in ['hooks', 'extensions']:
103 if each.ui_section in ['hooks', 'extensions']:
104 v = each.ui_active
104 v = each.ui_active
105
105
106 settings[each.ui_section + '_' + k] = v
106 settings[each.ui_section + '_' + k] = v
107 return settings
107 return settings
108
108
109 @HasPermissionAllDecorator('hg.admin')
109 @HasPermissionAllDecorator('hg.admin')
110 @auth.CSRFRequired()
110 @auth.CSRFRequired()
111 @jsonify
111 @jsonify
112 def delete_svn_pattern(self):
112 def delete_svn_pattern(self):
113 if not request.is_xhr:
113 if not request.is_xhr:
114 raise HTTPBadRequest()
114 raise HTTPBadRequest()
115
115
116 delete_pattern_id = request.POST.get('delete_svn_pattern')
116 delete_pattern_id = request.POST.get('delete_svn_pattern')
117 model = VcsSettingsModel()
117 model = VcsSettingsModel()
118 try:
118 try:
119 model.delete_global_svn_pattern(delete_pattern_id)
119 model.delete_global_svn_pattern(delete_pattern_id)
120 except SettingNotFound:
120 except SettingNotFound:
121 raise HTTPBadRequest()
121 raise HTTPBadRequest()
122
122
123 Session().commit()
123 Session().commit()
124 return True
124 return True
125
125
126 @HasPermissionAllDecorator('hg.admin')
126 @HasPermissionAllDecorator('hg.admin')
127 @auth.CSRFRequired()
127 @auth.CSRFRequired()
128 def settings_vcs_update(self):
128 def settings_vcs_update(self):
129 """POST /admin/settings: All items in the collection"""
129 """POST /admin/settings: All items in the collection"""
130 # url('admin_settings_vcs')
130 # url('admin_settings_vcs')
131 c.active = 'vcs'
131 c.active = 'vcs'
132
132
133 model = VcsSettingsModel()
133 model = VcsSettingsModel()
134 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
134 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
136
136
137 application_form = ApplicationUiSettingsForm()()
137 application_form = ApplicationUiSettingsForm()()
138 try:
138 try:
139 form_result = application_form.to_python(dict(request.POST))
139 form_result = application_form.to_python(dict(request.POST))
140 except formencode.Invalid as errors:
140 except formencode.Invalid as errors:
141 h.flash(
141 h.flash(
142 _("Some form inputs contain invalid data."),
142 _("Some form inputs contain invalid data."),
143 category='error')
143 category='error')
144 return htmlfill.render(
144 return htmlfill.render(
145 render('admin/settings/settings.html'),
145 render('admin/settings/settings.html'),
146 defaults=errors.value,
146 defaults=errors.value,
147 errors=errors.error_dict or {},
147 errors=errors.error_dict or {},
148 prefix_error=False,
148 prefix_error=False,
149 encoding="UTF-8",
149 encoding="UTF-8",
150 force_defaults=False
150 force_defaults=False
151 )
151 )
152
152
153 try:
153 try:
154 model.update_global_ssl_setting(form_result['web_push_ssl'])
154 model.update_global_ssl_setting(form_result['web_push_ssl'])
155 if c.visual.allow_repo_location_change:
155 if c.visual.allow_repo_location_change:
156 model.update_global_path_setting(
156 model.update_global_path_setting(
157 form_result['paths_root_path'])
157 form_result['paths_root_path'])
158 model.update_global_hook_settings(form_result)
158 model.update_global_hook_settings(form_result)
159 model.create_global_svn_settings(form_result)
159 model.create_global_svn_settings(form_result)
160 model.create_or_update_global_hg_settings(form_result)
160 model.create_or_update_global_hg_settings(form_result)
161 model.create_or_update_global_pr_settings(form_result)
161 model.create_or_update_global_pr_settings(form_result)
162 except Exception:
162 except Exception:
163 log.exception("Exception while updating settings")
163 log.exception("Exception while updating settings")
164 h.flash(_('Error occurred during updating '
164 h.flash(_('Error occurred during updating '
165 'application settings'), category='error')
165 'application settings'), category='error')
166 else:
166 else:
167 Session().commit()
167 Session().commit()
168 h.flash(_('Updated VCS settings'), category='success')
168 h.flash(_('Updated VCS settings'), category='success')
169 return redirect(url('admin_settings_vcs'))
169 return redirect(url('admin_settings_vcs'))
170
170
171 return htmlfill.render(
171 return htmlfill.render(
172 render('admin/settings/settings.html'),
172 render('admin/settings/settings.html'),
173 defaults=self._form_defaults(),
173 defaults=self._form_defaults(),
174 encoding="UTF-8",
174 encoding="UTF-8",
175 force_defaults=False)
175 force_defaults=False)
176
176
177 @HasPermissionAllDecorator('hg.admin')
177 @HasPermissionAllDecorator('hg.admin')
178 def settings_vcs(self):
178 def settings_vcs(self):
179 """GET /admin/settings: All items in the collection"""
179 """GET /admin/settings: All items in the collection"""
180 # url('admin_settings_vcs')
180 # url('admin_settings_vcs')
181 c.active = 'vcs'
181 c.active = 'vcs'
182 model = VcsSettingsModel()
182 model = VcsSettingsModel()
183 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
183 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
184 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
184 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
185
185
186 return htmlfill.render(
186 return htmlfill.render(
187 render('admin/settings/settings.html'),
187 render('admin/settings/settings.html'),
188 defaults=self._form_defaults(),
188 defaults=self._form_defaults(),
189 encoding="UTF-8",
189 encoding="UTF-8",
190 force_defaults=False)
190 force_defaults=False)
191
191
192 @HasPermissionAllDecorator('hg.admin')
192 @HasPermissionAllDecorator('hg.admin')
193 @auth.CSRFRequired()
193 @auth.CSRFRequired()
194 def settings_mapping_update(self):
194 def settings_mapping_update(self):
195 """POST /admin/settings/mapping: All items in the collection"""
195 """POST /admin/settings/mapping: All items in the collection"""
196 # url('admin_settings_mapping')
196 # url('admin_settings_mapping')
197 c.active = 'mapping'
197 c.active = 'mapping'
198 rm_obsolete = request.POST.get('destroy', False)
198 rm_obsolete = request.POST.get('destroy', False)
199 invalidate_cache = request.POST.get('invalidate', False)
199 invalidate_cache = request.POST.get('invalidate', False)
200 log.debug(
200 log.debug(
201 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
201 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
202
202
203 if invalidate_cache:
203 if invalidate_cache:
204 log.debug('invalidating all repositories cache')
204 log.debug('invalidating all repositories cache')
205 for repo in Repository.get_all():
205 for repo in Repository.get_all():
206 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
206 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
207
207
208 filesystem_repos = ScmModel().repo_scan()
208 filesystem_repos = ScmModel().repo_scan()
209 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
209 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
210 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
210 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
211 h.flash(_('Repositories successfully '
211 h.flash(_('Repositories successfully '
212 'rescanned added: %s ; removed: %s') %
212 'rescanned added: %s ; removed: %s') %
213 (_repr(added), _repr(removed)),
213 (_repr(added), _repr(removed)),
214 category='success')
214 category='success')
215 return redirect(url('admin_settings_mapping'))
215 return redirect(url('admin_settings_mapping'))
216
216
217 @HasPermissionAllDecorator('hg.admin')
217 @HasPermissionAllDecorator('hg.admin')
218 def settings_mapping(self):
218 def settings_mapping(self):
219 """GET /admin/settings/mapping: All items in the collection"""
219 """GET /admin/settings/mapping: All items in the collection"""
220 # url('admin_settings_mapping')
220 # url('admin_settings_mapping')
221 c.active = 'mapping'
221 c.active = 'mapping'
222
222
223 return htmlfill.render(
223 return htmlfill.render(
224 render('admin/settings/settings.html'),
224 render('admin/settings/settings.html'),
225 defaults=self._form_defaults(),
225 defaults=self._form_defaults(),
226 encoding="UTF-8",
226 encoding="UTF-8",
227 force_defaults=False)
227 force_defaults=False)
228
228
229 @HasPermissionAllDecorator('hg.admin')
229 @HasPermissionAllDecorator('hg.admin')
230 @auth.CSRFRequired()
230 @auth.CSRFRequired()
231 def settings_global_update(self):
231 def settings_global_update(self):
232 """POST /admin/settings/global: All items in the collection"""
232 """POST /admin/settings/global: All items in the collection"""
233 # url('admin_settings_global')
233 # url('admin_settings_global')
234 c.active = 'global'
234 c.active = 'global'
235 application_form = ApplicationSettingsForm()()
235 application_form = ApplicationSettingsForm()()
236 try:
236 try:
237 form_result = application_form.to_python(dict(request.POST))
237 form_result = application_form.to_python(dict(request.POST))
238 except formencode.Invalid as errors:
238 except formencode.Invalid as errors:
239 return htmlfill.render(
239 return htmlfill.render(
240 render('admin/settings/settings.html'),
240 render('admin/settings/settings.html'),
241 defaults=errors.value,
241 defaults=errors.value,
242 errors=errors.error_dict or {},
242 errors=errors.error_dict or {},
243 prefix_error=False,
243 prefix_error=False,
244 encoding="UTF-8",
244 encoding="UTF-8",
245 force_defaults=False)
245 force_defaults=False)
246
246
247 try:
247 try:
248 settings = [
248 settings = [
249 ('title', 'rhodecode_title'),
249 ('title', 'rhodecode_title'),
250 ('realm', 'rhodecode_realm'),
250 ('realm', 'rhodecode_realm'),
251 ('pre_code', 'rhodecode_pre_code'),
251 ('pre_code', 'rhodecode_pre_code'),
252 ('post_code', 'rhodecode_post_code'),
252 ('post_code', 'rhodecode_post_code'),
253 ('captcha_public_key', 'rhodecode_captcha_public_key'),
253 ('captcha_public_key', 'rhodecode_captcha_public_key'),
254 ('captcha_private_key', 'rhodecode_captcha_private_key'),
254 ('captcha_private_key', 'rhodecode_captcha_private_key'),
255 ]
255 ]
256 for setting, form_key in settings:
256 for setting, form_key in settings:
257 sett = SettingsModel().create_or_update_setting(
257 sett = SettingsModel().create_or_update_setting(
258 setting, form_result[form_key])
258 setting, form_result[form_key])
259 Session().add(sett)
259 Session().add(sett)
260
260
261 Session().commit()
261 Session().commit()
262 SettingsModel().invalidate_settings_cache()
262 SettingsModel().invalidate_settings_cache()
263 h.flash(_('Updated application settings'), category='success')
263 h.flash(_('Updated application settings'), category='success')
264 except Exception:
264 except Exception:
265 log.exception("Exception while updating application settings")
265 log.exception("Exception while updating application settings")
266 h.flash(
266 h.flash(
267 _('Error occurred during updating application settings'),
267 _('Error occurred during updating application settings'),
268 category='error')
268 category='error')
269
269
270 return redirect(url('admin_settings_global'))
270 return redirect(url('admin_settings_global'))
271
271
272 @HasPermissionAllDecorator('hg.admin')
272 @HasPermissionAllDecorator('hg.admin')
273 def settings_global(self):
273 def settings_global(self):
274 """GET /admin/settings/global: All items in the collection"""
274 """GET /admin/settings/global: All items in the collection"""
275 # url('admin_settings_global')
275 # url('admin_settings_global')
276 c.active = 'global'
276 c.active = 'global'
277
277
278 return htmlfill.render(
278 return htmlfill.render(
279 render('admin/settings/settings.html'),
279 render('admin/settings/settings.html'),
280 defaults=self._form_defaults(),
280 defaults=self._form_defaults(),
281 encoding="UTF-8",
281 encoding="UTF-8",
282 force_defaults=False)
282 force_defaults=False)
283
283
284 @HasPermissionAllDecorator('hg.admin')
284 @HasPermissionAllDecorator('hg.admin')
285 @auth.CSRFRequired()
285 @auth.CSRFRequired()
286 def settings_visual_update(self):
286 def settings_visual_update(self):
287 """POST /admin/settings/visual: All items in the collection"""
287 """POST /admin/settings/visual: All items in the collection"""
288 # url('admin_settings_visual')
288 # url('admin_settings_visual')
289 c.active = 'visual'
289 c.active = 'visual'
290 application_form = ApplicationVisualisationForm()()
290 application_form = ApplicationVisualisationForm()()
291 try:
291 try:
292 form_result = application_form.to_python(dict(request.POST))
292 form_result = application_form.to_python(dict(request.POST))
293 except formencode.Invalid as errors:
293 except formencode.Invalid as errors:
294 return htmlfill.render(
294 return htmlfill.render(
295 render('admin/settings/settings.html'),
295 render('admin/settings/settings.html'),
296 defaults=errors.value,
296 defaults=errors.value,
297 errors=errors.error_dict or {},
297 errors=errors.error_dict or {},
298 prefix_error=False,
298 prefix_error=False,
299 encoding="UTF-8",
299 encoding="UTF-8",
300 force_defaults=False
300 force_defaults=False
301 )
301 )
302
302
303 try:
303 try:
304 settings = [
304 settings = [
305 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
305 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
306 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
306 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
307 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
307 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
308 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
308 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
309 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
309 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
310 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
310 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
311 ('show_version', 'rhodecode_show_version', 'bool'),
311 ('show_version', 'rhodecode_show_version', 'bool'),
312 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
312 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
313 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
313 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
314 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
314 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
315 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
315 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
316 ('support_url', 'rhodecode_support_url', 'unicode'),
316 ('support_url', 'rhodecode_support_url', 'unicode'),
317 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
317 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
318 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
318 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
319 ]
319 ]
320 for setting, form_key, type_ in settings:
320 for setting, form_key, type_ in settings:
321 sett = SettingsModel().create_or_update_setting(
321 sett = SettingsModel().create_or_update_setting(
322 setting, form_result[form_key], type_)
322 setting, form_result[form_key], type_)
323 Session().add(sett)
323 Session().add(sett)
324
324
325 Session().commit()
325 Session().commit()
326 SettingsModel().invalidate_settings_cache()
326 SettingsModel().invalidate_settings_cache()
327 h.flash(_('Updated visualisation settings'), category='success')
327 h.flash(_('Updated visualisation settings'), category='success')
328 except Exception:
328 except Exception:
329 log.exception("Exception updating visualization settings")
329 log.exception("Exception updating visualization settings")
330 h.flash(_('Error occurred during updating '
330 h.flash(_('Error occurred during updating '
331 'visualisation settings'),
331 'visualisation settings'),
332 category='error')
332 category='error')
333
333
334 return redirect(url('admin_settings_visual'))
334 return redirect(url('admin_settings_visual'))
335
335
336 @HasPermissionAllDecorator('hg.admin')
336 @HasPermissionAllDecorator('hg.admin')
337 def settings_visual(self):
337 def settings_visual(self):
338 """GET /admin/settings/visual: All items in the collection"""
338 """GET /admin/settings/visual: All items in the collection"""
339 # url('admin_settings_visual')
339 # url('admin_settings_visual')
340 c.active = 'visual'
340 c.active = 'visual'
341
341
342 return htmlfill.render(
342 return htmlfill.render(
343 render('admin/settings/settings.html'),
343 render('admin/settings/settings.html'),
344 defaults=self._form_defaults(),
344 defaults=self._form_defaults(),
345 encoding="UTF-8",
345 encoding="UTF-8",
346 force_defaults=False)
346 force_defaults=False)
347
347
348 @HasPermissionAllDecorator('hg.admin')
348 @HasPermissionAllDecorator('hg.admin')
349 @auth.CSRFRequired()
349 @auth.CSRFRequired()
350 def settings_issuetracker_test(self):
350 def settings_issuetracker_test(self):
351 if request.is_xhr:
351 if request.is_xhr:
352 return h.urlify_commit_message(
352 return h.urlify_commit_message(
353 request.POST.get('test_text', ''),
353 request.POST.get('test_text', ''),
354 'repo_group/test_repo1')
354 'repo_group/test_repo1')
355 else:
355 else:
356 raise HTTPBadRequest()
356 raise HTTPBadRequest()
357
357
358 @HasPermissionAllDecorator('hg.admin')
358 @HasPermissionAllDecorator('hg.admin')
359 @auth.CSRFRequired()
359 @auth.CSRFRequired()
360 def settings_issuetracker_delete(self):
360 def settings_issuetracker_delete(self):
361 uid = request.POST.get('uid')
361 uid = request.POST.get('uid')
362 IssueTrackerSettingsModel().delete_entries(uid)
362 IssueTrackerSettingsModel().delete_entries(uid)
363 h.flash(_('Removed issue tracker entry'), category='success')
363 h.flash(_('Removed issue tracker entry'), category='success')
364 return redirect(url('admin_settings_issuetracker'))
364 return redirect(url('admin_settings_issuetracker'))
365
365
366 @HasPermissionAllDecorator('hg.admin')
366 @HasPermissionAllDecorator('hg.admin')
367 def settings_issuetracker(self):
367 def settings_issuetracker(self):
368 """GET /admin/settings/issue-tracker: All items in the collection"""
368 """GET /admin/settings/issue-tracker: All items in the collection"""
369 # url('admin_settings_issuetracker')
369 # url('admin_settings_issuetracker')
370 c.active = 'issuetracker'
370 c.active = 'issuetracker'
371 defaults = SettingsModel().get_all_settings()
371 defaults = SettingsModel().get_all_settings()
372
372
373 entry_key = 'rhodecode_issuetracker_pat_'
373 entry_key = 'rhodecode_issuetracker_pat_'
374
374
375 c.issuetracker_entries = {}
375 c.issuetracker_entries = {}
376 for k, v in defaults.items():
376 for k, v in defaults.items():
377 if k.startswith(entry_key):
377 if k.startswith(entry_key):
378 uid = k[len(entry_key):]
378 uid = k[len(entry_key):]
379 c.issuetracker_entries[uid] = None
379 c.issuetracker_entries[uid] = None
380
380
381 for uid in c.issuetracker_entries:
381 for uid in c.issuetracker_entries:
382 c.issuetracker_entries[uid] = AttributeDict({
382 c.issuetracker_entries[uid] = AttributeDict({
383 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
383 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
384 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
384 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
385 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
385 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
386 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
386 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
387 })
387 })
388
388
389 return render('admin/settings/settings.html')
389 return render('admin/settings/settings.html')
390
390
391 @HasPermissionAllDecorator('hg.admin')
391 @HasPermissionAllDecorator('hg.admin')
392 @auth.CSRFRequired()
392 @auth.CSRFRequired()
393 def settings_issuetracker_save(self):
393 def settings_issuetracker_save(self):
394 settings_model = IssueTrackerSettingsModel()
394 settings_model = IssueTrackerSettingsModel()
395
395
396 form = IssueTrackerPatternsForm()().to_python(request.POST)
396 form = IssueTrackerPatternsForm()().to_python(request.POST)
397 for uid in form['delete_patterns']:
397 for uid in form['delete_patterns']:
398 settings_model.delete_entries(uid)
398 settings_model.delete_entries(uid)
399
399
400 for pattern in form['patterns']:
400 for pattern in form['patterns']:
401 for setting, value, type_ in pattern:
401 for setting, value, type_ in pattern:
402 sett = settings_model.create_or_update_setting(
402 sett = settings_model.create_or_update_setting(
403 setting, value, type_)
403 setting, value, type_)
404 Session().add(sett)
404 Session().add(sett)
405
405
406 Session().commit()
406 Session().commit()
407
407
408 SettingsModel().invalidate_settings_cache()
408 SettingsModel().invalidate_settings_cache()
409 h.flash(_('Updated issue tracker entries'), category='success')
409 h.flash(_('Updated issue tracker entries'), category='success')
410 return redirect(url('admin_settings_issuetracker'))
410 return redirect(url('admin_settings_issuetracker'))
411
411
412 @HasPermissionAllDecorator('hg.admin')
412 @HasPermissionAllDecorator('hg.admin')
413 @auth.CSRFRequired()
413 @auth.CSRFRequired()
414 def settings_email_update(self):
414 def settings_email_update(self):
415 """POST /admin/settings/email: All items in the collection"""
415 """POST /admin/settings/email: All items in the collection"""
416 # url('admin_settings_email')
416 # url('admin_settings_email')
417 c.active = 'email'
417 c.active = 'email'
418
418
419 test_email = request.POST.get('test_email')
419 test_email = request.POST.get('test_email')
420
420
421 if not test_email:
421 if not test_email:
422 h.flash(_('Please enter email address'), category='error')
422 h.flash(_('Please enter email address'), category='error')
423 return redirect(url('admin_settings_email'))
423 return redirect(url('admin_settings_email'))
424
424
425 email_kwargs = {
425 email_kwargs = {
426 'date': datetime.datetime.now(),
426 'date': datetime.datetime.now(),
427 'user': c.rhodecode_user,
427 'user': c.rhodecode_user,
428 'rhodecode_version': c.rhodecode_version
428 'rhodecode_version': c.rhodecode_version
429 }
429 }
430
430
431 (subject, headers, email_body,
431 (subject, headers, email_body,
432 email_body_plaintext) = EmailNotificationModel().render_email(
432 email_body_plaintext) = EmailNotificationModel().render_email(
433 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
433 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
434
434
435 recipients = [test_email] if test_email else None
435 recipients = [test_email] if test_email else None
436
436
437 run_task(tasks.send_email, recipients, subject,
437 run_task(tasks.send_email, recipients, subject,
438 email_body_plaintext, email_body)
438 email_body_plaintext, email_body)
439
439
440 h.flash(_('Send email task created'), category='success')
440 h.flash(_('Send email task created'), category='success')
441 return redirect(url('admin_settings_email'))
441 return redirect(url('admin_settings_email'))
442
442
443 @HasPermissionAllDecorator('hg.admin')
443 @HasPermissionAllDecorator('hg.admin')
444 def settings_email(self):
444 def settings_email(self):
445 """GET /admin/settings/email: All items in the collection"""
445 """GET /admin/settings/email: All items in the collection"""
446 # url('admin_settings_email')
446 # url('admin_settings_email')
447 c.active = 'email'
447 c.active = 'email'
448 c.rhodecode_ini = rhodecode.CONFIG
448 c.rhodecode_ini = rhodecode.CONFIG
449
449
450 return htmlfill.render(
450 return htmlfill.render(
451 render('admin/settings/settings.html'),
451 render('admin/settings/settings.html'),
452 defaults=self._form_defaults(),
452 defaults=self._form_defaults(),
453 encoding="UTF-8",
453 encoding="UTF-8",
454 force_defaults=False)
454 force_defaults=False)
455
455
456 @HasPermissionAllDecorator('hg.admin')
456 @HasPermissionAllDecorator('hg.admin')
457 @auth.CSRFRequired()
457 @auth.CSRFRequired()
458 def settings_hooks_update(self):
458 def settings_hooks_update(self):
459 """POST or DELETE /admin/settings/hooks: All items in the collection"""
459 """POST or DELETE /admin/settings/hooks: All items in the collection"""
460 # url('admin_settings_hooks')
460 # url('admin_settings_hooks')
461 c.active = 'hooks'
461 c.active = 'hooks'
462 if c.visual.allow_custom_hooks_settings:
462 if c.visual.allow_custom_hooks_settings:
463 ui_key = request.POST.get('new_hook_ui_key')
463 ui_key = request.POST.get('new_hook_ui_key')
464 ui_value = request.POST.get('new_hook_ui_value')
464 ui_value = request.POST.get('new_hook_ui_value')
465
465
466 hook_id = request.POST.get('hook_id')
466 hook_id = request.POST.get('hook_id')
467 new_hook = False
467 new_hook = False
468
468
469 model = SettingsModel()
469 model = SettingsModel()
470 try:
470 try:
471 if ui_value and ui_key:
471 if ui_value and ui_key:
472 model.create_or_update_hook(ui_key, ui_value)
472 model.create_or_update_hook(ui_key, ui_value)
473 h.flash(_('Added new hook'), category='success')
473 h.flash(_('Added new hook'), category='success')
474 new_hook = True
474 new_hook = True
475 elif hook_id:
475 elif hook_id:
476 RhodeCodeUi.delete(hook_id)
476 RhodeCodeUi.delete(hook_id)
477 Session().commit()
477 Session().commit()
478
478
479 # check for edits
479 # check for edits
480 update = False
480 update = False
481 _d = request.POST.dict_of_lists()
481 _d = request.POST.dict_of_lists()
482 for k, v in zip(_d.get('hook_ui_key', []),
482 for k, v in zip(_d.get('hook_ui_key', []),
483 _d.get('hook_ui_value_new', [])):
483 _d.get('hook_ui_value_new', [])):
484 model.create_or_update_hook(k, v)
484 model.create_or_update_hook(k, v)
485 update = True
485 update = True
486
486
487 if update and not new_hook:
487 if update and not new_hook:
488 h.flash(_('Updated hooks'), category='success')
488 h.flash(_('Updated hooks'), category='success')
489 Session().commit()
489 Session().commit()
490 except Exception:
490 except Exception:
491 log.exception("Exception during hook creation")
491 log.exception("Exception during hook creation")
492 h.flash(_('Error occurred during hook creation'),
492 h.flash(_('Error occurred during hook creation'),
493 category='error')
493 category='error')
494
494
495 return redirect(url('admin_settings_hooks'))
495 return redirect(url('admin_settings_hooks'))
496
496
497 @HasPermissionAllDecorator('hg.admin')
497 @HasPermissionAllDecorator('hg.admin')
498 def settings_hooks(self):
498 def settings_hooks(self):
499 """GET /admin/settings/hooks: All items in the collection"""
499 """GET /admin/settings/hooks: All items in the collection"""
500 # url('admin_settings_hooks')
500 # url('admin_settings_hooks')
501 c.active = 'hooks'
501 c.active = 'hooks'
502
502
503 model = SettingsModel()
503 model = SettingsModel()
504 c.hooks = model.get_builtin_hooks()
504 c.hooks = model.get_builtin_hooks()
505 c.custom_hooks = model.get_custom_hooks()
505 c.custom_hooks = model.get_custom_hooks()
506
506
507 return htmlfill.render(
507 return htmlfill.render(
508 render('admin/settings/settings.html'),
508 render('admin/settings/settings.html'),
509 defaults=self._form_defaults(),
509 defaults=self._form_defaults(),
510 encoding="UTF-8",
510 encoding="UTF-8",
511 force_defaults=False)
511 force_defaults=False)
512
512
513 @HasPermissionAllDecorator('hg.admin')
513 @HasPermissionAllDecorator('hg.admin')
514 def settings_search(self):
514 def settings_search(self):
515 """GET /admin/settings/search: All items in the collection"""
515 """GET /admin/settings/search: All items in the collection"""
516 # url('admin_settings_search')
516 # url('admin_settings_search')
517 c.active = 'search'
517 c.active = 'search'
518
518
519 from rhodecode.lib.index import searcher_from_config
519 from rhodecode.lib.index import searcher_from_config
520 searcher = searcher_from_config(config)
520 searcher = searcher_from_config(config)
521 c.statistics = searcher.statistics()
521 c.statistics = searcher.statistics()
522
522
523 return render('admin/settings/settings.html')
523 return render('admin/settings/settings.html')
524
524
525 @HasPermissionAllDecorator('hg.admin')
525 @HasPermissionAllDecorator('hg.admin')
526 def settings_system(self):
526 def settings_system(self):
527 """GET /admin/settings/system: All items in the collection"""
527 """GET /admin/settings/system: All items in the collection"""
528 # url('admin_settings_system')
528 # url('admin_settings_system')
529 snapshot = str2bool(request.GET.get('snapshot'))
529 snapshot = str2bool(request.GET.get('snapshot'))
530 c.active = 'system'
530 c.active = 'system'
531
531
532 defaults = self._form_defaults()
532 defaults = self._form_defaults()
533 c.rhodecode_ini = rhodecode.CONFIG
533 c.rhodecode_ini = rhodecode.CONFIG
534 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
534 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
535 server_info = ScmModel().get_server_info(request.environ)
535 server_info = ScmModel().get_server_info(request.environ)
536 for key, val in server_info.iteritems():
536 for key, val in server_info.iteritems():
537 setattr(c, key, val)
537 setattr(c, key, val)
538
538
539 if c.disk['percent'] > 90:
539 if c.disk['percent'] > 90:
540 h.flash(h.literal(_(
540 h.flash(h.literal(_(
541 'Critical: your disk space is very low <b>%s%%</b> used' %
541 'Critical: your disk space is very low <b>%s%%</b> used' %
542 c.disk['percent'])), 'error')
542 c.disk['percent'])), 'error')
543 elif c.disk['percent'] > 70:
543 elif c.disk['percent'] > 70:
544 h.flash(h.literal(_(
544 h.flash(h.literal(_(
545 'Warning: your disk space is running low <b>%s%%</b> used' %
545 'Warning: your disk space is running low <b>%s%%</b> used' %
546 c.disk['percent'])), 'warning')
546 c.disk['percent'])), 'warning')
547
547
548 try:
548 try:
549 c.uptime_age = h._age(
549 c.uptime_age = h._age(
550 h.time_to_datetime(c.boot_time), False, show_suffix=False)
550 h.time_to_datetime(c.boot_time), False, show_suffix=False)
551 except TypeError:
551 except TypeError:
552 c.uptime_age = c.boot_time
552 c.uptime_age = c.boot_time
553
553
554 try:
554 try:
555 c.system_memory = '%s/%s, %s%% (%s%%) used%s' % (
555 c.system_memory = '%s/%s, %s%% (%s%%) used%s' % (
556 h.format_byte_size_binary(c.memory['used']),
556 h.format_byte_size_binary(c.memory['used']),
557 h.format_byte_size_binary(c.memory['total']),
557 h.format_byte_size_binary(c.memory['total']),
558 c.memory['percent2'],
558 c.memory['percent2'],
559 c.memory['percent'],
559 c.memory['percent'],
560 ' %s' % c.memory['error'] if 'error' in c.memory else '')
560 ' %s' % c.memory['error'] if 'error' in c.memory else '')
561 except TypeError:
561 except TypeError:
562 c.system_memory = 'NOT AVAILABLE'
562 c.system_memory = 'NOT AVAILABLE'
563
563
564 rhodecode_ini_safe = rhodecode.CONFIG.copy()
564 rhodecode_ini_safe = rhodecode.CONFIG.copy()
565 blacklist = [
565 blacklist = [
566 'rhodecode_license_key',
566 'rhodecode_license_key',
567 'routes.map',
567 'routes.map',
568 'pylons.h',
568 'pylons.h',
569 'pylons.app_globals',
569 'pylons.app_globals',
570 'pylons.environ_config',
570 'pylons.environ_config',
571 'sqlalchemy.db1.url',
571 'sqlalchemy.db1.url',
572 ('app_conf', 'sqlalchemy.db1.url')
572 ('app_conf', 'sqlalchemy.db1.url')
573 ]
573 ]
574 for k in blacklist:
574 for k in blacklist:
575 if isinstance(k, tuple):
575 if isinstance(k, tuple):
576 section, key = k
576 section, key = k
577 if section in rhodecode_ini_safe:
577 if section in rhodecode_ini_safe:
578 rhodecode_ini_safe[section].pop(key, None)
578 rhodecode_ini_safe[section].pop(key, None)
579 else:
579 else:
580 rhodecode_ini_safe.pop(k, None)
580 rhodecode_ini_safe.pop(k, None)
581
581
582 c.rhodecode_ini_safe = rhodecode_ini_safe
582 c.rhodecode_ini_safe = rhodecode_ini_safe
583
583
584 # TODO: marcink, figure out how to allow only selected users to do this
584 # TODO: marcink, figure out how to allow only selected users to do this
585 c.allowed_to_snapshot = False
585 c.allowed_to_snapshot = False
586
586
587 if snapshot:
587 if snapshot:
588 if c.allowed_to_snapshot:
588 if c.allowed_to_snapshot:
589 return render('admin/settings/settings_system_snapshot.html')
589 return render('admin/settings/settings_system_snapshot.html')
590 else:
590 else:
591 h.flash('You are not allowed to do this', category='warning')
591 h.flash('You are not allowed to do this', category='warning')
592
592
593 return htmlfill.render(
593 return htmlfill.render(
594 render('admin/settings/settings.html'),
594 render('admin/settings/settings.html'),
595 defaults=defaults,
595 defaults=defaults,
596 encoding="UTF-8",
596 encoding="UTF-8",
597 force_defaults=False)
597 force_defaults=False)
598
598
599 @staticmethod
599 @staticmethod
600 def get_update_data(update_url):
600 def get_update_data(update_url):
601 """Return the JSON update data."""
601 """Return the JSON update data."""
602 ver = rhodecode.__version__
602 ver = rhodecode.__version__
603 log.debug('Checking for upgrade on `%s` server', update_url)
603 log.debug('Checking for upgrade on `%s` server', update_url)
604 opener = urllib2.build_opener()
604 opener = urllib2.build_opener()
605 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
605 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
606 response = opener.open(update_url)
606 response = opener.open(update_url)
607 response_data = response.read()
607 response_data = response.read()
608 data = json.loads(response_data)
608 data = json.loads(response_data)
609
609
610 return data
610 return data
611
611
612 @HasPermissionAllDecorator('hg.admin')
612 @HasPermissionAllDecorator('hg.admin')
613 def settings_system_update(self):
613 def settings_system_update(self):
614 """GET /admin/settings/system/updates: All items in the collection"""
614 """GET /admin/settings/system/updates: All items in the collection"""
615 # url('admin_settings_system_update')
615 # url('admin_settings_system_update')
616 defaults = self._form_defaults()
616 defaults = self._form_defaults()
617 update_url = defaults.get('rhodecode_update_url', '')
617 update_url = defaults.get('rhodecode_update_url', '')
618
618
619 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
619 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
620 try:
620 try:
621 data = self.get_update_data(update_url)
621 data = self.get_update_data(update_url)
622 except urllib2.URLError as e:
622 except urllib2.URLError as e:
623 log.exception("Exception contacting upgrade server")
623 log.exception("Exception contacting upgrade server")
624 return _err('Failed to contact upgrade server: %r' % e)
624 return _err('Failed to contact upgrade server: %r' % e)
625 except ValueError as e:
625 except ValueError as e:
626 log.exception("Bad data sent from update server")
626 log.exception("Bad data sent from update server")
627 return _err('Bad data sent from update server')
627 return _err('Bad data sent from update server')
628
628
629 latest = data['versions'][0]
629 latest = data['versions'][0]
630
630
631 c.update_url = update_url
631 c.update_url = update_url
632 c.latest_data = latest
632 c.latest_data = latest
633 c.latest_ver = latest['version']
633 c.latest_ver = latest['version']
634 c.cur_ver = rhodecode.__version__
634 c.cur_ver = rhodecode.__version__
635 c.should_upgrade = False
635 c.should_upgrade = False
636
636
637 if (packaging.version.Version(c.latest_ver) >
637 if (packaging.version.Version(c.latest_ver) >
638 packaging.version.Version(c.cur_ver)):
638 packaging.version.Version(c.cur_ver)):
639 c.should_upgrade = True
639 c.should_upgrade = True
640 c.important_notices = latest['general']
640 c.important_notices = latest['general']
641
641
642 return render('admin/settings/settings_system_update.html')
642 return render('admin/settings/settings_system_update.html')
643
643
644 @HasPermissionAllDecorator('hg.admin')
644 @HasPermissionAllDecorator('hg.admin')
645 def settings_supervisor(self):
645 def settings_supervisor(self):
646 c.rhodecode_ini = rhodecode.CONFIG
646 c.rhodecode_ini = rhodecode.CONFIG
647 c.active = 'supervisor'
647 c.active = 'supervisor'
648
648
649 c.supervisor_procs = OrderedDict([
649 c.supervisor_procs = OrderedDict([
650 (SUPERVISOR_MASTER, {}),
650 (SUPERVISOR_MASTER, {}),
651 ])
651 ])
652
652
653 c.log_size = 10240
653 c.log_size = 10240
654 supervisor = SupervisorModel()
654 supervisor = SupervisorModel()
655
655
656 _connection = supervisor.get_connection(
656 _connection = supervisor.get_connection(
657 c.rhodecode_ini.get('supervisor.uri'))
657 c.rhodecode_ini.get('supervisor.uri'))
658 c.connection_error = None
658 c.connection_error = None
659 try:
659 try:
660 _connection.supervisor.getAllProcessInfo()
660 _connection.supervisor.getAllProcessInfo()
661 except Exception as e:
661 except Exception as e:
662 c.connection_error = str(e)
662 c.connection_error = str(e)
663 log.exception("Exception reading supervisor data")
663 log.exception("Exception reading supervisor data")
664 return render('admin/settings/settings.html')
664 return render('admin/settings/settings.html')
665
665
666 groupid = c.rhodecode_ini.get('supervisor.group_id')
666 groupid = c.rhodecode_ini.get('supervisor.group_id')
667
667
668 # feed our group processes to the main
668 # feed our group processes to the main
669 for proc in supervisor.get_group_processes(_connection, groupid):
669 for proc in supervisor.get_group_processes(_connection, groupid):
670 c.supervisor_procs[proc['name']] = {}
670 c.supervisor_procs[proc['name']] = {}
671
671
672 for k in c.supervisor_procs.keys():
672 for k in c.supervisor_procs.keys():
673 try:
673 try:
674 # master process info
674 # master process info
675 if k == SUPERVISOR_MASTER:
675 if k == SUPERVISOR_MASTER:
676 _data = supervisor.get_master_state(_connection)
676 _data = supervisor.get_master_state(_connection)
677 _data['name'] = 'supervisor master'
677 _data['name'] = 'supervisor master'
678 _data['description'] = 'pid %s, id: %s, ver: %s' % (
678 _data['description'] = 'pid %s, id: %s, ver: %s' % (
679 _data['pid'], _data['id'], _data['ver'])
679 _data['pid'], _data['id'], _data['ver'])
680 c.supervisor_procs[k] = _data
680 c.supervisor_procs[k] = _data
681 else:
681 else:
682 procid = groupid + ":" + k
682 procid = groupid + ":" + k
683 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
683 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
684 except Exception as e:
684 except Exception as e:
685 log.exception("Exception reading supervisor data")
685 log.exception("Exception reading supervisor data")
686 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
686 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
687
687
688 return render('admin/settings/settings.html')
688 return render('admin/settings/settings.html')
689
689
690 @HasPermissionAllDecorator('hg.admin')
690 @HasPermissionAllDecorator('hg.admin')
691 def settings_supervisor_log(self, procid):
691 def settings_supervisor_log(self, procid):
692 import rhodecode
692 import rhodecode
693 c.rhodecode_ini = rhodecode.CONFIG
693 c.rhodecode_ini = rhodecode.CONFIG
694 c.active = 'supervisor_tail'
694 c.active = 'supervisor_tail'
695
695
696 supervisor = SupervisorModel()
696 supervisor = SupervisorModel()
697 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
697 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
698 groupid = c.rhodecode_ini.get('supervisor.group_id')
698 groupid = c.rhodecode_ini.get('supervisor.group_id')
699 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
699 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
700
700
701 c.log_size = 10240
701 c.log_size = 10240
702 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
702 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
703 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
703 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
704
704
705 return render('admin/settings/settings.html')
705 return render('admin/settings/settings.html')
706
706
707 @HasPermissionAllDecorator('hg.admin')
707 @HasPermissionAllDecorator('hg.admin')
708 @auth.CSRFRequired()
708 @auth.CSRFRequired()
709 def settings_labs_update(self):
709 def settings_labs_update(self):
710 """POST /admin/settings/labs: All items in the collection"""
710 """POST /admin/settings/labs: All items in the collection"""
711 # url('admin_settings/labs', method={'POST'})
711 # url('admin_settings/labs', method={'POST'})
712 c.active = 'labs'
712 c.active = 'labs'
713
713
714 application_form = LabsSettingsForm()()
714 application_form = LabsSettingsForm()()
715 try:
715 try:
716 form_result = application_form.to_python(dict(request.POST))
716 form_result = application_form.to_python(dict(request.POST))
717 except formencode.Invalid as errors:
717 except formencode.Invalid as errors:
718 h.flash(
718 h.flash(
719 _('Some form inputs contain invalid data.'),
719 _('Some form inputs contain invalid data.'),
720 category='error')
720 category='error')
721 return htmlfill.render(
721 return htmlfill.render(
722 render('admin/settings/settings.html'),
722 render('admin/settings/settings.html'),
723 defaults=errors.value,
723 defaults=errors.value,
724 errors=errors.error_dict or {},
724 errors=errors.error_dict or {},
725 prefix_error=False,
725 prefix_error=False,
726 encoding='UTF-8',
726 encoding='UTF-8',
727 force_defaults=False
727 force_defaults=False
728 )
728 )
729
729
730 try:
730 try:
731 session = Session()
731 session = Session()
732 for setting in _LAB_SETTINGS:
732 for setting in _LAB_SETTINGS:
733 setting_name = setting.key[len('rhodecode_'):]
733 setting_name = setting.key[len('rhodecode_'):]
734 sett = SettingsModel().create_or_update_setting(
734 sett = SettingsModel().create_or_update_setting(
735 setting_name, form_result[setting.key], setting.type)
735 setting_name, form_result[setting.key], setting.type)
736 session.add(sett)
736 session.add(sett)
737
737
738 except Exception:
738 except Exception:
739 log.exception('Exception while updating lab settings')
739 log.exception('Exception while updating lab settings')
740 h.flash(_('Error occurred during updating labs settings'),
740 h.flash(_('Error occurred during updating labs settings'),
741 category='error')
741 category='error')
742 else:
742 else:
743 Session().commit()
743 Session().commit()
744 SettingsModel().invalidate_settings_cache()
744 SettingsModel().invalidate_settings_cache()
745 h.flash(_('Updated Labs settings'), category='success')
745 h.flash(_('Updated Labs settings'), category='success')
746 return redirect(url('admin_settings_labs'))
746 return redirect(url('admin_settings_labs'))
747
747
748 return htmlfill.render(
748 return htmlfill.render(
749 render('admin/settings/settings.html'),
749 render('admin/settings/settings.html'),
750 defaults=self._form_defaults(),
750 defaults=self._form_defaults(),
751 encoding='UTF-8',
751 encoding='UTF-8',
752 force_defaults=False)
752 force_defaults=False)
753
753
754 @HasPermissionAllDecorator('hg.admin')
754 @HasPermissionAllDecorator('hg.admin')
755 def settings_labs(self):
755 def settings_labs(self):
756 """GET /admin/settings/labs: All items in the collection"""
756 """GET /admin/settings/labs: All items in the collection"""
757 # url('admin_settings_labs')
757 # url('admin_settings_labs')
758 if not c.labs_active:
758 if not c.labs_active:
759 redirect(url('admin_settings'))
759 redirect(url('admin_settings'))
760
760
761 c.active = 'labs'
761 c.active = 'labs'
762 c.lab_settings = _LAB_SETTINGS
762 c.lab_settings = _LAB_SETTINGS
763
763
764 return htmlfill.render(
764 return htmlfill.render(
765 render('admin/settings/settings.html'),
765 render('admin/settings/settings.html'),
766 defaults=self._form_defaults(),
766 defaults=self._form_defaults(),
767 encoding='UTF-8',
767 encoding='UTF-8',
768 force_defaults=False)
768 force_defaults=False)
769
769
770 def _form_defaults(self):
770 def _form_defaults(self):
771 defaults = SettingsModel().get_all_settings()
771 defaults = SettingsModel().get_all_settings()
772 defaults.update(self._get_hg_ui_settings())
772 defaults.update(self._get_hg_ui_settings())
773 defaults.update({
773 defaults.update({
774 'new_svn_branch': '',
774 'new_svn_branch': '',
775 'new_svn_tag': '',
775 'new_svn_tag': '',
776 })
776 })
777 return defaults
777 return defaults
778
778
779
779
780 # :param key: name of the setting including the 'rhodecode_' prefix
780 # :param key: name of the setting including the 'rhodecode_' prefix
781 # :param type: the RhodeCodeSetting type to use.
781 # :param type: the RhodeCodeSetting type to use.
782 # :param group: the i18ned group in which we should dispaly this setting
782 # :param group: the i18ned group in which we should dispaly this setting
783 # :param label: the i18ned label we should display for this setting
783 # :param label: the i18ned label we should display for this setting
784 # :param help: the i18ned help we should dispaly for this setting
784 # :param help: the i18ned help we should dispaly for this setting
785 LabSetting = collections.namedtuple(
785 LabSetting = collections.namedtuple(
786 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
786 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
787
787
788
788
789 # This list has to be kept in sync with the form
789 # This list has to be kept in sync with the form
790 # rhodecode.model.forms.LabsSettingsForm.
790 # rhodecode.model.forms.LabsSettingsForm.
791 _LAB_SETTINGS = [
791 _LAB_SETTINGS = [
792 LabSetting(
792 LabSetting(
793 key='rhodecode_proxy_subversion_http_requests',
793 key='rhodecode_proxy_subversion_http_requests',
794 type='bool',
794 type='bool',
795 group=lazy_ugettext('Subversion HTTP Support'),
795 group=lazy_ugettext('Subversion HTTP Support'),
796 label=lazy_ugettext('Proxy subversion HTTP requests'),
796 label=lazy_ugettext('Proxy subversion HTTP requests'),
797 help='' # Do not translate the empty string!
797 help='' # Do not translate the empty string!
798 ),
798 ),
799 LabSetting(
799 LabSetting(
800 key='rhodecode_subversion_http_server_url',
800 key='rhodecode_subversion_http_server_url',
801 type='str',
801 type='str',
802 group=lazy_ugettext('Subversion HTTP Server URL'),
802 group=lazy_ugettext('Subversion HTTP Server URL'),
803 label='', # Do not translate the empty string!
803 label='', # Do not translate the empty string!
804 help=lazy_ugettext('e.g. http://localhost:8080/')
804 help=lazy_ugettext('e.g. http://localhost:8080/')
805 ),
805 ),
806 ]
806 ]
General Comments 0
You need to be logged in to leave comments. Login now