##// END OF EJS Templates
user lowernames of repos for better sorting
marcink -
r2935:20c0af65 beta
parent child Browse files
Show More
@@ -1,509 +1,510 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repositories controller for RhodeCode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29 from formencode import htmlfill
30 30
31 31 from webob.exc import HTTPInternalServerError
32 32 from pylons import request, session, tmpl_context as c, url
33 33 from pylons.controllers.util import redirect
34 34 from pylons.i18n.translation import _
35 35 from sqlalchemy.exc import IntegrityError
36 36
37 37 import rhodecode
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator
41 41 from rhodecode.lib.base import BaseController, render
42 42 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
43 43 from rhodecode.lib.helpers import get_token
44 44 from rhodecode.model.meta import Session
45 45 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
46 46 from rhodecode.model.forms import RepoForm
47 47 from rhodecode.model.scm import ScmModel
48 48 from rhodecode.model.repo import RepoModel
49 49 from rhodecode.lib.compat import json
50 from sqlalchemy.sql.expression import func
50 51
51 52 log = logging.getLogger(__name__)
52 53
53 54
54 55 class ReposController(BaseController):
55 56 """
56 57 REST Controller styled on the Atom Publishing Protocol"""
57 58 # To properly map this controller, ensure your config/routing.py
58 59 # file has a resource setup:
59 60 # map.resource('repo', 'repos')
60 61
61 62 @LoginRequired()
62 63 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
63 64 def __before__(self):
64 65 c.admin_user = session.get('admin_user')
65 66 c.admin_username = session.get('admin_username')
66 67 super(ReposController, self).__before__()
67 68
68 69 def __load_defaults(self):
69 70 c.repo_groups = RepoGroup.groups_choices(check_perms=True)
70 71 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
71 72
72 73 repo_model = RepoModel()
73 74 c.users_array = repo_model.get_users_js()
74 75 c.users_groups_array = repo_model.get_users_groups_js()
75 76 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
76 77 c.landing_revs_choices = choices
77 78
78 79 def __load_data(self, repo_name=None):
79 80 """
80 81 Load defaults settings for edit, and update
81 82
82 83 :param repo_name:
83 84 """
84 85 self.__load_defaults()
85 86
86 87 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
87 88 repo = db_repo.scm_instance
88 89
89 90 if c.repo_info is None:
90 91 h.flash(_('%s repository is not mapped to db perhaps'
91 92 ' it was created or renamed from the filesystem'
92 93 ' please run the application again'
93 94 ' in order to rescan repositories') % repo_name,
94 95 category='error')
95 96
96 97 return redirect(url('repos'))
97 98
98 99 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
99 100 c.landing_revs_choices = choices
100 101
101 102 c.default_user_id = User.get_by_username('default').user_id
102 103 c.in_public_journal = UserFollowing.query()\
103 104 .filter(UserFollowing.user_id == c.default_user_id)\
104 105 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
105 106
106 107 if c.repo_info.stats:
107 108 # this is on what revision we ended up so we add +1 for count
108 109 last_rev = c.repo_info.stats.stat_on_revision + 1
109 110 else:
110 111 last_rev = 0
111 112 c.stats_revision = last_rev
112 113
113 114 c.repo_last_rev = repo.count() if repo.revisions else 0
114 115
115 116 if last_rev == 0 or c.repo_last_rev == 0:
116 117 c.stats_percentage = 0
117 118 else:
118 119 c.stats_percentage = '%.2f' % ((float((last_rev)) /
119 120 c.repo_last_rev) * 100)
120 121
121 122 defaults = RepoModel()._get_defaults(repo_name)
122 123
123 124 c.repos_list = [('', _('--REMOVE FORK--'))]
124 125 c.repos_list += [(x.repo_id, x.repo_name) for x in
125 126 Repository.query().order_by(Repository.repo_name).all()
126 127 if x.repo_id != c.repo_info.repo_id]
127 128
128 129 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
129 130 return defaults
130 131
131 132 @HasPermissionAllDecorator('hg.admin')
132 133 def index(self, format='html'):
133 134 """GET /repos: All items in the collection"""
134 135 # url('repos')
135 136
136 137 c.repos_list = Repository.query()\
137 .order_by(Repository.repo_name)\
138 .order_by(func.lower(Repository.repo_name))\
138 139 .all()
139 140
140 141 repos_data = []
141 142 total_records = len(c.repos_list)
142 143
143 144 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
144 145 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
145 146
146 147 quick_menu = lambda repo_name: (template.get_def("quick_menu")
147 148 .render(repo_name, _=_, h=h, c=c))
148 149 repo_lnk = lambda name, rtype, private, fork_of: (
149 150 template.get_def("repo_name")
150 151 .render(name, rtype, private, fork_of, short_name=False,
151 152 admin=True, _=_, h=h, c=c))
152 153
153 154 repo_actions = lambda repo_name: (template.get_def("repo_actions")
154 155 .render(repo_name, _=_, h=h, c=c))
155 156
156 157 for repo in c.repos_list:
157 158 repos_data.append({
158 159 "menu": quick_menu(repo.repo_name),
159 "raw_name": repo.repo_name,
160 "raw_name": repo.repo_name.lower(),
160 161 "name": repo_lnk(repo.repo_name, repo.repo_type,
161 162 repo.private, repo.fork),
162 163 "desc": repo.description,
163 164 "owner": repo.user.username,
164 165 "action": repo_actions(repo.repo_name),
165 166 })
166 167
167 168 c.data = json.dumps({
168 169 "totalRecords": total_records,
169 170 "startIndex": 0,
170 171 "sort": "name",
171 172 "dir": "asc",
172 173 "records": repos_data
173 174 })
174 175
175 176 return render('admin/repos/repos.html')
176 177
177 178 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
178 179 def create(self):
179 180 """
180 181 POST /repos: Create a new item"""
181 182 # url('repos')
182 183
183 184 self.__load_defaults()
184 185 form_result = {}
185 186 try:
186 187 form_result = RepoForm(repo_groups=c.repo_groups_choices,
187 188 landing_revs=c.landing_revs_choices)()\
188 189 .to_python(dict(request.POST))
189 190 new_repo = RepoModel().create(form_result,
190 191 self.rhodecode_user.user_id)
191 192 if form_result['clone_uri']:
192 193 h.flash(_('created repository %s from %s') \
193 194 % (form_result['repo_name'], form_result['clone_uri']),
194 195 category='success')
195 196 else:
196 197 h.flash(_('created repository %s') % form_result['repo_name'],
197 198 category='success')
198 199
199 200 if request.POST.get('user_created'):
200 201 # created by regular non admin user
201 202 action_logger(self.rhodecode_user, 'user_created_repo',
202 203 form_result['repo_name_full'], self.ip_addr,
203 204 self.sa)
204 205 else:
205 206 action_logger(self.rhodecode_user, 'admin_created_repo',
206 207 form_result['repo_name_full'], self.ip_addr,
207 208 self.sa)
208 209 Session().commit()
209 210 except formencode.Invalid, errors:
210 211
211 212 c.new_repo = errors.value['repo_name']
212 213
213 214 if request.POST.get('user_created'):
214 215 r = render('admin/repos/repo_add_create_repository.html')
215 216 else:
216 217 r = render('admin/repos/repo_add.html')
217 218
218 219 return htmlfill.render(
219 220 r,
220 221 defaults=errors.value,
221 222 errors=errors.error_dict or {},
222 223 prefix_error=False,
223 224 encoding="UTF-8")
224 225
225 226 except Exception:
226 227 log.error(traceback.format_exc())
227 228 msg = _('error occurred during creation of repository %s') \
228 229 % form_result.get('repo_name')
229 230 h.flash(msg, category='error')
230 231 return redirect(url('repos'))
231 232 #redirect to our new repo !
232 233 return redirect(url('summary_home', repo_name=new_repo.repo_name))
233 234
234 235 @HasPermissionAllDecorator('hg.admin')
235 236 def new(self, format='html'):
236 237 """GET /repos/new: Form to create a new item"""
237 238 new_repo = request.GET.get('repo', '')
238 239 c.new_repo = repo_name_slug(new_repo)
239 240 self.__load_defaults()
240 241 return render('admin/repos/repo_add.html')
241 242
242 243 @HasPermissionAllDecorator('hg.admin')
243 244 def update(self, repo_name):
244 245 """
245 246 PUT /repos/repo_name: Update an existing item"""
246 247 # Forms posted to this method should contain a hidden field:
247 248 # <input type="hidden" name="_method" value="PUT" />
248 249 # Or using helpers:
249 250 # h.form(url('repo', repo_name=ID),
250 251 # method='put')
251 252 # url('repo', repo_name=ID)
252 253 self.__load_defaults()
253 254 repo_model = RepoModel()
254 255 changed_name = repo_name
255 256 #override the choices with extracted revisions !
256 257 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
257 258 c.landing_revs_choices = choices
258 259
259 260 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
260 261 repo_groups=c.repo_groups_choices,
261 262 landing_revs=c.landing_revs_choices)()
262 263 try:
263 264 form_result = _form.to_python(dict(request.POST))
264 265 repo = repo_model.update(repo_name, form_result)
265 266 invalidate_cache('get_repo_cached_%s' % repo_name)
266 267 h.flash(_('Repository %s updated successfully') % repo_name,
267 268 category='success')
268 269 changed_name = repo.repo_name
269 270 action_logger(self.rhodecode_user, 'admin_updated_repo',
270 271 changed_name, self.ip_addr, self.sa)
271 272 Session().commit()
272 273 except formencode.Invalid, errors:
273 274 defaults = self.__load_data(repo_name)
274 275 defaults.update(errors.value)
275 276 return htmlfill.render(
276 277 render('admin/repos/repo_edit.html'),
277 278 defaults=defaults,
278 279 errors=errors.error_dict or {},
279 280 prefix_error=False,
280 281 encoding="UTF-8")
281 282
282 283 except Exception:
283 284 log.error(traceback.format_exc())
284 285 h.flash(_('error occurred during update of repository %s') \
285 286 % repo_name, category='error')
286 287 return redirect(url('edit_repo', repo_name=changed_name))
287 288
288 289 @HasPermissionAllDecorator('hg.admin')
289 290 def delete(self, repo_name):
290 291 """
291 292 DELETE /repos/repo_name: Delete an existing item"""
292 293 # Forms posted to this method should contain a hidden field:
293 294 # <input type="hidden" name="_method" value="DELETE" />
294 295 # Or using helpers:
295 296 # h.form(url('repo', repo_name=ID),
296 297 # method='delete')
297 298 # url('repo', repo_name=ID)
298 299
299 300 repo_model = RepoModel()
300 301 repo = repo_model.get_by_repo_name(repo_name)
301 302 if not repo:
302 303 h.flash(_('%s repository is not mapped to db perhaps'
303 304 ' it was moved or renamed from the filesystem'
304 305 ' please run the application again'
305 306 ' in order to rescan repositories') % repo_name,
306 307 category='error')
307 308
308 309 return redirect(url('repos'))
309 310 try:
310 311 action_logger(self.rhodecode_user, 'admin_deleted_repo',
311 312 repo_name, self.ip_addr, self.sa)
312 313 repo_model.delete(repo)
313 314 invalidate_cache('get_repo_cached_%s' % repo_name)
314 315 h.flash(_('deleted repository %s') % repo_name, category='success')
315 316 Session().commit()
316 317 except IntegrityError, e:
317 318 if e.message.find('repositories_fork_id_fkey') != -1:
318 319 log.error(traceback.format_exc())
319 320 h.flash(_('Cannot delete %s it still contains attached '
320 321 'forks') % repo_name,
321 322 category='warning')
322 323 else:
323 324 log.error(traceback.format_exc())
324 325 h.flash(_('An error occurred during '
325 326 'deletion of %s') % repo_name,
326 327 category='error')
327 328
328 329 except Exception, e:
329 330 log.error(traceback.format_exc())
330 331 h.flash(_('An error occurred during deletion of %s') % repo_name,
331 332 category='error')
332 333
333 334 return redirect(url('repos'))
334 335
335 336 @HasRepoPermissionAllDecorator('repository.admin')
336 337 def delete_perm_user(self, repo_name):
337 338 """
338 339 DELETE an existing repository permission user
339 340
340 341 :param repo_name:
341 342 """
342 343 try:
343 344 RepoModel().revoke_user_permission(repo=repo_name,
344 345 user=request.POST['user_id'])
345 346 Session().commit()
346 347 except Exception:
347 348 log.error(traceback.format_exc())
348 349 h.flash(_('An error occurred during deletion of repository user'),
349 350 category='error')
350 351 raise HTTPInternalServerError()
351 352
352 353 @HasRepoPermissionAllDecorator('repository.admin')
353 354 def delete_perm_users_group(self, repo_name):
354 355 """
355 356 DELETE an existing repository permission users group
356 357
357 358 :param repo_name:
358 359 """
359 360
360 361 try:
361 362 RepoModel().revoke_users_group_permission(
362 363 repo=repo_name, group_name=request.POST['users_group_id']
363 364 )
364 365 Session().commit()
365 366 except Exception:
366 367 log.error(traceback.format_exc())
367 368 h.flash(_('An error occurred during deletion of repository'
368 369 ' users groups'),
369 370 category='error')
370 371 raise HTTPInternalServerError()
371 372
372 373 @HasPermissionAllDecorator('hg.admin')
373 374 def repo_stats(self, repo_name):
374 375 """
375 376 DELETE an existing repository statistics
376 377
377 378 :param repo_name:
378 379 """
379 380
380 381 try:
381 382 RepoModel().delete_stats(repo_name)
382 383 Session().commit()
383 384 except Exception, e:
384 385 log.error(traceback.format_exc())
385 386 h.flash(_('An error occurred during deletion of repository stats'),
386 387 category='error')
387 388 return redirect(url('edit_repo', repo_name=repo_name))
388 389
389 390 @HasPermissionAllDecorator('hg.admin')
390 391 def repo_cache(self, repo_name):
391 392 """
392 393 INVALIDATE existing repository cache
393 394
394 395 :param repo_name:
395 396 """
396 397
397 398 try:
398 399 ScmModel().mark_for_invalidation(repo_name)
399 400 Session().commit()
400 401 except Exception, e:
401 402 log.error(traceback.format_exc())
402 403 h.flash(_('An error occurred during cache invalidation'),
403 404 category='error')
404 405 return redirect(url('edit_repo', repo_name=repo_name))
405 406
406 407 @HasPermissionAllDecorator('hg.admin')
407 408 def repo_locking(self, repo_name):
408 409 """
409 410 Unlock repository when it is locked !
410 411
411 412 :param repo_name:
412 413 """
413 414
414 415 try:
415 416 repo = Repository.get_by_repo_name(repo_name)
416 417 if request.POST.get('set_lock'):
417 418 Repository.lock(repo, c.rhodecode_user.user_id)
418 419 elif request.POST.get('set_unlock'):
419 420 Repository.unlock(repo)
420 421 except Exception, e:
421 422 log.error(traceback.format_exc())
422 423 h.flash(_('An error occurred during unlocking'),
423 424 category='error')
424 425 return redirect(url('edit_repo', repo_name=repo_name))
425 426
426 427 @HasPermissionAllDecorator('hg.admin')
427 428 def repo_public_journal(self, repo_name):
428 429 """
429 430 Set's this repository to be visible in public journal,
430 431 in other words assing default user to follow this repo
431 432
432 433 :param repo_name:
433 434 """
434 435
435 436 cur_token = request.POST.get('auth_token')
436 437 token = get_token()
437 438 if cur_token == token:
438 439 try:
439 440 repo_id = Repository.get_by_repo_name(repo_name).repo_id
440 441 user_id = User.get_by_username('default').user_id
441 442 self.scm_model.toggle_following_repo(repo_id, user_id)
442 443 h.flash(_('Updated repository visibility in public journal'),
443 444 category='success')
444 445 Session().commit()
445 446 except:
446 447 h.flash(_('An error occurred during setting this'
447 448 ' repository in public journal'),
448 449 category='error')
449 450
450 451 else:
451 452 h.flash(_('Token mismatch'), category='error')
452 453 return redirect(url('edit_repo', repo_name=repo_name))
453 454
454 455 @HasPermissionAllDecorator('hg.admin')
455 456 def repo_pull(self, repo_name):
456 457 """
457 458 Runs task to update given repository with remote changes,
458 459 ie. make pull on remote location
459 460
460 461 :param repo_name:
461 462 """
462 463 try:
463 464 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
464 465 h.flash(_('Pulled from remote location'), category='success')
465 466 except Exception, e:
466 467 h.flash(_('An error occurred during pull from remote location'),
467 468 category='error')
468 469
469 470 return redirect(url('edit_repo', repo_name=repo_name))
470 471
471 472 @HasPermissionAllDecorator('hg.admin')
472 473 def repo_as_fork(self, repo_name):
473 474 """
474 475 Mark given repository as a fork of another
475 476
476 477 :param repo_name:
477 478 """
478 479 try:
479 480 fork_id = request.POST.get('id_fork_of')
480 481 repo = ScmModel().mark_as_fork(repo_name, fork_id,
481 482 self.rhodecode_user.username)
482 483 fork = repo.fork.repo_name if repo.fork else _('Nothing')
483 484 Session().commit()
484 485 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
485 486 category='success')
486 487 except Exception, e:
487 488 log.error(traceback.format_exc())
488 489 h.flash(_('An error occurred during this operation'),
489 490 category='error')
490 491
491 492 return redirect(url('edit_repo', repo_name=repo_name))
492 493
493 494 @HasPermissionAllDecorator('hg.admin')
494 495 def show(self, repo_name, format='html'):
495 496 """GET /repos/repo_name: Show a specific item"""
496 497 # url('repo', repo_name=ID)
497 498
498 499 @HasPermissionAllDecorator('hg.admin')
499 500 def edit(self, repo_name, format='html'):
500 501 """GET /repos/repo_name/edit: Form to edit an existing item"""
501 502 # url('edit_repo', repo_name=ID)
502 503 defaults = self.__load_data(repo_name)
503 504
504 505 return htmlfill.render(
505 506 render('admin/repos/repo_edit.html'),
506 507 defaults=defaults,
507 508 encoding="UTF-8",
508 509 force_defaults=False
509 510 )
General Comments 0
You need to be logged in to leave comments. Login now