##// END OF EJS Templates
Fixed issue #501 error on setting set_as_fork to same repo...
marcink -
r2629:d2901d90 beta
parent child Browse files
Show More
@@ -1,446 +1,448 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
31 from webob.exc import HTTPInternalServerError
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 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
39 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
42 from rhodecode.lib.helpers import get_token
42 from rhodecode.lib.helpers import get_token
43 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
44 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
44 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
45 from rhodecode.model.forms import RepoForm
45 from rhodecode.model.forms import RepoForm
46 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.repo import RepoModel
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 class ReposController(BaseController):
52 class ReposController(BaseController):
53 """
53 """
54 REST Controller styled on the Atom Publishing Protocol"""
54 REST Controller styled on the Atom Publishing Protocol"""
55 # To properly map this controller, ensure your config/routing.py
55 # To properly map this controller, ensure your config/routing.py
56 # file has a resource setup:
56 # file has a resource setup:
57 # map.resource('repo', 'repos')
57 # map.resource('repo', 'repos')
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
61 def __before__(self):
61 def __before__(self):
62 c.admin_user = session.get('admin_user')
62 c.admin_user = session.get('admin_user')
63 c.admin_username = session.get('admin_username')
63 c.admin_username = session.get('admin_username')
64 super(ReposController, self).__before__()
64 super(ReposController, self).__before__()
65
65
66 def __load_defaults(self):
66 def __load_defaults(self):
67 c.repo_groups = RepoGroup.groups_choices()
67 c.repo_groups = RepoGroup.groups_choices()
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
69
69
70 repo_model = RepoModel()
70 repo_model = RepoModel()
71 c.users_array = repo_model.get_users_js()
71 c.users_array = repo_model.get_users_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
72 c.users_groups_array = repo_model.get_users_groups_js()
73 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
73 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
74 c.landing_revs_choices = choices
74 c.landing_revs_choices = choices
75
75
76 def __load_data(self, repo_name=None):
76 def __load_data(self, repo_name=None):
77 """
77 """
78 Load defaults settings for edit, and update
78 Load defaults settings for edit, and update
79
79
80 :param repo_name:
80 :param repo_name:
81 """
81 """
82 self.__load_defaults()
82 self.__load_defaults()
83
83
84 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
84 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
85 repo = db_repo.scm_instance
85 repo = db_repo.scm_instance
86
86
87 if c.repo_info is None:
87 if c.repo_info is None:
88 h.flash(_('%s repository is not mapped to db perhaps'
88 h.flash(_('%s repository is not mapped to db perhaps'
89 ' it was created or renamed from the filesystem'
89 ' it was created or renamed from the filesystem'
90 ' please run the application again'
90 ' please run the application again'
91 ' in order to rescan repositories') % repo_name,
91 ' in order to rescan repositories') % repo_name,
92 category='error')
92 category='error')
93
93
94 return redirect(url('repos'))
94 return redirect(url('repos'))
95
95
96 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
96 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
97 c.landing_revs_choices = choices
97 c.landing_revs_choices = choices
98
98
99 c.default_user_id = User.get_by_username('default').user_id
99 c.default_user_id = User.get_by_username('default').user_id
100 c.in_public_journal = UserFollowing.query()\
100 c.in_public_journal = UserFollowing.query()\
101 .filter(UserFollowing.user_id == c.default_user_id)\
101 .filter(UserFollowing.user_id == c.default_user_id)\
102 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
102 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
103
103
104 if c.repo_info.stats:
104 if c.repo_info.stats:
105 # this is on what revision we ended up so we add +1 for count
105 # this is on what revision we ended up so we add +1 for count
106 last_rev = c.repo_info.stats.stat_on_revision + 1
106 last_rev = c.repo_info.stats.stat_on_revision + 1
107 else:
107 else:
108 last_rev = 0
108 last_rev = 0
109 c.stats_revision = last_rev
109 c.stats_revision = last_rev
110
110
111 c.repo_last_rev = repo.count() if repo.revisions else 0
111 c.repo_last_rev = repo.count() if repo.revisions else 0
112
112
113 if last_rev == 0 or c.repo_last_rev == 0:
113 if last_rev == 0 or c.repo_last_rev == 0:
114 c.stats_percentage = 0
114 c.stats_percentage = 0
115 else:
115 else:
116 c.stats_percentage = '%.2f' % ((float((last_rev)) /
116 c.stats_percentage = '%.2f' % ((float((last_rev)) /
117 c.repo_last_rev) * 100)
117 c.repo_last_rev) * 100)
118
118
119 defaults = RepoModel()._get_defaults(repo_name)
119 defaults = RepoModel()._get_defaults(repo_name)
120
120
121 c.repos_list = [('', _('--REMOVE FORK--'))]
121 c.repos_list = [('', _('--REMOVE FORK--'))]
122 c.repos_list += [(x.repo_id, x.repo_name) for x in
122 c.repos_list += [(x.repo_id, x.repo_name) for x in
123 Repository.query().order_by(Repository.repo_name).all()]
123 Repository.query().order_by(Repository.repo_name).all()
124 if x.repo_id != c.repo_info.repo_id]
124
125
126 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
125 return defaults
127 return defaults
126
128
127 @HasPermissionAllDecorator('hg.admin')
129 @HasPermissionAllDecorator('hg.admin')
128 def index(self, format='html'):
130 def index(self, format='html'):
129 """GET /repos: All items in the collection"""
131 """GET /repos: All items in the collection"""
130 # url('repos')
132 # url('repos')
131
133
132 c.repos_list = ScmModel().get_repos(Repository.query()
134 c.repos_list = ScmModel().get_repos(Repository.query()
133 .order_by(Repository.repo_name)
135 .order_by(Repository.repo_name)
134 .all(), sort_key='name_sort')
136 .all(), sort_key='name_sort')
135 return render('admin/repos/repos.html')
137 return render('admin/repos/repos.html')
136
138
137 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
139 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
138 def create(self):
140 def create(self):
139 """
141 """
140 POST /repos: Create a new item"""
142 POST /repos: Create a new item"""
141 # url('repos')
143 # url('repos')
142
144
143 self.__load_defaults()
145 self.__load_defaults()
144 form_result = {}
146 form_result = {}
145 try:
147 try:
146 form_result = RepoForm(repo_groups=c.repo_groups_choices,
148 form_result = RepoForm(repo_groups=c.repo_groups_choices,
147 landing_revs=c.landing_revs_choices)()\
149 landing_revs=c.landing_revs_choices)()\
148 .to_python(dict(request.POST))
150 .to_python(dict(request.POST))
149 RepoModel().create(form_result, self.rhodecode_user.user_id)
151 RepoModel().create(form_result, self.rhodecode_user.user_id)
150 if form_result['clone_uri']:
152 if form_result['clone_uri']:
151 h.flash(_('created repository %s from %s') \
153 h.flash(_('created repository %s from %s') \
152 % (form_result['repo_name'], form_result['clone_uri']),
154 % (form_result['repo_name'], form_result['clone_uri']),
153 category='success')
155 category='success')
154 else:
156 else:
155 h.flash(_('created repository %s') % form_result['repo_name'],
157 h.flash(_('created repository %s') % form_result['repo_name'],
156 category='success')
158 category='success')
157
159
158 if request.POST.get('user_created'):
160 if request.POST.get('user_created'):
159 # created by regular non admin user
161 # created by regular non admin user
160 action_logger(self.rhodecode_user, 'user_created_repo',
162 action_logger(self.rhodecode_user, 'user_created_repo',
161 form_result['repo_name_full'], self.ip_addr,
163 form_result['repo_name_full'], self.ip_addr,
162 self.sa)
164 self.sa)
163 else:
165 else:
164 action_logger(self.rhodecode_user, 'admin_created_repo',
166 action_logger(self.rhodecode_user, 'admin_created_repo',
165 form_result['repo_name_full'], self.ip_addr,
167 form_result['repo_name_full'], self.ip_addr,
166 self.sa)
168 self.sa)
167 Session.commit()
169 Session.commit()
168 except formencode.Invalid, errors:
170 except formencode.Invalid, errors:
169
171
170 c.new_repo = errors.value['repo_name']
172 c.new_repo = errors.value['repo_name']
171
173
172 if request.POST.get('user_created'):
174 if request.POST.get('user_created'):
173 r = render('admin/repos/repo_add_create_repository.html')
175 r = render('admin/repos/repo_add_create_repository.html')
174 else:
176 else:
175 r = render('admin/repos/repo_add.html')
177 r = render('admin/repos/repo_add.html')
176
178
177 return htmlfill.render(
179 return htmlfill.render(
178 r,
180 r,
179 defaults=errors.value,
181 defaults=errors.value,
180 errors=errors.error_dict or {},
182 errors=errors.error_dict or {},
181 prefix_error=False,
183 prefix_error=False,
182 encoding="UTF-8")
184 encoding="UTF-8")
183
185
184 except Exception:
186 except Exception:
185 log.error(traceback.format_exc())
187 log.error(traceback.format_exc())
186 msg = _('error occurred during creation of repository %s') \
188 msg = _('error occurred during creation of repository %s') \
187 % form_result.get('repo_name')
189 % form_result.get('repo_name')
188 h.flash(msg, category='error')
190 h.flash(msg, category='error')
189 if request.POST.get('user_created'):
191 if request.POST.get('user_created'):
190 return redirect(url('home'))
192 return redirect(url('home'))
191 return redirect(url('repos'))
193 return redirect(url('repos'))
192
194
193 @HasPermissionAllDecorator('hg.admin')
195 @HasPermissionAllDecorator('hg.admin')
194 def new(self, format='html'):
196 def new(self, format='html'):
195 """GET /repos/new: Form to create a new item"""
197 """GET /repos/new: Form to create a new item"""
196 new_repo = request.GET.get('repo', '')
198 new_repo = request.GET.get('repo', '')
197 c.new_repo = repo_name_slug(new_repo)
199 c.new_repo = repo_name_slug(new_repo)
198 self.__load_defaults()
200 self.__load_defaults()
199 return render('admin/repos/repo_add.html')
201 return render('admin/repos/repo_add.html')
200
202
201 @HasPermissionAllDecorator('hg.admin')
203 @HasPermissionAllDecorator('hg.admin')
202 def update(self, repo_name):
204 def update(self, repo_name):
203 """
205 """
204 PUT /repos/repo_name: Update an existing item"""
206 PUT /repos/repo_name: Update an existing item"""
205 # Forms posted to this method should contain a hidden field:
207 # Forms posted to this method should contain a hidden field:
206 # <input type="hidden" name="_method" value="PUT" />
208 # <input type="hidden" name="_method" value="PUT" />
207 # Or using helpers:
209 # Or using helpers:
208 # h.form(url('repo', repo_name=ID),
210 # h.form(url('repo', repo_name=ID),
209 # method='put')
211 # method='put')
210 # url('repo', repo_name=ID)
212 # url('repo', repo_name=ID)
211 self.__load_defaults()
213 self.__load_defaults()
212 repo_model = RepoModel()
214 repo_model = RepoModel()
213 changed_name = repo_name
215 changed_name = repo_name
214 #override the choices with extracted revisions !
216 #override the choices with extracted revisions !
215 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
217 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
216 c.landing_revs_choices = choices
218 c.landing_revs_choices = choices
217
219
218 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
220 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
219 repo_groups=c.repo_groups_choices,
221 repo_groups=c.repo_groups_choices,
220 landing_revs=c.landing_revs_choices)()
222 landing_revs=c.landing_revs_choices)()
221 try:
223 try:
222 form_result = _form.to_python(dict(request.POST))
224 form_result = _form.to_python(dict(request.POST))
223 repo = repo_model.update(repo_name, form_result)
225 repo = repo_model.update(repo_name, form_result)
224 invalidate_cache('get_repo_cached_%s' % repo_name)
226 invalidate_cache('get_repo_cached_%s' % repo_name)
225 h.flash(_('Repository %s updated successfully') % repo_name,
227 h.flash(_('Repository %s updated successfully') % repo_name,
226 category='success')
228 category='success')
227 changed_name = repo.repo_name
229 changed_name = repo.repo_name
228 action_logger(self.rhodecode_user, 'admin_updated_repo',
230 action_logger(self.rhodecode_user, 'admin_updated_repo',
229 changed_name, self.ip_addr, self.sa)
231 changed_name, self.ip_addr, self.sa)
230 Session.commit()
232 Session.commit()
231 except formencode.Invalid, errors:
233 except formencode.Invalid, errors:
232 defaults = self.__load_data(repo_name)
234 defaults = self.__load_data(repo_name)
233 defaults.update(errors.value)
235 defaults.update(errors.value)
234 return htmlfill.render(
236 return htmlfill.render(
235 render('admin/repos/repo_edit.html'),
237 render('admin/repos/repo_edit.html'),
236 defaults=defaults,
238 defaults=defaults,
237 errors=errors.error_dict or {},
239 errors=errors.error_dict or {},
238 prefix_error=False,
240 prefix_error=False,
239 encoding="UTF-8")
241 encoding="UTF-8")
240
242
241 except Exception:
243 except Exception:
242 log.error(traceback.format_exc())
244 log.error(traceback.format_exc())
243 h.flash(_('error occurred during update of repository %s') \
245 h.flash(_('error occurred during update of repository %s') \
244 % repo_name, category='error')
246 % repo_name, category='error')
245 return redirect(url('edit_repo', repo_name=changed_name))
247 return redirect(url('edit_repo', repo_name=changed_name))
246
248
247 @HasPermissionAllDecorator('hg.admin')
249 @HasPermissionAllDecorator('hg.admin')
248 def delete(self, repo_name):
250 def delete(self, repo_name):
249 """
251 """
250 DELETE /repos/repo_name: Delete an existing item"""
252 DELETE /repos/repo_name: Delete an existing item"""
251 # Forms posted to this method should contain a hidden field:
253 # Forms posted to this method should contain a hidden field:
252 # <input type="hidden" name="_method" value="DELETE" />
254 # <input type="hidden" name="_method" value="DELETE" />
253 # Or using helpers:
255 # Or using helpers:
254 # h.form(url('repo', repo_name=ID),
256 # h.form(url('repo', repo_name=ID),
255 # method='delete')
257 # method='delete')
256 # url('repo', repo_name=ID)
258 # url('repo', repo_name=ID)
257
259
258 repo_model = RepoModel()
260 repo_model = RepoModel()
259 repo = repo_model.get_by_repo_name(repo_name)
261 repo = repo_model.get_by_repo_name(repo_name)
260 if not repo:
262 if not repo:
261 h.flash(_('%s repository is not mapped to db perhaps'
263 h.flash(_('%s repository is not mapped to db perhaps'
262 ' it was moved or renamed from the filesystem'
264 ' it was moved or renamed from the filesystem'
263 ' please run the application again'
265 ' please run the application again'
264 ' in order to rescan repositories') % repo_name,
266 ' in order to rescan repositories') % repo_name,
265 category='error')
267 category='error')
266
268
267 return redirect(url('repos'))
269 return redirect(url('repos'))
268 try:
270 try:
269 action_logger(self.rhodecode_user, 'admin_deleted_repo',
271 action_logger(self.rhodecode_user, 'admin_deleted_repo',
270 repo_name, self.ip_addr, self.sa)
272 repo_name, self.ip_addr, self.sa)
271 repo_model.delete(repo)
273 repo_model.delete(repo)
272 invalidate_cache('get_repo_cached_%s' % repo_name)
274 invalidate_cache('get_repo_cached_%s' % repo_name)
273 h.flash(_('deleted repository %s') % repo_name, category='success')
275 h.flash(_('deleted repository %s') % repo_name, category='success')
274 Session.commit()
276 Session.commit()
275 except IntegrityError, e:
277 except IntegrityError, e:
276 if e.message.find('repositories_fork_id_fkey') != -1:
278 if e.message.find('repositories_fork_id_fkey') != -1:
277 log.error(traceback.format_exc())
279 log.error(traceback.format_exc())
278 h.flash(_('Cannot delete %s it still contains attached '
280 h.flash(_('Cannot delete %s it still contains attached '
279 'forks') % repo_name,
281 'forks') % repo_name,
280 category='warning')
282 category='warning')
281 else:
283 else:
282 log.error(traceback.format_exc())
284 log.error(traceback.format_exc())
283 h.flash(_('An error occurred during '
285 h.flash(_('An error occurred during '
284 'deletion of %s') % repo_name,
286 'deletion of %s') % repo_name,
285 category='error')
287 category='error')
286
288
287 except Exception, e:
289 except Exception, e:
288 log.error(traceback.format_exc())
290 log.error(traceback.format_exc())
289 h.flash(_('An error occurred during deletion of %s') % repo_name,
291 h.flash(_('An error occurred during deletion of %s') % repo_name,
290 category='error')
292 category='error')
291
293
292 return redirect(url('repos'))
294 return redirect(url('repos'))
293
295
294 @HasRepoPermissionAllDecorator('repository.admin')
296 @HasRepoPermissionAllDecorator('repository.admin')
295 def delete_perm_user(self, repo_name):
297 def delete_perm_user(self, repo_name):
296 """
298 """
297 DELETE an existing repository permission user
299 DELETE an existing repository permission user
298
300
299 :param repo_name:
301 :param repo_name:
300 """
302 """
301 try:
303 try:
302 RepoModel().revoke_user_permission(repo=repo_name,
304 RepoModel().revoke_user_permission(repo=repo_name,
303 user=request.POST['user_id'])
305 user=request.POST['user_id'])
304 Session.commit()
306 Session.commit()
305 except Exception:
307 except Exception:
306 log.error(traceback.format_exc())
308 log.error(traceback.format_exc())
307 h.flash(_('An error occurred during deletion of repository user'),
309 h.flash(_('An error occurred during deletion of repository user'),
308 category='error')
310 category='error')
309 raise HTTPInternalServerError()
311 raise HTTPInternalServerError()
310
312
311 @HasRepoPermissionAllDecorator('repository.admin')
313 @HasRepoPermissionAllDecorator('repository.admin')
312 def delete_perm_users_group(self, repo_name):
314 def delete_perm_users_group(self, repo_name):
313 """
315 """
314 DELETE an existing repository permission users group
316 DELETE an existing repository permission users group
315
317
316 :param repo_name:
318 :param repo_name:
317 """
319 """
318
320
319 try:
321 try:
320 RepoModel().revoke_users_group_permission(
322 RepoModel().revoke_users_group_permission(
321 repo=repo_name, group_name=request.POST['users_group_id']
323 repo=repo_name, group_name=request.POST['users_group_id']
322 )
324 )
323 Session.commit()
325 Session.commit()
324 except Exception:
326 except Exception:
325 log.error(traceback.format_exc())
327 log.error(traceback.format_exc())
326 h.flash(_('An error occurred during deletion of repository'
328 h.flash(_('An error occurred during deletion of repository'
327 ' users groups'),
329 ' users groups'),
328 category='error')
330 category='error')
329 raise HTTPInternalServerError()
331 raise HTTPInternalServerError()
330
332
331 @HasPermissionAllDecorator('hg.admin')
333 @HasPermissionAllDecorator('hg.admin')
332 def repo_stats(self, repo_name):
334 def repo_stats(self, repo_name):
333 """
335 """
334 DELETE an existing repository statistics
336 DELETE an existing repository statistics
335
337
336 :param repo_name:
338 :param repo_name:
337 """
339 """
338
340
339 try:
341 try:
340 RepoModel().delete_stats(repo_name)
342 RepoModel().delete_stats(repo_name)
341 Session.commit()
343 Session.commit()
342 except Exception, e:
344 except Exception, e:
343 h.flash(_('An error occurred during deletion of repository stats'),
345 h.flash(_('An error occurred during deletion of repository stats'),
344 category='error')
346 category='error')
345 return redirect(url('edit_repo', repo_name=repo_name))
347 return redirect(url('edit_repo', repo_name=repo_name))
346
348
347 @HasPermissionAllDecorator('hg.admin')
349 @HasPermissionAllDecorator('hg.admin')
348 def repo_cache(self, repo_name):
350 def repo_cache(self, repo_name):
349 """
351 """
350 INVALIDATE existing repository cache
352 INVALIDATE existing repository cache
351
353
352 :param repo_name:
354 :param repo_name:
353 """
355 """
354
356
355 try:
357 try:
356 ScmModel().mark_for_invalidation(repo_name)
358 ScmModel().mark_for_invalidation(repo_name)
357 Session.commit()
359 Session.commit()
358 except Exception, e:
360 except Exception, e:
359 h.flash(_('An error occurred during cache invalidation'),
361 h.flash(_('An error occurred during cache invalidation'),
360 category='error')
362 category='error')
361 return redirect(url('edit_repo', repo_name=repo_name))
363 return redirect(url('edit_repo', repo_name=repo_name))
362
364
363 @HasPermissionAllDecorator('hg.admin')
365 @HasPermissionAllDecorator('hg.admin')
364 def repo_public_journal(self, repo_name):
366 def repo_public_journal(self, repo_name):
365 """
367 """
366 Set's this repository to be visible in public journal,
368 Set's this repository to be visible in public journal,
367 in other words assing default user to follow this repo
369 in other words assing default user to follow this repo
368
370
369 :param repo_name:
371 :param repo_name:
370 """
372 """
371
373
372 cur_token = request.POST.get('auth_token')
374 cur_token = request.POST.get('auth_token')
373 token = get_token()
375 token = get_token()
374 if cur_token == token:
376 if cur_token == token:
375 try:
377 try:
376 repo_id = Repository.get_by_repo_name(repo_name).repo_id
378 repo_id = Repository.get_by_repo_name(repo_name).repo_id
377 user_id = User.get_by_username('default').user_id
379 user_id = User.get_by_username('default').user_id
378 self.scm_model.toggle_following_repo(repo_id, user_id)
380 self.scm_model.toggle_following_repo(repo_id, user_id)
379 h.flash(_('Updated repository visibility in public journal'),
381 h.flash(_('Updated repository visibility in public journal'),
380 category='success')
382 category='success')
381 Session.commit()
383 Session.commit()
382 except:
384 except:
383 h.flash(_('An error occurred during setting this'
385 h.flash(_('An error occurred during setting this'
384 ' repository in public journal'),
386 ' repository in public journal'),
385 category='error')
387 category='error')
386
388
387 else:
389 else:
388 h.flash(_('Token mismatch'), category='error')
390 h.flash(_('Token mismatch'), category='error')
389 return redirect(url('edit_repo', repo_name=repo_name))
391 return redirect(url('edit_repo', repo_name=repo_name))
390
392
391 @HasPermissionAllDecorator('hg.admin')
393 @HasPermissionAllDecorator('hg.admin')
392 def repo_pull(self, repo_name):
394 def repo_pull(self, repo_name):
393 """
395 """
394 Runs task to update given repository with remote changes,
396 Runs task to update given repository with remote changes,
395 ie. make pull on remote location
397 ie. make pull on remote location
396
398
397 :param repo_name:
399 :param repo_name:
398 """
400 """
399 try:
401 try:
400 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
402 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
401 h.flash(_('Pulled from remote location'), category='success')
403 h.flash(_('Pulled from remote location'), category='success')
402 except Exception, e:
404 except Exception, e:
403 h.flash(_('An error occurred during pull from remote location'),
405 h.flash(_('An error occurred during pull from remote location'),
404 category='error')
406 category='error')
405
407
406 return redirect(url('edit_repo', repo_name=repo_name))
408 return redirect(url('edit_repo', repo_name=repo_name))
407
409
408 @HasPermissionAllDecorator('hg.admin')
410 @HasPermissionAllDecorator('hg.admin')
409 def repo_as_fork(self, repo_name):
411 def repo_as_fork(self, repo_name):
410 """
412 """
411 Mark given repository as a fork of another
413 Mark given repository as a fork of another
412
414
413 :param repo_name:
415 :param repo_name:
414 """
416 """
415 try:
417 try:
416 fork_id = request.POST.get('id_fork_of')
418 fork_id = request.POST.get('id_fork_of')
417 repo = ScmModel().mark_as_fork(repo_name, fork_id,
419 repo = ScmModel().mark_as_fork(repo_name, fork_id,
418 self.rhodecode_user.username)
420 self.rhodecode_user.username)
419 fork = repo.fork.repo_name if repo.fork else _('Nothing')
421 fork = repo.fork.repo_name if repo.fork else _('Nothing')
420 Session.commit()
422 Session().commit()
421 h.flash(_('Marked repo %s as fork of %s') % (repo_name,fork),
423 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
422 category='success')
424 category='success')
423 except Exception, e:
425 except Exception, e:
424 raise
426 log.error(traceback.format_exc())
425 h.flash(_('An error occurred during this operation'),
427 h.flash(_('An error occurred during this operation'),
426 category='error')
428 category='error')
427
429
428 return redirect(url('edit_repo', repo_name=repo_name))
430 return redirect(url('edit_repo', repo_name=repo_name))
429
431
430 @HasPermissionAllDecorator('hg.admin')
432 @HasPermissionAllDecorator('hg.admin')
431 def show(self, repo_name, format='html'):
433 def show(self, repo_name, format='html'):
432 """GET /repos/repo_name: Show a specific item"""
434 """GET /repos/repo_name: Show a specific item"""
433 # url('repo', repo_name=ID)
435 # url('repo', repo_name=ID)
434
436
435 @HasPermissionAllDecorator('hg.admin')
437 @HasPermissionAllDecorator('hg.admin')
436 def edit(self, repo_name, format='html'):
438 def edit(self, repo_name, format='html'):
437 """GET /repos/repo_name/edit: Form to edit an existing item"""
439 """GET /repos/repo_name/edit: Form to edit an existing item"""
438 # url('edit_repo', repo_name=ID)
440 # url('edit_repo', repo_name=ID)
439 defaults = self.__load_data(repo_name)
441 defaults = self.__load_data(repo_name)
440
442
441 return htmlfill.render(
443 return htmlfill.render(
442 render('admin/repos/repo_edit.html'),
444 render('admin/repos/repo_edit.html'),
443 defaults=defaults,
445 defaults=defaults,
444 encoding="UTF-8",
446 encoding="UTF-8",
445 force_defaults=False
447 force_defaults=False
446 )
448 )
@@ -1,598 +1,600 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 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 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import re
27 import re
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31 import cStringIO
31 import cStringIO
32 import pkg_resources
32 import pkg_resources
33 from os.path import dirname as dn, join as jn
33 from os.path import dirname as dn, join as jn
34
34
35 from sqlalchemy import func
35 from sqlalchemy import func
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 import rhodecode
38 import rhodecode
39 from rhodecode.lib.vcs import get_backend
39 from rhodecode.lib.vcs import get_backend
40 from rhodecode.lib.vcs.exceptions import RepositoryError
40 from rhodecode.lib.vcs.exceptions import RepositoryError
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
41 from rhodecode.lib.vcs.utils.lazy import LazyProperty
42 from rhodecode.lib.vcs.nodes import FileNode
42 from rhodecode.lib.vcs.nodes import FileNode
43
43
44 from rhodecode import BACKENDS
44 from rhodecode import BACKENDS
45 from rhodecode.lib import helpers as h
45 from rhodecode.lib import helpers as h
46 from rhodecode.lib.utils2 import safe_str, safe_unicode
46 from rhodecode.lib.utils2 import safe_str, safe_unicode
47 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
47 from rhodecode.lib.auth import HasRepoPermissionAny, HasReposGroupPermissionAny
48 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
48 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
49 action_logger, EmptyChangeset, REMOVED_REPO_PAT
49 action_logger, EmptyChangeset, REMOVED_REPO_PAT
50 from rhodecode.model import BaseModel
50 from rhodecode.model import BaseModel
51 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
51 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
52 UserFollowing, UserLog, User, RepoGroup, PullRequest
52 UserFollowing, UserLog, User, RepoGroup, PullRequest
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 class UserTemp(object):
57 class UserTemp(object):
58 def __init__(self, user_id):
58 def __init__(self, user_id):
59 self.user_id = user_id
59 self.user_id = user_id
60
60
61 def __repr__(self):
61 def __repr__(self):
62 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
62 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
63
63
64
64
65 class RepoTemp(object):
65 class RepoTemp(object):
66 def __init__(self, repo_id):
66 def __init__(self, repo_id):
67 self.repo_id = repo_id
67 self.repo_id = repo_id
68
68
69 def __repr__(self):
69 def __repr__(self):
70 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
70 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
71
71
72
72
73 class CachedRepoList(object):
73 class CachedRepoList(object):
74 """
74 """
75 Cached repo list, uses in-memory cache after initialization, that is
75 Cached repo list, uses in-memory cache after initialization, that is
76 super fast
76 super fast
77 """
77 """
78
78
79 def __init__(self, db_repo_list, repos_path, order_by=None):
79 def __init__(self, db_repo_list, repos_path, order_by=None):
80 self.db_repo_list = db_repo_list
80 self.db_repo_list = db_repo_list
81 self.repos_path = repos_path
81 self.repos_path = repos_path
82 self.order_by = order_by
82 self.order_by = order_by
83 self.reversed = (order_by or '').startswith('-')
83 self.reversed = (order_by or '').startswith('-')
84
84
85 def __len__(self):
85 def __len__(self):
86 return len(self.db_repo_list)
86 return len(self.db_repo_list)
87
87
88 def __repr__(self):
88 def __repr__(self):
89 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
89 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
90
90
91 def __iter__(self):
91 def __iter__(self):
92 # pre-propagated cache_map to save executing select statements
92 # pre-propagated cache_map to save executing select statements
93 # for each repo
93 # for each repo
94 cache_map = CacheInvalidation.get_cache_map()
94 cache_map = CacheInvalidation.get_cache_map()
95
95
96 for dbr in self.db_repo_list:
96 for dbr in self.db_repo_list:
97 scmr = dbr.scm_instance_cached(cache_map)
97 scmr = dbr.scm_instance_cached(cache_map)
98 # check permission at this level
98 # check permission at this level
99 if not HasRepoPermissionAny(
99 if not HasRepoPermissionAny(
100 'repository.read', 'repository.write', 'repository.admin'
100 'repository.read', 'repository.write', 'repository.admin'
101 )(dbr.repo_name, 'get repo check'):
101 )(dbr.repo_name, 'get repo check'):
102 continue
102 continue
103
103
104 if scmr is None:
104 if scmr is None:
105 log.error(
105 log.error(
106 '%s this repository is present in database but it '
106 '%s this repository is present in database but it '
107 'cannot be created as an scm instance' % dbr.repo_name
107 'cannot be created as an scm instance' % dbr.repo_name
108 )
108 )
109 continue
109 continue
110
110
111 last_change = scmr.last_change
111 last_change = scmr.last_change
112 tip = h.get_changeset_safe(scmr, 'tip')
112 tip = h.get_changeset_safe(scmr, 'tip')
113
113
114 tmp_d = {}
114 tmp_d = {}
115 tmp_d['name'] = dbr.repo_name
115 tmp_d['name'] = dbr.repo_name
116 tmp_d['name_sort'] = tmp_d['name'].lower()
116 tmp_d['name_sort'] = tmp_d['name'].lower()
117 tmp_d['description'] = dbr.description
117 tmp_d['description'] = dbr.description
118 tmp_d['description_sort'] = tmp_d['description'].lower()
118 tmp_d['description_sort'] = tmp_d['description'].lower()
119 tmp_d['last_change'] = last_change
119 tmp_d['last_change'] = last_change
120 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
120 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
121 tmp_d['tip'] = tip.raw_id
121 tmp_d['tip'] = tip.raw_id
122 tmp_d['tip_sort'] = tip.revision
122 tmp_d['tip_sort'] = tip.revision
123 tmp_d['rev'] = tip.revision
123 tmp_d['rev'] = tip.revision
124 tmp_d['contact'] = dbr.user.full_contact
124 tmp_d['contact'] = dbr.user.full_contact
125 tmp_d['contact_sort'] = tmp_d['contact']
125 tmp_d['contact_sort'] = tmp_d['contact']
126 tmp_d['owner_sort'] = tmp_d['contact']
126 tmp_d['owner_sort'] = tmp_d['contact']
127 tmp_d['repo_archives'] = list(scmr._get_archives())
127 tmp_d['repo_archives'] = list(scmr._get_archives())
128 tmp_d['last_msg'] = tip.message
128 tmp_d['last_msg'] = tip.message
129 tmp_d['author'] = tip.author
129 tmp_d['author'] = tip.author
130 tmp_d['dbrepo'] = dbr.get_dict()
130 tmp_d['dbrepo'] = dbr.get_dict()
131 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
131 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
132 yield tmp_d
132 yield tmp_d
133
133
134
134
135 class SimpleCachedRepoList(CachedRepoList):
135 class SimpleCachedRepoList(CachedRepoList):
136 """
136 """
137 Lighter version of CachedRepoList without the scm initialisation
137 Lighter version of CachedRepoList without the scm initialisation
138 """
138 """
139
139
140 def __iter__(self):
140 def __iter__(self):
141 for dbr in self.db_repo_list:
141 for dbr in self.db_repo_list:
142 # check permission at this level
142 # check permission at this level
143 if not HasRepoPermissionAny(
143 if not HasRepoPermissionAny(
144 'repository.read', 'repository.write', 'repository.admin'
144 'repository.read', 'repository.write', 'repository.admin'
145 )(dbr.repo_name, 'get repo check'):
145 )(dbr.repo_name, 'get repo check'):
146 continue
146 continue
147
147
148 tmp_d = {}
148 tmp_d = {}
149 tmp_d['name'] = dbr.repo_name
149 tmp_d['name'] = dbr.repo_name
150 tmp_d['name_sort'] = tmp_d['name'].lower()
150 tmp_d['name_sort'] = tmp_d['name'].lower()
151 tmp_d['description'] = dbr.description
151 tmp_d['description'] = dbr.description
152 tmp_d['description_sort'] = tmp_d['description'].lower()
152 tmp_d['description_sort'] = tmp_d['description'].lower()
153 tmp_d['dbrepo'] = dbr.get_dict()
153 tmp_d['dbrepo'] = dbr.get_dict()
154 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
154 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
155 yield tmp_d
155 yield tmp_d
156
156
157
157
158 class GroupList(object):
158 class GroupList(object):
159
159
160 def __init__(self, db_repo_group_list):
160 def __init__(self, db_repo_group_list):
161 self.db_repo_group_list = db_repo_group_list
161 self.db_repo_group_list = db_repo_group_list
162
162
163 def __len__(self):
163 def __len__(self):
164 return len(self.db_repo_group_list)
164 return len(self.db_repo_group_list)
165
165
166 def __repr__(self):
166 def __repr__(self):
167 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
167 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
168
168
169 def __iter__(self):
169 def __iter__(self):
170 for dbgr in self.db_repo_group_list:
170 for dbgr in self.db_repo_group_list:
171 # check permission at this level
171 # check permission at this level
172 if not HasReposGroupPermissionAny(
172 if not HasReposGroupPermissionAny(
173 'group.read', 'group.write', 'group.admin'
173 'group.read', 'group.write', 'group.admin'
174 )(dbgr.group_name, 'get group repo check'):
174 )(dbgr.group_name, 'get group repo check'):
175 continue
175 continue
176
176
177 yield dbgr
177 yield dbgr
178
178
179
179
180 class ScmModel(BaseModel):
180 class ScmModel(BaseModel):
181 """
181 """
182 Generic Scm Model
182 Generic Scm Model
183 """
183 """
184
184
185 def __get_repo(self, instance):
185 def __get_repo(self, instance):
186 cls = Repository
186 cls = Repository
187 if isinstance(instance, cls):
187 if isinstance(instance, cls):
188 return instance
188 return instance
189 elif isinstance(instance, int) or str(instance).isdigit():
189 elif isinstance(instance, int) or str(instance).isdigit():
190 return cls.get(instance)
190 return cls.get(instance)
191 elif isinstance(instance, basestring):
191 elif isinstance(instance, basestring):
192 return cls.get_by_repo_name(instance)
192 return cls.get_by_repo_name(instance)
193 elif instance:
193 elif instance:
194 raise Exception('given object must be int, basestr or Instance'
194 raise Exception('given object must be int, basestr or Instance'
195 ' of %s got %s' % (type(cls), type(instance)))
195 ' of %s got %s' % (type(cls), type(instance)))
196
196
197 @LazyProperty
197 @LazyProperty
198 def repos_path(self):
198 def repos_path(self):
199 """
199 """
200 Get's the repositories root path from database
200 Get's the repositories root path from database
201 """
201 """
202
202
203 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
203 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
204
204
205 return q.ui_value
205 return q.ui_value
206
206
207 def repo_scan(self, repos_path=None):
207 def repo_scan(self, repos_path=None):
208 """
208 """
209 Listing of repositories in given path. This path should not be a
209 Listing of repositories in given path. This path should not be a
210 repository itself. Return a dictionary of repository objects
210 repository itself. Return a dictionary of repository objects
211
211
212 :param repos_path: path to directory containing repositories
212 :param repos_path: path to directory containing repositories
213 """
213 """
214
214
215 if repos_path is None:
215 if repos_path is None:
216 repos_path = self.repos_path
216 repos_path = self.repos_path
217
217
218 log.info('scanning for repositories in %s' % repos_path)
218 log.info('scanning for repositories in %s' % repos_path)
219
219
220 baseui = make_ui('db')
220 baseui = make_ui('db')
221 repos = {}
221 repos = {}
222
222
223 for name, path in get_filesystem_repos(repos_path, recursive=True):
223 for name, path in get_filesystem_repos(repos_path, recursive=True):
224 # skip removed repos
224 # skip removed repos
225 if REMOVED_REPO_PAT.match(name):
225 if REMOVED_REPO_PAT.match(name):
226 continue
226 continue
227
227
228 # name need to be decomposed and put back together using the /
228 # name need to be decomposed and put back together using the /
229 # since this is internal storage separator for rhodecode
229 # since this is internal storage separator for rhodecode
230 name = Repository.url_sep().join(name.split(os.sep))
230 name = Repository.url_sep().join(name.split(os.sep))
231
231
232 try:
232 try:
233 if name in repos:
233 if name in repos:
234 raise RepositoryError('Duplicate repository name %s '
234 raise RepositoryError('Duplicate repository name %s '
235 'found in %s' % (name, path))
235 'found in %s' % (name, path))
236 else:
236 else:
237
237
238 klass = get_backend(path[0])
238 klass = get_backend(path[0])
239
239
240 if path[0] == 'hg' and path[0] in BACKENDS.keys():
240 if path[0] == 'hg' and path[0] in BACKENDS.keys():
241 repos[name] = klass(safe_str(path[1]), baseui=baseui)
241 repos[name] = klass(safe_str(path[1]), baseui=baseui)
242
242
243 if path[0] == 'git' and path[0] in BACKENDS.keys():
243 if path[0] == 'git' and path[0] in BACKENDS.keys():
244 repos[name] = klass(path[1])
244 repos[name] = klass(path[1])
245 except OSError:
245 except OSError:
246 continue
246 continue
247
247
248 return repos
248 return repos
249
249
250 def get_repos(self, all_repos=None, sort_key=None, simple=False):
250 def get_repos(self, all_repos=None, sort_key=None, simple=False):
251 """
251 """
252 Get all repos from db and for each repo create it's
252 Get all repos from db and for each repo create it's
253 backend instance and fill that backed with information from database
253 backend instance and fill that backed with information from database
254
254
255 :param all_repos: list of repository names as strings
255 :param all_repos: list of repository names as strings
256 give specific repositories list, good for filtering
256 give specific repositories list, good for filtering
257
257
258 :param sort_key: initial sorting of repos
258 :param sort_key: initial sorting of repos
259 :param simple: use SimpleCachedList - one without the SCM info
259 :param simple: use SimpleCachedList - one without the SCM info
260 """
260 """
261 if all_repos is None:
261 if all_repos is None:
262 all_repos = self.sa.query(Repository)\
262 all_repos = self.sa.query(Repository)\
263 .filter(Repository.group_id == None)\
263 .filter(Repository.group_id == None)\
264 .order_by(func.lower(Repository.repo_name)).all()
264 .order_by(func.lower(Repository.repo_name)).all()
265 if simple:
265 if simple:
266 repo_iter = SimpleCachedRepoList(all_repos,
266 repo_iter = SimpleCachedRepoList(all_repos,
267 repos_path=self.repos_path,
267 repos_path=self.repos_path,
268 order_by=sort_key)
268 order_by=sort_key)
269 else:
269 else:
270 repo_iter = CachedRepoList(all_repos,
270 repo_iter = CachedRepoList(all_repos,
271 repos_path=self.repos_path,
271 repos_path=self.repos_path,
272 order_by=sort_key)
272 order_by=sort_key)
273
273
274 return repo_iter
274 return repo_iter
275
275
276 def get_repos_groups(self, all_groups=None):
276 def get_repos_groups(self, all_groups=None):
277 if all_groups is None:
277 if all_groups is None:
278 all_groups = RepoGroup.query()\
278 all_groups = RepoGroup.query()\
279 .filter(RepoGroup.group_parent_id == None).all()
279 .filter(RepoGroup.group_parent_id == None).all()
280 group_iter = GroupList(all_groups)
280 group_iter = GroupList(all_groups)
281
281
282 return group_iter
282 return group_iter
283
283
284 def mark_for_invalidation(self, repo_name):
284 def mark_for_invalidation(self, repo_name):
285 """
285 """
286 Puts cache invalidation task into db for
286 Puts cache invalidation task into db for
287 further global cache invalidation
287 further global cache invalidation
288
288
289 :param repo_name: this repo that should invalidation take place
289 :param repo_name: this repo that should invalidation take place
290 """
290 """
291 CacheInvalidation.set_invalidate(repo_name)
291 CacheInvalidation.set_invalidate(repo_name)
292
292
293 def toggle_following_repo(self, follow_repo_id, user_id):
293 def toggle_following_repo(self, follow_repo_id, user_id):
294
294
295 f = self.sa.query(UserFollowing)\
295 f = self.sa.query(UserFollowing)\
296 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
296 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
297 .filter(UserFollowing.user_id == user_id).scalar()
297 .filter(UserFollowing.user_id == user_id).scalar()
298
298
299 if f is not None:
299 if f is not None:
300 try:
300 try:
301 self.sa.delete(f)
301 self.sa.delete(f)
302 action_logger(UserTemp(user_id),
302 action_logger(UserTemp(user_id),
303 'stopped_following_repo',
303 'stopped_following_repo',
304 RepoTemp(follow_repo_id))
304 RepoTemp(follow_repo_id))
305 return
305 return
306 except:
306 except:
307 log.error(traceback.format_exc())
307 log.error(traceback.format_exc())
308 raise
308 raise
309
309
310 try:
310 try:
311 f = UserFollowing()
311 f = UserFollowing()
312 f.user_id = user_id
312 f.user_id = user_id
313 f.follows_repo_id = follow_repo_id
313 f.follows_repo_id = follow_repo_id
314 self.sa.add(f)
314 self.sa.add(f)
315
315
316 action_logger(UserTemp(user_id),
316 action_logger(UserTemp(user_id),
317 'started_following_repo',
317 'started_following_repo',
318 RepoTemp(follow_repo_id))
318 RepoTemp(follow_repo_id))
319 except:
319 except:
320 log.error(traceback.format_exc())
320 log.error(traceback.format_exc())
321 raise
321 raise
322
322
323 def toggle_following_user(self, follow_user_id, user_id):
323 def toggle_following_user(self, follow_user_id, user_id):
324 f = self.sa.query(UserFollowing)\
324 f = self.sa.query(UserFollowing)\
325 .filter(UserFollowing.follows_user_id == follow_user_id)\
325 .filter(UserFollowing.follows_user_id == follow_user_id)\
326 .filter(UserFollowing.user_id == user_id).scalar()
326 .filter(UserFollowing.user_id == user_id).scalar()
327
327
328 if f is not None:
328 if f is not None:
329 try:
329 try:
330 self.sa.delete(f)
330 self.sa.delete(f)
331 return
331 return
332 except:
332 except:
333 log.error(traceback.format_exc())
333 log.error(traceback.format_exc())
334 raise
334 raise
335
335
336 try:
336 try:
337 f = UserFollowing()
337 f = UserFollowing()
338 f.user_id = user_id
338 f.user_id = user_id
339 f.follows_user_id = follow_user_id
339 f.follows_user_id = follow_user_id
340 self.sa.add(f)
340 self.sa.add(f)
341 except:
341 except:
342 log.error(traceback.format_exc())
342 log.error(traceback.format_exc())
343 raise
343 raise
344
344
345 def is_following_repo(self, repo_name, user_id, cache=False):
345 def is_following_repo(self, repo_name, user_id, cache=False):
346 r = self.sa.query(Repository)\
346 r = self.sa.query(Repository)\
347 .filter(Repository.repo_name == repo_name).scalar()
347 .filter(Repository.repo_name == repo_name).scalar()
348
348
349 f = self.sa.query(UserFollowing)\
349 f = self.sa.query(UserFollowing)\
350 .filter(UserFollowing.follows_repository == r)\
350 .filter(UserFollowing.follows_repository == r)\
351 .filter(UserFollowing.user_id == user_id).scalar()
351 .filter(UserFollowing.user_id == user_id).scalar()
352
352
353 return f is not None
353 return f is not None
354
354
355 def is_following_user(self, username, user_id, cache=False):
355 def is_following_user(self, username, user_id, cache=False):
356 u = User.get_by_username(username)
356 u = User.get_by_username(username)
357
357
358 f = self.sa.query(UserFollowing)\
358 f = self.sa.query(UserFollowing)\
359 .filter(UserFollowing.follows_user == u)\
359 .filter(UserFollowing.follows_user == u)\
360 .filter(UserFollowing.user_id == user_id).scalar()
360 .filter(UserFollowing.user_id == user_id).scalar()
361
361
362 return f is not None
362 return f is not None
363
363
364 def get_followers(self, repo):
364 def get_followers(self, repo):
365 repo = self._get_repo(repo)
365 repo = self._get_repo(repo)
366
366
367 return self.sa.query(UserFollowing)\
367 return self.sa.query(UserFollowing)\
368 .filter(UserFollowing.follows_repository == repo).count()
368 .filter(UserFollowing.follows_repository == repo).count()
369
369
370 def get_forks(self, repo):
370 def get_forks(self, repo):
371 repo = self._get_repo(repo)
371 repo = self._get_repo(repo)
372 return self.sa.query(Repository)\
372 return self.sa.query(Repository)\
373 .filter(Repository.fork == repo).count()
373 .filter(Repository.fork == repo).count()
374
374
375 def get_pull_requests(self, repo):
375 def get_pull_requests(self, repo):
376 repo = self._get_repo(repo)
376 repo = self._get_repo(repo)
377 return self.sa.query(PullRequest)\
377 return self.sa.query(PullRequest)\
378 .filter(PullRequest.other_repo == repo).count()
378 .filter(PullRequest.other_repo == repo).count()
379
379
380 def mark_as_fork(self, repo, fork, user):
380 def mark_as_fork(self, repo, fork, user):
381 repo = self.__get_repo(repo)
381 repo = self.__get_repo(repo)
382 fork = self.__get_repo(fork)
382 fork = self.__get_repo(fork)
383 if fork and repo.repo_id == fork.repo_id:
384 raise Exception("Cannot set repository as fork of itself")
383 repo.fork = fork
385 repo.fork = fork
384 self.sa.add(repo)
386 self.sa.add(repo)
385 return repo
387 return repo
386
388
387 def pull_changes(self, repo, username):
389 def pull_changes(self, repo, username):
388 dbrepo = self.__get_repo(repo)
390 dbrepo = self.__get_repo(repo)
389 clone_uri = dbrepo.clone_uri
391 clone_uri = dbrepo.clone_uri
390 if not clone_uri:
392 if not clone_uri:
391 raise Exception("This repository doesn't have a clone uri")
393 raise Exception("This repository doesn't have a clone uri")
392
394
393 repo = dbrepo.scm_instance
395 repo = dbrepo.scm_instance
394 try:
396 try:
395 extras = {
397 extras = {
396 'ip': '',
398 'ip': '',
397 'username': username,
399 'username': username,
398 'action': 'push_remote',
400 'action': 'push_remote',
399 'repository': dbrepo.repo_name,
401 'repository': dbrepo.repo_name,
400 'scm': repo.alias,
402 'scm': repo.alias,
401 }
403 }
402 Repository.inject_ui(repo, extras=extras)
404 Repository.inject_ui(repo, extras=extras)
403
405
404 if repo.alias == 'git':
406 if repo.alias == 'git':
405 repo.fetch(clone_uri)
407 repo.fetch(clone_uri)
406 else:
408 else:
407 repo.pull(clone_uri)
409 repo.pull(clone_uri)
408 self.mark_for_invalidation(dbrepo.repo_name)
410 self.mark_for_invalidation(dbrepo.repo_name)
409 except:
411 except:
410 log.error(traceback.format_exc())
412 log.error(traceback.format_exc())
411 raise
413 raise
412
414
413 def commit_change(self, repo, repo_name, cs, user, author, message,
415 def commit_change(self, repo, repo_name, cs, user, author, message,
414 content, f_path):
416 content, f_path):
415
417
416 if repo.alias == 'hg':
418 if repo.alias == 'hg':
417 from rhodecode.lib.vcs.backends.hg import \
419 from rhodecode.lib.vcs.backends.hg import \
418 MercurialInMemoryChangeset as IMC
420 MercurialInMemoryChangeset as IMC
419 elif repo.alias == 'git':
421 elif repo.alias == 'git':
420 from rhodecode.lib.vcs.backends.git import \
422 from rhodecode.lib.vcs.backends.git import \
421 GitInMemoryChangeset as IMC
423 GitInMemoryChangeset as IMC
422
424
423 # decoding here will force that we have proper encoded values
425 # decoding here will force that we have proper encoded values
424 # in any other case this will throw exceptions and deny commit
426 # in any other case this will throw exceptions and deny commit
425 content = safe_str(content)
427 content = safe_str(content)
426 path = safe_str(f_path)
428 path = safe_str(f_path)
427 # message and author needs to be unicode
429 # message and author needs to be unicode
428 # proper backend should then translate that into required type
430 # proper backend should then translate that into required type
429 message = safe_unicode(message)
431 message = safe_unicode(message)
430 author = safe_unicode(author)
432 author = safe_unicode(author)
431 m = IMC(repo)
433 m = IMC(repo)
432 m.change(FileNode(path, content))
434 m.change(FileNode(path, content))
433 tip = m.commit(message=message,
435 tip = m.commit(message=message,
434 author=author,
436 author=author,
435 parents=[cs], branch=cs.branch)
437 parents=[cs], branch=cs.branch)
436
438
437 new_cs = tip.short_id
439 new_cs = tip.short_id
438 action = 'push_local:%s' % new_cs
440 action = 'push_local:%s' % new_cs
439
441
440 action_logger(user, action, repo_name)
442 action_logger(user, action, repo_name)
441
443
442 self.mark_for_invalidation(repo_name)
444 self.mark_for_invalidation(repo_name)
443
445
444 def create_node(self, repo, repo_name, cs, user, author, message, content,
446 def create_node(self, repo, repo_name, cs, user, author, message, content,
445 f_path):
447 f_path):
446 if repo.alias == 'hg':
448 if repo.alias == 'hg':
447 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
449 from rhodecode.lib.vcs.backends.hg import MercurialInMemoryChangeset as IMC
448 elif repo.alias == 'git':
450 elif repo.alias == 'git':
449 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
451 from rhodecode.lib.vcs.backends.git import GitInMemoryChangeset as IMC
450 # decoding here will force that we have proper encoded values
452 # decoding here will force that we have proper encoded values
451 # in any other case this will throw exceptions and deny commit
453 # in any other case this will throw exceptions and deny commit
452
454
453 if isinstance(content, (basestring,)):
455 if isinstance(content, (basestring,)):
454 content = safe_str(content)
456 content = safe_str(content)
455 elif isinstance(content, (file, cStringIO.OutputType,)):
457 elif isinstance(content, (file, cStringIO.OutputType,)):
456 content = content.read()
458 content = content.read()
457 else:
459 else:
458 raise Exception('Content is of unrecognized type %s' % (
460 raise Exception('Content is of unrecognized type %s' % (
459 type(content)
461 type(content)
460 ))
462 ))
461
463
462 message = safe_unicode(message)
464 message = safe_unicode(message)
463 author = safe_unicode(author)
465 author = safe_unicode(author)
464 path = safe_str(f_path)
466 path = safe_str(f_path)
465 m = IMC(repo)
467 m = IMC(repo)
466
468
467 if isinstance(cs, EmptyChangeset):
469 if isinstance(cs, EmptyChangeset):
468 # EmptyChangeset means we we're editing empty repository
470 # EmptyChangeset means we we're editing empty repository
469 parents = None
471 parents = None
470 else:
472 else:
471 parents = [cs]
473 parents = [cs]
472
474
473 m.add(FileNode(path, content=content))
475 m.add(FileNode(path, content=content))
474 tip = m.commit(message=message,
476 tip = m.commit(message=message,
475 author=author,
477 author=author,
476 parents=parents, branch=cs.branch)
478 parents=parents, branch=cs.branch)
477 new_cs = tip.short_id
479 new_cs = tip.short_id
478 action = 'push_local:%s' % new_cs
480 action = 'push_local:%s' % new_cs
479
481
480 action_logger(user, action, repo_name)
482 action_logger(user, action, repo_name)
481
483
482 self.mark_for_invalidation(repo_name)
484 self.mark_for_invalidation(repo_name)
483
485
484 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
486 def get_nodes(self, repo_name, revision, root_path='/', flat=True):
485 """
487 """
486 recursive walk in root dir and return a set of all path in that dir
488 recursive walk in root dir and return a set of all path in that dir
487 based on repository walk function
489 based on repository walk function
488
490
489 :param repo_name: name of repository
491 :param repo_name: name of repository
490 :param revision: revision for which to list nodes
492 :param revision: revision for which to list nodes
491 :param root_path: root path to list
493 :param root_path: root path to list
492 :param flat: return as a list, if False returns a dict with decription
494 :param flat: return as a list, if False returns a dict with decription
493
495
494 """
496 """
495 _files = list()
497 _files = list()
496 _dirs = list()
498 _dirs = list()
497 try:
499 try:
498 _repo = self.__get_repo(repo_name)
500 _repo = self.__get_repo(repo_name)
499 changeset = _repo.scm_instance.get_changeset(revision)
501 changeset = _repo.scm_instance.get_changeset(revision)
500 root_path = root_path.lstrip('/')
502 root_path = root_path.lstrip('/')
501 for topnode, dirs, files in changeset.walk(root_path):
503 for topnode, dirs, files in changeset.walk(root_path):
502 for f in files:
504 for f in files:
503 _files.append(f.path if flat else {"name": f.path,
505 _files.append(f.path if flat else {"name": f.path,
504 "type": "file"})
506 "type": "file"})
505 for d in dirs:
507 for d in dirs:
506 _dirs.append(d.path if flat else {"name": d.path,
508 _dirs.append(d.path if flat else {"name": d.path,
507 "type": "dir"})
509 "type": "dir"})
508 except RepositoryError:
510 except RepositoryError:
509 log.debug(traceback.format_exc())
511 log.debug(traceback.format_exc())
510 raise
512 raise
511
513
512 return _dirs, _files
514 return _dirs, _files
513
515
514 def get_unread_journal(self):
516 def get_unread_journal(self):
515 return self.sa.query(UserLog).count()
517 return self.sa.query(UserLog).count()
516
518
517 def get_repo_landing_revs(self, repo=None):
519 def get_repo_landing_revs(self, repo=None):
518 """
520 """
519 Generates select option with tags branches and bookmarks (for hg only)
521 Generates select option with tags branches and bookmarks (for hg only)
520 grouped by type
522 grouped by type
521
523
522 :param repo:
524 :param repo:
523 :type repo:
525 :type repo:
524 """
526 """
525
527
526 hist_l = []
528 hist_l = []
527 choices = []
529 choices = []
528 repo = self.__get_repo(repo)
530 repo = self.__get_repo(repo)
529 hist_l.append(['tip', _('latest tip')])
531 hist_l.append(['tip', _('latest tip')])
530 choices.append('tip')
532 choices.append('tip')
531 if not repo:
533 if not repo:
532 return choices, hist_l
534 return choices, hist_l
533
535
534 repo = repo.scm_instance
536 repo = repo.scm_instance
535
537
536 branches_group = ([(k, k) for k, v in
538 branches_group = ([(k, k) for k, v in
537 repo.branches.iteritems()], _("Branches"))
539 repo.branches.iteritems()], _("Branches"))
538 hist_l.append(branches_group)
540 hist_l.append(branches_group)
539 choices.extend([x[0] for x in branches_group[0]])
541 choices.extend([x[0] for x in branches_group[0]])
540
542
541 if repo.alias == 'hg':
543 if repo.alias == 'hg':
542 bookmarks_group = ([(k, k) for k, v in
544 bookmarks_group = ([(k, k) for k, v in
543 repo.bookmarks.iteritems()], _("Bookmarks"))
545 repo.bookmarks.iteritems()], _("Bookmarks"))
544 hist_l.append(bookmarks_group)
546 hist_l.append(bookmarks_group)
545 choices.extend([x[0] for x in bookmarks_group[0]])
547 choices.extend([x[0] for x in bookmarks_group[0]])
546
548
547 tags_group = ([(k, k) for k, v in
549 tags_group = ([(k, k) for k, v in
548 repo.tags.iteritems()], _("Tags"))
550 repo.tags.iteritems()], _("Tags"))
549 hist_l.append(tags_group)
551 hist_l.append(tags_group)
550 choices.extend([x[0] for x in tags_group[0]])
552 choices.extend([x[0] for x in tags_group[0]])
551
553
552 return choices, hist_l
554 return choices, hist_l
553
555
554 def install_git_hook(self, repo, force_create=False):
556 def install_git_hook(self, repo, force_create=False):
555 """
557 """
556 Creates a rhodecode hook inside a git repository
558 Creates a rhodecode hook inside a git repository
557
559
558 :param repo: Instance of VCS repo
560 :param repo: Instance of VCS repo
559 :param force_create: Create even if same name hook exists
561 :param force_create: Create even if same name hook exists
560 """
562 """
561
563
562 loc = jn(repo.path, 'hooks')
564 loc = jn(repo.path, 'hooks')
563 if not repo.bare:
565 if not repo.bare:
564 loc = jn(repo.path, '.git', 'hooks')
566 loc = jn(repo.path, '.git', 'hooks')
565 if not os.path.isdir(loc):
567 if not os.path.isdir(loc):
566 os.makedirs(loc)
568 os.makedirs(loc)
567
569
568 tmpl = pkg_resources.resource_string(
570 tmpl = pkg_resources.resource_string(
569 'rhodecode', jn('config', 'post_receive_tmpl.py')
571 'rhodecode', jn('config', 'post_receive_tmpl.py')
570 )
572 )
571
573
572 _hook_file = jn(loc, 'post-receive')
574 _hook_file = jn(loc, 'post-receive')
573 _rhodecode_hook = False
575 _rhodecode_hook = False
574 log.debug('Installing git hook in repo %s' % repo)
576 log.debug('Installing git hook in repo %s' % repo)
575 if os.path.exists(_hook_file):
577 if os.path.exists(_hook_file):
576 # let's take a look at this hook, maybe it's rhodecode ?
578 # let's take a look at this hook, maybe it's rhodecode ?
577 log.debug('hook exists, checking if it is from rhodecode')
579 log.debug('hook exists, checking if it is from rhodecode')
578 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
580 _HOOK_VER_PAT = re.compile(r'^RC_HOOK_VER')
579 with open(_hook_file, 'rb') as f:
581 with open(_hook_file, 'rb') as f:
580 data = f.read()
582 data = f.read()
581 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
583 matches = re.compile(r'(?:%s)\s*=\s*(.*)'
582 % 'RC_HOOK_VER').search(data)
584 % 'RC_HOOK_VER').search(data)
583 if matches:
585 if matches:
584 try:
586 try:
585 ver = matches.groups()[0]
587 ver = matches.groups()[0]
586 log.debug('got %s it is rhodecode' % (ver))
588 log.debug('got %s it is rhodecode' % (ver))
587 _rhodecode_hook = True
589 _rhodecode_hook = True
588 except:
590 except:
589 log.error(traceback.format_exc())
591 log.error(traceback.format_exc())
590
592
591 if _rhodecode_hook or force_create:
593 if _rhodecode_hook or force_create:
592 log.debug('writing hook file !')
594 log.debug('writing hook file !')
593 with open(_hook_file, 'wb') as f:
595 with open(_hook_file, 'wb') as f:
594 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
596 tmpl = tmpl.replace('_TMPL_', rhodecode.__version__)
595 f.write(tmpl)
597 f.write(tmpl)
596 os.chmod(_hook_file, 0755)
598 os.chmod(_hook_file, 0755)
597 else:
599 else:
598 log.debug('skipping writing hook file') No newline at end of file
600 log.debug('skipping writing hook file')
@@ -1,164 +1,164 b''
1 """Pylons application test package
1 """Pylons application test package
2
2
3 This package assumes the Pylons environment is already loaded, such as
3 This package assumes the Pylons environment is already loaded, such as
4 when this script is imported from the `nosetests --with-pylons=test.ini`
4 when this script is imported from the `nosetests --with-pylons=test.ini`
5 command.
5 command.
6
6
7 This module initializes the application via ``websetup`` (`paster
7 This module initializes the application via ``websetup`` (`paster
8 setup-app`) and provides the base testing objects.
8 setup-app`) and provides the base testing objects.
9 """
9 """
10 import os
10 import os
11 import time
11 import time
12 import logging
12 import logging
13 import datetime
13 import datetime
14 import hashlib
14 import hashlib
15 import tempfile
15 import tempfile
16 from os.path import join as jn
16 from os.path import join as jn
17
17
18 from unittest import TestCase
18 from unittest import TestCase
19 from tempfile import _RandomNameSequence
19 from tempfile import _RandomNameSequence
20
20
21 from paste.deploy import loadapp
21 from paste.deploy import loadapp
22 from paste.script.appinstall import SetupCommand
22 from paste.script.appinstall import SetupCommand
23 from pylons import config, url
23 from pylons import config, url
24 from routes.util import URLGenerator
24 from routes.util import URLGenerator
25 from webtest import TestApp
25 from webtest import TestApp
26
26
27 from rhodecode import is_windows
27 from rhodecode import is_windows
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 from rhodecode.model.db import User
29 from rhodecode.model.db import User
30 from rhodecode.tests.nose_parametrized import parameterized
30 from rhodecode.tests.nose_parametrized import parameterized
31
31
32 import pylons.test
32 import pylons.test
33
33
34
34
35 os.environ['TZ'] = 'UTC'
35 os.environ['TZ'] = 'UTC'
36 if not is_windows:
36 if not is_windows:
37 time.tzset()
37 time.tzset()
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 __all__ = [
41 __all__ = [
42 'parameterized', 'environ', 'url', 'get_new_dir', 'TestController',
42 'parameterized', 'environ', 'url', 'get_new_dir', 'TestController',
43 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO',
43 'TESTS_TMP_PATH', 'HG_REPO', 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO',
44 'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_REGULAR_LOGIN',
44 'HG_FORK', 'GIT_FORK', 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_REGULAR_LOGIN',
45 'TEST_USER_REGULAR_PASS', 'TEST_USER_REGULAR_EMAIL',
45 'TEST_USER_REGULAR_PASS', 'TEST_USER_REGULAR_EMAIL',
46 'TEST_USER_REGULAR2_LOGIN', 'TEST_USER_REGULAR2_PASS',
46 'TEST_USER_REGULAR2_LOGIN', 'TEST_USER_REGULAR2_PASS',
47 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO', 'TEST_HG_REPO_CLONE',
47 'TEST_USER_REGULAR2_EMAIL', 'TEST_HG_REPO', 'TEST_HG_REPO_CLONE',
48 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO', 'TEST_GIT_REPO_CLONE',
48 'TEST_HG_REPO_PULL', 'TEST_GIT_REPO', 'TEST_GIT_REPO_CLONE',
49 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO', 'SCM_TESTS',
49 'TEST_GIT_REPO_PULL', 'HG_REMOTE_REPO', 'GIT_REMOTE_REPO', 'SCM_TESTS',
50 ]
50 ]
51
51
52 # Invoke websetup with the current config file
52 # Invoke websetup with the current config file
53 # SetupCommand('setup-app').run([config_file])
53 # SetupCommand('setup-app').run([config_file])
54
54
55 ##RUNNING DESIRED TESTS
55 ##RUNNING DESIRED TESTS
56 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
56 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
57 # nosetests --pdb --pdb-failures
57 # nosetests --pdb --pdb-failures
58 # nosetests --with-coverage --cover-package=rhodecode.model.validators rhodecode.tests.test_validators
58 # nosetests --with-coverage --cover-package=rhodecode.model.validators rhodecode.tests.test_validators
59 environ = {}
59 environ = {}
60
60
61 #SOME GLOBALS FOR TESTS
61 #SOME GLOBALS FOR TESTS
62
62
63 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
63 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
64 TEST_USER_ADMIN_LOGIN = 'test_admin'
64 TEST_USER_ADMIN_LOGIN = 'test_admin'
65 TEST_USER_ADMIN_PASS = 'test12'
65 TEST_USER_ADMIN_PASS = 'test12'
66 TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com'
66 TEST_USER_ADMIN_EMAIL = 'test_admin@mail.com'
67
67
68 TEST_USER_REGULAR_LOGIN = 'test_regular'
68 TEST_USER_REGULAR_LOGIN = 'test_regular'
69 TEST_USER_REGULAR_PASS = 'test12'
69 TEST_USER_REGULAR_PASS = 'test12'
70 TEST_USER_REGULAR_EMAIL = 'test_regular@mail.com'
70 TEST_USER_REGULAR_EMAIL = 'test_regular@mail.com'
71
71
72 TEST_USER_REGULAR2_LOGIN = 'test_regular2'
72 TEST_USER_REGULAR2_LOGIN = 'test_regular2'
73 TEST_USER_REGULAR2_PASS = 'test12'
73 TEST_USER_REGULAR2_PASS = 'test12'
74 TEST_USER_REGULAR2_EMAIL = 'test_regular2@mail.com'
74 TEST_USER_REGULAR2_EMAIL = 'test_regular2@mail.com'
75
75
76 HG_REPO = 'vcs_test_hg'
76 HG_REPO = 'vcs_test_hg'
77 GIT_REPO = 'vcs_test_git'
77 GIT_REPO = 'vcs_test_git'
78
78
79 NEW_HG_REPO = 'vcs_test_hg_new'
79 NEW_HG_REPO = 'vcs_test_hg_new'
80 NEW_GIT_REPO = 'vcs_test_git_new'
80 NEW_GIT_REPO = 'vcs_test_git_new'
81
81
82 HG_FORK = 'vcs_test_hg_fork'
82 HG_FORK = 'vcs_test_hg_fork'
83 GIT_FORK = 'vcs_test_git_fork'
83 GIT_FORK = 'vcs_test_git_fork'
84
84
85 ## VCS
85 ## VCS
86 SCM_TESTS = ['hg', 'git']
86 SCM_TESTS = ['hg', 'git']
87 uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
87 uniq_suffix = str(int(time.mktime(datetime.datetime.now().timetuple())))
88
88
89 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
89 GIT_REMOTE_REPO = 'git://github.com/codeinn/vcs.git'
90
90
91 TEST_GIT_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
91 TEST_GIT_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
92 TEST_GIT_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)
92 TEST_GIT_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcsgitclone%s' % uniq_suffix)
93 TEST_GIT_REPO_PULL = jn(TESTS_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)
93 TEST_GIT_REPO_PULL = jn(TESTS_TMP_PATH, 'vcsgitpull%s' % uniq_suffix)
94
94
95
95
96 HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
96 HG_REMOTE_REPO = 'http://bitbucket.org/marcinkuzminski/vcs'
97
97
98 TEST_HG_REPO = jn(TESTS_TMP_PATH, HG_REPO)
98 TEST_HG_REPO = jn(TESTS_TMP_PATH, HG_REPO)
99 TEST_HG_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcshgclone%s' % uniq_suffix)
99 TEST_HG_REPO_CLONE = jn(TESTS_TMP_PATH, 'vcshgclone%s' % uniq_suffix)
100 TEST_HG_REPO_PULL = jn(TESTS_TMP_PATH, 'vcshgpull%s' % uniq_suffix)
100 TEST_HG_REPO_PULL = jn(TESTS_TMP_PATH, 'vcshgpull%s' % uniq_suffix)
101
101
102 TEST_DIR = tempfile.gettempdir()
102 TEST_DIR = tempfile.gettempdir()
103 TEST_REPO_PREFIX = 'vcs-test'
103 TEST_REPO_PREFIX = 'vcs-test'
104
104
105 # cached repos if any !
105 # cached repos if any !
106 # comment out to get some other repos from bb or github
106 # comment out to get some other repos from bb or github
107 GIT_REMOTE_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
107 GIT_REMOTE_REPO = jn(TESTS_TMP_PATH, GIT_REPO)
108 HG_REMOTE_REPO = jn(TESTS_TMP_PATH, HG_REPO)
108 HG_REMOTE_REPO = jn(TESTS_TMP_PATH, HG_REPO)
109
109
110
110
111 def get_new_dir(title):
111 def get_new_dir(title):
112 """
112 """
113 Returns always new directory path.
113 Returns always new directory path.
114 """
114 """
115 from rhodecode.tests.vcs.utils import get_normalized_path
115 from rhodecode.tests.vcs.utils import get_normalized_path
116 name = TEST_REPO_PREFIX
116 name = TEST_REPO_PREFIX
117 if title:
117 if title:
118 name = '-'.join((name, title))
118 name = '-'.join((name, title))
119 hex = hashlib.sha1(str(time.time())).hexdigest()
119 hex = hashlib.sha1(str(time.time())).hexdigest()
120 name = '-'.join((name, hex))
120 name = '-'.join((name, hex))
121 path = os.path.join(TEST_DIR, name)
121 path = os.path.join(TEST_DIR, name)
122 return get_normalized_path(path)
122 return get_normalized_path(path)
123
123
124
124
125 class TestController(TestCase):
125 class TestController(TestCase):
126
126
127 def __init__(self, *args, **kwargs):
127 def __init__(self, *args, **kwargs):
128 wsgiapp = pylons.test.pylonsapp
128 wsgiapp = pylons.test.pylonsapp
129 config = wsgiapp.config
129 config = wsgiapp.config
130
130
131 self.app = TestApp(wsgiapp)
131 self.app = TestApp(wsgiapp)
132 url._push_object(URLGenerator(config['routes.map'], environ))
132 url._push_object(URLGenerator(config['routes.map'], environ))
133 self.Session = Session
133 self.Session = Session
134 self.index_location = config['app_conf']['index_dir']
134 self.index_location = config['app_conf']['index_dir']
135 TestCase.__init__(self, *args, **kwargs)
135 TestCase.__init__(self, *args, **kwargs)
136
136
137 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
137 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
138 password=TEST_USER_ADMIN_PASS):
138 password=TEST_USER_ADMIN_PASS):
139 self._logged_username = username
139 self._logged_username = username
140 response = self.app.post(url(controller='login', action='index'),
140 response = self.app.post(url(controller='login', action='index'),
141 {'username': username,
141 {'username': username,
142 'password': password})
142 'password': password})
143
143
144 if 'invalid user name' in response.body:
144 if 'invalid user name' in response.body:
145 self.fail('could not login using %s %s' % (username, password))
145 self.fail('could not login using %s %s' % (username, password))
146
146
147 self.assertEqual(response.status, '302 Found')
147 self.assertEqual(response.status, '302 Found')
148 ses = response.session['rhodecode_user']
148 ses = response.session['rhodecode_user']
149 self.assertEqual(ses.get('username'), username)
149 self.assertEqual(ses.get('username'), username)
150 response = response.follow()
150 response = response.follow()
151 self.assertEqual(ses.get('is_authenticated'), True)
151 self.assertEqual(ses.get('is_authenticated'), True)
152
152
153 return response.session['rhodecode_user']
153 return response.session['rhodecode_user']
154
154
155 def _get_logged_user(self):
155 def _get_logged_user(self):
156 return User.get_by_username(self._logged_username)
156 return User.get_by_username(self._logged_username)
157
157
158 def checkSessionFlash(self, response, msg):
158 def checkSessionFlash(self, response, msg):
159 self.assertTrue('flash' in response.session)
159 self.assertTrue('flash' in response.session)
160 if not msg in response.session['flash'][0][1]:
160 if not msg in response.session['flash'][0][1]:
161 self.fail(
161 self.fail(
162 'msg `%s` not found in session flash: got `%s` instead' % (
162 'msg `%s` not found in session flash: got `%s` instead' % (
163 msg, response.session['flash'][0][1])
163 msg, response.session['flash'])
164 )
164 )
@@ -1,213 +1,266 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 from rhodecode.lib.auth import get_crypt_password, check_password
3 from rhodecode.lib.auth import get_crypt_password, check_password
4 from rhodecode.model.db import User, RhodeCodeSetting
4 from rhodecode.model.db import User, RhodeCodeSetting, Repository
5 from rhodecode.tests import *
5 from rhodecode.tests import *
6 from rhodecode.lib import helpers as h
6 from rhodecode.lib import helpers as h
7 from rhodecode.model.user import UserModel
7 from rhodecode.model.user import UserModel
8 from rhodecode.model.scm import ScmModel
8
9
9
10
10 class TestAdminSettingsController(TestController):
11 class TestAdminSettingsController(TestController):
11
12
12 def test_index(self):
13 def test_index(self):
13 response = self.app.get(url('admin_settings'))
14 response = self.app.get(url('admin_settings'))
14 # Test response...
15 # Test response...
15
16
16 def test_index_as_xml(self):
17 def test_index_as_xml(self):
17 response = self.app.get(url('formatted_admin_settings', format='xml'))
18 response = self.app.get(url('formatted_admin_settings', format='xml'))
18
19
19 def test_create(self):
20 def test_create(self):
20 response = self.app.post(url('admin_settings'))
21 response = self.app.post(url('admin_settings'))
21
22
22 def test_new(self):
23 def test_new(self):
23 response = self.app.get(url('admin_new_setting'))
24 response = self.app.get(url('admin_new_setting'))
24
25
25 def test_new_as_xml(self):
26 def test_new_as_xml(self):
26 response = self.app.get(url('formatted_admin_new_setting', format='xml'))
27 response = self.app.get(url('formatted_admin_new_setting', format='xml'))
27
28
28 def test_update(self):
29 def test_update(self):
29 response = self.app.put(url('admin_setting', setting_id=1))
30 response = self.app.put(url('admin_setting', setting_id=1))
30
31
31 def test_update_browser_fakeout(self):
32 def test_update_browser_fakeout(self):
32 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='put'))
33 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='put'))
33
34
34 def test_delete(self):
35 def test_delete(self):
35 response = self.app.delete(url('admin_setting', setting_id=1))
36 response = self.app.delete(url('admin_setting', setting_id=1))
36
37
37 def test_delete_browser_fakeout(self):
38 def test_delete_browser_fakeout(self):
38 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='delete'))
39 response = self.app.post(url('admin_setting', setting_id=1), params=dict(_method='delete'))
39
40
40 def test_show(self):
41 def test_show(self):
41 response = self.app.get(url('admin_setting', setting_id=1))
42 response = self.app.get(url('admin_setting', setting_id=1))
42
43
43 def test_show_as_xml(self):
44 def test_show_as_xml(self):
44 response = self.app.get(url('formatted_admin_setting', setting_id=1, format='xml'))
45 response = self.app.get(url('formatted_admin_setting', setting_id=1, format='xml'))
45
46
46 def test_edit(self):
47 def test_edit(self):
47 response = self.app.get(url('admin_edit_setting', setting_id=1))
48 response = self.app.get(url('admin_edit_setting', setting_id=1))
48
49
49 def test_edit_as_xml(self):
50 def test_edit_as_xml(self):
50 response = self.app.get(url('formatted_admin_edit_setting',
51 response = self.app.get(url('formatted_admin_edit_setting',
51 setting_id=1, format='xml'))
52 setting_id=1, format='xml'))
52
53
53 def test_ga_code_active(self):
54 def test_ga_code_active(self):
54 self.log_user()
55 self.log_user()
55 old_title = 'RhodeCode'
56 old_title = 'RhodeCode'
56 old_realm = 'RhodeCode authentication'
57 old_realm = 'RhodeCode authentication'
57 new_ga_code = 'ga-test-123456789'
58 new_ga_code = 'ga-test-123456789'
58 response = self.app.post(url('admin_setting', setting_id='global'),
59 response = self.app.post(url('admin_setting', setting_id='global'),
59 params=dict(
60 params=dict(
60 _method='put',
61 _method='put',
61 rhodecode_title=old_title,
62 rhodecode_title=old_title,
62 rhodecode_realm=old_realm,
63 rhodecode_realm=old_realm,
63 rhodecode_ga_code=new_ga_code
64 rhodecode_ga_code=new_ga_code
64 ))
65 ))
65
66
66 self.checkSessionFlash(response, 'Updated application settings')
67 self.checkSessionFlash(response, 'Updated application settings')
67
68
68 self.assertEqual(RhodeCodeSetting
69 self.assertEqual(RhodeCodeSetting
69 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
70 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
70
71
71 response = response.follow()
72 response = response.follow()
72 response.mustcontain("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code)
73 response.mustcontain("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code)
73
74
74 def test_ga_code_inactive(self):
75 def test_ga_code_inactive(self):
75 self.log_user()
76 self.log_user()
76 old_title = 'RhodeCode'
77 old_title = 'RhodeCode'
77 old_realm = 'RhodeCode authentication'
78 old_realm = 'RhodeCode authentication'
78 new_ga_code = ''
79 new_ga_code = ''
79 response = self.app.post(url('admin_setting', setting_id='global'),
80 response = self.app.post(url('admin_setting', setting_id='global'),
80 params=dict(
81 params=dict(
81 _method='put',
82 _method='put',
82 rhodecode_title=old_title,
83 rhodecode_title=old_title,
83 rhodecode_realm=old_realm,
84 rhodecode_realm=old_realm,
84 rhodecode_ga_code=new_ga_code
85 rhodecode_ga_code=new_ga_code
85 ))
86 ))
86
87
87 self.assertTrue('Updated application settings' in
88 self.assertTrue('Updated application settings' in
88 response.session['flash'][0][1])
89 response.session['flash'][0][1])
89 self.assertEqual(RhodeCodeSetting
90 self.assertEqual(RhodeCodeSetting
90 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
91 .get_app_settings()['rhodecode_ga_code'], new_ga_code)
91
92
92 response = response.follow()
93 response = response.follow()
93 self.assertFalse("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
94 self.assertFalse("""_gaq.push(['_setAccount', '%s']);""" % new_ga_code
94 in response.body)
95 in response.body)
95
96
96 def test_title_change(self):
97 def test_title_change(self):
97 self.log_user()
98 self.log_user()
98 old_title = 'RhodeCode'
99 old_title = 'RhodeCode'
99 new_title = old_title + '_changed'
100 new_title = old_title + '_changed'
100 old_realm = 'RhodeCode authentication'
101 old_realm = 'RhodeCode authentication'
101
102
102 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
103 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
103 response = self.app.post(url('admin_setting', setting_id='global'),
104 response = self.app.post(url('admin_setting', setting_id='global'),
104 params=dict(
105 params=dict(
105 _method='put',
106 _method='put',
106 rhodecode_title=new_title,
107 rhodecode_title=new_title,
107 rhodecode_realm=old_realm,
108 rhodecode_realm=old_realm,
108 rhodecode_ga_code=''
109 rhodecode_ga_code=''
109 ))
110 ))
110
111
111 self.checkSessionFlash(response, 'Updated application settings')
112 self.checkSessionFlash(response, 'Updated application settings')
112 self.assertEqual(RhodeCodeSetting
113 self.assertEqual(RhodeCodeSetting
113 .get_app_settings()['rhodecode_title'],
114 .get_app_settings()['rhodecode_title'],
114 new_title.decode('utf-8'))
115 new_title.decode('utf-8'))
115
116
116 response = response.follow()
117 response = response.follow()
117 response.mustcontain("""<h1><a href="/">%s</a></h1>""" % new_title)
118 response.mustcontain("""<h1><a href="/">%s</a></h1>""" % new_title)
118
119
119 def test_my_account(self):
120 def test_my_account(self):
120 self.log_user()
121 self.log_user()
121 response = self.app.get(url('admin_settings_my_account'))
122 response = self.app.get(url('admin_settings_my_account'))
122
123
123 self.assertTrue('value="test_admin' in response.body)
124 self.assertTrue('value="test_admin' in response.body)
124
125
125 @parameterized.expand([('firstname', 'new_username'),
126 @parameterized.expand([('firstname', 'new_username'),
126 ('lastname', 'new_username'),
127 ('lastname', 'new_username'),
127 ('admin', True),
128 ('admin', True),
128 ('admin', False),
129 ('admin', False),
129 ('ldap_dn', 'test'),
130 ('ldap_dn', 'test'),
130 ('ldap_dn', None),
131 ('ldap_dn', None),
131 ('active', False),
132 ('active', False),
132 ('active', True),
133 ('active', True),
133 ('email', 'some@email.com'),
134 ('email', 'some@email.com'),
134 ])
135 ])
135 def test_my_account_update(self, name, expected):
136 def test_my_account_update(self, name, expected):
136 uname = 'testme'
137 uname = 'testme'
137 usr = UserModel().create_or_update(username=uname, password='qweqwe',
138 usr = UserModel().create_or_update(username=uname, password='qweqwe',
138 email='testme@rhodecod.org')
139 email='testme@rhodecod.org')
139 self.Session().commit()
140 self.Session().commit()
140 params = usr.get_api_data()
141 params = usr.get_api_data()
141 user_id = usr.user_id
142 user_id = usr.user_id
142 self.log_user(username=uname, password='qweqwe')
143 self.log_user(username=uname, password='qweqwe')
143 params.update({name: expected})
144 params.update({name: expected})
144 params.update({'password_confirmation': ''})
145 params.update({'password_confirmation': ''})
145 params.update({'new_password': ''})
146 params.update({'new_password': ''})
146
147
147 try:
148 try:
148 response = self.app.put(url('admin_settings_my_account_update',
149 response = self.app.put(url('admin_settings_my_account_update',
149 id=user_id), params)
150 id=user_id), params)
150
151
151 self.checkSessionFlash(response,
152 self.checkSessionFlash(response,
152 'Your account was updated successfully')
153 'Your account was updated successfully')
153
154
154 updated_user = User.get_by_username(uname)
155 updated_user = User.get_by_username(uname)
155 updated_params = updated_user.get_api_data()
156 updated_params = updated_user.get_api_data()
156 updated_params.update({'password_confirmation': ''})
157 updated_params.update({'password_confirmation': ''})
157 updated_params.update({'new_password': ''})
158 updated_params.update({'new_password': ''})
158
159
159 params['last_login'] = updated_params['last_login']
160 params['last_login'] = updated_params['last_login']
160 if name == 'email':
161 if name == 'email':
161 params['emails'] = [expected]
162 params['emails'] = [expected]
162 if name == 'ldap_dn':
163 if name == 'ldap_dn':
163 #cannot update this via form
164 #cannot update this via form
164 params['ldap_dn'] = None
165 params['ldap_dn'] = None
165 if name == 'active':
166 if name == 'active':
166 #my account cannot deactivate account
167 #my account cannot deactivate account
167 params['active'] = True
168 params['active'] = True
168 if name == 'admin':
169 if name == 'admin':
169 #my account cannot make you an admin !
170 #my account cannot make you an admin !
170 params['admin'] = False
171 params['admin'] = False
171
172
172 self.assertEqual(params, updated_params)
173 self.assertEqual(params, updated_params)
173
174
174 finally:
175 finally:
175 UserModel().delete('testme')
176 UserModel().delete('testme')
176
177
177 def test_my_account_update_err_email_exists(self):
178 def test_my_account_update_err_email_exists(self):
178 self.log_user()
179 self.log_user()
179
180
180 new_email = 'test_regular@mail.com' # already exisitn email
181 new_email = 'test_regular@mail.com' # already exisitn email
181 response = self.app.put(url('admin_settings_my_account_update'),
182 response = self.app.put(url('admin_settings_my_account_update'),
182 params=dict(
183 params=dict(
183 username='test_admin',
184 username='test_admin',
184 new_password='test12',
185 new_password='test12',
185 password_confirmation='test122',
186 password_confirmation='test122',
186 firstname='NewName',
187 firstname='NewName',
187 lastname='NewLastname',
188 lastname='NewLastname',
188 email=new_email,)
189 email=new_email,)
189 )
190 )
190
191
191 response.mustcontain('This e-mail address is already taken')
192 response.mustcontain('This e-mail address is already taken')
192
193
193 def test_my_account_update_err(self):
194 def test_my_account_update_err(self):
194 self.log_user('test_regular2', 'test12')
195 self.log_user('test_regular2', 'test12')
195
196
196 new_email = 'newmail.pl'
197 new_email = 'newmail.pl'
197 response = self.app.post(url('admin_settings_my_account_update'),
198 response = self.app.post(url('admin_settings_my_account_update'),
198 params=dict(
199 params=dict(
199 _method='put',
200 _method='put',
200 username='test_admin',
201 username='test_admin',
201 new_password='test12',
202 new_password='test12',
202 password_confirmation='test122',
203 password_confirmation='test122',
203 firstname='NewName',
204 firstname='NewName',
204 lastname='NewLastname',
205 lastname='NewLastname',
205 email=new_email,)
206 email=new_email,)
206 )
207 )
207
208
208 response.mustcontain('An email address must contain a single @')
209 response.mustcontain('An email address must contain a single @')
209 from rhodecode.model import validators
210 from rhodecode.model import validators
210 msg = validators.ValidUsername(edit=False,
211 msg = validators.ValidUsername(edit=False,
211 old_data={})._messages['username_exists']
212 old_data={})._messages['username_exists']
212 msg = h.html_escape(msg % {'username': 'test_admin'})
213 msg = h.html_escape(msg % {'username': 'test_admin'})
213 response.mustcontain(u"%s" % msg)
214 response.mustcontain(u"%s" % msg)
215
216 def test_set_repo_fork_has_no_self_id(self):
217 self.log_user()
218 repo = Repository.get_by_repo_name(HG_REPO)
219 response = self.app.get(url('edit_repo', repo_name=HG_REPO))
220 opt = """<option value="%s">vcs_test_git</option>""" % repo.repo_id
221 assert opt not in response.body
222
223 def test_set_fork_of_repo(self):
224 self.log_user()
225 repo = Repository.get_by_repo_name(HG_REPO)
226 repo2 = Repository.get_by_repo_name(GIT_REPO)
227 response = self.app.put(url('repo_as_fork', repo_name=HG_REPO),
228 params=dict(
229 id_fork_of=repo2.repo_id
230 ))
231 repo = Repository.get_by_repo_name(HG_REPO)
232 repo2 = Repository.get_by_repo_name(GIT_REPO)
233 self.checkSessionFlash(response,
234 'Marked repo %s as fork of %s' % (repo.repo_name, repo2.repo_name))
235
236 assert repo.fork == repo2
237 response = response.follow()
238 # check if given repo is selected
239
240 opt = """<option value="%s" selected="selected">%s</option>""" % (
241 repo2.repo_id, repo2.repo_name)
242 response.mustcontain(opt)
243
244 # clean session flash
245 #response = self.app.get(url('edit_repo', repo_name=HG_REPO))
246
247 ## mark it as None
248 response = self.app.put(url('repo_as_fork', repo_name=HG_REPO),
249 params=dict(
250 id_fork_of=None
251 ))
252 repo = Repository.get_by_repo_name(HG_REPO)
253 repo2 = Repository.get_by_repo_name(GIT_REPO)
254 self.checkSessionFlash(response,
255 'Marked repo %s as fork of %s' % (repo.repo_name, "Nothing"))
256 assert repo.fork == None
257
258 def test_set_fork_of_same_repo(self):
259 self.log_user()
260 repo = Repository.get_by_repo_name(HG_REPO)
261 response = self.app.put(url('repo_as_fork', repo_name=HG_REPO),
262 params=dict(
263 id_fork_of=repo.repo_id
264 ))
265 self.checkSessionFlash(response,
266 'An error occurred during this operation') No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now