##// END OF EJS Templates
Repo type can't be changed. Fixes issue 836
Simon Lopez -
r3858:d11ecf2f default
parent child Browse files
Show More
@@ -1,703 +1,703 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.repo
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repository model for rhodecode
7 7
8 8 :created_on: Jun 5, 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 from __future__ import with_statement
26 26 import os
27 27 import shutil
28 28 import logging
29 29 import traceback
30 30 from datetime import datetime
31 31
32 32 from rhodecode.lib.vcs.backends import get_backend
33 33 from rhodecode.lib.compat import json
34 34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
35 35 remove_prefix, obfuscate_url_pw
36 36 from rhodecode.lib.caching_query import FromCache
37 37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
38 38
39 39 from rhodecode.model import BaseModel
40 40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
41 41 Statistics, UserGroup, UserGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
42 42 RhodeCodeSetting, RepositoryField
43 43 from rhodecode.lib import helpers as h
44 44 from rhodecode.lib.auth import HasRepoPermissionAny
45 45 from rhodecode.lib.exceptions import AttachedForksError
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 class RepoModel(BaseModel):
51 51
52 52 cls = Repository
53 53 URL_SEPARATOR = Repository.url_sep()
54 54
55 55 def __get_users_group(self, users_group):
56 56 return self._get_instance(UserGroup, users_group,
57 57 callback=UserGroup.get_by_group_name)
58 58
59 59 def _get_repos_group(self, repos_group):
60 60 return self._get_instance(RepoGroup, repos_group,
61 61 callback=RepoGroup.get_by_group_name)
62 62
63 63 @LazyProperty
64 64 def repos_path(self):
65 65 """
66 66 Get's the repositories root path from database
67 67 """
68 68
69 69 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
70 70 return q.ui_value
71 71
72 72 def get(self, repo_id, cache=False):
73 73 repo = self.sa.query(Repository)\
74 74 .filter(Repository.repo_id == repo_id)
75 75
76 76 if cache:
77 77 repo = repo.options(FromCache("sql_cache_short",
78 78 "get_repo_%s" % repo_id))
79 79 return repo.scalar()
80 80
81 81 def get_repo(self, repository):
82 82 return self._get_repo(repository)
83 83
84 84 def get_by_repo_name(self, repo_name, cache=False):
85 85 repo = self.sa.query(Repository)\
86 86 .filter(Repository.repo_name == repo_name)
87 87
88 88 if cache:
89 89 repo = repo.options(FromCache("sql_cache_short",
90 90 "get_repo_%s" % repo_name))
91 91 return repo.scalar()
92 92
93 93 def get_all_user_repos(self, user):
94 94 """
95 95 Get's all repositories that user have at least read access
96 96
97 97 :param user:
98 98 :type user:
99 99 """
100 100 from rhodecode.lib.auth import AuthUser
101 101 user = self._get_user(user)
102 102 repos = AuthUser(user_id=user.user_id).permissions['repositories']
103 103 access_check = lambda r: r[1] in ['repository.read',
104 104 'repository.write',
105 105 'repository.admin']
106 106 repos = [x[0] for x in filter(access_check, repos.items())]
107 107 return Repository.query().filter(Repository.repo_name.in_(repos))
108 108
109 109 def get_users_js(self):
110 110 users = self.sa.query(User).filter(User.active == True).all()
111 111 return json.dumps([
112 112 {
113 113 'id': u.user_id,
114 114 'fname': u.name,
115 115 'lname': u.lastname,
116 116 'nname': u.username,
117 117 'gravatar_lnk': h.gravatar_url(u.email, 14)
118 118 } for u in users]
119 119 )
120 120
121 121 def get_users_groups_js(self):
122 122 users_groups = self.sa.query(UserGroup)\
123 123 .filter(UserGroup.users_group_active == True).all()
124 124
125 125 return json.dumps([
126 126 {
127 127 'id': gr.users_group_id,
128 128 'grname': gr.users_group_name,
129 129 'grmembers': len(gr.members),
130 130 } for gr in users_groups]
131 131 )
132 132
133 133 @classmethod
134 134 def _render_datatable(cls, tmpl, *args, **kwargs):
135 135 import rhodecode
136 136 from pylons import tmpl_context as c
137 137 from pylons.i18n.translation import _
138 138
139 139 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
140 140 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
141 141
142 142 tmpl = template.get_def(tmpl)
143 143 kwargs.update(dict(_=_, h=h, c=c))
144 144 return tmpl.render(*args, **kwargs)
145 145
146 146 @classmethod
147 147 def update_repoinfo(cls, repositories=None):
148 148 if not repositories:
149 149 repositories = Repository.getAll()
150 150 for repo in repositories:
151 151 repo.update_changeset_cache()
152 152
153 153 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
154 154 super_user_actions=False):
155 155 _render = self._render_datatable
156 156
157 157 def quick_menu(repo_name):
158 158 return _render('quick_menu', repo_name)
159 159
160 160 def repo_lnk(name, rtype, private, fork_of):
161 161 return _render('repo_name', name, rtype, private, fork_of,
162 162 short_name=not admin, admin=False)
163 163
164 164 def last_change(last_change):
165 165 return _render("last_change", last_change)
166 166
167 167 def rss_lnk(repo_name):
168 168 return _render("rss", repo_name)
169 169
170 170 def atom_lnk(repo_name):
171 171 return _render("atom", repo_name)
172 172
173 173 def last_rev(repo_name, cs_cache):
174 174 return _render('revision', repo_name, cs_cache.get('revision'),
175 175 cs_cache.get('raw_id'), cs_cache.get('author'),
176 176 cs_cache.get('message'))
177 177
178 178 def desc(desc):
179 179 from pylons import tmpl_context as c
180 180 if c.visual.stylify_metatags:
181 181 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
182 182 else:
183 183 return h.urlify_text(h.truncate(desc, 60))
184 184
185 185 def repo_actions(repo_name):
186 186 return _render('repo_actions', repo_name, super_user_actions)
187 187
188 188 def owner_actions(user_id, username):
189 189 return _render('user_name', user_id, username)
190 190
191 191 repos_data = []
192 192 for repo in repos_list:
193 193 if perm_check:
194 194 # check permission at this level
195 195 if not HasRepoPermissionAny(
196 196 'repository.read', 'repository.write', 'repository.admin'
197 197 )(repo.repo_name, 'get_repos_as_dict check'):
198 198 continue
199 199 cs_cache = repo.changeset_cache
200 200 row = {
201 201 "menu": quick_menu(repo.repo_name),
202 202 "raw_name": repo.repo_name.lower(),
203 203 "name": repo_lnk(repo.repo_name, repo.repo_type,
204 204 repo.private, repo.fork),
205 205 "last_change": last_change(repo.last_db_change),
206 206 "last_changeset": last_rev(repo.repo_name, cs_cache),
207 207 "raw_tip": cs_cache.get('revision'),
208 208 "desc": desc(repo.description),
209 209 "owner": h.person(repo.user.username),
210 210 "rss": rss_lnk(repo.repo_name),
211 211 "atom": atom_lnk(repo.repo_name),
212 212
213 213 }
214 214 if admin:
215 215 row.update({
216 216 "action": repo_actions(repo.repo_name),
217 217 "owner": owner_actions(repo.user.user_id,
218 218 h.person(repo.user.username))
219 219 })
220 220 repos_data.append(row)
221 221
222 222 return {
223 223 "totalRecords": len(repos_list),
224 224 "startIndex": 0,
225 225 "sort": "name",
226 226 "dir": "asc",
227 227 "records": repos_data
228 228 }
229 229
230 230 def _get_defaults(self, repo_name):
231 231 """
232 232 Get's information about repository, and returns a dict for
233 233 usage in forms
234 234
235 235 :param repo_name:
236 236 """
237 237
238 238 repo_info = Repository.get_by_repo_name(repo_name)
239 239
240 240 if repo_info is None:
241 241 return None
242 242
243 243 defaults = repo_info.get_dict()
244 244 group, repo_name, repo_name_full = repo_info.groups_and_repo
245 245 defaults['repo_name'] = repo_name
246 246 defaults['repo_group'] = getattr(group[-1] if group else None,
247 247 'group_id', None)
248 248
249 249 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
250 250 (1, 'repo_description'), (1, 'repo_enable_locking'),
251 251 (1, 'repo_landing_rev'), (0, 'clone_uri'),
252 252 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
253 253 attr = k
254 254 if strip:
255 255 attr = remove_prefix(k, 'repo_')
256 256
257 257 defaults[k] = defaults[attr]
258 258
259 259 # fill owner
260 260 if repo_info.user:
261 261 defaults.update({'user': repo_info.user.username})
262 262 else:
263 263 replacement_user = User.query().filter(User.admin ==
264 264 True).first().username
265 265 defaults.update({'user': replacement_user})
266 266
267 267 # fill repository users
268 268 for p in repo_info.repo_to_perm:
269 269 defaults.update({'u_perm_%s' % p.user.username:
270 270 p.permission.permission_name})
271 271
272 272 # fill repository groups
273 273 for p in repo_info.users_group_to_perm:
274 274 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
275 275 p.permission.permission_name})
276 276
277 277 return defaults
278 278
279 279 def update(self, org_repo_name, **kwargs):
280 280 try:
281 281 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
282 282
283 283 if 'user' in kwargs:
284 284 cur_repo.user = User.get_by_username(kwargs['user'])
285 285
286 286 if 'repo_group' in kwargs:
287 287 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
288 288
289 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
289 for strip, k in [(1, 'repo_enable_downloads'),
290 290 (1, 'repo_description'), (1, 'repo_enable_locking'),
291 291 (1, 'repo_landing_rev'), (0, 'clone_uri'),
292 292 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
293 293 if k in kwargs:
294 294 val = kwargs[k]
295 295 if strip:
296 296 k = remove_prefix(k, 'repo_')
297 297 setattr(cur_repo, k, val)
298 298
299 299 new_name = cur_repo.get_new_name(kwargs['repo_name'])
300 300 cur_repo.repo_name = new_name
301 301 #if private flag is set, reset default permission to NONE
302 302
303 303 if kwargs.get('repo_private'):
304 304 EMPTY_PERM = 'repository.none'
305 305 RepoModel().grant_user_permission(
306 306 repo=cur_repo, user='default', perm=EMPTY_PERM
307 307 )
308 308 #handle extra fields
309 309 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
310 310 k = RepositoryField.un_prefix_key(field)
311 311 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
312 312 if ex_field:
313 313 ex_field.field_value = kwargs[field]
314 314 self.sa.add(ex_field)
315 315 self.sa.add(cur_repo)
316 316
317 317 if org_repo_name != new_name:
318 318 # rename repository
319 319 self.__rename_repo(old=org_repo_name, new=new_name)
320 320
321 321 return cur_repo
322 322 except Exception:
323 323 log.error(traceback.format_exc())
324 324 raise
325 325
326 326 def create_repo(self, repo_name, repo_type, description, owner,
327 327 private=False, clone_uri=None, repos_group=None,
328 328 landing_rev='tip', just_db=False, fork_of=None,
329 329 copy_fork_permissions=False, enable_statistics=False,
330 330 enable_locking=False, enable_downloads=False):
331 331 """
332 332 Create repository
333 333
334 334 """
335 335 from rhodecode.model.scm import ScmModel
336 336
337 337 owner = self._get_user(owner)
338 338 fork_of = self._get_repo(fork_of)
339 339 repos_group = self._get_repos_group(repos_group)
340 340 try:
341 341
342 342 # repo name is just a name of repository
343 343 # while repo_name_full is a full qualified name that is combined
344 344 # with name and path of group
345 345 repo_name_full = repo_name
346 346 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
347 347
348 348 new_repo = Repository()
349 349 new_repo.enable_statistics = False
350 350 new_repo.repo_name = repo_name_full
351 351 new_repo.repo_type = repo_type
352 352 new_repo.user = owner
353 353 new_repo.group = repos_group
354 354 new_repo.description = description or repo_name
355 355 new_repo.private = private
356 356 new_repo.clone_uri = clone_uri
357 357 new_repo.landing_rev = landing_rev
358 358
359 359 new_repo.enable_statistics = enable_statistics
360 360 new_repo.enable_locking = enable_locking
361 361 new_repo.enable_downloads = enable_downloads
362 362
363 363 if repos_group:
364 364 new_repo.enable_locking = repos_group.enable_locking
365 365
366 366 if fork_of:
367 367 parent_repo = fork_of
368 368 new_repo.fork = parent_repo
369 369
370 370 self.sa.add(new_repo)
371 371
372 372 def _create_default_perms():
373 373 # create default permission
374 374 repo_to_perm = UserRepoToPerm()
375 375 default = 'repository.read'
376 376 for p in User.get_by_username('default').user_perms:
377 377 if p.permission.permission_name.startswith('repository.'):
378 378 default = p.permission.permission_name
379 379 break
380 380
381 381 default_perm = 'repository.none' if private else default
382 382
383 383 repo_to_perm.permission_id = self.sa.query(Permission)\
384 384 .filter(Permission.permission_name == default_perm)\
385 385 .one().permission_id
386 386
387 387 repo_to_perm.repository = new_repo
388 388 repo_to_perm.user_id = User.get_by_username('default').user_id
389 389
390 390 self.sa.add(repo_to_perm)
391 391
392 392 if fork_of:
393 393 if copy_fork_permissions:
394 394 repo = fork_of
395 395 user_perms = UserRepoToPerm.query()\
396 396 .filter(UserRepoToPerm.repository == repo).all()
397 397 group_perms = UserGroupRepoToPerm.query()\
398 398 .filter(UserGroupRepoToPerm.repository == repo).all()
399 399
400 400 for perm in user_perms:
401 401 UserRepoToPerm.create(perm.user, new_repo,
402 402 perm.permission)
403 403
404 404 for perm in group_perms:
405 405 UserGroupRepoToPerm.create(perm.users_group, new_repo,
406 406 perm.permission)
407 407 else:
408 408 _create_default_perms()
409 409 else:
410 410 _create_default_perms()
411 411
412 412 if not just_db:
413 413 self.__create_repo(repo_name, repo_type,
414 414 repos_group,
415 415 clone_uri)
416 416 log_create_repository(new_repo.get_dict(),
417 417 created_by=owner.username)
418 418
419 419 # now automatically start following this repository as owner
420 420 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
421 421 owner.user_id)
422 422 return new_repo
423 423 except Exception:
424 424 log.error(traceback.format_exc())
425 425 raise
426 426
427 427 def create(self, form_data, cur_user, just_db=False, fork=None):
428 428 """
429 429 Backward compatibility function, just a wrapper on top of create_repo
430 430
431 431 :param form_data:
432 432 :param cur_user:
433 433 :param just_db:
434 434 :param fork:
435 435 """
436 436 owner = cur_user
437 437 repo_name = form_data['repo_name_full']
438 438 repo_type = form_data['repo_type']
439 439 description = form_data['repo_description']
440 440 private = form_data['repo_private']
441 441 clone_uri = form_data.get('clone_uri')
442 442 repos_group = form_data['repo_group']
443 443 landing_rev = form_data['repo_landing_rev']
444 444 copy_fork_permissions = form_data.get('copy_permissions')
445 445 fork_of = form_data.get('fork_parent_id')
446 446
447 447 ## repo creation defaults, private and repo_type are filled in form
448 448 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
449 449 enable_statistics = defs.get('repo_enable_statistics')
450 450 enable_locking = defs.get('repo_enable_locking')
451 451 enable_downloads = defs.get('repo_enable_downloads')
452 452
453 453 return self.create_repo(
454 454 repo_name, repo_type, description, owner, private, clone_uri,
455 455 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
456 456 enable_statistics, enable_locking, enable_downloads
457 457 )
458 458
459 459 def create_fork(self, form_data, cur_user):
460 460 """
461 461 Simple wrapper into executing celery task for fork creation
462 462
463 463 :param form_data:
464 464 :param cur_user:
465 465 """
466 466 from rhodecode.lib.celerylib import tasks, run_task
467 467 run_task(tasks.create_repo_fork, form_data, cur_user)
468 468
469 469 def delete(self, repo, forks=None):
470 470 """
471 471 Delete given repository, forks parameter defines what do do with
472 472 attached forks. Throws AttachedForksError if deleted repo has attached
473 473 forks
474 474
475 475 :param repo:
476 476 :param forks: str 'delete' or 'detach'
477 477 """
478 478 repo = self._get_repo(repo)
479 479 if repo:
480 480 if forks == 'detach':
481 481 for r in repo.forks:
482 482 r.fork = None
483 483 self.sa.add(r)
484 484 elif forks == 'delete':
485 485 for r in repo.forks:
486 486 self.delete(r, forks='delete')
487 487 elif [f for f in repo.forks]:
488 488 raise AttachedForksError()
489 489
490 490 old_repo_dict = repo.get_dict()
491 491 owner = repo.user
492 492 try:
493 493 self.sa.delete(repo)
494 494 self.__delete_repo(repo)
495 495 log_delete_repository(old_repo_dict,
496 496 deleted_by=owner.username)
497 497 except Exception:
498 498 log.error(traceback.format_exc())
499 499 raise
500 500
501 501 def grant_user_permission(self, repo, user, perm):
502 502 """
503 503 Grant permission for user on given repository, or update existing one
504 504 if found
505 505
506 506 :param repo: Instance of Repository, repository_id, or repository name
507 507 :param user: Instance of User, user_id or username
508 508 :param perm: Instance of Permission, or permission_name
509 509 """
510 510 user = self._get_user(user)
511 511 repo = self._get_repo(repo)
512 512 permission = self._get_perm(perm)
513 513
514 514 # check if we have that permission already
515 515 obj = self.sa.query(UserRepoToPerm)\
516 516 .filter(UserRepoToPerm.user == user)\
517 517 .filter(UserRepoToPerm.repository == repo)\
518 518 .scalar()
519 519 if obj is None:
520 520 # create new !
521 521 obj = UserRepoToPerm()
522 522 obj.repository = repo
523 523 obj.user = user
524 524 obj.permission = permission
525 525 self.sa.add(obj)
526 526 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
527 527
528 528 def revoke_user_permission(self, repo, user):
529 529 """
530 530 Revoke permission for user on given repository
531 531
532 532 :param repo: Instance of Repository, repository_id, or repository name
533 533 :param user: Instance of User, user_id or username
534 534 """
535 535
536 536 user = self._get_user(user)
537 537 repo = self._get_repo(repo)
538 538
539 539 obj = self.sa.query(UserRepoToPerm)\
540 540 .filter(UserRepoToPerm.repository == repo)\
541 541 .filter(UserRepoToPerm.user == user)\
542 542 .scalar()
543 543 if obj:
544 544 self.sa.delete(obj)
545 545 log.debug('Revoked perm on %s on %s' % (repo, user))
546 546
547 547 def grant_users_group_permission(self, repo, group_name, perm):
548 548 """
549 549 Grant permission for user group on given repository, or update
550 550 existing one if found
551 551
552 552 :param repo: Instance of Repository, repository_id, or repository name
553 553 :param group_name: Instance of UserGroup, users_group_id,
554 554 or user group name
555 555 :param perm: Instance of Permission, or permission_name
556 556 """
557 557 repo = self._get_repo(repo)
558 558 group_name = self.__get_users_group(group_name)
559 559 permission = self._get_perm(perm)
560 560
561 561 # check if we have that permission already
562 562 obj = self.sa.query(UserGroupRepoToPerm)\
563 563 .filter(UserGroupRepoToPerm.users_group == group_name)\
564 564 .filter(UserGroupRepoToPerm.repository == repo)\
565 565 .scalar()
566 566
567 567 if obj is None:
568 568 # create new
569 569 obj = UserGroupRepoToPerm()
570 570
571 571 obj.repository = repo
572 572 obj.users_group = group_name
573 573 obj.permission = permission
574 574 self.sa.add(obj)
575 575 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
576 576
577 577 def revoke_users_group_permission(self, repo, group_name):
578 578 """
579 579 Revoke permission for user group on given repository
580 580
581 581 :param repo: Instance of Repository, repository_id, or repository name
582 582 :param group_name: Instance of UserGroup, users_group_id,
583 583 or user group name
584 584 """
585 585 repo = self._get_repo(repo)
586 586 group_name = self.__get_users_group(group_name)
587 587
588 588 obj = self.sa.query(UserGroupRepoToPerm)\
589 589 .filter(UserGroupRepoToPerm.repository == repo)\
590 590 .filter(UserGroupRepoToPerm.users_group == group_name)\
591 591 .scalar()
592 592 if obj:
593 593 self.sa.delete(obj)
594 594 log.debug('Revoked perm to %s on %s' % (repo, group_name))
595 595
596 596 def delete_stats(self, repo_name):
597 597 """
598 598 removes stats for given repo
599 599
600 600 :param repo_name:
601 601 """
602 602 repo = self._get_repo(repo_name)
603 603 try:
604 604 obj = self.sa.query(Statistics)\
605 605 .filter(Statistics.repository == repo).scalar()
606 606 if obj:
607 607 self.sa.delete(obj)
608 608 except Exception:
609 609 log.error(traceback.format_exc())
610 610 raise
611 611
612 612 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
613 613 """
614 614 makes repository on filesystem. It's group aware means it'll create
615 615 a repository within a group, and alter the paths accordingly of
616 616 group location
617 617
618 618 :param repo_name:
619 619 :param alias:
620 620 :param parent_id:
621 621 :param clone_uri:
622 622 """
623 623 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
624 624 from rhodecode.model.scm import ScmModel
625 625
626 626 if parent:
627 627 new_parent_path = os.sep.join(parent.full_path_splitted)
628 628 else:
629 629 new_parent_path = ''
630 630
631 631 # we need to make it str for mercurial
632 632 repo_path = os.path.join(*map(lambda x: safe_str(x),
633 633 [self.repos_path, new_parent_path, repo_name]))
634 634
635 635 # check if this path is not a repository
636 636 if is_valid_repo(repo_path, self.repos_path):
637 637 raise Exception('This path %s is a valid repository' % repo_path)
638 638
639 639 # check if this path is a group
640 640 if is_valid_repos_group(repo_path, self.repos_path):
641 641 raise Exception('This path %s is a valid group' % repo_path)
642 642
643 643 log.info('creating repo %s in %s @ %s' % (
644 644 repo_name, safe_unicode(repo_path),
645 645 obfuscate_url_pw(clone_uri)
646 646 )
647 647 )
648 648 backend = get_backend(alias)
649 649 if alias == 'hg':
650 650 backend(repo_path, create=True, src_url=clone_uri)
651 651 elif alias == 'git':
652 652 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
653 653 # add rhodecode hook into this repo
654 654 ScmModel().install_git_hook(repo=r)
655 655 else:
656 656 raise Exception('Undefined alias %s' % alias)
657 657
658 658 def __rename_repo(self, old, new):
659 659 """
660 660 renames repository on filesystem
661 661
662 662 :param old: old name
663 663 :param new: new name
664 664 """
665 665 log.info('renaming repo from %s to %s' % (old, new))
666 666
667 667 old_path = os.path.join(self.repos_path, old)
668 668 new_path = os.path.join(self.repos_path, new)
669 669 if os.path.isdir(new_path):
670 670 raise Exception(
671 671 'Was trying to rename to already existing dir %s' % new_path
672 672 )
673 673 shutil.move(old_path, new_path)
674 674
675 675 def __delete_repo(self, repo):
676 676 """
677 677 removes repo from filesystem, the removal is acctually made by
678 678 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
679 679 repository is no longer valid for rhodecode, can be undeleted later on
680 680 by reverting the renames on this repository
681 681
682 682 :param repo: repo object
683 683 """
684 684 rm_path = os.path.join(self.repos_path, repo.repo_name)
685 685 log.info("Removing %s" % (rm_path))
686 686 # disable hg/git internal that it doesn't get detected as repo
687 687 alias = repo.repo_type
688 688
689 689 bare = getattr(repo.scm_instance, 'bare', False)
690 690
691 691 if not bare:
692 692 # skip this for bare git repos
693 693 shutil.move(os.path.join(rm_path, '.%s' % alias),
694 694 os.path.join(rm_path, 'rm__.%s' % alias))
695 695 # disable repo
696 696 _now = datetime.now()
697 697 _ms = str(_now.microsecond).rjust(6, '0')
698 698 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
699 699 repo.just_name)
700 700 if repo.group:
701 701 args = repo.group.full_path_splitted + [_d]
702 702 _d = os.path.join(*args)
703 703 shutil.move(rm_path, os.path.join(self.repos_path, _d))
@@ -1,395 +1,387 b''
1 1 ## -*- coding: utf-8 -*-
2 2 ##
3 3 ## See also repo_settings.html
4 4 ##
5 5 <%inherit file="/base/base.html"/>
6 6
7 7 <%def name="title()">
8 8 ${_('Edit repository')} ${c.repo_info.repo_name} &middot; ${c.rhodecode_name}
9 9 </%def>
10 10
11 11 <%def name="breadcrumbs_links()">
12 12 ${_('Settings')}
13 13 </%def>
14 14
15 15 <%def name="page_nav()">
16 16 ${self.menu('admin')}
17 17 </%def>
18 18
19 19 <%def name="main()">
20 20 ${self.context_bar('options')}
21 21 <div class="box box-left">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
27 27 <div class="form">
28 28 <!-- fields -->
29 29 <div class="fields">
30 30 <div class="field">
31 31 <div class="label">
32 32 <label for="repo_name">${_('Name')}:</label>
33 33 </div>
34 34 <div class="input">
35 35 ${h.text('repo_name',class_="medium")}
36 36 </div>
37 37 </div>
38 38 <div class="field">
39 39 <div class="label">
40 40 <label for="clone_uri">${_('Clone uri')}:</label>
41 41 </div>
42 42 <div class="input">
43 43 ${h.text('clone_uri',class_="medium")}
44 44 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
45 45 </div>
46 46 </div>
47 47 <div class="field">
48 48 <div class="label">
49 49 <label for="repo_group">${_('Repository group')}:</label>
50 50 </div>
51 51 <div class="input">
52 52 ${h.select('repo_group','',c.repo_groups,class_="medium")}
53 53 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
54 54 </div>
55 55 </div>
56 56 <div class="field">
57 57 <div class="label">
58 <label for="repo_type">${_('Type')}:</label>
59 </div>
60 <div class="input">
61 ${h.select('repo_type','hg',c.backends,class_="medium")}
62 </div>
63 </div>
64 <div class="field">
65 <div class="label">
66 58 <label for="repo_landing_rev">${_('Landing revision')}:</label>
67 59 </div>
68 60 <div class="input">
69 61 ${h.select('repo_landing_rev','',c.landing_revs,class_="medium")}
70 62 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
71 63 </div>
72 64 </div>
73 65 <div class="field">
74 66 <div class="label label-textarea">
75 67 <label for="repo_description">${_('Description')}:</label>
76 68 </div>
77 69 <div class="textarea text-area editor">
78 70 ${h.textarea('repo_description')}
79 71 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
80 72 </div>
81 73 </div>
82 74
83 75 <div class="field">
84 76 <div class="label label-checkbox">
85 77 <label for="repo_private">${_('Private repository')}:</label>
86 78 </div>
87 79 <div class="checkboxes">
88 80 ${h.checkbox('repo_private',value="True")}
89 81 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
90 82 </div>
91 83 </div>
92 84 <div class="field">
93 85 <div class="label label-checkbox">
94 86 <label for="repo_enable_statistics">${_('Enable statistics')}:</label>
95 87 </div>
96 88 <div class="checkboxes">
97 89 ${h.checkbox('repo_enable_statistics',value="True")}
98 90 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
99 91 </div>
100 92 </div>
101 93 <div class="field">
102 94 <div class="label label-checkbox">
103 95 <label for="repo_enable_downloads">${_('Enable downloads')}:</label>
104 96 </div>
105 97 <div class="checkboxes">
106 98 ${h.checkbox('repo_enable_downloads',value="True")}
107 99 <span class="help-block">${_('Enable download menu on summary page.')}</span>
108 100 </div>
109 101 </div>
110 102 <div class="field">
111 103 <div class="label label-checkbox">
112 104 <label for="repo_enable_locking">${_('Enable locking')}:</label>
113 105 </div>
114 106 <div class="checkboxes">
115 107 ${h.checkbox('repo_enable_locking',value="True")}
116 108 <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
117 109 </div>
118 110 </div>
119 111 <div class="field">
120 112 <div class="label">
121 113 <label for="user">${_('Owner')}:</label>
122 114 </div>
123 115 <div class="input input-medium ac">
124 116 <div class="perm_ac">
125 117 ${h.text('user',class_='yui-ac-input')}
126 118 <span class="help-block">${_('Change owner of this repository.')}</span>
127 119 <div id="owner_container"></div>
128 120 </div>
129 121 </div>
130 122 </div>
131 123 %if c.visual.repository_fields:
132 124 ## EXTRA FIELDS
133 125 %for field in c.repo_fields:
134 126 <div class="field">
135 127 <div class="label">
136 128 <label for="${field.field_key_prefixed}">${field.field_label} (${field.field_key}):</label>
137 129 </div>
138 130 <div class="input input-medium">
139 131 ${h.text(field.field_key_prefixed, field.field_value, class_='medium')}
140 132 %if field.field_desc:
141 133 <span class="help-block">${field.field_desc}</span>
142 134 %endif
143 135 </div>
144 136 </div>
145 137 %endfor
146 138 %endif
147 139 <div class="buttons">
148 140 ${h.submit('save',_('Save'),class_="ui-btn large")}
149 141 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
150 142 </div>
151 143 </div>
152 144 </div>
153 145 ${h.end_form()}
154 146 </div>
155 147
156 148 <div class="box box-right">
157 149 <div class="title">
158 150 <h5>${_('Permissions')}</h5>
159 151 </div>
160 152 ${h.form(url('set_repo_perm_member', repo_name=c.repo_info.repo_name),method='post')}
161 153 <div class="form">
162 154 <div class="fields">
163 155 <div class="field">
164 156 <div class="label">
165 157 <label for="input">${_('Permissions')}:</label>
166 158 </div>
167 159 <div class="input">
168 160 ${h.hidden('repo_private')}
169 161 <%include file="repo_edit_perms.html"/>
170 162 </div>
171 163 </div>
172 164 <div class="buttons">
173 165 ${h.submit('save',_('Save'),class_="ui-btn large")}
174 166 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
175 167 </div>
176 168 </div>
177 169 </div>
178 170 ${h.end_form()}
179 171 </div>
180 172
181 173
182 174 <div class="box box-right" style="clear:right">
183 175 <div class="title">
184 176 <h5>${_('Advanced settings')}</h5>
185 177 </div>
186 178
187 179 <h3>${_('Statistics')}</h3>
188 180 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
189 181 <div class="form">
190 182 <div class="fields">
191 183 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")}
192 184 <div class="field" style="border:none;color:#888">
193 185 <ul>
194 186 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
195 187 <li>${_('Stats gathered')}: ${c.stats_percentage}%</li>
196 188 </ul>
197 189 </div>
198 190 </div>
199 191 </div>
200 192 ${h.end_form()}
201 193
202 194 %if c.repo_info.clone_uri:
203 195 <h3>${_('Remote')}</h3>
204 196 ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')}
205 197 <div class="form">
206 198 <div class="fields">
207 199 ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
208 200 <div class="field" style="border:none">
209 201 <ul>
210 202 <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li>
211 203 </ul>
212 204 </div>
213 205 </div>
214 206 </div>
215 207 ${h.end_form()}
216 208 %endif
217 209
218 210 <h3>${_('Cache')}</h3>
219 211 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
220 212 <div class="form">
221 213 <div class="fields">
222 214 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
223 215 <div class="field" style="border:none;color:#888">
224 216 <ul>
225 217 <li>${_('Manually invalidate cache for this repository. On first access repository will be cached again')}
226 218 </li>
227 219 </ul>
228 220 </div>
229 221 <div class="field" style="border:none;">
230 222 ${_('List of cached values')}
231 223 <table>
232 224 <tr>
233 225 <th>${_('Prefix')}</th>
234 226 <th>${_('Key')}</th>
235 227 <th>${_('Active')}</th>
236 228 </tr>
237 229 %for cache in c.repo_info.cache_keys:
238 230 <tr>
239 231 <td>${cache.get_prefix() or '-'}</td>
240 232 <td>${cache.cache_key}</td>
241 233 <td>${h.boolicon(cache.cache_active)}</td>
242 234 </tr>
243 235 %endfor
244 236 </table>
245 237 </div>
246 238 </div>
247 239 </div>
248 240 ${h.end_form()}
249 241
250 242 <h3>${_('Public journal')}</h3>
251 243 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
252 244 <div class="form">
253 245 ${h.hidden('auth_token',str(h.get_token()))}
254 246 <div class="field">
255 247 %if c.in_public_journal:
256 248 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
257 249 %else:
258 250 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
259 251 %endif
260 252 </div>
261 253 <div class="field" style="border:none;color:#888">
262 254 <ul>
263 255 <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
264 256 </li>
265 257 </ul>
266 258 </div>
267 259 </div>
268 260 ${h.end_form()}
269 261
270 262 <h3>${_('Locking')}</h3>
271 263 ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')}
272 264 <div class="form">
273 265 <div class="fields">
274 266 %if c.repo_info.locked[0]:
275 267 ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")}
276 268 ${'Locked by %s on %s' % (h.person_by_id(c.repo_info.locked[0]),h.fmt_date(h.time_to_datetime(c.repo_info.locked[1])))}
277 269 %else:
278 270 ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")}
279 271 ${_('Repository is not locked')}
280 272 %endif
281 273 </div>
282 274 <div class="field" style="border:none;color:#888">
283 275 <ul>
284 276 <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
285 277 </li>
286 278 </ul>
287 279 </div>
288 280 </div>
289 281 ${h.end_form()}
290 282
291 283 <h3>${_('Set as fork of')}</h3>
292 284 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
293 285 <div class="form">
294 286 <div class="fields">
295 287 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
296 288 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
297 289 </div>
298 290 <div class="field" style="border:none;color:#888">
299 291 <ul>
300 292 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
301 293 </ul>
302 294 </div>
303 295 </div>
304 296 ${h.end_form()}
305 297
306 298 <h3>${_('Delete')}</h3>
307 299 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
308 300 <div class="form">
309 301 <div class="fields">
310 302 <div class="field" style="border:none;color:#888">
311 303 ## <div class="label">
312 304 ## <label for="">${_('Remove repository')}:</label>
313 305 ## </div>
314 306 <div class="checkboxes">
315 307 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
316 308 %if c.repo_info.forks.count():
317 309 - ${ungettext('this repository has %s fork', 'this repository has %s forks', c.repo_info.forks.count()) % c.repo_info.forks.count()}
318 310 <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label>
319 311 <input type="radio" name="forks" value="delete_forks" /> <label for="forks">${_('Delete forks')}</label>
320 312 %endif
321 313 <ul>
322 314 <li>${_('This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems. If you need to fully delete it from file system please do it manually')}</li>
323 315 </ul>
324 316 </div>
325 317 </div>
326 318 </div>
327 319 </div>
328 320 ${h.end_form()}
329 321 </div>
330 322
331 323 ##TODO: this should be controlled by the VISUAL setting
332 324 %if c.visual.repository_fields:
333 325 <div class="box box-left" style="clear:left">
334 326 <!-- box / title -->
335 327 <div class="title">
336 328 <h5>${_('Extra fields')}</h5>
337 329 </div>
338 330
339 331 <div class="emails_wrap">
340 332 <table class="noborder">
341 333 %for field in c.repo_fields:
342 334 <tr>
343 335 <td>${field.field_label} (${field.field_key})</td>
344 336 <td>${field.field_type}</td>
345 337 <td>
346 338 ${h.form(url('delete_repo_fields', repo_name=c.repo_info.repo_name, field_id=field.repo_field_id),method='delete')}
347 339 ${h.submit('remove_%s' % field.repo_field_id, _('delete'), id="remove_field_%s" % field.repo_field_id,
348 340 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this field: %s') % field.field_key+"');")}
349 341 ${h.end_form()}
350 342 </td>
351 343 </tr>
352 344 %endfor
353 345 </table>
354 346 </div>
355 347
356 348 ${h.form(url('create_repo_fields', repo_name=c.repo_info.repo_name),method='put')}
357 349 <div class="form">
358 350 <!-- fields -->
359 351 <div class="fields">
360 352 <div class="field">
361 353 <div class="label">
362 354 <label for="new_field_key">${_('New field key')}:</label>
363 355 </div>
364 356 <div class="input">
365 357 ${h.text('new_field_key', class_='small')}
366 358 </div>
367 359 </div>
368 360 <div class="field">
369 361 <div class="label">
370 362 <label for="new_field_label">${_('New field label')}:</label>
371 363 </div>
372 364 <div class="input">
373 365 ${h.text('new_field_label', class_='small', placeholder=_('Enter short label'))}
374 366 </div>
375 367 </div>
376 368
377 369 <div class="field">
378 370 <div class="label">
379 371 <label for="new_field_desc">${_('New field description')}:</label>
380 372 </div>
381 373 <div class="input">
382 374 ${h.text('new_field_desc', class_='small', placeholder=_('Enter description of a field'))}
383 375 </div>
384 376 </div>
385 377
386 378 <div class="buttons">
387 379 ${h.submit('save',_('Add'),class_="ui-btn large")}
388 380 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
389 381 </div>
390 382 </div>
391 383 </div>
392 384 ${h.end_form()}
393 385 </div>
394 386 %endif
395 387 </%def>
General Comments 0
You need to be logged in to leave comments. Login now