##// END OF EJS Templates
removed duplicated logic of how we invalidate caches for repos
marcink -
r3693:6843cabe beta
parent child Browse files
Show More
@@ -1,606 +1,606 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Repositories controller for RhodeCode
6 Repositories controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from webob.exc import HTTPInternalServerError, HTTPForbidden
31 from webob.exc import HTTPInternalServerError, HTTPForbidden
32 from pylons import request, session, tmpl_context as c, url
32 from pylons import request, session, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.exc import IntegrityError
35 from sqlalchemy.exc import IntegrityError
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
41 HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
41 HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
43 from rhodecode.lib.utils import action_logger, repo_name_slug
44 from rhodecode.lib.helpers import get_token
44 from rhodecode.lib.helpers import get_token
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
47 RhodeCodeSetting, RepositoryField
47 RhodeCodeSetting, RepositoryField
48 from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
48 from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
49 from rhodecode.model.scm import ScmModel, GroupList
49 from rhodecode.model.scm import ScmModel, GroupList
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from sqlalchemy.sql.expression import func
52 from sqlalchemy.sql.expression import func
53 from rhodecode.lib.exceptions import AttachedForksError
53 from rhodecode.lib.exceptions import AttachedForksError
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class ReposController(BaseRepoController):
58 class ReposController(BaseRepoController):
59 """
59 """
60 REST Controller styled on the Atom Publishing Protocol"""
60 REST Controller styled on the Atom Publishing Protocol"""
61 # To properly map this controller, ensure your config/routing.py
61 # To properly map this controller, ensure your config/routing.py
62 # file has a resource setup:
62 # file has a resource setup:
63 # map.resource('repo', 'repos')
63 # map.resource('repo', 'repos')
64
64
65 @LoginRequired()
65 @LoginRequired()
66 def __before__(self):
66 def __before__(self):
67 c.admin_user = session.get('admin_user')
67 c.admin_user = session.get('admin_user')
68 c.admin_username = session.get('admin_username')
68 c.admin_username = session.get('admin_username')
69 super(ReposController, self).__before__()
69 super(ReposController, self).__before__()
70
70
71 def __load_defaults(self):
71 def __load_defaults(self):
72 acl_groups = GroupList(RepoGroup.query().all(),
72 acl_groups = GroupList(RepoGroup.query().all(),
73 perm_set=['group.write', 'group.admin'])
73 perm_set=['group.write', 'group.admin'])
74 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
74 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
75 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
75 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
76
76
77 repo_model = RepoModel()
77 repo_model = RepoModel()
78 c.users_array = repo_model.get_users_js()
78 c.users_array = repo_model.get_users_js()
79 c.users_groups_array = repo_model.get_users_groups_js()
79 c.users_groups_array = repo_model.get_users_groups_js()
80 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
80 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
81 c.landing_revs_choices = choices
81 c.landing_revs_choices = choices
82
82
83 def __load_data(self, repo_name=None):
83 def __load_data(self, repo_name=None):
84 """
84 """
85 Load defaults settings for edit, and update
85 Load defaults settings for edit, and update
86
86
87 :param repo_name:
87 :param repo_name:
88 """
88 """
89 self.__load_defaults()
89 self.__load_defaults()
90
90
91 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
91 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
92 repo = db_repo.scm_instance
92 repo = db_repo.scm_instance
93
93
94 if c.repo_info is None:
94 if c.repo_info is None:
95 h.not_mapped_error(repo_name)
95 h.not_mapped_error(repo_name)
96 return redirect(url('repos'))
96 return redirect(url('repos'))
97
97
98 ##override defaults for exact repo info here git/hg etc
98 ##override defaults for exact repo info here git/hg etc
99 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
99 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
100 c.landing_revs_choices = choices
100 c.landing_revs_choices = choices
101
101
102 c.default_user_id = User.get_by_username('default').user_id
102 c.default_user_id = User.get_by_username('default').user_id
103 c.in_public_journal = UserFollowing.query()\
103 c.in_public_journal = UserFollowing.query()\
104 .filter(UserFollowing.user_id == c.default_user_id)\
104 .filter(UserFollowing.user_id == c.default_user_id)\
105 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
105 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
106
106
107 if c.repo_info.stats:
107 if c.repo_info.stats:
108 # this is on what revision we ended up so we add +1 for count
108 # this is on what revision we ended up so we add +1 for count
109 last_rev = c.repo_info.stats.stat_on_revision + 1
109 last_rev = c.repo_info.stats.stat_on_revision + 1
110 else:
110 else:
111 last_rev = 0
111 last_rev = 0
112 c.stats_revision = last_rev
112 c.stats_revision = last_rev
113
113
114 c.repo_last_rev = repo.count() if repo.revisions else 0
114 c.repo_last_rev = repo.count() if repo.revisions else 0
115
115
116 if last_rev == 0 or c.repo_last_rev == 0:
116 if last_rev == 0 or c.repo_last_rev == 0:
117 c.stats_percentage = 0
117 c.stats_percentage = 0
118 else:
118 else:
119 c.stats_percentage = '%.2f' % ((float((last_rev)) /
119 c.stats_percentage = '%.2f' % ((float((last_rev)) /
120 c.repo_last_rev) * 100)
120 c.repo_last_rev) * 100)
121
121
122 c.repo_fields = RepositoryField.query()\
122 c.repo_fields = RepositoryField.query()\
123 .filter(RepositoryField.repository == db_repo).all()
123 .filter(RepositoryField.repository == db_repo).all()
124
124
125 defaults = RepoModel()._get_defaults(repo_name)
125 defaults = RepoModel()._get_defaults(repo_name)
126
126
127 c.repos_list = [('', _('--REMOVE FORK--'))]
127 c.repos_list = [('', _('--REMOVE FORK--'))]
128 c.repos_list += [(x.repo_id, x.repo_name) for x in
128 c.repos_list += [(x.repo_id, x.repo_name) for x in
129 Repository.query().order_by(Repository.repo_name).all()
129 Repository.query().order_by(Repository.repo_name).all()
130 if x.repo_id != c.repo_info.repo_id]
130 if x.repo_id != c.repo_info.repo_id]
131
131
132 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
132 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
133 return defaults
133 return defaults
134
134
135 @HasPermissionAllDecorator('hg.admin')
135 @HasPermissionAllDecorator('hg.admin')
136 def index(self, format='html'):
136 def index(self, format='html'):
137 """GET /repos: All items in the collection"""
137 """GET /repos: All items in the collection"""
138 # url('repos')
138 # url('repos')
139
139
140 c.repos_list = Repository.query()\
140 c.repos_list = Repository.query()\
141 .order_by(func.lower(Repository.repo_name))\
141 .order_by(func.lower(Repository.repo_name))\
142 .all()
142 .all()
143
143
144 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
144 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
145 admin=True,
145 admin=True,
146 super_user_actions=True)
146 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 @NotAnonymous()
152 @NotAnonymous()
153 def create(self):
153 def create(self):
154 """
154 """
155 POST /repos: Create a new item"""
155 POST /repos: Create a new item"""
156 # url('repos')
156 # url('repos')
157
157
158 self.__load_defaults()
158 self.__load_defaults()
159 form_result = {}
159 form_result = {}
160 try:
160 try:
161 form_result = RepoForm(repo_groups=c.repo_groups_choices,
161 form_result = RepoForm(repo_groups=c.repo_groups_choices,
162 landing_revs=c.landing_revs_choices)()\
162 landing_revs=c.landing_revs_choices)()\
163 .to_python(dict(request.POST))
163 .to_python(dict(request.POST))
164
164
165 new_repo = RepoModel().create(form_result,
165 new_repo = RepoModel().create(form_result,
166 self.rhodecode_user.user_id)
166 self.rhodecode_user.user_id)
167 if form_result['clone_uri']:
167 if form_result['clone_uri']:
168 h.flash(_('Created repository %s from %s') \
168 h.flash(_('Created repository %s from %s') \
169 % (form_result['repo_name'], form_result['clone_uri']),
169 % (form_result['repo_name'], form_result['clone_uri']),
170 category='success')
170 category='success')
171 else:
171 else:
172 repo_url = h.link_to(form_result['repo_name'],
172 repo_url = h.link_to(form_result['repo_name'],
173 h.url('summary_home', repo_name=form_result['repo_name_full']))
173 h.url('summary_home', repo_name=form_result['repo_name_full']))
174 h.flash(h.literal(_('Created repository %s') % repo_url),
174 h.flash(h.literal(_('Created repository %s') % repo_url),
175 category='success')
175 category='success')
176
176
177 if request.POST.get('user_created'):
177 if request.POST.get('user_created'):
178 # created by regular non admin user
178 # created by regular non admin user
179 action_logger(self.rhodecode_user, 'user_created_repo',
179 action_logger(self.rhodecode_user, 'user_created_repo',
180 form_result['repo_name_full'], self.ip_addr,
180 form_result['repo_name_full'], self.ip_addr,
181 self.sa)
181 self.sa)
182 else:
182 else:
183 action_logger(self.rhodecode_user, 'admin_created_repo',
183 action_logger(self.rhodecode_user, 'admin_created_repo',
184 form_result['repo_name_full'], self.ip_addr,
184 form_result['repo_name_full'], self.ip_addr,
185 self.sa)
185 self.sa)
186 Session().commit()
186 Session().commit()
187 except formencode.Invalid, errors:
187 except formencode.Invalid, errors:
188 return htmlfill.render(
188 return htmlfill.render(
189 render('admin/repos/repo_add.html'),
189 render('admin/repos/repo_add.html'),
190 defaults=errors.value,
190 defaults=errors.value,
191 errors=errors.error_dict or {},
191 errors=errors.error_dict or {},
192 prefix_error=False,
192 prefix_error=False,
193 encoding="UTF-8")
193 encoding="UTF-8")
194
194
195 except Exception:
195 except Exception:
196 log.error(traceback.format_exc())
196 log.error(traceback.format_exc())
197 msg = _('Error creating repository %s') \
197 msg = _('Error creating repository %s') \
198 % form_result.get('repo_name')
198 % form_result.get('repo_name')
199 h.flash(msg, category='error')
199 h.flash(msg, category='error')
200 if c.rhodecode_user.is_admin:
200 if c.rhodecode_user.is_admin:
201 return redirect(url('repos'))
201 return redirect(url('repos'))
202 return redirect(url('home'))
202 return redirect(url('home'))
203 #redirect to our new repo !
203 #redirect to our new repo !
204 return redirect(url('summary_home', repo_name=new_repo.repo_name))
204 return redirect(url('summary_home', repo_name=new_repo.repo_name))
205
205
206 @NotAnonymous()
206 @NotAnonymous()
207 def create_repository(self):
207 def create_repository(self):
208 """GET /_admin/create_repository: Form to create a new item"""
208 """GET /_admin/create_repository: Form to create a new item"""
209 new_repo = request.GET.get('repo', '')
209 new_repo = request.GET.get('repo', '')
210 parent_group = request.GET.get('parent_group')
210 parent_group = request.GET.get('parent_group')
211 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
211 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
212 #you're not super admin nor have global create permissions,
212 #you're not super admin nor have global create permissions,
213 #but maybe you have at least write permission to a parent group ?
213 #but maybe you have at least write permission to a parent group ?
214 _gr = RepoGroup.get(parent_group)
214 _gr = RepoGroup.get(parent_group)
215 gr_name = _gr.group_name if _gr else None
215 gr_name = _gr.group_name if _gr else None
216 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
216 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
217 raise HTTPForbidden
217 raise HTTPForbidden
218
218
219 acl_groups = GroupList(RepoGroup.query().all(),
219 acl_groups = GroupList(RepoGroup.query().all(),
220 perm_set=['group.write', 'group.admin'])
220 perm_set=['group.write', 'group.admin'])
221 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
221 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
222 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
222 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
223 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
223 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
224
224
225 c.new_repo = repo_name_slug(new_repo)
225 c.new_repo = repo_name_slug(new_repo)
226
226
227 ## apply the defaults from defaults page
227 ## apply the defaults from defaults page
228 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
228 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
229 if parent_group:
229 if parent_group:
230 defaults.update({'repo_group': parent_group})
230 defaults.update({'repo_group': parent_group})
231
231
232 return htmlfill.render(
232 return htmlfill.render(
233 render('admin/repos/repo_add.html'),
233 render('admin/repos/repo_add.html'),
234 defaults=defaults,
234 defaults=defaults,
235 errors={},
235 errors={},
236 prefix_error=False,
236 prefix_error=False,
237 encoding="UTF-8"
237 encoding="UTF-8"
238 )
238 )
239
239
240 @HasRepoPermissionAllDecorator('repository.admin')
240 @HasRepoPermissionAllDecorator('repository.admin')
241 def update(self, repo_name):
241 def update(self, repo_name):
242 """
242 """
243 PUT /repos/repo_name: Update an existing item"""
243 PUT /repos/repo_name: Update an existing item"""
244 # Forms posted to this method should contain a hidden field:
244 # Forms posted to this method should contain a hidden field:
245 # <input type="hidden" name="_method" value="PUT" />
245 # <input type="hidden" name="_method" value="PUT" />
246 # Or using helpers:
246 # Or using helpers:
247 # h.form(url('repo', repo_name=ID),
247 # h.form(url('repo', repo_name=ID),
248 # method='put')
248 # method='put')
249 # url('repo', repo_name=ID)
249 # url('repo', repo_name=ID)
250 self.__load_defaults()
250 self.__load_defaults()
251 repo_model = RepoModel()
251 repo_model = RepoModel()
252 changed_name = repo_name
252 changed_name = repo_name
253 #override the choices with extracted revisions !
253 #override the choices with extracted revisions !
254 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
254 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
255 c.landing_revs_choices = choices
255 c.landing_revs_choices = choices
256 repo = Repository.get_by_repo_name(repo_name)
256 repo = Repository.get_by_repo_name(repo_name)
257 _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
257 _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
258 'repo_group': repo.group.get_dict() \
258 'repo_group': repo.group.get_dict() \
259 if repo.group else {}},
259 if repo.group else {}},
260 repo_groups=c.repo_groups_choices,
260 repo_groups=c.repo_groups_choices,
261 landing_revs=c.landing_revs_choices)()
261 landing_revs=c.landing_revs_choices)()
262 try:
262 try:
263 form_result = _form.to_python(dict(request.POST))
263 form_result = _form.to_python(dict(request.POST))
264 repo = repo_model.update(repo_name, **form_result)
264 repo = repo_model.update(repo_name, **form_result)
265 invalidate_cache('get_repo_cached_%s' % repo_name)
265 ScmModel().mark_for_invalidation(repo_name)
266 h.flash(_('Repository %s updated successfully') % repo_name,
266 h.flash(_('Repository %s updated successfully') % repo_name,
267 category='success')
267 category='success')
268 changed_name = repo.repo_name
268 changed_name = repo.repo_name
269 action_logger(self.rhodecode_user, 'admin_updated_repo',
269 action_logger(self.rhodecode_user, 'admin_updated_repo',
270 changed_name, self.ip_addr, self.sa)
270 changed_name, self.ip_addr, self.sa)
271 Session().commit()
271 Session().commit()
272 except formencode.Invalid, errors:
272 except formencode.Invalid, errors:
273 defaults = self.__load_data(repo_name)
273 defaults = self.__load_data(repo_name)
274 defaults.update(errors.value)
274 defaults.update(errors.value)
275 return htmlfill.render(
275 return htmlfill.render(
276 render('admin/repos/repo_edit.html'),
276 render('admin/repos/repo_edit.html'),
277 defaults=defaults,
277 defaults=defaults,
278 errors=errors.error_dict or {},
278 errors=errors.error_dict or {},
279 prefix_error=False,
279 prefix_error=False,
280 encoding="UTF-8")
280 encoding="UTF-8")
281
281
282 except Exception:
282 except Exception:
283 log.error(traceback.format_exc())
283 log.error(traceback.format_exc())
284 h.flash(_('Error occurred during update of repository %s') \
284 h.flash(_('Error occurred during update of repository %s') \
285 % repo_name, category='error')
285 % repo_name, category='error')
286 return redirect(url('edit_repo', repo_name=changed_name))
286 return redirect(url('edit_repo', repo_name=changed_name))
287
287
288 @HasRepoPermissionAllDecorator('repository.admin')
288 @HasRepoPermissionAllDecorator('repository.admin')
289 def delete(self, repo_name):
289 def delete(self, repo_name):
290 """
290 """
291 DELETE /repos/repo_name: Delete an existing item"""
291 DELETE /repos/repo_name: Delete an existing item"""
292 # Forms posted to this method should contain a hidden field:
292 # Forms posted to this method should contain a hidden field:
293 # <input type="hidden" name="_method" value="DELETE" />
293 # <input type="hidden" name="_method" value="DELETE" />
294 # Or using helpers:
294 # Or using helpers:
295 # h.form(url('repo', repo_name=ID),
295 # h.form(url('repo', repo_name=ID),
296 # method='delete')
296 # method='delete')
297 # url('repo', repo_name=ID)
297 # url('repo', repo_name=ID)
298
298
299 repo_model = RepoModel()
299 repo_model = RepoModel()
300 repo = repo_model.get_by_repo_name(repo_name)
300 repo = repo_model.get_by_repo_name(repo_name)
301 if not repo:
301 if not repo:
302 h.not_mapped_error(repo_name)
302 h.not_mapped_error(repo_name)
303 return redirect(url('repos'))
303 return redirect(url('repos'))
304 try:
304 try:
305 _forks = repo.forks.count()
305 _forks = repo.forks.count()
306 handle_forks = None
306 handle_forks = None
307 if _forks and request.POST.get('forks'):
307 if _forks and request.POST.get('forks'):
308 do = request.POST['forks']
308 do = request.POST['forks']
309 if do == 'detach_forks':
309 if do == 'detach_forks':
310 handle_forks = 'detach'
310 handle_forks = 'detach'
311 h.flash(_('Detached %s forks') % _forks, category='success')
311 h.flash(_('Detached %s forks') % _forks, category='success')
312 elif do == 'delete_forks':
312 elif do == 'delete_forks':
313 handle_forks = 'delete'
313 handle_forks = 'delete'
314 h.flash(_('Deleted %s forks') % _forks, category='success')
314 h.flash(_('Deleted %s forks') % _forks, category='success')
315 repo_model.delete(repo, forks=handle_forks)
315 repo_model.delete(repo, forks=handle_forks)
316 action_logger(self.rhodecode_user, 'admin_deleted_repo',
316 action_logger(self.rhodecode_user, 'admin_deleted_repo',
317 repo_name, self.ip_addr, self.sa)
317 repo_name, self.ip_addr, self.sa)
318 invalidate_cache('get_repo_cached_%s' % repo_name)
318 ScmModel().mark_for_invalidation(repo_name)
319 h.flash(_('Deleted repository %s') % repo_name, category='success')
319 h.flash(_('Deleted repository %s') % repo_name, category='success')
320 Session().commit()
320 Session().commit()
321 except AttachedForksError:
321 except AttachedForksError:
322 h.flash(_('Cannot delete %s it still contains attached forks')
322 h.flash(_('Cannot delete %s it still contains attached forks')
323 % repo_name, category='warning')
323 % repo_name, category='warning')
324
324
325 except Exception:
325 except Exception:
326 log.error(traceback.format_exc())
326 log.error(traceback.format_exc())
327 h.flash(_('An error occurred during deletion of %s') % repo_name,
327 h.flash(_('An error occurred during deletion of %s') % repo_name,
328 category='error')
328 category='error')
329
329
330 return redirect(url('repos'))
330 return redirect(url('repos'))
331
331
332 @HasRepoPermissionAllDecorator('repository.admin')
332 @HasRepoPermissionAllDecorator('repository.admin')
333 def set_repo_perm_member(self, repo_name):
333 def set_repo_perm_member(self, repo_name):
334 form = RepoPermsForm()().to_python(request.POST)
334 form = RepoPermsForm()().to_python(request.POST)
335
335
336 perms_new = form['perms_new']
336 perms_new = form['perms_new']
337 perms_updates = form['perms_updates']
337 perms_updates = form['perms_updates']
338 cur_repo = repo_name
338 cur_repo = repo_name
339
339
340 # update permissions
340 # update permissions
341 for member, perm, member_type in perms_updates:
341 for member, perm, member_type in perms_updates:
342 if member_type == 'user':
342 if member_type == 'user':
343 # this updates existing one
343 # this updates existing one
344 RepoModel().grant_user_permission(
344 RepoModel().grant_user_permission(
345 repo=cur_repo, user=member, perm=perm
345 repo=cur_repo, user=member, perm=perm
346 )
346 )
347 else:
347 else:
348 RepoModel().grant_users_group_permission(
348 RepoModel().grant_users_group_permission(
349 repo=cur_repo, group_name=member, perm=perm
349 repo=cur_repo, group_name=member, perm=perm
350 )
350 )
351 # set new permissions
351 # set new permissions
352 for member, perm, member_type in perms_new:
352 for member, perm, member_type in perms_new:
353 if member_type == 'user':
353 if member_type == 'user':
354 RepoModel().grant_user_permission(
354 RepoModel().grant_user_permission(
355 repo=cur_repo, user=member, perm=perm
355 repo=cur_repo, user=member, perm=perm
356 )
356 )
357 else:
357 else:
358 RepoModel().grant_users_group_permission(
358 RepoModel().grant_users_group_permission(
359 repo=cur_repo, group_name=member, perm=perm
359 repo=cur_repo, group_name=member, perm=perm
360 )
360 )
361 #TODO: implement this
361 #TODO: implement this
362 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
362 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
363 # repo_name, self.ip_addr, self.sa)
363 # repo_name, self.ip_addr, self.sa)
364 Session().commit()
364 Session().commit()
365 h.flash(_('Repository permissions updated'), category='success')
365 h.flash(_('Repository permissions updated'), category='success')
366 return redirect(url('edit_repo', repo_name=repo_name))
366 return redirect(url('edit_repo', repo_name=repo_name))
367
367
368 @HasRepoPermissionAllDecorator('repository.admin')
368 @HasRepoPermissionAllDecorator('repository.admin')
369 def delete_perm_user(self, repo_name):
369 def delete_perm_user(self, repo_name):
370 """
370 """
371 DELETE an existing repository permission user
371 DELETE an existing repository permission user
372
372
373 :param repo_name:
373 :param repo_name:
374 """
374 """
375 try:
375 try:
376 RepoModel().revoke_user_permission(repo=repo_name,
376 RepoModel().revoke_user_permission(repo=repo_name,
377 user=request.POST['user_id'])
377 user=request.POST['user_id'])
378 #TODO: implement this
378 #TODO: implement this
379 #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
379 #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
380 # repo_name, self.ip_addr, self.sa)
380 # repo_name, self.ip_addr, self.sa)
381 Session().commit()
381 Session().commit()
382 except Exception:
382 except Exception:
383 log.error(traceback.format_exc())
383 log.error(traceback.format_exc())
384 h.flash(_('An error occurred during deletion of repository user'),
384 h.flash(_('An error occurred during deletion of repository user'),
385 category='error')
385 category='error')
386 raise HTTPInternalServerError()
386 raise HTTPInternalServerError()
387
387
388 @HasRepoPermissionAllDecorator('repository.admin')
388 @HasRepoPermissionAllDecorator('repository.admin')
389 def delete_perm_users_group(self, repo_name):
389 def delete_perm_users_group(self, repo_name):
390 """
390 """
391 DELETE an existing repository permission user group
391 DELETE an existing repository permission user group
392
392
393 :param repo_name:
393 :param repo_name:
394 """
394 """
395
395
396 try:
396 try:
397 RepoModel().revoke_users_group_permission(
397 RepoModel().revoke_users_group_permission(
398 repo=repo_name, group_name=request.POST['users_group_id']
398 repo=repo_name, group_name=request.POST['users_group_id']
399 )
399 )
400 Session().commit()
400 Session().commit()
401 except Exception:
401 except Exception:
402 log.error(traceback.format_exc())
402 log.error(traceback.format_exc())
403 h.flash(_('An error occurred during deletion of repository'
403 h.flash(_('An error occurred during deletion of repository'
404 ' user groups'),
404 ' user groups'),
405 category='error')
405 category='error')
406 raise HTTPInternalServerError()
406 raise HTTPInternalServerError()
407
407
408 @HasRepoPermissionAllDecorator('repository.admin')
408 @HasRepoPermissionAllDecorator('repository.admin')
409 def repo_stats(self, repo_name):
409 def repo_stats(self, repo_name):
410 """
410 """
411 DELETE an existing repository statistics
411 DELETE an existing repository statistics
412
412
413 :param repo_name:
413 :param repo_name:
414 """
414 """
415
415
416 try:
416 try:
417 RepoModel().delete_stats(repo_name)
417 RepoModel().delete_stats(repo_name)
418 Session().commit()
418 Session().commit()
419 except Exception, e:
419 except Exception, e:
420 log.error(traceback.format_exc())
420 log.error(traceback.format_exc())
421 h.flash(_('An error occurred during deletion of repository stats'),
421 h.flash(_('An error occurred during deletion of repository stats'),
422 category='error')
422 category='error')
423 return redirect(url('edit_repo', repo_name=repo_name))
423 return redirect(url('edit_repo', repo_name=repo_name))
424
424
425 @HasRepoPermissionAllDecorator('repository.admin')
425 @HasRepoPermissionAllDecorator('repository.admin')
426 def repo_cache(self, repo_name):
426 def repo_cache(self, repo_name):
427 """
427 """
428 INVALIDATE existing repository cache
428 INVALIDATE existing repository cache
429
429
430 :param repo_name:
430 :param repo_name:
431 """
431 """
432
432
433 try:
433 try:
434 ScmModel().mark_for_invalidation(repo_name)
434 ScmModel().mark_for_invalidation(repo_name)
435 Session().commit()
435 Session().commit()
436 except Exception, e:
436 except Exception, e:
437 log.error(traceback.format_exc())
437 log.error(traceback.format_exc())
438 h.flash(_('An error occurred during cache invalidation'),
438 h.flash(_('An error occurred during cache invalidation'),
439 category='error')
439 category='error')
440 return redirect(url('edit_repo', repo_name=repo_name))
440 return redirect(url('edit_repo', repo_name=repo_name))
441
441
442 @HasRepoPermissionAllDecorator('repository.admin')
442 @HasRepoPermissionAllDecorator('repository.admin')
443 def repo_locking(self, repo_name):
443 def repo_locking(self, repo_name):
444 """
444 """
445 Unlock repository when it is locked !
445 Unlock repository when it is locked !
446
446
447 :param repo_name:
447 :param repo_name:
448 """
448 """
449
449
450 try:
450 try:
451 repo = Repository.get_by_repo_name(repo_name)
451 repo = Repository.get_by_repo_name(repo_name)
452 if request.POST.get('set_lock'):
452 if request.POST.get('set_lock'):
453 Repository.lock(repo, c.rhodecode_user.user_id)
453 Repository.lock(repo, c.rhodecode_user.user_id)
454 elif request.POST.get('set_unlock'):
454 elif request.POST.get('set_unlock'):
455 Repository.unlock(repo)
455 Repository.unlock(repo)
456 except Exception, e:
456 except Exception, e:
457 log.error(traceback.format_exc())
457 log.error(traceback.format_exc())
458 h.flash(_('An error occurred during unlocking'),
458 h.flash(_('An error occurred during unlocking'),
459 category='error')
459 category='error')
460 return redirect(url('edit_repo', repo_name=repo_name))
460 return redirect(url('edit_repo', repo_name=repo_name))
461
461
462 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
462 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
463 def toggle_locking(self, repo_name):
463 def toggle_locking(self, repo_name):
464 """
464 """
465 Toggle locking of repository by simple GET call to url
465 Toggle locking of repository by simple GET call to url
466
466
467 :param repo_name:
467 :param repo_name:
468 """
468 """
469
469
470 try:
470 try:
471 repo = Repository.get_by_repo_name(repo_name)
471 repo = Repository.get_by_repo_name(repo_name)
472
472
473 if repo.enable_locking:
473 if repo.enable_locking:
474 if repo.locked[0]:
474 if repo.locked[0]:
475 Repository.unlock(repo)
475 Repository.unlock(repo)
476 action = _('Unlocked')
476 action = _('Unlocked')
477 else:
477 else:
478 Repository.lock(repo, c.rhodecode_user.user_id)
478 Repository.lock(repo, c.rhodecode_user.user_id)
479 action = _('Locked')
479 action = _('Locked')
480
480
481 h.flash(_('Repository has been %s') % action,
481 h.flash(_('Repository has been %s') % action,
482 category='success')
482 category='success')
483 except Exception, e:
483 except Exception, e:
484 log.error(traceback.format_exc())
484 log.error(traceback.format_exc())
485 h.flash(_('An error occurred during unlocking'),
485 h.flash(_('An error occurred during unlocking'),
486 category='error')
486 category='error')
487 return redirect(url('summary_home', repo_name=repo_name))
487 return redirect(url('summary_home', repo_name=repo_name))
488
488
489 @HasRepoPermissionAllDecorator('repository.admin')
489 @HasRepoPermissionAllDecorator('repository.admin')
490 def repo_public_journal(self, repo_name):
490 def repo_public_journal(self, repo_name):
491 """
491 """
492 Set's this repository to be visible in public journal,
492 Set's this repository to be visible in public journal,
493 in other words assing default user to follow this repo
493 in other words assing default user to follow this repo
494
494
495 :param repo_name:
495 :param repo_name:
496 """
496 """
497
497
498 cur_token = request.POST.get('auth_token')
498 cur_token = request.POST.get('auth_token')
499 token = get_token()
499 token = get_token()
500 if cur_token == token:
500 if cur_token == token:
501 try:
501 try:
502 repo_id = Repository.get_by_repo_name(repo_name).repo_id
502 repo_id = Repository.get_by_repo_name(repo_name).repo_id
503 user_id = User.get_by_username('default').user_id
503 user_id = User.get_by_username('default').user_id
504 self.scm_model.toggle_following_repo(repo_id, user_id)
504 self.scm_model.toggle_following_repo(repo_id, user_id)
505 h.flash(_('Updated repository visibility in public journal'),
505 h.flash(_('Updated repository visibility in public journal'),
506 category='success')
506 category='success')
507 Session().commit()
507 Session().commit()
508 except Exception:
508 except Exception:
509 h.flash(_('An error occurred during setting this'
509 h.flash(_('An error occurred during setting this'
510 ' repository in public journal'),
510 ' repository in public journal'),
511 category='error')
511 category='error')
512
512
513 else:
513 else:
514 h.flash(_('Token mismatch'), category='error')
514 h.flash(_('Token mismatch'), category='error')
515 return redirect(url('edit_repo', repo_name=repo_name))
515 return redirect(url('edit_repo', repo_name=repo_name))
516
516
517 @HasRepoPermissionAllDecorator('repository.admin')
517 @HasRepoPermissionAllDecorator('repository.admin')
518 def repo_pull(self, repo_name):
518 def repo_pull(self, repo_name):
519 """
519 """
520 Runs task to update given repository with remote changes,
520 Runs task to update given repository with remote changes,
521 ie. make pull on remote location
521 ie. make pull on remote location
522
522
523 :param repo_name:
523 :param repo_name:
524 """
524 """
525 try:
525 try:
526 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
526 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
527 h.flash(_('Pulled from remote location'), category='success')
527 h.flash(_('Pulled from remote location'), category='success')
528 except Exception, e:
528 except Exception, e:
529 h.flash(_('An error occurred during pull from remote location'),
529 h.flash(_('An error occurred during pull from remote location'),
530 category='error')
530 category='error')
531
531
532 return redirect(url('edit_repo', repo_name=repo_name))
532 return redirect(url('edit_repo', repo_name=repo_name))
533
533
534 @HasRepoPermissionAllDecorator('repository.admin')
534 @HasRepoPermissionAllDecorator('repository.admin')
535 def repo_as_fork(self, repo_name):
535 def repo_as_fork(self, repo_name):
536 """
536 """
537 Mark given repository as a fork of another
537 Mark given repository as a fork of another
538
538
539 :param repo_name:
539 :param repo_name:
540 """
540 """
541 try:
541 try:
542 fork_id = request.POST.get('id_fork_of')
542 fork_id = request.POST.get('id_fork_of')
543 repo = ScmModel().mark_as_fork(repo_name, fork_id,
543 repo = ScmModel().mark_as_fork(repo_name, fork_id,
544 self.rhodecode_user.username)
544 self.rhodecode_user.username)
545 fork = repo.fork.repo_name if repo.fork else _('Nothing')
545 fork = repo.fork.repo_name if repo.fork else _('Nothing')
546 Session().commit()
546 Session().commit()
547 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
547 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
548 category='success')
548 category='success')
549 except Exception, e:
549 except Exception, e:
550 log.error(traceback.format_exc())
550 log.error(traceback.format_exc())
551 h.flash(_('An error occurred during this operation'),
551 h.flash(_('An error occurred during this operation'),
552 category='error')
552 category='error')
553
553
554 return redirect(url('edit_repo', repo_name=repo_name))
554 return redirect(url('edit_repo', repo_name=repo_name))
555
555
556 @HasPermissionAllDecorator('hg.admin')
556 @HasPermissionAllDecorator('hg.admin')
557 def show(self, repo_name, format='html'):
557 def show(self, repo_name, format='html'):
558 """GET /repos/repo_name: Show a specific item"""
558 """GET /repos/repo_name: Show a specific item"""
559 # url('repo', repo_name=ID)
559 # url('repo', repo_name=ID)
560
560
561 @HasRepoPermissionAllDecorator('repository.admin')
561 @HasRepoPermissionAllDecorator('repository.admin')
562 def edit(self, repo_name, format='html'):
562 def edit(self, repo_name, format='html'):
563 """GET /repos/repo_name/edit: Form to edit an existing item"""
563 """GET /repos/repo_name/edit: Form to edit an existing item"""
564 # url('edit_repo', repo_name=ID)
564 # url('edit_repo', repo_name=ID)
565 defaults = self.__load_data(repo_name)
565 defaults = self.__load_data(repo_name)
566
566
567 return htmlfill.render(
567 return htmlfill.render(
568 render('admin/repos/repo_edit.html'),
568 render('admin/repos/repo_edit.html'),
569 defaults=defaults,
569 defaults=defaults,
570 encoding="UTF-8",
570 encoding="UTF-8",
571 force_defaults=False
571 force_defaults=False
572 )
572 )
573
573
574 @HasPermissionAllDecorator('hg.admin')
574 @HasPermissionAllDecorator('hg.admin')
575 def create_repo_field(self, repo_name):
575 def create_repo_field(self, repo_name):
576 try:
576 try:
577 form_result = RepoFieldForm()().to_python(dict(request.POST))
577 form_result = RepoFieldForm()().to_python(dict(request.POST))
578 new_field = RepositoryField()
578 new_field = RepositoryField()
579 new_field.repository = Repository.get_by_repo_name(repo_name)
579 new_field.repository = Repository.get_by_repo_name(repo_name)
580 new_field.field_key = form_result['new_field_key']
580 new_field.field_key = form_result['new_field_key']
581 new_field.field_type = form_result['new_field_type'] # python type
581 new_field.field_type = form_result['new_field_type'] # python type
582 new_field.field_value = form_result['new_field_value'] # set initial blank value
582 new_field.field_value = form_result['new_field_value'] # set initial blank value
583 new_field.field_desc = form_result['new_field_desc']
583 new_field.field_desc = form_result['new_field_desc']
584 new_field.field_label = form_result['new_field_label']
584 new_field.field_label = form_result['new_field_label']
585 Session().add(new_field)
585 Session().add(new_field)
586 Session().commit()
586 Session().commit()
587
587
588 except Exception, e:
588 except Exception, e:
589 log.error(traceback.format_exc())
589 log.error(traceback.format_exc())
590 msg = _('An error occurred during creation of field')
590 msg = _('An error occurred during creation of field')
591 if isinstance(e, formencode.Invalid):
591 if isinstance(e, formencode.Invalid):
592 msg += ". " + e.msg
592 msg += ". " + e.msg
593 h.flash(msg, category='error')
593 h.flash(msg, category='error')
594 return redirect(url('edit_repo', repo_name=repo_name))
594 return redirect(url('edit_repo', repo_name=repo_name))
595
595
596 @HasPermissionAllDecorator('hg.admin')
596 @HasPermissionAllDecorator('hg.admin')
597 def delete_repo_field(self, repo_name, field_id):
597 def delete_repo_field(self, repo_name, field_id):
598 field = RepositoryField.get_or_404(field_id)
598 field = RepositoryField.get_or_404(field_id)
599 try:
599 try:
600 Session().delete(field)
600 Session().delete(field)
601 Session().commit()
601 Session().commit()
602 except Exception, e:
602 except Exception, e:
603 log.error(traceback.format_exc())
603 log.error(traceback.format_exc())
604 msg = _('An error occurred during removal of field')
604 msg = _('An error occurred during removal of field')
605 h.flash(msg, category='error')
605 h.flash(msg, category='error')
606 return redirect(url('edit_repo', repo_name=repo_name))
606 return redirect(url('edit_repo', repo_name=repo_name))
@@ -1,516 +1,515 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 import pkg_resources
29 import pkg_resources
30 import platform
30 import platform
31
31
32 from sqlalchemy import func
32 from sqlalchemy import func
33 from formencode import htmlfill
33 from formencode import htmlfill
34 from pylons import request, session, tmpl_context as c, url, config
34 from pylons import request, session, tmpl_context as c, url, config
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, NotAnonymous, HasPermissionAny,\
40 HasPermissionAnyDecorator, NotAnonymous, HasPermissionAny,\
41 HasReposGroupPermissionAll, HasReposGroupPermissionAny, AuthUser
41 HasReposGroupPermissionAll, HasReposGroupPermissionAny, AuthUser
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.celerylib import tasks, run_task
43 from rhodecode.lib.celerylib import tasks, run_task
44 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
44 from rhodecode.lib.utils import repo2db_mapper, set_rhodecode_config, \
45 set_rhodecode_config, repo_name_slug, check_git_version
45 check_git_version
46 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
46 from rhodecode.model.db import RhodeCodeUi, Repository, RepoGroup, \
47 RhodeCodeSetting, PullRequest, PullRequestReviewers
47 RhodeCodeSetting, PullRequest, PullRequestReviewers
48 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
49 ApplicationUiSettingsForm, ApplicationVisualisationForm
49 ApplicationUiSettingsForm, ApplicationVisualisationForm
50 from rhodecode.model.scm import ScmModel, GroupList
50 from rhodecode.model.scm import ScmModel, GroupList
51 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
52 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.repo import RepoModel
53 from rhodecode.model.db import User
53 from rhodecode.model.db import User
54 from rhodecode.model.notification import EmailNotificationModel
54 from rhodecode.model.notification import EmailNotificationModel
55 from rhodecode.model.meta import Session
55 from rhodecode.model.meta import Session
56 from rhodecode.lib.utils2 import str2bool, safe_unicode
56 from rhodecode.lib.utils2 import str2bool, safe_unicode
57 from rhodecode.lib.compat import json
57 from rhodecode.lib.compat import json
58 from webob.exc import HTTPForbidden
59 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
60
59
61
60
62 class SettingsController(BaseController):
61 class SettingsController(BaseController):
63 """REST Controller styled on the Atom Publishing Protocol"""
62 """REST Controller styled on the Atom Publishing Protocol"""
64 # To properly map this controller, ensure your config/routing.py
63 # To properly map this controller, ensure your config/routing.py
65 # file has a resource setup:
64 # file has a resource setup:
66 # map.resource('setting', 'settings', controller='admin/settings',
65 # map.resource('setting', 'settings', controller='admin/settings',
67 # path_prefix='/admin', name_prefix='admin_')
66 # path_prefix='/admin', name_prefix='admin_')
68
67
69 @LoginRequired()
68 @LoginRequired()
70 def __before__(self):
69 def __before__(self):
71 c.admin_user = session.get('admin_user')
70 c.admin_user = session.get('admin_user')
72 c.admin_username = session.get('admin_username')
71 c.admin_username = session.get('admin_username')
73 c.modules = sorted([(p.project_name, p.version)
72 c.modules = sorted([(p.project_name, p.version)
74 for p in pkg_resources.working_set]
73 for p in pkg_resources.working_set]
75 + [('git', check_git_version())],
74 + [('git', check_git_version())],
76 key=lambda k: k[0].lower())
75 key=lambda k: k[0].lower())
77 c.py_version = platform.python_version()
76 c.py_version = platform.python_version()
78 c.platform = platform.platform()
77 c.platform = platform.platform()
79 super(SettingsController, self).__before__()
78 super(SettingsController, self).__before__()
80
79
81 @HasPermissionAllDecorator('hg.admin')
80 @HasPermissionAllDecorator('hg.admin')
82 def index(self, format='html'):
81 def index(self, format='html'):
83 """GET /admin/settings: All items in the collection"""
82 """GET /admin/settings: All items in the collection"""
84 # url('admin_settings')
83 # url('admin_settings')
85
84
86 defaults = RhodeCodeSetting.get_app_settings()
85 defaults = RhodeCodeSetting.get_app_settings()
87 defaults.update(self._get_hg_ui_settings())
86 defaults.update(self._get_hg_ui_settings())
88
87
89 return htmlfill.render(
88 return htmlfill.render(
90 render('admin/settings/settings.html'),
89 render('admin/settings/settings.html'),
91 defaults=defaults,
90 defaults=defaults,
92 encoding="UTF-8",
91 encoding="UTF-8",
93 force_defaults=False
92 force_defaults=False
94 )
93 )
95
94
96 @HasPermissionAllDecorator('hg.admin')
95 @HasPermissionAllDecorator('hg.admin')
97 def create(self):
96 def create(self):
98 """POST /admin/settings: Create a new item"""
97 """POST /admin/settings: Create a new item"""
99 # url('admin_settings')
98 # url('admin_settings')
100
99
101 @HasPermissionAllDecorator('hg.admin')
100 @HasPermissionAllDecorator('hg.admin')
102 def new(self, format='html'):
101 def new(self, format='html'):
103 """GET /admin/settings/new: Form to create a new item"""
102 """GET /admin/settings/new: Form to create a new item"""
104 # url('admin_new_setting')
103 # url('admin_new_setting')
105
104
106 @HasPermissionAllDecorator('hg.admin')
105 @HasPermissionAllDecorator('hg.admin')
107 def update(self, setting_id):
106 def update(self, setting_id):
108 """PUT /admin/settings/setting_id: Update an existing item"""
107 """PUT /admin/settings/setting_id: Update an existing item"""
109 # Forms posted to this method should contain a hidden field:
108 # Forms posted to this method should contain a hidden field:
110 # <input type="hidden" name="_method" value="PUT" />
109 # <input type="hidden" name="_method" value="PUT" />
111 # Or using helpers:
110 # Or using helpers:
112 # h.form(url('admin_setting', setting_id=ID),
111 # h.form(url('admin_setting', setting_id=ID),
113 # method='put')
112 # method='put')
114 # url('admin_setting', setting_id=ID)
113 # url('admin_setting', setting_id=ID)
115
114
116 if setting_id == 'mapping':
115 if setting_id == 'mapping':
117 rm_obsolete = request.POST.get('destroy', False)
116 rm_obsolete = request.POST.get('destroy', False)
118 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
117 log.debug('Rescanning directories with destroy=%s' % rm_obsolete)
119 initial = ScmModel().repo_scan()
118 initial = ScmModel().repo_scan()
120 log.debug('invalidating all repositories')
119 log.debug('invalidating all repositories')
121 for repo_name in initial.keys():
120 for repo_name in initial.keys():
122 invalidate_cache('get_repo_cached_%s' % repo_name)
121 ScmModel().mark_for_invalidation(repo_name)
123
122
124 added, removed = repo2db_mapper(initial, rm_obsolete)
123 added, removed = repo2db_mapper(initial, rm_obsolete)
125 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
124 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
126 h.flash(_('Repositories successfully '
125 h.flash(_('Repositories successfully '
127 'rescanned added: %s ; removed: %s') %
126 'rescanned added: %s ; removed: %s') %
128 (_repr(added), _repr(removed)),
127 (_repr(added), _repr(removed)),
129 category='success')
128 category='success')
130
129
131 if setting_id == 'whoosh':
130 if setting_id == 'whoosh':
132 repo_location = self._get_hg_ui_settings()['paths_root_path']
131 repo_location = self._get_hg_ui_settings()['paths_root_path']
133 full_index = request.POST.get('full_index', False)
132 full_index = request.POST.get('full_index', False)
134 run_task(tasks.whoosh_index, repo_location, full_index)
133 run_task(tasks.whoosh_index, repo_location, full_index)
135 h.flash(_('Whoosh reindex task scheduled'), category='success')
134 h.flash(_('Whoosh reindex task scheduled'), category='success')
136
135
137 if setting_id == 'global':
136 if setting_id == 'global':
138
137
139 application_form = ApplicationSettingsForm()()
138 application_form = ApplicationSettingsForm()()
140 try:
139 try:
141 form_result = application_form.to_python(dict(request.POST))
140 form_result = application_form.to_python(dict(request.POST))
142 except formencode.Invalid, errors:
141 except formencode.Invalid, errors:
143 return htmlfill.render(
142 return htmlfill.render(
144 render('admin/settings/settings.html'),
143 render('admin/settings/settings.html'),
145 defaults=errors.value,
144 defaults=errors.value,
146 errors=errors.error_dict or {},
145 errors=errors.error_dict or {},
147 prefix_error=False,
146 prefix_error=False,
148 encoding="UTF-8"
147 encoding="UTF-8"
149 )
148 )
150
149
151 try:
150 try:
152 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
151 sett1 = RhodeCodeSetting.get_by_name_or_create('title')
153 sett1.app_settings_value = form_result['rhodecode_title']
152 sett1.app_settings_value = form_result['rhodecode_title']
154 Session().add(sett1)
153 Session().add(sett1)
155
154
156 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
155 sett2 = RhodeCodeSetting.get_by_name_or_create('realm')
157 sett2.app_settings_value = form_result['rhodecode_realm']
156 sett2.app_settings_value = form_result['rhodecode_realm']
158 Session().add(sett2)
157 Session().add(sett2)
159
158
160 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
159 sett3 = RhodeCodeSetting.get_by_name_or_create('ga_code')
161 sett3.app_settings_value = form_result['rhodecode_ga_code']
160 sett3.app_settings_value = form_result['rhodecode_ga_code']
162 Session().add(sett3)
161 Session().add(sett3)
163
162
164 Session().commit()
163 Session().commit()
165 set_rhodecode_config(config)
164 set_rhodecode_config(config)
166 h.flash(_('Updated application settings'), category='success')
165 h.flash(_('Updated application settings'), category='success')
167
166
168 except Exception:
167 except Exception:
169 log.error(traceback.format_exc())
168 log.error(traceback.format_exc())
170 h.flash(_('Error occurred during updating '
169 h.flash(_('Error occurred during updating '
171 'application settings'),
170 'application settings'),
172 category='error')
171 category='error')
173
172
174 if setting_id == 'visual':
173 if setting_id == 'visual':
175
174
176 application_form = ApplicationVisualisationForm()()
175 application_form = ApplicationVisualisationForm()()
177 try:
176 try:
178 form_result = application_form.to_python(dict(request.POST))
177 form_result = application_form.to_python(dict(request.POST))
179 except formencode.Invalid, errors:
178 except formencode.Invalid, errors:
180 return htmlfill.render(
179 return htmlfill.render(
181 render('admin/settings/settings.html'),
180 render('admin/settings/settings.html'),
182 defaults=errors.value,
181 defaults=errors.value,
183 errors=errors.error_dict or {},
182 errors=errors.error_dict or {},
184 prefix_error=False,
183 prefix_error=False,
185 encoding="UTF-8"
184 encoding="UTF-8"
186 )
185 )
187
186
188 try:
187 try:
189 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
188 sett1 = RhodeCodeSetting.get_by_name_or_create('show_public_icon')
190 sett1.app_settings_value = \
189 sett1.app_settings_value = \
191 form_result['rhodecode_show_public_icon']
190 form_result['rhodecode_show_public_icon']
192 Session().add(sett1)
191 Session().add(sett1)
193
192
194 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
193 sett2 = RhodeCodeSetting.get_by_name_or_create('show_private_icon')
195 sett2.app_settings_value = \
194 sett2.app_settings_value = \
196 form_result['rhodecode_show_private_icon']
195 form_result['rhodecode_show_private_icon']
197 Session().add(sett2)
196 Session().add(sett2)
198
197
199 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
198 sett3 = RhodeCodeSetting.get_by_name_or_create('stylify_metatags')
200 sett3.app_settings_value = \
199 sett3.app_settings_value = \
201 form_result['rhodecode_stylify_metatags']
200 form_result['rhodecode_stylify_metatags']
202 Session().add(sett3)
201 Session().add(sett3)
203
202
204 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
203 sett4 = RhodeCodeSetting.get_by_name_or_create('lightweight_dashboard')
205 sett4.app_settings_value = \
204 sett4.app_settings_value = \
206 form_result['rhodecode_lightweight_dashboard']
205 form_result['rhodecode_lightweight_dashboard']
207 Session().add(sett4)
206 Session().add(sett4)
208
207
209 sett4 = RhodeCodeSetting.get_by_name_or_create('repository_fields')
208 sett4 = RhodeCodeSetting.get_by_name_or_create('repository_fields')
210 sett4.app_settings_value = \
209 sett4.app_settings_value = \
211 form_result['rhodecode_repository_fields']
210 form_result['rhodecode_repository_fields']
212 Session().add(sett4)
211 Session().add(sett4)
213
212
214 Session().commit()
213 Session().commit()
215 set_rhodecode_config(config)
214 set_rhodecode_config(config)
216 h.flash(_('Updated visualisation settings'),
215 h.flash(_('Updated visualisation settings'),
217 category='success')
216 category='success')
218
217
219 except Exception:
218 except Exception:
220 log.error(traceback.format_exc())
219 log.error(traceback.format_exc())
221 h.flash(_('Error occurred during updating '
220 h.flash(_('Error occurred during updating '
222 'visualisation settings'),
221 'visualisation settings'),
223 category='error')
222 category='error')
224
223
225 if setting_id == 'vcs':
224 if setting_id == 'vcs':
226 application_form = ApplicationUiSettingsForm()()
225 application_form = ApplicationUiSettingsForm()()
227 try:
226 try:
228 form_result = application_form.to_python(dict(request.POST))
227 form_result = application_form.to_python(dict(request.POST))
229 except formencode.Invalid, errors:
228 except formencode.Invalid, errors:
230 return htmlfill.render(
229 return htmlfill.render(
231 render('admin/settings/settings.html'),
230 render('admin/settings/settings.html'),
232 defaults=errors.value,
231 defaults=errors.value,
233 errors=errors.error_dict or {},
232 errors=errors.error_dict or {},
234 prefix_error=False,
233 prefix_error=False,
235 encoding="UTF-8"
234 encoding="UTF-8"
236 )
235 )
237
236
238 try:
237 try:
239 sett = RhodeCodeUi.get_by_key('push_ssl')
238 sett = RhodeCodeUi.get_by_key('push_ssl')
240 sett.ui_value = form_result['web_push_ssl']
239 sett.ui_value = form_result['web_push_ssl']
241 Session().add(sett)
240 Session().add(sett)
242
241
243 sett = RhodeCodeUi.get_by_key('/')
242 sett = RhodeCodeUi.get_by_key('/')
244 sett.ui_value = form_result['paths_root_path']
243 sett.ui_value = form_result['paths_root_path']
245 Session().add(sett)
244 Session().add(sett)
246
245
247 #HOOKS
246 #HOOKS
248 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
247 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE)
249 sett.ui_active = form_result['hooks_changegroup_update']
248 sett.ui_active = form_result['hooks_changegroup_update']
250 Session().add(sett)
249 Session().add(sett)
251
250
252 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
251 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_REPO_SIZE)
253 sett.ui_active = form_result['hooks_changegroup_repo_size']
252 sett.ui_active = form_result['hooks_changegroup_repo_size']
254 Session().add(sett)
253 Session().add(sett)
255
254
256 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
255 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PUSH)
257 sett.ui_active = form_result['hooks_changegroup_push_logger']
256 sett.ui_active = form_result['hooks_changegroup_push_logger']
258 Session().add(sett)
257 Session().add(sett)
259
258
260 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
259 sett = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_PULL)
261 sett.ui_active = form_result['hooks_outgoing_pull_logger']
260 sett.ui_active = form_result['hooks_outgoing_pull_logger']
262
261
263 Session().add(sett)
262 Session().add(sett)
264
263
265 ## EXTENSIONS
264 ## EXTENSIONS
266 sett = RhodeCodeUi.get_by_key('largefiles')
265 sett = RhodeCodeUi.get_by_key('largefiles')
267 if not sett:
266 if not sett:
268 #make one if it's not there !
267 #make one if it's not there !
269 sett = RhodeCodeUi()
268 sett = RhodeCodeUi()
270 sett.ui_key = 'largefiles'
269 sett.ui_key = 'largefiles'
271 sett.ui_section = 'extensions'
270 sett.ui_section = 'extensions'
272 sett.ui_active = form_result['extensions_largefiles']
271 sett.ui_active = form_result['extensions_largefiles']
273 Session().add(sett)
272 Session().add(sett)
274
273
275 sett = RhodeCodeUi.get_by_key('hgsubversion')
274 sett = RhodeCodeUi.get_by_key('hgsubversion')
276 if not sett:
275 if not sett:
277 #make one if it's not there !
276 #make one if it's not there !
278 sett = RhodeCodeUi()
277 sett = RhodeCodeUi()
279 sett.ui_key = 'hgsubversion'
278 sett.ui_key = 'hgsubversion'
280 sett.ui_section = 'extensions'
279 sett.ui_section = 'extensions'
281
280
282 sett.ui_active = form_result['extensions_hgsubversion']
281 sett.ui_active = form_result['extensions_hgsubversion']
283 Session().add(sett)
282 Session().add(sett)
284
283
285 # sett = RhodeCodeUi.get_by_key('hggit')
284 # sett = RhodeCodeUi.get_by_key('hggit')
286 # if not sett:
285 # if not sett:
287 # #make one if it's not there !
286 # #make one if it's not there !
288 # sett = RhodeCodeUi()
287 # sett = RhodeCodeUi()
289 # sett.ui_key = 'hggit'
288 # sett.ui_key = 'hggit'
290 # sett.ui_section = 'extensions'
289 # sett.ui_section = 'extensions'
291 #
290 #
292 # sett.ui_active = form_result['extensions_hggit']
291 # sett.ui_active = form_result['extensions_hggit']
293 # Session().add(sett)
292 # Session().add(sett)
294
293
295 Session().commit()
294 Session().commit()
296
295
297 h.flash(_('Updated VCS settings'), category='success')
296 h.flash(_('Updated VCS settings'), category='success')
298
297
299 except Exception:
298 except Exception:
300 log.error(traceback.format_exc())
299 log.error(traceback.format_exc())
301 h.flash(_('Error occurred during updating '
300 h.flash(_('Error occurred during updating '
302 'application settings'), category='error')
301 'application settings'), category='error')
303
302
304 if setting_id == 'hooks':
303 if setting_id == 'hooks':
305 ui_key = request.POST.get('new_hook_ui_key')
304 ui_key = request.POST.get('new_hook_ui_key')
306 ui_value = request.POST.get('new_hook_ui_value')
305 ui_value = request.POST.get('new_hook_ui_value')
307 try:
306 try:
308
307
309 if ui_value and ui_key:
308 if ui_value and ui_key:
310 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
309 RhodeCodeUi.create_or_update_hook(ui_key, ui_value)
311 h.flash(_('Added new hook'),
310 h.flash(_('Added new hook'),
312 category='success')
311 category='success')
313
312
314 # check for edits
313 # check for edits
315 update = False
314 update = False
316 _d = request.POST.dict_of_lists()
315 _d = request.POST.dict_of_lists()
317 for k, v in zip(_d.get('hook_ui_key', []),
316 for k, v in zip(_d.get('hook_ui_key', []),
318 _d.get('hook_ui_value_new', [])):
317 _d.get('hook_ui_value_new', [])):
319 RhodeCodeUi.create_or_update_hook(k, v)
318 RhodeCodeUi.create_or_update_hook(k, v)
320 update = True
319 update = True
321
320
322 if update:
321 if update:
323 h.flash(_('Updated hooks'), category='success')
322 h.flash(_('Updated hooks'), category='success')
324 Session().commit()
323 Session().commit()
325 except Exception:
324 except Exception:
326 log.error(traceback.format_exc())
325 log.error(traceback.format_exc())
327 h.flash(_('Error occurred during hook creation'),
326 h.flash(_('Error occurred during hook creation'),
328 category='error')
327 category='error')
329
328
330 return redirect(url('admin_edit_setting', setting_id='hooks'))
329 return redirect(url('admin_edit_setting', setting_id='hooks'))
331
330
332 if setting_id == 'email':
331 if setting_id == 'email':
333 test_email = request.POST.get('test_email')
332 test_email = request.POST.get('test_email')
334 test_email_subj = 'RhodeCode TestEmail'
333 test_email_subj = 'RhodeCode TestEmail'
335 test_email_body = 'RhodeCode Email test'
334 test_email_body = 'RhodeCode Email test'
336
335
337 test_email_html_body = EmailNotificationModel()\
336 test_email_html_body = EmailNotificationModel()\
338 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
337 .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT,
339 body=test_email_body)
338 body=test_email_body)
340
339
341 recipients = [test_email] if test_email else None
340 recipients = [test_email] if test_email else None
342
341
343 run_task(tasks.send_email, recipients, test_email_subj,
342 run_task(tasks.send_email, recipients, test_email_subj,
344 test_email_body, test_email_html_body)
343 test_email_body, test_email_html_body)
345
344
346 h.flash(_('Email task created'), category='success')
345 h.flash(_('Email task created'), category='success')
347 return redirect(url('admin_settings'))
346 return redirect(url('admin_settings'))
348
347
349 @HasPermissionAllDecorator('hg.admin')
348 @HasPermissionAllDecorator('hg.admin')
350 def delete(self, setting_id):
349 def delete(self, setting_id):
351 """DELETE /admin/settings/setting_id: Delete an existing item"""
350 """DELETE /admin/settings/setting_id: Delete an existing item"""
352 # Forms posted to this method should contain a hidden field:
351 # Forms posted to this method should contain a hidden field:
353 # <input type="hidden" name="_method" value="DELETE" />
352 # <input type="hidden" name="_method" value="DELETE" />
354 # Or using helpers:
353 # Or using helpers:
355 # h.form(url('admin_setting', setting_id=ID),
354 # h.form(url('admin_setting', setting_id=ID),
356 # method='delete')
355 # method='delete')
357 # url('admin_setting', setting_id=ID)
356 # url('admin_setting', setting_id=ID)
358 if setting_id == 'hooks':
357 if setting_id == 'hooks':
359 hook_id = request.POST.get('hook_id')
358 hook_id = request.POST.get('hook_id')
360 RhodeCodeUi.delete(hook_id)
359 RhodeCodeUi.delete(hook_id)
361 Session().commit()
360 Session().commit()
362
361
363 @HasPermissionAllDecorator('hg.admin')
362 @HasPermissionAllDecorator('hg.admin')
364 def show(self, setting_id, format='html'):
363 def show(self, setting_id, format='html'):
365 """
364 """
366 GET /admin/settings/setting_id: Show a specific item"""
365 GET /admin/settings/setting_id: Show a specific item"""
367 # url('admin_setting', setting_id=ID)
366 # url('admin_setting', setting_id=ID)
368
367
369 @HasPermissionAllDecorator('hg.admin')
368 @HasPermissionAllDecorator('hg.admin')
370 def edit(self, setting_id, format='html'):
369 def edit(self, setting_id, format='html'):
371 """
370 """
372 GET /admin/settings/setting_id/edit: Form to
371 GET /admin/settings/setting_id/edit: Form to
373 edit an existing item"""
372 edit an existing item"""
374 # url('admin_edit_setting', setting_id=ID)
373 # url('admin_edit_setting', setting_id=ID)
375
374
376 c.hooks = RhodeCodeUi.get_builtin_hooks()
375 c.hooks = RhodeCodeUi.get_builtin_hooks()
377 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
376 c.custom_hooks = RhodeCodeUi.get_custom_hooks()
378
377
379 return htmlfill.render(
378 return htmlfill.render(
380 render('admin/settings/hooks.html'),
379 render('admin/settings/hooks.html'),
381 defaults={},
380 defaults={},
382 encoding="UTF-8",
381 encoding="UTF-8",
383 force_defaults=False
382 force_defaults=False
384 )
383 )
385
384
386 def _load_my_repos_data(self):
385 def _load_my_repos_data(self):
387 repos_list = Session().query(Repository)\
386 repos_list = Session().query(Repository)\
388 .filter(Repository.user_id ==
387 .filter(Repository.user_id ==
389 self.rhodecode_user.user_id)\
388 self.rhodecode_user.user_id)\
390 .order_by(func.lower(Repository.repo_name)).all()
389 .order_by(func.lower(Repository.repo_name)).all()
391
390
392 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
391 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
393 admin=True)
392 admin=True)
394 #json used to render the grid
393 #json used to render the grid
395 return json.dumps(repos_data)
394 return json.dumps(repos_data)
396
395
397 @NotAnonymous()
396 @NotAnonymous()
398 def my_account(self):
397 def my_account(self):
399 """
398 """
400 GET /_admin/my_account Displays info about my account
399 GET /_admin/my_account Displays info about my account
401 """
400 """
402 # url('admin_settings_my_account')
401 # url('admin_settings_my_account')
403
402
404 c.user = User.get(self.rhodecode_user.user_id)
403 c.user = User.get(self.rhodecode_user.user_id)
405 c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
404 c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
406 ip_addr=self.ip_addr)
405 ip_addr=self.ip_addr)
407 c.ldap_dn = c.user.ldap_dn
406 c.ldap_dn = c.user.ldap_dn
408
407
409 if c.user.username == 'default':
408 if c.user.username == 'default':
410 h.flash(_("You can't edit this user since it's"
409 h.flash(_("You can't edit this user since it's"
411 " crucial for entire application"), category='warning')
410 " crucial for entire application"), category='warning')
412 return redirect(url('users'))
411 return redirect(url('users'))
413
412
414 #json used to render the grid
413 #json used to render the grid
415 c.data = self._load_my_repos_data()
414 c.data = self._load_my_repos_data()
416
415
417 defaults = c.user.get_dict()
416 defaults = c.user.get_dict()
418
417
419 c.form = htmlfill.render(
418 c.form = htmlfill.render(
420 render('admin/users/user_edit_my_account_form.html'),
419 render('admin/users/user_edit_my_account_form.html'),
421 defaults=defaults,
420 defaults=defaults,
422 encoding="UTF-8",
421 encoding="UTF-8",
423 force_defaults=False
422 force_defaults=False
424 )
423 )
425 return render('admin/users/user_edit_my_account.html')
424 return render('admin/users/user_edit_my_account.html')
426
425
427 @NotAnonymous()
426 @NotAnonymous()
428 def my_account_update(self):
427 def my_account_update(self):
429 """PUT /_admin/my_account_update: Update an existing item"""
428 """PUT /_admin/my_account_update: Update an existing item"""
430 # Forms posted to this method should contain a hidden field:
429 # Forms posted to this method should contain a hidden field:
431 # <input type="hidden" name="_method" value="PUT" />
430 # <input type="hidden" name="_method" value="PUT" />
432 # Or using helpers:
431 # Or using helpers:
433 # h.form(url('admin_settings_my_account_update'),
432 # h.form(url('admin_settings_my_account_update'),
434 # method='put')
433 # method='put')
435 # url('admin_settings_my_account_update', id=ID)
434 # url('admin_settings_my_account_update', id=ID)
436 uid = self.rhodecode_user.user_id
435 uid = self.rhodecode_user.user_id
437 c.user = User.get(self.rhodecode_user.user_id)
436 c.user = User.get(self.rhodecode_user.user_id)
438 c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
437 c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
439 ip_addr=self.ip_addr)
438 ip_addr=self.ip_addr)
440 c.ldap_dn = c.user.ldap_dn
439 c.ldap_dn = c.user.ldap_dn
441 email = self.rhodecode_user.email
440 email = self.rhodecode_user.email
442 _form = UserForm(edit=True,
441 _form = UserForm(edit=True,
443 old_data={'user_id': uid, 'email': email})()
442 old_data={'user_id': uid, 'email': email})()
444 form_result = {}
443 form_result = {}
445 try:
444 try:
446 form_result = _form.to_python(dict(request.POST))
445 form_result = _form.to_python(dict(request.POST))
447 skip_attrs = ['admin', 'active'] # skip attr for my account
446 skip_attrs = ['admin', 'active'] # skip attr for my account
448 if c.ldap_dn:
447 if c.ldap_dn:
449 #forbid updating username for ldap accounts
448 #forbid updating username for ldap accounts
450 skip_attrs.append('username')
449 skip_attrs.append('username')
451 UserModel().update(uid, form_result, skip_attrs=skip_attrs)
450 UserModel().update(uid, form_result, skip_attrs=skip_attrs)
452 h.flash(_('Your account was updated successfully'),
451 h.flash(_('Your account was updated successfully'),
453 category='success')
452 category='success')
454 Session().commit()
453 Session().commit()
455 except formencode.Invalid, errors:
454 except formencode.Invalid, errors:
456 #json used to render the grid
455 #json used to render the grid
457 c.data = self._load_my_repos_data()
456 c.data = self._load_my_repos_data()
458 c.form = htmlfill.render(
457 c.form = htmlfill.render(
459 render('admin/users/user_edit_my_account_form.html'),
458 render('admin/users/user_edit_my_account_form.html'),
460 defaults=errors.value,
459 defaults=errors.value,
461 errors=errors.error_dict or {},
460 errors=errors.error_dict or {},
462 prefix_error=False,
461 prefix_error=False,
463 encoding="UTF-8")
462 encoding="UTF-8")
464 return render('admin/users/user_edit_my_account.html')
463 return render('admin/users/user_edit_my_account.html')
465 except Exception:
464 except Exception:
466 log.error(traceback.format_exc())
465 log.error(traceback.format_exc())
467 h.flash(_('Error occurred during update of user %s') \
466 h.flash(_('Error occurred during update of user %s') \
468 % form_result.get('username'), category='error')
467 % form_result.get('username'), category='error')
469
468
470 return redirect(url('my_account'))
469 return redirect(url('my_account'))
471
470
472 @NotAnonymous()
471 @NotAnonymous()
473 def my_account_my_pullrequests(self):
472 def my_account_my_pullrequests(self):
474 c.show_closed = request.GET.get('pr_show_closed')
473 c.show_closed = request.GET.get('pr_show_closed')
475
474
476 def _filter(pr):
475 def _filter(pr):
477 s = sorted(pr, key=lambda o: o.created_on, reverse=True)
476 s = sorted(pr, key=lambda o: o.created_on, reverse=True)
478 if not c.show_closed:
477 if not c.show_closed:
479 s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
478 s = filter(lambda p: p.status != PullRequest.STATUS_CLOSED, s)
480 return s
479 return s
481
480
482 c.my_pull_requests = _filter(PullRequest.query()\
481 c.my_pull_requests = _filter(PullRequest.query()\
483 .filter(PullRequest.user_id ==
482 .filter(PullRequest.user_id ==
484 self.rhodecode_user.user_id)\
483 self.rhodecode_user.user_id)\
485 .all())
484 .all())
486
485
487 c.participate_in_pull_requests = _filter([
486 c.participate_in_pull_requests = _filter([
488 x.pull_request for x in PullRequestReviewers.query()\
487 x.pull_request for x in PullRequestReviewers.query()\
489 .filter(PullRequestReviewers.user_id ==
488 .filter(PullRequestReviewers.user_id ==
490 self.rhodecode_user.user_id).all()])
489 self.rhodecode_user.user_id).all()])
491
490
492 return render('admin/users/user_edit_my_account_pullrequests.html')
491 return render('admin/users/user_edit_my_account_pullrequests.html')
493
492
494 def _get_hg_ui_settings(self):
493 def _get_hg_ui_settings(self):
495 ret = RhodeCodeUi.query().all()
494 ret = RhodeCodeUi.query().all()
496
495
497 if not ret:
496 if not ret:
498 raise Exception('Could not get application ui settings !')
497 raise Exception('Could not get application ui settings !')
499 settings = {}
498 settings = {}
500 for each in ret:
499 for each in ret:
501 k = each.ui_key
500 k = each.ui_key
502 v = each.ui_value
501 v = each.ui_value
503 if k == '/':
502 if k == '/':
504 k = 'root_path'
503 k = 'root_path'
505
504
506 if k == 'push_ssl':
505 if k == 'push_ssl':
507 v = str2bool(v)
506 v = str2bool(v)
508
507
509 if k.find('.') != -1:
508 if k.find('.') != -1:
510 k = k.replace('.', '_')
509 k = k.replace('.', '_')
511
510
512 if each.ui_section in ['hooks', 'extensions']:
511 if each.ui_section in ['hooks', 'extensions']:
513 v = each.ui_active
512 v = each.ui_active
514
513
515 settings[each.ui_section + '_' + k] = v
514 settings[each.ui_section + '_' + k] = v
516 return settings
515 return settings
@@ -1,345 +1,345 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import logging
5 import logging
6 import time
6 import time
7 import traceback
7 import traceback
8
8
9 from paste.auth.basic import AuthBasicAuthenticator
9 from paste.auth.basic import AuthBasicAuthenticator
10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
11 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
11 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
12
12
13 from pylons import config, tmpl_context as c, request, session, url
13 from pylons import config, tmpl_context as c, request, session, url
14 from pylons.controllers import WSGIController
14 from pylons.controllers import WSGIController
15 from pylons.controllers.util import redirect
15 from pylons.controllers.util import redirect
16 from pylons.templating import render_mako as render
16 from pylons.templating import render_mako as render
17
17
18 from rhodecode import __version__, BACKENDS
18 from rhodecode import __version__, BACKENDS
19
19
20 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict,\
20 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict,\
21 safe_str, safe_int
21 safe_str, safe_int
22 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
22 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
23 HasPermissionAnyMiddleware, CookieStoreWrapper
23 HasPermissionAnyMiddleware, CookieStoreWrapper
24 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
24 from rhodecode.lib.utils import get_repo_slug
25 from rhodecode.model import meta
25 from rhodecode.model import meta
26
26
27 from rhodecode.model.db import Repository, RhodeCodeUi, User, RhodeCodeSetting
27 from rhodecode.model.db import Repository, RhodeCodeUi, User, RhodeCodeSetting
28 from rhodecode.model.notification import NotificationModel
28 from rhodecode.model.notification import NotificationModel
29 from rhodecode.model.scm import ScmModel
29 from rhodecode.model.scm import ScmModel
30 from rhodecode.model.meta import Session
30 from rhodecode.model.meta import Session
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34
34
35 def _filter_proxy(ip):
35 def _filter_proxy(ip):
36 """
36 """
37 HEADERS can have mutliple ips inside the left-most being the original
37 HEADERS can have mutliple ips inside the left-most being the original
38 client, and each successive proxy that passed the request adding the IP
38 client, and each successive proxy that passed the request adding the IP
39 address where it received the request from.
39 address where it received the request from.
40
40
41 :param ip:
41 :param ip:
42 """
42 """
43 if ',' in ip:
43 if ',' in ip:
44 _ips = ip.split(',')
44 _ips = ip.split(',')
45 _first_ip = _ips[0].strip()
45 _first_ip = _ips[0].strip()
46 log.debug('Got multiple IPs %s, using %s' % (','.join(_ips), _first_ip))
46 log.debug('Got multiple IPs %s, using %s' % (','.join(_ips), _first_ip))
47 return _first_ip
47 return _first_ip
48 return ip
48 return ip
49
49
50
50
51 def _get_ip_addr(environ):
51 def _get_ip_addr(environ):
52 proxy_key = 'HTTP_X_REAL_IP'
52 proxy_key = 'HTTP_X_REAL_IP'
53 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
53 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
54 def_key = 'REMOTE_ADDR'
54 def_key = 'REMOTE_ADDR'
55
55
56 ip = environ.get(proxy_key)
56 ip = environ.get(proxy_key)
57 if ip:
57 if ip:
58 return _filter_proxy(ip)
58 return _filter_proxy(ip)
59
59
60 ip = environ.get(proxy_key2)
60 ip = environ.get(proxy_key2)
61 if ip:
61 if ip:
62 return _filter_proxy(ip)
62 return _filter_proxy(ip)
63
63
64 ip = environ.get(def_key, '0.0.0.0')
64 ip = environ.get(def_key, '0.0.0.0')
65 return _filter_proxy(ip)
65 return _filter_proxy(ip)
66
66
67
67
68 def _get_access_path(environ):
68 def _get_access_path(environ):
69 path = environ.get('PATH_INFO')
69 path = environ.get('PATH_INFO')
70 org_req = environ.get('pylons.original_request')
70 org_req = environ.get('pylons.original_request')
71 if org_req:
71 if org_req:
72 path = org_req.environ.get('PATH_INFO')
72 path = org_req.environ.get('PATH_INFO')
73 return path
73 return path
74
74
75
75
76 class BasicAuth(AuthBasicAuthenticator):
76 class BasicAuth(AuthBasicAuthenticator):
77
77
78 def __init__(self, realm, authfunc, auth_http_code=None):
78 def __init__(self, realm, authfunc, auth_http_code=None):
79 self.realm = realm
79 self.realm = realm
80 self.authfunc = authfunc
80 self.authfunc = authfunc
81 self._rc_auth_http_code = auth_http_code
81 self._rc_auth_http_code = auth_http_code
82
82
83 def build_authentication(self):
83 def build_authentication(self):
84 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
84 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
85 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
85 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
86 # return 403 if alternative http return code is specified in
86 # return 403 if alternative http return code is specified in
87 # RhodeCode config
87 # RhodeCode config
88 return HTTPForbidden(headers=head)
88 return HTTPForbidden(headers=head)
89 return HTTPUnauthorized(headers=head)
89 return HTTPUnauthorized(headers=head)
90
90
91 def authenticate(self, environ):
91 def authenticate(self, environ):
92 authorization = AUTHORIZATION(environ)
92 authorization = AUTHORIZATION(environ)
93 if not authorization:
93 if not authorization:
94 return self.build_authentication()
94 return self.build_authentication()
95 (authmeth, auth) = authorization.split(' ', 1)
95 (authmeth, auth) = authorization.split(' ', 1)
96 if 'basic' != authmeth.lower():
96 if 'basic' != authmeth.lower():
97 return self.build_authentication()
97 return self.build_authentication()
98 auth = auth.strip().decode('base64')
98 auth = auth.strip().decode('base64')
99 _parts = auth.split(':', 1)
99 _parts = auth.split(':', 1)
100 if len(_parts) == 2:
100 if len(_parts) == 2:
101 username, password = _parts
101 username, password = _parts
102 if self.authfunc(environ, username, password):
102 if self.authfunc(environ, username, password):
103 return username
103 return username
104 return self.build_authentication()
104 return self.build_authentication()
105
105
106 __call__ = authenticate
106 __call__ = authenticate
107
107
108
108
109 class BaseVCSController(object):
109 class BaseVCSController(object):
110
110
111 def __init__(self, application, config):
111 def __init__(self, application, config):
112 self.application = application
112 self.application = application
113 self.config = config
113 self.config = config
114 # base path of repo locations
114 # base path of repo locations
115 self.basepath = self.config['base_path']
115 self.basepath = self.config['base_path']
116 #authenticate this mercurial request using authfunc
116 #authenticate this mercurial request using authfunc
117 self.authenticate = BasicAuth('', authfunc,
117 self.authenticate = BasicAuth('', authfunc,
118 config.get('auth_ret_code'))
118 config.get('auth_ret_code'))
119 self.ip_addr = '0.0.0.0'
119 self.ip_addr = '0.0.0.0'
120
120
121 def _handle_request(self, environ, start_response):
121 def _handle_request(self, environ, start_response):
122 raise NotImplementedError()
122 raise NotImplementedError()
123
123
124 def _get_by_id(self, repo_name):
124 def _get_by_id(self, repo_name):
125 """
125 """
126 Get's a special pattern _<ID> from clone url and tries to replace it
126 Get's a special pattern _<ID> from clone url and tries to replace it
127 with a repository_name for support of _<ID> non changable urls
127 with a repository_name for support of _<ID> non changable urls
128
128
129 :param repo_name:
129 :param repo_name:
130 """
130 """
131 try:
131 try:
132 data = repo_name.split('/')
132 data = repo_name.split('/')
133 if len(data) >= 2:
133 if len(data) >= 2:
134 by_id = data[1].split('_')
134 by_id = data[1].split('_')
135 if len(by_id) == 2 and by_id[1].isdigit():
135 if len(by_id) == 2 and by_id[1].isdigit():
136 _repo_name = Repository.get(by_id[1]).repo_name
136 _repo_name = Repository.get(by_id[1]).repo_name
137 data[1] = _repo_name
137 data[1] = _repo_name
138 except Exception:
138 except Exception:
139 log.debug('Failed to extract repo_name from id %s' % (
139 log.debug('Failed to extract repo_name from id %s' % (
140 traceback.format_exc()
140 traceback.format_exc()
141 )
141 )
142 )
142 )
143
143
144 return '/'.join(data)
144 return '/'.join(data)
145
145
146 def _invalidate_cache(self, repo_name):
146 def _invalidate_cache(self, repo_name):
147 """
147 """
148 Set's cache for this repository for invalidation on next access
148 Set's cache for this repository for invalidation on next access
149
149
150 :param repo_name: full repo name, also a cache key
150 :param repo_name: full repo name, also a cache key
151 """
151 """
152 invalidate_cache('get_repo_cached_%s' % repo_name)
152 ScmModel().mark_for_invalidation(repo_name)
153
153
154 def _check_permission(self, action, user, repo_name, ip_addr=None):
154 def _check_permission(self, action, user, repo_name, ip_addr=None):
155 """
155 """
156 Checks permissions using action (push/pull) user and repository
156 Checks permissions using action (push/pull) user and repository
157 name
157 name
158
158
159 :param action: push or pull action
159 :param action: push or pull action
160 :param user: user instance
160 :param user: user instance
161 :param repo_name: repository name
161 :param repo_name: repository name
162 """
162 """
163 #check IP
163 #check IP
164 authuser = AuthUser(user_id=user.user_id, ip_addr=ip_addr)
164 authuser = AuthUser(user_id=user.user_id, ip_addr=ip_addr)
165 if not authuser.ip_allowed:
165 if not authuser.ip_allowed:
166 return False
166 return False
167 else:
167 else:
168 log.info('Access for IP:%s allowed' % (ip_addr))
168 log.info('Access for IP:%s allowed' % (ip_addr))
169 if action == 'push':
169 if action == 'push':
170 if not HasPermissionAnyMiddleware('repository.write',
170 if not HasPermissionAnyMiddleware('repository.write',
171 'repository.admin')(user,
171 'repository.admin')(user,
172 repo_name):
172 repo_name):
173 return False
173 return False
174
174
175 else:
175 else:
176 #any other action need at least read permission
176 #any other action need at least read permission
177 if not HasPermissionAnyMiddleware('repository.read',
177 if not HasPermissionAnyMiddleware('repository.read',
178 'repository.write',
178 'repository.write',
179 'repository.admin')(user,
179 'repository.admin')(user,
180 repo_name):
180 repo_name):
181 return False
181 return False
182
182
183 return True
183 return True
184
184
185 def _get_ip_addr(self, environ):
185 def _get_ip_addr(self, environ):
186 return _get_ip_addr(environ)
186 return _get_ip_addr(environ)
187
187
188 def _check_ssl(self, environ, start_response):
188 def _check_ssl(self, environ, start_response):
189 """
189 """
190 Checks the SSL check flag and returns False if SSL is not present
190 Checks the SSL check flag and returns False if SSL is not present
191 and required True otherwise
191 and required True otherwise
192 """
192 """
193 org_proto = environ['wsgi._org_proto']
193 org_proto = environ['wsgi._org_proto']
194 #check if we have SSL required ! if not it's a bad request !
194 #check if we have SSL required ! if not it's a bad request !
195 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl').ui_value)
195 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl').ui_value)
196 if require_ssl and org_proto == 'http':
196 if require_ssl and org_proto == 'http':
197 log.debug('proto is %s and SSL is required BAD REQUEST !'
197 log.debug('proto is %s and SSL is required BAD REQUEST !'
198 % org_proto)
198 % org_proto)
199 return False
199 return False
200 return True
200 return True
201
201
202 def _check_locking_state(self, environ, action, repo, user_id):
202 def _check_locking_state(self, environ, action, repo, user_id):
203 """
203 """
204 Checks locking on this repository, if locking is enabled and lock is
204 Checks locking on this repository, if locking is enabled and lock is
205 present returns a tuple of make_lock, locked, locked_by.
205 present returns a tuple of make_lock, locked, locked_by.
206 make_lock can have 3 states None (do nothing) True, make lock
206 make_lock can have 3 states None (do nothing) True, make lock
207 False release lock, This value is later propagated to hooks, which
207 False release lock, This value is later propagated to hooks, which
208 do the locking. Think about this as signals passed to hooks what to do.
208 do the locking. Think about this as signals passed to hooks what to do.
209
209
210 """
210 """
211 locked = False # defines that locked error should be thrown to user
211 locked = False # defines that locked error should be thrown to user
212 make_lock = None
212 make_lock = None
213 repo = Repository.get_by_repo_name(repo)
213 repo = Repository.get_by_repo_name(repo)
214 user = User.get(user_id)
214 user = User.get(user_id)
215
215
216 # this is kind of hacky, but due to how mercurial handles client-server
216 # this is kind of hacky, but due to how mercurial handles client-server
217 # server see all operation on changeset; bookmarks, phases and
217 # server see all operation on changeset; bookmarks, phases and
218 # obsolescence marker in different transaction, we don't want to check
218 # obsolescence marker in different transaction, we don't want to check
219 # locking on those
219 # locking on those
220 obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
220 obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
221 locked_by = repo.locked
221 locked_by = repo.locked
222 if repo and repo.enable_locking and not obsolete_call:
222 if repo and repo.enable_locking and not obsolete_call:
223 if action == 'push':
223 if action == 'push':
224 #check if it's already locked !, if it is compare users
224 #check if it's already locked !, if it is compare users
225 user_id, _date = repo.locked
225 user_id, _date = repo.locked
226 if user.user_id == user_id:
226 if user.user_id == user_id:
227 log.debug('Got push from user %s, now unlocking' % (user))
227 log.debug('Got push from user %s, now unlocking' % (user))
228 # unlock if we have push from user who locked
228 # unlock if we have push from user who locked
229 make_lock = False
229 make_lock = False
230 else:
230 else:
231 # we're not the same user who locked, ban with 423 !
231 # we're not the same user who locked, ban with 423 !
232 locked = True
232 locked = True
233 if action == 'pull':
233 if action == 'pull':
234 if repo.locked[0] and repo.locked[1]:
234 if repo.locked[0] and repo.locked[1]:
235 locked = True
235 locked = True
236 else:
236 else:
237 log.debug('Setting lock on repo %s by %s' % (repo, user))
237 log.debug('Setting lock on repo %s by %s' % (repo, user))
238 make_lock = True
238 make_lock = True
239
239
240 else:
240 else:
241 log.debug('Repository %s do not have locking enabled' % (repo))
241 log.debug('Repository %s do not have locking enabled' % (repo))
242 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
242 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
243 % (make_lock, locked, locked_by))
243 % (make_lock, locked, locked_by))
244 return make_lock, locked, locked_by
244 return make_lock, locked, locked_by
245
245
246 def __call__(self, environ, start_response):
246 def __call__(self, environ, start_response):
247 start = time.time()
247 start = time.time()
248 try:
248 try:
249 return self._handle_request(environ, start_response)
249 return self._handle_request(environ, start_response)
250 finally:
250 finally:
251 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
251 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
252 log.debug('Request time: %.3fs' % (time.time() - start))
252 log.debug('Request time: %.3fs' % (time.time() - start))
253 meta.Session.remove()
253 meta.Session.remove()
254
254
255
255
256 class BaseController(WSGIController):
256 class BaseController(WSGIController):
257
257
258 def __before__(self):
258 def __before__(self):
259 """
259 """
260 __before__ is called before controller methods and after __call__
260 __before__ is called before controller methods and after __call__
261 """
261 """
262 c.rhodecode_version = __version__
262 c.rhodecode_version = __version__
263 c.rhodecode_instanceid = config.get('instance_id')
263 c.rhodecode_instanceid = config.get('instance_id')
264 c.rhodecode_name = config.get('rhodecode_title')
264 c.rhodecode_name = config.get('rhodecode_title')
265 c.use_gravatar = str2bool(config.get('use_gravatar'))
265 c.use_gravatar = str2bool(config.get('use_gravatar'))
266 c.ga_code = config.get('rhodecode_ga_code')
266 c.ga_code = config.get('rhodecode_ga_code')
267 # Visual options
267 # Visual options
268 c.visual = AttributeDict({})
268 c.visual = AttributeDict({})
269 rc_config = RhodeCodeSetting.get_app_settings()
269 rc_config = RhodeCodeSetting.get_app_settings()
270
270
271 c.visual.show_public_icon = str2bool(rc_config.get('rhodecode_show_public_icon'))
271 c.visual.show_public_icon = str2bool(rc_config.get('rhodecode_show_public_icon'))
272 c.visual.show_private_icon = str2bool(rc_config.get('rhodecode_show_private_icon'))
272 c.visual.show_private_icon = str2bool(rc_config.get('rhodecode_show_private_icon'))
273 c.visual.stylify_metatags = str2bool(rc_config.get('rhodecode_stylify_metatags'))
273 c.visual.stylify_metatags = str2bool(rc_config.get('rhodecode_stylify_metatags'))
274 c.visual.lightweight_dashboard = str2bool(rc_config.get('rhodecode_lightweight_dashboard'))
274 c.visual.lightweight_dashboard = str2bool(rc_config.get('rhodecode_lightweight_dashboard'))
275 c.visual.lightweight_dashboard_items = safe_int(config.get('dashboard_items', 100))
275 c.visual.lightweight_dashboard_items = safe_int(config.get('dashboard_items', 100))
276 c.visual.repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
276 c.visual.repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
277 c.repo_name = get_repo_slug(request) # can be empty
277 c.repo_name = get_repo_slug(request) # can be empty
278 c.backends = BACKENDS.keys()
278 c.backends = BACKENDS.keys()
279 c.unread_notifications = NotificationModel()\
279 c.unread_notifications = NotificationModel()\
280 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
280 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
281 self.cut_off_limit = int(config.get('cut_off_limit'))
281 self.cut_off_limit = int(config.get('cut_off_limit'))
282
282
283 self.sa = meta.Session
283 self.sa = meta.Session
284 self.scm_model = ScmModel(self.sa)
284 self.scm_model = ScmModel(self.sa)
285
285
286 def __call__(self, environ, start_response):
286 def __call__(self, environ, start_response):
287 """Invoke the Controller"""
287 """Invoke the Controller"""
288 # WSGIController.__call__ dispatches to the Controller method
288 # WSGIController.__call__ dispatches to the Controller method
289 # the request is routed to. This routing information is
289 # the request is routed to. This routing information is
290 # available in environ['pylons.routes_dict']
290 # available in environ['pylons.routes_dict']
291 try:
291 try:
292 self.ip_addr = _get_ip_addr(environ)
292 self.ip_addr = _get_ip_addr(environ)
293 # make sure that we update permissions each time we call controller
293 # make sure that we update permissions each time we call controller
294 api_key = request.GET.get('api_key')
294 api_key = request.GET.get('api_key')
295 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
295 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
296 user_id = cookie_store.get('user_id', None)
296 user_id = cookie_store.get('user_id', None)
297 username = get_container_username(environ, config)
297 username = get_container_username(environ, config)
298 auth_user = AuthUser(user_id, api_key, username, self.ip_addr)
298 auth_user = AuthUser(user_id, api_key, username, self.ip_addr)
299 request.user = auth_user
299 request.user = auth_user
300 self.rhodecode_user = c.rhodecode_user = auth_user
300 self.rhodecode_user = c.rhodecode_user = auth_user
301 if not self.rhodecode_user.is_authenticated and \
301 if not self.rhodecode_user.is_authenticated and \
302 self.rhodecode_user.user_id is not None:
302 self.rhodecode_user.user_id is not None:
303 self.rhodecode_user.set_authenticated(
303 self.rhodecode_user.set_authenticated(
304 cookie_store.get('is_authenticated')
304 cookie_store.get('is_authenticated')
305 )
305 )
306 log.info('IP: %s User: %s accessed %s' % (
306 log.info('IP: %s User: %s accessed %s' % (
307 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
307 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
308 )
308 )
309 return WSGIController.__call__(self, environ, start_response)
309 return WSGIController.__call__(self, environ, start_response)
310 finally:
310 finally:
311 meta.Session.remove()
311 meta.Session.remove()
312
312
313
313
314 class BaseRepoController(BaseController):
314 class BaseRepoController(BaseController):
315 """
315 """
316 Base class for controllers responsible for loading all needed data for
316 Base class for controllers responsible for loading all needed data for
317 repository loaded items are
317 repository loaded items are
318
318
319 c.rhodecode_repo: instance of scm repository
319 c.rhodecode_repo: instance of scm repository
320 c.rhodecode_db_repo: instance of db
320 c.rhodecode_db_repo: instance of db
321 c.repository_followers: number of followers
321 c.repository_followers: number of followers
322 c.repository_forks: number of forks
322 c.repository_forks: number of forks
323 c.repository_following: weather the current user is following the current repo
323 c.repository_following: weather the current user is following the current repo
324 """
324 """
325
325
326 def __before__(self):
326 def __before__(self):
327 super(BaseRepoController, self).__before__()
327 super(BaseRepoController, self).__before__()
328 if c.repo_name:
328 if c.repo_name:
329
329
330 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
330 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
331 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
331 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
332 # update last change according to VCS data
332 # update last change according to VCS data
333 dbr.update_changeset_cache(dbr.get_changeset())
333 dbr.update_changeset_cache(dbr.get_changeset())
334 if c.rhodecode_repo is None:
334 if c.rhodecode_repo is None:
335 log.error('%s this repository is present in database but it '
335 log.error('%s this repository is present in database but it '
336 'cannot be created as an scm instance', c.repo_name)
336 'cannot be created as an scm instance', c.repo_name)
337
337
338 redirect(url('home'))
338 redirect(url('home'))
339
339
340 # some globals counter for menu
340 # some globals counter for menu
341 c.repository_followers = self.scm_model.get_followers(dbr)
341 c.repository_followers = self.scm_model.get_followers(dbr)
342 c.repository_forks = self.scm_model.get_forks(dbr)
342 c.repository_forks = self.scm_model.get_forks(dbr)
343 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
343 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
344 c.repository_following = self.scm_model.is_following_repo(c.repo_name,
344 c.repository_following = self.scm_model.is_following_repo(c.repo_name,
345 self.rhodecode_user.user_id)
345 self.rhodecode_user.user_id)
@@ -1,792 +1,779 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import paste
31 import paste
32 import beaker
32 import beaker
33 import tarfile
33 import tarfile
34 import shutil
34 import shutil
35 import decorator
35 import decorator
36 import warnings
36 import warnings
37 from os.path import abspath
37 from os.path import abspath
38 from os.path import dirname as dn, join as jn
38 from os.path import dirname as dn, join as jn
39
39
40 from paste.script.command import Command, BadCommand
40 from paste.script.command import Command, BadCommand
41
41
42 from mercurial import ui, config
42 from mercurial import ui, config
43
43
44 from webhelpers.text import collapse, remove_formatting, strip_tags
44 from webhelpers.text import collapse, remove_formatting, strip_tags
45
45
46 from rhodecode.lib.vcs import get_backend
46 from rhodecode.lib.vcs import get_backend
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
47 from rhodecode.lib.vcs.backends.base import BaseChangeset
48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
49 from rhodecode.lib.vcs.utils.helpers import get_scm
49 from rhodecode.lib.vcs.utils.helpers import get_scm
50 from rhodecode.lib.vcs.exceptions import VCSError
50 from rhodecode.lib.vcs.exceptions import VCSError
51
51
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model import meta
54 from rhodecode.model import meta
55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
57 from rhodecode.model.meta import Session
57 from rhodecode.model.meta import Session
58 from rhodecode.model.repos_group import ReposGroupModel
58 from rhodecode.model.repos_group import ReposGroupModel
59 from rhodecode.lib.utils2 import safe_str, safe_unicode
59 from rhodecode.lib.utils2 import safe_str, safe_unicode
60 from rhodecode.lib.vcs.utils.fakemod import create_module
60 from rhodecode.lib.vcs.utils.fakemod import create_module
61
61
62 log = logging.getLogger(__name__)
62 log = logging.getLogger(__name__)
63
63
64 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
64 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
65
65
66
66
67 def recursive_replace(str_, replace=' '):
67 def recursive_replace(str_, replace=' '):
68 """
68 """
69 Recursive replace of given sign to just one instance
69 Recursive replace of given sign to just one instance
70
70
71 :param str_: given string
71 :param str_: given string
72 :param replace: char to find and replace multiple instances
72 :param replace: char to find and replace multiple instances
73
73
74 Examples::
74 Examples::
75 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
75 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
76 'Mighty-Mighty-Bo-sstones'
76 'Mighty-Mighty-Bo-sstones'
77 """
77 """
78
78
79 if str_.find(replace * 2) == -1:
79 if str_.find(replace * 2) == -1:
80 return str_
80 return str_
81 else:
81 else:
82 str_ = str_.replace(replace * 2, replace)
82 str_ = str_.replace(replace * 2, replace)
83 return recursive_replace(str_, replace)
83 return recursive_replace(str_, replace)
84
84
85
85
86 def repo_name_slug(value):
86 def repo_name_slug(value):
87 """
87 """
88 Return slug of name of repository
88 Return slug of name of repository
89 This function is called on each creation/modification
89 This function is called on each creation/modification
90 of repository to prevent bad names in repo
90 of repository to prevent bad names in repo
91 """
91 """
92
92
93 slug = remove_formatting(value)
93 slug = remove_formatting(value)
94 slug = strip_tags(slug)
94 slug = strip_tags(slug)
95
95
96 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
96 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
97 slug = slug.replace(c, '-')
97 slug = slug.replace(c, '-')
98 slug = recursive_replace(slug, '-')
98 slug = recursive_replace(slug, '-')
99 slug = collapse(slug, '-')
99 slug = collapse(slug, '-')
100 return slug
100 return slug
101
101
102
102
103 def get_repo_slug(request):
103 def get_repo_slug(request):
104 _repo = request.environ['pylons.routes_dict'].get('repo_name')
104 _repo = request.environ['pylons.routes_dict'].get('repo_name')
105 if _repo:
105 if _repo:
106 _repo = _repo.rstrip('/')
106 _repo = _repo.rstrip('/')
107 return _repo
107 return _repo
108
108
109
109
110 def get_repos_group_slug(request):
110 def get_repos_group_slug(request):
111 _group = request.environ['pylons.routes_dict'].get('group_name')
111 _group = request.environ['pylons.routes_dict'].get('group_name')
112 if _group:
112 if _group:
113 _group = _group.rstrip('/')
113 _group = _group.rstrip('/')
114 return _group
114 return _group
115
115
116
116
117 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
117 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
118 """
118 """
119 Action logger for various actions made by users
119 Action logger for various actions made by users
120
120
121 :param user: user that made this action, can be a unique username string or
121 :param user: user that made this action, can be a unique username string or
122 object containing user_id attribute
122 object containing user_id attribute
123 :param action: action to log, should be on of predefined unique actions for
123 :param action: action to log, should be on of predefined unique actions for
124 easy translations
124 easy translations
125 :param repo: string name of repository or object containing repo_id,
125 :param repo: string name of repository or object containing repo_id,
126 that action was made on
126 that action was made on
127 :param ipaddr: optional ip address from what the action was made
127 :param ipaddr: optional ip address from what the action was made
128 :param sa: optional sqlalchemy session
128 :param sa: optional sqlalchemy session
129
129
130 """
130 """
131
131
132 if not sa:
132 if not sa:
133 sa = meta.Session()
133 sa = meta.Session()
134
134
135 try:
135 try:
136 if hasattr(user, 'user_id'):
136 if hasattr(user, 'user_id'):
137 user_obj = User.get(user.user_id)
137 user_obj = User.get(user.user_id)
138 elif isinstance(user, basestring):
138 elif isinstance(user, basestring):
139 user_obj = User.get_by_username(user)
139 user_obj = User.get_by_username(user)
140 else:
140 else:
141 raise Exception('You have to provide a user object or a username')
141 raise Exception('You have to provide a user object or a username')
142
142
143 if hasattr(repo, 'repo_id'):
143 if hasattr(repo, 'repo_id'):
144 repo_obj = Repository.get(repo.repo_id)
144 repo_obj = Repository.get(repo.repo_id)
145 repo_name = repo_obj.repo_name
145 repo_name = repo_obj.repo_name
146 elif isinstance(repo, basestring):
146 elif isinstance(repo, basestring):
147 repo_name = repo.lstrip('/')
147 repo_name = repo.lstrip('/')
148 repo_obj = Repository.get_by_repo_name(repo_name)
148 repo_obj = Repository.get_by_repo_name(repo_name)
149 else:
149 else:
150 repo_obj = None
150 repo_obj = None
151 repo_name = ''
151 repo_name = ''
152
152
153 user_log = UserLog()
153 user_log = UserLog()
154 user_log.user_id = user_obj.user_id
154 user_log.user_id = user_obj.user_id
155 user_log.username = user_obj.username
155 user_log.username = user_obj.username
156 user_log.action = safe_unicode(action)
156 user_log.action = safe_unicode(action)
157
157
158 user_log.repository = repo_obj
158 user_log.repository = repo_obj
159 user_log.repository_name = repo_name
159 user_log.repository_name = repo_name
160
160
161 user_log.action_date = datetime.datetime.now()
161 user_log.action_date = datetime.datetime.now()
162 user_log.user_ip = ipaddr
162 user_log.user_ip = ipaddr
163 sa.add(user_log)
163 sa.add(user_log)
164
164
165 log.info('Logging action:%s on %s by user:%s ip:%s' %
165 log.info('Logging action:%s on %s by user:%s ip:%s' %
166 (action, safe_unicode(repo), user_obj, ipaddr))
166 (action, safe_unicode(repo), user_obj, ipaddr))
167 if commit:
167 if commit:
168 sa.commit()
168 sa.commit()
169 except Exception:
169 except Exception:
170 log.error(traceback.format_exc())
170 log.error(traceback.format_exc())
171 raise
171 raise
172
172
173
173
174 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
174 def get_filesystem_repos(path, recursive=False, skip_removed_repos=True):
175 """
175 """
176 Scans given path for repos and return (name,(type,path)) tuple
176 Scans given path for repos and return (name,(type,path)) tuple
177
177
178 :param path: path to scan for repositories
178 :param path: path to scan for repositories
179 :param recursive: recursive search and return names with subdirs in front
179 :param recursive: recursive search and return names with subdirs in front
180 """
180 """
181
181
182 # remove ending slash for better results
182 # remove ending slash for better results
183 path = path.rstrip(os.sep)
183 path = path.rstrip(os.sep)
184 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
184 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
185
185
186 def _get_repos(p):
186 def _get_repos(p):
187 if not os.access(p, os.W_OK):
187 if not os.access(p, os.W_OK):
188 log.warn('ignoring repo path without write access: %s', p)
188 log.warn('ignoring repo path without write access: %s', p)
189 return
189 return
190 for dirpath in os.listdir(p):
190 for dirpath in os.listdir(p):
191 if os.path.isfile(os.path.join(p, dirpath)):
191 if os.path.isfile(os.path.join(p, dirpath)):
192 continue
192 continue
193 cur_path = os.path.join(p, dirpath)
193 cur_path = os.path.join(p, dirpath)
194
194
195 # skip removed repos
195 # skip removed repos
196 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
196 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
197 continue
197 continue
198
198
199 #skip .<somethin> dirs
199 #skip .<somethin> dirs
200 if dirpath.startswith('.'):
200 if dirpath.startswith('.'):
201 continue
201 continue
202
202
203 try:
203 try:
204 scm_info = get_scm(cur_path)
204 scm_info = get_scm(cur_path)
205 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
205 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
206 except VCSError:
206 except VCSError:
207 if not recursive:
207 if not recursive:
208 continue
208 continue
209 #check if this dir containts other repos for recursive scan
209 #check if this dir containts other repos for recursive scan
210 rec_path = os.path.join(p, dirpath)
210 rec_path = os.path.join(p, dirpath)
211 if os.path.isdir(rec_path):
211 if os.path.isdir(rec_path):
212 for inner_scm in _get_repos(rec_path):
212 for inner_scm in _get_repos(rec_path):
213 yield inner_scm
213 yield inner_scm
214
214
215 return _get_repos(path)
215 return _get_repos(path)
216
216
217
217
218 def is_valid_repo(repo_name, base_path, scm=None):
218 def is_valid_repo(repo_name, base_path, scm=None):
219 """
219 """
220 Returns True if given path is a valid repository False otherwise.
220 Returns True if given path is a valid repository False otherwise.
221 If scm param is given also compare if given scm is the same as expected
221 If scm param is given also compare if given scm is the same as expected
222 from scm parameter
222 from scm parameter
223
223
224 :param repo_name:
224 :param repo_name:
225 :param base_path:
225 :param base_path:
226 :param scm:
226 :param scm:
227
227
228 :return True: if given path is a valid repository
228 :return True: if given path is a valid repository
229 """
229 """
230 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
230 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
231
231
232 try:
232 try:
233 scm_ = get_scm(full_path)
233 scm_ = get_scm(full_path)
234 if scm:
234 if scm:
235 return scm_[0] == scm
235 return scm_[0] == scm
236 return True
236 return True
237 except VCSError:
237 except VCSError:
238 return False
238 return False
239
239
240
240
241 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
241 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
242 """
242 """
243 Returns True if given path is a repository group False otherwise
243 Returns True if given path is a repository group False otherwise
244
244
245 :param repo_name:
245 :param repo_name:
246 :param base_path:
246 :param base_path:
247 """
247 """
248 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
248 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
249
249
250 # check if it's not a repo
250 # check if it's not a repo
251 if is_valid_repo(repos_group_name, base_path):
251 if is_valid_repo(repos_group_name, base_path):
252 return False
252 return False
253
253
254 try:
254 try:
255 # we need to check bare git repos at higher level
255 # we need to check bare git repos at higher level
256 # since we might match branches/hooks/info/objects or possible
256 # since we might match branches/hooks/info/objects or possible
257 # other things inside bare git repo
257 # other things inside bare git repo
258 get_scm(os.path.dirname(full_path))
258 get_scm(os.path.dirname(full_path))
259 return False
259 return False
260 except VCSError:
260 except VCSError:
261 pass
261 pass
262
262
263 # check if it's a valid path
263 # check if it's a valid path
264 if skip_path_check or os.path.isdir(full_path):
264 if skip_path_check or os.path.isdir(full_path):
265 return True
265 return True
266
266
267 return False
267 return False
268
268
269
269
270 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
270 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
271 while True:
271 while True:
272 ok = raw_input(prompt)
272 ok = raw_input(prompt)
273 if ok in ('y', 'ye', 'yes'):
273 if ok in ('y', 'ye', 'yes'):
274 return True
274 return True
275 if ok in ('n', 'no', 'nop', 'nope'):
275 if ok in ('n', 'no', 'nop', 'nope'):
276 return False
276 return False
277 retries = retries - 1
277 retries = retries - 1
278 if retries < 0:
278 if retries < 0:
279 raise IOError
279 raise IOError
280 print complaint
280 print complaint
281
281
282 #propagated from mercurial documentation
282 #propagated from mercurial documentation
283 ui_sections = ['alias', 'auth',
283 ui_sections = ['alias', 'auth',
284 'decode/encode', 'defaults',
284 'decode/encode', 'defaults',
285 'diff', 'email',
285 'diff', 'email',
286 'extensions', 'format',
286 'extensions', 'format',
287 'merge-patterns', 'merge-tools',
287 'merge-patterns', 'merge-tools',
288 'hooks', 'http_proxy',
288 'hooks', 'http_proxy',
289 'smtp', 'patch',
289 'smtp', 'patch',
290 'paths', 'profiling',
290 'paths', 'profiling',
291 'server', 'trusted',
291 'server', 'trusted',
292 'ui', 'web', ]
292 'ui', 'web', ]
293
293
294
294
295 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
295 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
296 """
296 """
297 A function that will read python rc files or database
297 A function that will read python rc files or database
298 and make an mercurial ui object from read options
298 and make an mercurial ui object from read options
299
299
300 :param path: path to mercurial config file
300 :param path: path to mercurial config file
301 :param checkpaths: check the path
301 :param checkpaths: check the path
302 :param read_from: read from 'file' or 'db'
302 :param read_from: read from 'file' or 'db'
303 """
303 """
304
304
305 baseui = ui.ui()
305 baseui = ui.ui()
306
306
307 # clean the baseui object
307 # clean the baseui object
308 baseui._ocfg = config.config()
308 baseui._ocfg = config.config()
309 baseui._ucfg = config.config()
309 baseui._ucfg = config.config()
310 baseui._tcfg = config.config()
310 baseui._tcfg = config.config()
311
311
312 if read_from == 'file':
312 if read_from == 'file':
313 if not os.path.isfile(path):
313 if not os.path.isfile(path):
314 log.debug('hgrc file is not present at %s, skipping...' % path)
314 log.debug('hgrc file is not present at %s, skipping...' % path)
315 return False
315 return False
316 log.debug('reading hgrc from %s' % path)
316 log.debug('reading hgrc from %s' % path)
317 cfg = config.config()
317 cfg = config.config()
318 cfg.read(path)
318 cfg.read(path)
319 for section in ui_sections:
319 for section in ui_sections:
320 for k, v in cfg.items(section):
320 for k, v in cfg.items(section):
321 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
321 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
322 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
322 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
323
323
324 elif read_from == 'db':
324 elif read_from == 'db':
325 sa = meta.Session()
325 sa = meta.Session()
326 ret = sa.query(RhodeCodeUi)\
326 ret = sa.query(RhodeCodeUi)\
327 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
327 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
328 .all()
328 .all()
329
329
330 hg_ui = ret
330 hg_ui = ret
331 for ui_ in hg_ui:
331 for ui_ in hg_ui:
332 if ui_.ui_active:
332 if ui_.ui_active:
333 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
333 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
334 ui_.ui_key, ui_.ui_value)
334 ui_.ui_key, ui_.ui_value)
335 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
335 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
336 safe_str(ui_.ui_value))
336 safe_str(ui_.ui_value))
337 if ui_.ui_key == 'push_ssl':
337 if ui_.ui_key == 'push_ssl':
338 # force set push_ssl requirement to False, rhodecode
338 # force set push_ssl requirement to False, rhodecode
339 # handles that
339 # handles that
340 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
340 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
341 False)
341 False)
342 if clear_session:
342 if clear_session:
343 meta.Session.remove()
343 meta.Session.remove()
344 return baseui
344 return baseui
345
345
346
346
347 def set_rhodecode_config(config):
347 def set_rhodecode_config(config):
348 """
348 """
349 Updates pylons config with new settings from database
349 Updates pylons config with new settings from database
350
350
351 :param config:
351 :param config:
352 """
352 """
353 hgsettings = RhodeCodeSetting.get_app_settings()
353 hgsettings = RhodeCodeSetting.get_app_settings()
354
354
355 for k, v in hgsettings.items():
355 for k, v in hgsettings.items():
356 config[k] = v
356 config[k] = v
357
357
358
358
359 def invalidate_cache(cache_key, *args):
360 """
361 Puts cache invalidation task into db for
362 further global cache invalidation
363 """
364
365 from rhodecode.model.scm import ScmModel
366
367 if cache_key.startswith('get_repo_cached_'):
368 name = cache_key.split('get_repo_cached_')[-1]
369 ScmModel().mark_for_invalidation(name)
370
371
372 def map_groups(path):
359 def map_groups(path):
373 """
360 """
374 Given a full path to a repository, create all nested groups that this
361 Given a full path to a repository, create all nested groups that this
375 repo is inside. This function creates parent-child relationships between
362 repo is inside. This function creates parent-child relationships between
376 groups and creates default perms for all new groups.
363 groups and creates default perms for all new groups.
377
364
378 :param paths: full path to repository
365 :param paths: full path to repository
379 """
366 """
380 sa = meta.Session()
367 sa = meta.Session()
381 groups = path.split(Repository.url_sep())
368 groups = path.split(Repository.url_sep())
382 parent = None
369 parent = None
383 group = None
370 group = None
384
371
385 # last element is repo in nested groups structure
372 # last element is repo in nested groups structure
386 groups = groups[:-1]
373 groups = groups[:-1]
387 rgm = ReposGroupModel(sa)
374 rgm = ReposGroupModel(sa)
388 for lvl, group_name in enumerate(groups):
375 for lvl, group_name in enumerate(groups):
389 group_name = '/'.join(groups[:lvl] + [group_name])
376 group_name = '/'.join(groups[:lvl] + [group_name])
390 group = RepoGroup.get_by_group_name(group_name)
377 group = RepoGroup.get_by_group_name(group_name)
391 desc = '%s group' % group_name
378 desc = '%s group' % group_name
392
379
393 # skip folders that are now removed repos
380 # skip folders that are now removed repos
394 if REMOVED_REPO_PAT.match(group_name):
381 if REMOVED_REPO_PAT.match(group_name):
395 break
382 break
396
383
397 if group is None:
384 if group is None:
398 log.debug('creating group level: %s group_name: %s' % (lvl,
385 log.debug('creating group level: %s group_name: %s' % (lvl,
399 group_name))
386 group_name))
400 group = RepoGroup(group_name, parent)
387 group = RepoGroup(group_name, parent)
401 group.group_description = desc
388 group.group_description = desc
402 sa.add(group)
389 sa.add(group)
403 rgm._create_default_perms(group)
390 rgm._create_default_perms(group)
404 sa.flush()
391 sa.flush()
405 parent = group
392 parent = group
406 return group
393 return group
407
394
408
395
409 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
396 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
410 install_git_hook=False):
397 install_git_hook=False):
411 """
398 """
412 maps all repos given in initial_repo_list, non existing repositories
399 maps all repos given in initial_repo_list, non existing repositories
413 are created, if remove_obsolete is True it also check for db entries
400 are created, if remove_obsolete is True it also check for db entries
414 that are not in initial_repo_list and removes them.
401 that are not in initial_repo_list and removes them.
415
402
416 :param initial_repo_list: list of repositories found by scanning methods
403 :param initial_repo_list: list of repositories found by scanning methods
417 :param remove_obsolete: check for obsolete entries in database
404 :param remove_obsolete: check for obsolete entries in database
418 :param install_git_hook: if this is True, also check and install githook
405 :param install_git_hook: if this is True, also check and install githook
419 for a repo if missing
406 for a repo if missing
420 """
407 """
421 from rhodecode.model.repo import RepoModel
408 from rhodecode.model.repo import RepoModel
422 from rhodecode.model.scm import ScmModel
409 from rhodecode.model.scm import ScmModel
423 sa = meta.Session()
410 sa = meta.Session()
424 rm = RepoModel()
411 rm = RepoModel()
425 user = sa.query(User).filter(User.admin == True).first()
412 user = sa.query(User).filter(User.admin == True).first()
426 if user is None:
413 if user is None:
427 raise Exception('Missing administrative account!')
414 raise Exception('Missing administrative account!')
428 added = []
415 added = []
429
416
430 ##creation defaults
417 ##creation defaults
431 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
418 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
432 enable_statistics = defs.get('repo_enable_statistics')
419 enable_statistics = defs.get('repo_enable_statistics')
433 enable_locking = defs.get('repo_enable_locking')
420 enable_locking = defs.get('repo_enable_locking')
434 enable_downloads = defs.get('repo_enable_downloads')
421 enable_downloads = defs.get('repo_enable_downloads')
435 private = defs.get('repo_private')
422 private = defs.get('repo_private')
436
423
437 for name, repo in initial_repo_list.items():
424 for name, repo in initial_repo_list.items():
438 group = map_groups(name)
425 group = map_groups(name)
439 db_repo = rm.get_by_repo_name(name)
426 db_repo = rm.get_by_repo_name(name)
440 # found repo that is on filesystem not in RhodeCode database
427 # found repo that is on filesystem not in RhodeCode database
441 if not db_repo:
428 if not db_repo:
442 log.info('repository %s not found, creating now' % name)
429 log.info('repository %s not found, creating now' % name)
443 added.append(name)
430 added.append(name)
444 desc = (repo.description
431 desc = (repo.description
445 if repo.description != 'unknown'
432 if repo.description != 'unknown'
446 else '%s repository' % name)
433 else '%s repository' % name)
447
434
448 new_repo = rm.create_repo(
435 new_repo = rm.create_repo(
449 repo_name=name,
436 repo_name=name,
450 repo_type=repo.alias,
437 repo_type=repo.alias,
451 description=desc,
438 description=desc,
452 repos_group=getattr(group, 'group_id', None),
439 repos_group=getattr(group, 'group_id', None),
453 owner=user,
440 owner=user,
454 just_db=True,
441 just_db=True,
455 enable_locking=enable_locking,
442 enable_locking=enable_locking,
456 enable_downloads=enable_downloads,
443 enable_downloads=enable_downloads,
457 enable_statistics=enable_statistics,
444 enable_statistics=enable_statistics,
458 private=private
445 private=private
459 )
446 )
460 # we added that repo just now, and make sure it has githook
447 # we added that repo just now, and make sure it has githook
461 # installed
448 # installed
462 if new_repo.repo_type == 'git':
449 if new_repo.repo_type == 'git':
463 ScmModel().install_git_hook(new_repo.scm_instance)
450 ScmModel().install_git_hook(new_repo.scm_instance)
464 new_repo.update_changeset_cache()
451 new_repo.update_changeset_cache()
465 elif install_git_hook:
452 elif install_git_hook:
466 if db_repo.repo_type == 'git':
453 if db_repo.repo_type == 'git':
467 ScmModel().install_git_hook(db_repo.scm_instance)
454 ScmModel().install_git_hook(db_repo.scm_instance)
468 # during starting install all cache keys for all repositories in the
455 # during starting install all cache keys for all repositories in the
469 # system, this will register all repos and multiple instances
456 # system, this will register all repos and multiple instances
470 cache_key = CacheInvalidation._get_cache_key(name)
457 cache_key = CacheInvalidation._get_cache_key(name)
471 log.debug("Creating invalidation cache key for %s: %s", name, cache_key)
458 log.debug("Creating invalidation cache key for %s: %s", name, cache_key)
472 CacheInvalidation.invalidate(name)
459 CacheInvalidation.invalidate(name)
473
460
474 sa.commit()
461 sa.commit()
475 removed = []
462 removed = []
476 if remove_obsolete:
463 if remove_obsolete:
477 # remove from database those repositories that are not in the filesystem
464 # remove from database those repositories that are not in the filesystem
478 for repo in sa.query(Repository).all():
465 for repo in sa.query(Repository).all():
479 if repo.repo_name not in initial_repo_list.keys():
466 if repo.repo_name not in initial_repo_list.keys():
480 log.debug("Removing non-existing repository found in db `%s`" %
467 log.debug("Removing non-existing repository found in db `%s`" %
481 repo.repo_name)
468 repo.repo_name)
482 try:
469 try:
483 sa.delete(repo)
470 sa.delete(repo)
484 sa.commit()
471 sa.commit()
485 removed.append(repo.repo_name)
472 removed.append(repo.repo_name)
486 except Exception:
473 except Exception:
487 #don't hold further removals on error
474 #don't hold further removals on error
488 log.error(traceback.format_exc())
475 log.error(traceback.format_exc())
489 sa.rollback()
476 sa.rollback()
490 return added, removed
477 return added, removed
491
478
492
479
493 # set cache regions for beaker so celery can utilise it
480 # set cache regions for beaker so celery can utilise it
494 def add_cache(settings):
481 def add_cache(settings):
495 cache_settings = {'regions': None}
482 cache_settings = {'regions': None}
496 for key in settings.keys():
483 for key in settings.keys():
497 for prefix in ['beaker.cache.', 'cache.']:
484 for prefix in ['beaker.cache.', 'cache.']:
498 if key.startswith(prefix):
485 if key.startswith(prefix):
499 name = key.split(prefix)[1].strip()
486 name = key.split(prefix)[1].strip()
500 cache_settings[name] = settings[key].strip()
487 cache_settings[name] = settings[key].strip()
501 if cache_settings['regions']:
488 if cache_settings['regions']:
502 for region in cache_settings['regions'].split(','):
489 for region in cache_settings['regions'].split(','):
503 region = region.strip()
490 region = region.strip()
504 region_settings = {}
491 region_settings = {}
505 for key, value in cache_settings.items():
492 for key, value in cache_settings.items():
506 if key.startswith(region):
493 if key.startswith(region):
507 region_settings[key.split('.')[1]] = value
494 region_settings[key.split('.')[1]] = value
508 region_settings['expire'] = int(region_settings.get('expire',
495 region_settings['expire'] = int(region_settings.get('expire',
509 60))
496 60))
510 region_settings.setdefault('lock_dir',
497 region_settings.setdefault('lock_dir',
511 cache_settings.get('lock_dir'))
498 cache_settings.get('lock_dir'))
512 region_settings.setdefault('data_dir',
499 region_settings.setdefault('data_dir',
513 cache_settings.get('data_dir'))
500 cache_settings.get('data_dir'))
514
501
515 if 'type' not in region_settings:
502 if 'type' not in region_settings:
516 region_settings['type'] = cache_settings.get('type',
503 region_settings['type'] = cache_settings.get('type',
517 'memory')
504 'memory')
518 beaker.cache.cache_regions[region] = region_settings
505 beaker.cache.cache_regions[region] = region_settings
519
506
520
507
521 def load_rcextensions(root_path):
508 def load_rcextensions(root_path):
522 import rhodecode
509 import rhodecode
523 from rhodecode.config import conf
510 from rhodecode.config import conf
524
511
525 path = os.path.join(root_path, 'rcextensions', '__init__.py')
512 path = os.path.join(root_path, 'rcextensions', '__init__.py')
526 if os.path.isfile(path):
513 if os.path.isfile(path):
527 rcext = create_module('rc', path)
514 rcext = create_module('rc', path)
528 EXT = rhodecode.EXTENSIONS = rcext
515 EXT = rhodecode.EXTENSIONS = rcext
529 log.debug('Found rcextensions now loading %s...' % rcext)
516 log.debug('Found rcextensions now loading %s...' % rcext)
530
517
531 # Additional mappings that are not present in the pygments lexers
518 # Additional mappings that are not present in the pygments lexers
532 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
519 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
533
520
534 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
521 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
535
522
536 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
523 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
537 log.debug('settings custom INDEX_EXTENSIONS')
524 log.debug('settings custom INDEX_EXTENSIONS')
538 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
525 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
539
526
540 #ADDITIONAL MAPPINGS
527 #ADDITIONAL MAPPINGS
541 log.debug('adding extra into INDEX_EXTENSIONS')
528 log.debug('adding extra into INDEX_EXTENSIONS')
542 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
529 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
543
530
544 # auto check if the module is not missing any data, set to default if is
531 # auto check if the module is not missing any data, set to default if is
545 # this will help autoupdate new feature of rcext module
532 # this will help autoupdate new feature of rcext module
546 from rhodecode.config import rcextensions
533 from rhodecode.config import rcextensions
547 for k in dir(rcextensions):
534 for k in dir(rcextensions):
548 if not k.startswith('_') and not hasattr(EXT, k):
535 if not k.startswith('_') and not hasattr(EXT, k):
549 setattr(EXT, k, getattr(rcextensions, k))
536 setattr(EXT, k, getattr(rcextensions, k))
550
537
551
538
552 def get_custom_lexer(extension):
539 def get_custom_lexer(extension):
553 """
540 """
554 returns a custom lexer if it's defined in rcextensions module, or None
541 returns a custom lexer if it's defined in rcextensions module, or None
555 if there's no custom lexer defined
542 if there's no custom lexer defined
556 """
543 """
557 import rhodecode
544 import rhodecode
558 from pygments import lexers
545 from pygments import lexers
559 #check if we didn't define this extension as other lexer
546 #check if we didn't define this extension as other lexer
560 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
547 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
561 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
548 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
562 return lexers.get_lexer_by_name(_lexer_name)
549 return lexers.get_lexer_by_name(_lexer_name)
563
550
564
551
565 #==============================================================================
552 #==============================================================================
566 # TEST FUNCTIONS AND CREATORS
553 # TEST FUNCTIONS AND CREATORS
567 #==============================================================================
554 #==============================================================================
568 def create_test_index(repo_location, config, full_index):
555 def create_test_index(repo_location, config, full_index):
569 """
556 """
570 Makes default test index
557 Makes default test index
571
558
572 :param config: test config
559 :param config: test config
573 :param full_index:
560 :param full_index:
574 """
561 """
575
562
576 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
563 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
577 from rhodecode.lib.pidlock import DaemonLock, LockHeld
564 from rhodecode.lib.pidlock import DaemonLock, LockHeld
578
565
579 repo_location = repo_location
566 repo_location = repo_location
580
567
581 index_location = os.path.join(config['app_conf']['index_dir'])
568 index_location = os.path.join(config['app_conf']['index_dir'])
582 if not os.path.exists(index_location):
569 if not os.path.exists(index_location):
583 os.makedirs(index_location)
570 os.makedirs(index_location)
584
571
585 try:
572 try:
586 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
573 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
587 WhooshIndexingDaemon(index_location=index_location,
574 WhooshIndexingDaemon(index_location=index_location,
588 repo_location=repo_location)\
575 repo_location=repo_location)\
589 .run(full_index=full_index)
576 .run(full_index=full_index)
590 l.release()
577 l.release()
591 except LockHeld:
578 except LockHeld:
592 pass
579 pass
593
580
594
581
595 def create_test_env(repos_test_path, config):
582 def create_test_env(repos_test_path, config):
596 """
583 """
597 Makes a fresh database and
584 Makes a fresh database and
598 install test repository into tmp dir
585 install test repository into tmp dir
599 """
586 """
600 from rhodecode.lib.db_manage import DbManage
587 from rhodecode.lib.db_manage import DbManage
601 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
588 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
602
589
603 # PART ONE create db
590 # PART ONE create db
604 dbconf = config['sqlalchemy.db1.url']
591 dbconf = config['sqlalchemy.db1.url']
605 log.debug('making test db %s' % dbconf)
592 log.debug('making test db %s' % dbconf)
606
593
607 # create test dir if it doesn't exist
594 # create test dir if it doesn't exist
608 if not os.path.isdir(repos_test_path):
595 if not os.path.isdir(repos_test_path):
609 log.debug('Creating testdir %s' % repos_test_path)
596 log.debug('Creating testdir %s' % repos_test_path)
610 os.makedirs(repos_test_path)
597 os.makedirs(repos_test_path)
611
598
612 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
599 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
613 tests=True)
600 tests=True)
614 dbmanage.create_tables(override=True)
601 dbmanage.create_tables(override=True)
615 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
602 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
616 dbmanage.create_default_user()
603 dbmanage.create_default_user()
617 dbmanage.admin_prompt()
604 dbmanage.admin_prompt()
618 dbmanage.create_permissions()
605 dbmanage.create_permissions()
619 dbmanage.populate_default_permissions()
606 dbmanage.populate_default_permissions()
620 Session().commit()
607 Session().commit()
621 # PART TWO make test repo
608 # PART TWO make test repo
622 log.debug('making test vcs repositories')
609 log.debug('making test vcs repositories')
623
610
624 idx_path = config['app_conf']['index_dir']
611 idx_path = config['app_conf']['index_dir']
625 data_path = config['app_conf']['cache_dir']
612 data_path = config['app_conf']['cache_dir']
626
613
627 #clean index and data
614 #clean index and data
628 if idx_path and os.path.exists(idx_path):
615 if idx_path and os.path.exists(idx_path):
629 log.debug('remove %s' % idx_path)
616 log.debug('remove %s' % idx_path)
630 shutil.rmtree(idx_path)
617 shutil.rmtree(idx_path)
631
618
632 if data_path and os.path.exists(data_path):
619 if data_path and os.path.exists(data_path):
633 log.debug('remove %s' % data_path)
620 log.debug('remove %s' % data_path)
634 shutil.rmtree(data_path)
621 shutil.rmtree(data_path)
635
622
636 #CREATE DEFAULT TEST REPOS
623 #CREATE DEFAULT TEST REPOS
637 cur_dir = dn(dn(abspath(__file__)))
624 cur_dir = dn(dn(abspath(__file__)))
638 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
625 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
639 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
626 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
640 tar.close()
627 tar.close()
641
628
642 cur_dir = dn(dn(abspath(__file__)))
629 cur_dir = dn(dn(abspath(__file__)))
643 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
630 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
644 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
631 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
645 tar.close()
632 tar.close()
646
633
647 #LOAD VCS test stuff
634 #LOAD VCS test stuff
648 from rhodecode.tests.vcs import setup_package
635 from rhodecode.tests.vcs import setup_package
649 setup_package()
636 setup_package()
650
637
651
638
652 #==============================================================================
639 #==============================================================================
653 # PASTER COMMANDS
640 # PASTER COMMANDS
654 #==============================================================================
641 #==============================================================================
655 class BasePasterCommand(Command):
642 class BasePasterCommand(Command):
656 """
643 """
657 Abstract Base Class for paster commands.
644 Abstract Base Class for paster commands.
658
645
659 The celery commands are somewhat aggressive about loading
646 The celery commands are somewhat aggressive about loading
660 celery.conf, and since our module sets the `CELERY_LOADER`
647 celery.conf, and since our module sets the `CELERY_LOADER`
661 environment variable to our loader, we have to bootstrap a bit and
648 environment variable to our loader, we have to bootstrap a bit and
662 make sure we've had a chance to load the pylons config off of the
649 make sure we've had a chance to load the pylons config off of the
663 command line, otherwise everything fails.
650 command line, otherwise everything fails.
664 """
651 """
665 min_args = 1
652 min_args = 1
666 min_args_error = "Please provide a paster config file as an argument."
653 min_args_error = "Please provide a paster config file as an argument."
667 takes_config_file = 1
654 takes_config_file = 1
668 requires_config_file = True
655 requires_config_file = True
669
656
670 def notify_msg(self, msg, log=False):
657 def notify_msg(self, msg, log=False):
671 """Make a notification to user, additionally if logger is passed
658 """Make a notification to user, additionally if logger is passed
672 it logs this action using given logger
659 it logs this action using given logger
673
660
674 :param msg: message that will be printed to user
661 :param msg: message that will be printed to user
675 :param log: logging instance, to use to additionally log this message
662 :param log: logging instance, to use to additionally log this message
676
663
677 """
664 """
678 if log and isinstance(log, logging):
665 if log and isinstance(log, logging):
679 log(msg)
666 log(msg)
680
667
681 def run(self, args):
668 def run(self, args):
682 """
669 """
683 Overrides Command.run
670 Overrides Command.run
684
671
685 Checks for a config file argument and loads it.
672 Checks for a config file argument and loads it.
686 """
673 """
687 if len(args) < self.min_args:
674 if len(args) < self.min_args:
688 raise BadCommand(
675 raise BadCommand(
689 self.min_args_error % {'min_args': self.min_args,
676 self.min_args_error % {'min_args': self.min_args,
690 'actual_args': len(args)})
677 'actual_args': len(args)})
691
678
692 # Decrement because we're going to lob off the first argument.
679 # Decrement because we're going to lob off the first argument.
693 # @@ This is hacky
680 # @@ This is hacky
694 self.min_args -= 1
681 self.min_args -= 1
695 self.bootstrap_config(args[0])
682 self.bootstrap_config(args[0])
696 self.update_parser()
683 self.update_parser()
697 return super(BasePasterCommand, self).run(args[1:])
684 return super(BasePasterCommand, self).run(args[1:])
698
685
699 def update_parser(self):
686 def update_parser(self):
700 """
687 """
701 Abstract method. Allows for the class's parser to be updated
688 Abstract method. Allows for the class's parser to be updated
702 before the superclass's `run` method is called. Necessary to
689 before the superclass's `run` method is called. Necessary to
703 allow options/arguments to be passed through to the underlying
690 allow options/arguments to be passed through to the underlying
704 celery command.
691 celery command.
705 """
692 """
706 raise NotImplementedError("Abstract Method.")
693 raise NotImplementedError("Abstract Method.")
707
694
708 def bootstrap_config(self, conf):
695 def bootstrap_config(self, conf):
709 """
696 """
710 Loads the pylons configuration.
697 Loads the pylons configuration.
711 """
698 """
712 from pylons import config as pylonsconfig
699 from pylons import config as pylonsconfig
713
700
714 self.path_to_ini_file = os.path.realpath(conf)
701 self.path_to_ini_file = os.path.realpath(conf)
715 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
702 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
716 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
703 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
717
704
718 def _init_session(self):
705 def _init_session(self):
719 """
706 """
720 Inits SqlAlchemy Session
707 Inits SqlAlchemy Session
721 """
708 """
722 logging.config.fileConfig(self.path_to_ini_file)
709 logging.config.fileConfig(self.path_to_ini_file)
723 from pylons import config
710 from pylons import config
724 from rhodecode.model import init_model
711 from rhodecode.model import init_model
725 from rhodecode.lib.utils2 import engine_from_config
712 from rhodecode.lib.utils2 import engine_from_config
726
713
727 #get to remove repos !!
714 #get to remove repos !!
728 add_cache(config)
715 add_cache(config)
729 engine = engine_from_config(config, 'sqlalchemy.db1.')
716 engine = engine_from_config(config, 'sqlalchemy.db1.')
730 init_model(engine)
717 init_model(engine)
731
718
732
719
733 def check_git_version():
720 def check_git_version():
734 """
721 """
735 Checks what version of git is installed in system, and issues a warning
722 Checks what version of git is installed in system, and issues a warning
736 if it's too old for RhodeCode to properly work.
723 if it's too old for RhodeCode to properly work.
737 """
724 """
738 from rhodecode import BACKENDS
725 from rhodecode import BACKENDS
739 from rhodecode.lib.vcs.backends.git.repository import GitRepository
726 from rhodecode.lib.vcs.backends.git.repository import GitRepository
740 from distutils.version import StrictVersion
727 from distutils.version import StrictVersion
741
728
742 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
729 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
743 _safe=True)
730 _safe=True)
744
731
745 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
732 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
746 if len(ver.split('.')) > 3:
733 if len(ver.split('.')) > 3:
747 #StrictVersion needs to be only 3 element type
734 #StrictVersion needs to be only 3 element type
748 ver = '.'.join(ver.split('.')[:3])
735 ver = '.'.join(ver.split('.')[:3])
749 try:
736 try:
750 _ver = StrictVersion(ver)
737 _ver = StrictVersion(ver)
751 except Exception:
738 except Exception:
752 _ver = StrictVersion('0.0.0')
739 _ver = StrictVersion('0.0.0')
753 stderr = traceback.format_exc()
740 stderr = traceback.format_exc()
754
741
755 req_ver = '1.7.4'
742 req_ver = '1.7.4'
756 to_old_git = False
743 to_old_git = False
757 if _ver < StrictVersion(req_ver):
744 if _ver < StrictVersion(req_ver):
758 to_old_git = True
745 to_old_git = True
759
746
760 if 'git' in BACKENDS:
747 if 'git' in BACKENDS:
761 log.debug('GIT version detected: %s' % stdout)
748 log.debug('GIT version detected: %s' % stdout)
762 if stderr:
749 if stderr:
763 log.warning('Unable to detect git version, org error was: %r' % stderr)
750 log.warning('Unable to detect git version, org error was: %r' % stderr)
764 elif to_old_git:
751 elif to_old_git:
765 log.warning('RhodeCode detected git version %s, which is too old '
752 log.warning('RhodeCode detected git version %s, which is too old '
766 'for the system to function properly. Make sure '
753 'for the system to function properly. Make sure '
767 'its version is at least %s' % (ver, req_ver))
754 'its version is at least %s' % (ver, req_ver))
768 return _ver
755 return _ver
769
756
770
757
771 @decorator.decorator
758 @decorator.decorator
772 def jsonify(func, *args, **kwargs):
759 def jsonify(func, *args, **kwargs):
773 """Action decorator that formats output for JSON
760 """Action decorator that formats output for JSON
774
761
775 Given a function that will return content, this decorator will turn
762 Given a function that will return content, this decorator will turn
776 the result into JSON, with a content-type of 'application/json' and
763 the result into JSON, with a content-type of 'application/json' and
777 output it.
764 output it.
778
765
779 """
766 """
780 from pylons.decorators.util import get_pylons
767 from pylons.decorators.util import get_pylons
781 from rhodecode.lib.ext_json import json
768 from rhodecode.lib.ext_json import json
782 pylons = get_pylons(args)
769 pylons = get_pylons(args)
783 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
770 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
784 data = func(*args, **kwargs)
771 data = func(*args, **kwargs)
785 if isinstance(data, (list, tuple)):
772 if isinstance(data, (list, tuple)):
786 msg = "JSON responses with Array envelopes are susceptible to " \
773 msg = "JSON responses with Array envelopes are susceptible to " \
787 "cross-site data leak attacks, see " \
774 "cross-site data leak attacks, see " \
788 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
775 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
789 warnings.warn(msg, Warning, 2)
776 warnings.warn(msg, Warning, 2)
790 log.warning(msg)
777 log.warning(msg)
791 log.debug("Returning JSON wrapped action output")
778 log.debug("Returning JSON wrapped action output")
792 return json.dumps(data, encoding='utf-8')
779 return json.dumps(data, encoding='utf-8')
@@ -1,122 +1,122 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.tests.fixture import Fixture
2 from rhodecode.tests.fixture import Fixture
3 from rhodecode.model.db import Repository
3 from rhodecode.model.db import Repository
4 from rhodecode.lib.utils import invalidate_cache
5 from rhodecode.model.repo import RepoModel
4 from rhodecode.model.repo import RepoModel
6 from rhodecode.model.meta import Session
5 from rhodecode.model.meta import Session
6 from rhodecode.model.scm import ScmModel
7
7
8 fixture = Fixture()
8 fixture = Fixture()
9
9
10
10
11 class TestSummaryController(TestController):
11 class TestSummaryController(TestController):
12
12
13 def test_index(self):
13 def test_index(self):
14 self.log_user()
14 self.log_user()
15 ID = Repository.get_by_repo_name(HG_REPO).repo_id
15 ID = Repository.get_by_repo_name(HG_REPO).repo_id
16 response = self.app.get(url(controller='summary',
16 response = self.app.get(url(controller='summary',
17 action='index',
17 action='index',
18 repo_name=HG_REPO))
18 repo_name=HG_REPO))
19
19
20 #repo type
20 #repo type
21 response.mustcontain(
21 response.mustcontain(
22 """<img style="margin-bottom:2px" class="icon" """
22 """<img style="margin-bottom:2px" class="icon" """
23 """title="Mercurial repository" alt="Mercurial repository" """
23 """title="Mercurial repository" alt="Mercurial repository" """
24 """src="/images/icons/hgicon.png"/>"""
24 """src="/images/icons/hgicon.png"/>"""
25 )
25 )
26 response.mustcontain(
26 response.mustcontain(
27 """<img style="margin-bottom:2px" class="icon" """
27 """<img style="margin-bottom:2px" class="icon" """
28 """title="Public repository" alt="Public """
28 """title="Public repository" alt="Public """
29 """repository" src="/images/icons/lock_open.png"/>"""
29 """repository" src="/images/icons/lock_open.png"/>"""
30 )
30 )
31
31
32 #codes stats
32 #codes stats
33 self._enable_stats()
33 self._enable_stats()
34
34
35 invalidate_cache('get_repo_cached_%s' % HG_REPO)
35 ScmModel().mark_for_invalidation(HG_REPO)
36 response = self.app.get(url(controller='summary', action='index',
36 response = self.app.get(url(controller='summary', action='index',
37 repo_name=HG_REPO))
37 repo_name=HG_REPO))
38 response.mustcontain(
38 response.mustcontain(
39 """var data = [["py", {"count": 42, "desc": ["Python"]}], """
39 """var data = [["py", {"count": 42, "desc": ["Python"]}], """
40 """["rst", {"count": 11, "desc": ["Rst"]}], """
40 """["rst", {"count": 11, "desc": ["Rst"]}], """
41 """["sh", {"count": 2, "desc": ["Bash"]}], """
41 """["sh", {"count": 2, "desc": ["Bash"]}], """
42 """["makefile", {"count": 1, "desc": ["Makefile", "Makefile"]}],"""
42 """["makefile", {"count": 1, "desc": ["Makefile", "Makefile"]}],"""
43 """ ["cfg", {"count": 1, "desc": ["Ini"]}], """
43 """ ["cfg", {"count": 1, "desc": ["Ini"]}], """
44 """["css", {"count": 1, "desc": ["Css"]}], """
44 """["css", {"count": 1, "desc": ["Css"]}], """
45 """["bat", {"count": 1, "desc": ["Batch"]}]];"""
45 """["bat", {"count": 1, "desc": ["Batch"]}]];"""
46 )
46 )
47
47
48 # clone url...
48 # clone url...
49 response.mustcontain('''id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"''' % HG_REPO)
49 response.mustcontain('''id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"''' % HG_REPO)
50 response.mustcontain('''id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"''' % ID)
50 response.mustcontain('''id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"''' % ID)
51
51
52 def test_index_git(self):
52 def test_index_git(self):
53 self.log_user()
53 self.log_user()
54 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
54 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
55 response = self.app.get(url(controller='summary',
55 response = self.app.get(url(controller='summary',
56 action='index',
56 action='index',
57 repo_name=GIT_REPO))
57 repo_name=GIT_REPO))
58
58
59 #repo type
59 #repo type
60 response.mustcontain(
60 response.mustcontain(
61 """<img style="margin-bottom:2px" class="icon" """
61 """<img style="margin-bottom:2px" class="icon" """
62 """title="Git repository" alt="Git repository" """
62 """title="Git repository" alt="Git repository" """
63 """src="/images/icons/giticon.png"/>"""
63 """src="/images/icons/giticon.png"/>"""
64 )
64 )
65 response.mustcontain(
65 response.mustcontain(
66 """<img style="margin-bottom:2px" class="icon" """
66 """<img style="margin-bottom:2px" class="icon" """
67 """title="Public repository" alt="Public """
67 """title="Public repository" alt="Public """
68 """repository" src="/images/icons/lock_open.png"/>"""
68 """repository" src="/images/icons/lock_open.png"/>"""
69 )
69 )
70
70
71 # clone url...
71 # clone url...
72 response.mustcontain('''id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"''' % GIT_REPO)
72 response.mustcontain('''id="clone_url" readonly="readonly" value="http://test_admin@localhost:80/%s"''' % GIT_REPO)
73 response.mustcontain('''id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"''' % ID)
73 response.mustcontain('''id="clone_url_id" readonly="readonly" value="http://test_admin@localhost:80/_%s"''' % ID)
74
74
75 def test_index_by_id_hg(self):
75 def test_index_by_id_hg(self):
76 self.log_user()
76 self.log_user()
77 ID = Repository.get_by_repo_name(HG_REPO).repo_id
77 ID = Repository.get_by_repo_name(HG_REPO).repo_id
78 response = self.app.get(url(controller='summary',
78 response = self.app.get(url(controller='summary',
79 action='index',
79 action='index',
80 repo_name='_%s' % ID))
80 repo_name='_%s' % ID))
81
81
82 #repo type
82 #repo type
83 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
83 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
84 """title="Mercurial repository" alt="Mercurial """
84 """title="Mercurial repository" alt="Mercurial """
85 """repository" src="/images/icons/hgicon.png"/>""")
85 """repository" src="/images/icons/hgicon.png"/>""")
86 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
86 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
87 """title="Public repository" alt="Public """
87 """title="Public repository" alt="Public """
88 """repository" src="/images/icons/lock_open.png"/>""")
88 """repository" src="/images/icons/lock_open.png"/>""")
89
89
90 def test_index_by_repo_having_id_path_in_name_hg(self):
90 def test_index_by_repo_having_id_path_in_name_hg(self):
91 self.log_user()
91 self.log_user()
92 fixture.create_repo(name='repo_1')
92 fixture.create_repo(name='repo_1')
93 response = self.app.get(url(controller='summary',
93 response = self.app.get(url(controller='summary',
94 action='index',
94 action='index',
95 repo_name='repo_1'))
95 repo_name='repo_1'))
96
96
97 try:
97 try:
98 response.mustcontain("repo_1")
98 response.mustcontain("repo_1")
99 finally:
99 finally:
100 RepoModel().delete(Repository.get_by_repo_name('repo_1'))
100 RepoModel().delete(Repository.get_by_repo_name('repo_1'))
101 Session().commit()
101 Session().commit()
102
102
103 def test_index_by_id_git(self):
103 def test_index_by_id_git(self):
104 self.log_user()
104 self.log_user()
105 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
105 ID = Repository.get_by_repo_name(GIT_REPO).repo_id
106 response = self.app.get(url(controller='summary',
106 response = self.app.get(url(controller='summary',
107 action='index',
107 action='index',
108 repo_name='_%s' % ID))
108 repo_name='_%s' % ID))
109
109
110 #repo type
110 #repo type
111 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
111 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
112 """title="Git repository" alt="Git """
112 """title="Git repository" alt="Git """
113 """repository" src="/images/icons/giticon.png"/>""")
113 """repository" src="/images/icons/giticon.png"/>""")
114 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
114 response.mustcontain("""<img style="margin-bottom:2px" class="icon" """
115 """title="Public repository" alt="Public """
115 """title="Public repository" alt="Public """
116 """repository" src="/images/icons/lock_open.png"/>""")
116 """repository" src="/images/icons/lock_open.png"/>""")
117
117
118 def _enable_stats(self):
118 def _enable_stats(self):
119 r = Repository.get_by_repo_name(HG_REPO)
119 r = Repository.get_by_repo_name(HG_REPO)
120 r.enable_statistics = True
120 r.enable_statistics = True
121 self.Session.add(r)
121 self.Session.add(r)
122 self.Session.commit()
122 self.Session.commit()
General Comments 0
You need to be logged in to leave comments. Login now