##// END OF EJS Templates
Resolve error occurring during recursive group creation in API create-repo function
marcink -
r2120:d5527ceb beta
parent child Browse files
Show More
@@ -1,584 +1,586
1 1 .. _changelog:
2 2
3 3 =========
4 4 Changelog
5 5 =========
6 6
7 7
8 8 1.3.4 (**2012-XX-XX**)
9 9 ----------------------
10 10
11 11 :status: in-progress
12 12 :branch: beta
13 13
14 14 news
15 15 ++++
16 16
17 17 - Whoosh logging is now controlled by the .ini files logging setup
18 18 - added clone-url into edit form on /settings page
19 19 - added help text into repo add/edit forms
20 20 - created rcextensions module with additional mappings (ref #322) and
21 21 post push/pull/create repo hooks callbacks
22 22
23 23 fixes
24 24 +++++
25 25
26 26 - fixed #390 cache invalidation problems on repos inside group
27 27 - fixed #385 clone by ID url was loosing proxy prefix in URL
28 28 - fixed some unicode problems with waitress
29 29 - fixed issue with escaping < and > in changeset commits
30 - fixed error occurring during recursive group creation in API
31 create_repo function
30 32
31 33 1.3.3 (**2012-03-02**)
32 34 ----------------------
33 35
34 36 news
35 37 ++++
36 38
37 39
38 40 fixes
39 41 +++++
40 42
41 43 - fixed some python2.5 compatibility issues
42 44 - fixed issues with removed repos was accidentally added as groups, after
43 45 full rescan of paths
44 46 - fixes #376 Cannot edit user (using container auth)
45 47 - fixes #378 Invalid image urls on changeset screen with proxy-prefix
46 48 configuration
47 49 - fixed initial sorting of repos inside repo group
48 50 - fixes issue when user tried to resubmit same permission into user/user_groups
49 51 - bumped beaker version that fixes #375 leap error bug
50 52 - fixed raw_changeset for git. It was generated with hg patch headers
51 53 - fixed vcs issue with last_changeset for filenodes
52 54 - fixed missing commit after hook delete
53 55 - fixed #372 issues with git operation detection that caused a security issue
54 56 for git repos
55 57
56 58 1.3.2 (**2012-02-28**)
57 59 ----------------------
58 60
59 61 news
60 62 ++++
61 63
62 64
63 65 fixes
64 66 +++++
65 67
66 68 - fixed git protocol issues with repos-groups
67 69 - fixed git remote repos validator that prevented from cloning remote git repos
68 70 - fixes #370 ending slashes fixes for repo and groups
69 71 - fixes #368 improved git-protocol detection to handle other clients
70 72 - fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
71 73 Moved To Root
72 74 - fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys
73 75 - fixed #373 missing cascade drop on user_group_to_perm table
74 76
75 77 1.3.1 (**2012-02-27**)
76 78 ----------------------
77 79
78 80 news
79 81 ++++
80 82
81 83
82 84 fixes
83 85 +++++
84 86
85 87 - redirection loop occurs when remember-me wasn't checked during login
86 88 - fixes issues with git blob history generation
87 89 - don't fetch branch for git in file history dropdown. Causes unneeded slowness
88 90
89 91 1.3.0 (**2012-02-26**)
90 92 ----------------------
91 93
92 94 news
93 95 ++++
94 96
95 97 - code review, inspired by github code-comments
96 98 - #215 rst and markdown README files support
97 99 - #252 Container-based and proxy pass-through authentication support
98 100 - #44 branch browser. Filtering of changelog by branches
99 101 - mercurial bookmarks support
100 102 - new hover top menu, optimized to add maximum size for important views
101 103 - configurable clone url template with possibility to specify protocol like
102 104 ssh:// or http:// and also manually alter other parts of clone_url.
103 105 - enabled largefiles extension by default
104 106 - optimized summary file pages and saved a lot of unused space in them
105 107 - #239 option to manually mark repository as fork
106 108 - #320 mapping of commit authors to RhodeCode users
107 109 - #304 hashes are displayed using monospace font
108 110 - diff configuration, toggle white lines and context lines
109 111 - #307 configurable diffs, whitespace toggle, increasing context lines
110 112 - sorting on branches, tags and bookmarks using YUI datatable
111 113 - improved file filter on files page
112 114 - implements #330 api method for listing nodes ar particular revision
113 115 - #73 added linking issues in commit messages to chosen issue tracker url
114 116 based on user defined regular expression
115 117 - added linking of changesets in commit messages
116 118 - new compact changelog with expandable commit messages
117 119 - firstname and lastname are optional in user creation
118 120 - #348 added post-create repository hook
119 121 - #212 global encoding settings is now configurable from .ini files
120 122 - #227 added repository groups permissions
121 123 - markdown gets codehilite extensions
122 124 - new API methods, delete_repositories, grante/revoke permissions for groups
123 125 and repos
124 126
125 127
126 128 fixes
127 129 +++++
128 130
129 131 - rewrote dbsession management for atomic operations, and better error handling
130 132 - fixed sorting of repo tables
131 133 - #326 escape of special html entities in diffs
132 134 - normalized user_name => username in api attributes
133 135 - fixes #298 ldap created users with mixed case emails created conflicts
134 136 on saving a form
135 137 - fixes issue when owner of a repo couldn't revoke permissions for users
136 138 and groups
137 139 - fixes #271 rare JSON serialization problem with statistics
138 140 - fixes #337 missing validation check for conflicting names of a group with a
139 141 repositories group
140 142 - #340 fixed session problem for mysql and celery tasks
141 143 - fixed #331 RhodeCode mangles repository names if the a repository group
142 144 contains the "full path" to the repositories
143 145 - #355 RhodeCode doesn't store encrypted LDAP passwords
144 146
145 147 1.2.5 (**2012-01-28**)
146 148 ----------------------
147 149
148 150 news
149 151 ++++
150 152
151 153 fixes
152 154 +++++
153 155
154 156 - #340 Celery complains about MySQL server gone away, added session cleanup
155 157 for celery tasks
156 158 - #341 "scanning for repositories in None" log message during Rescan was missing
157 159 a parameter
158 160 - fixed creating archives with subrepos. Some hooks were triggered during that
159 161 operation leading to crash.
160 162 - fixed missing email in account page.
161 163 - Reverted Mercurial to 2.0.1 for windows due to bug in Mercurial that makes
162 164 forking on windows impossible
163 165
164 166 1.2.4 (**2012-01-19**)
165 167 ----------------------
166 168
167 169 news
168 170 ++++
169 171
170 172 - RhodeCode is bundled with mercurial series 2.0.X by default, with
171 173 full support to largefiles extension. Enabled by default in new installations
172 174 - #329 Ability to Add/Remove Groups to/from a Repository via AP
173 175 - added requires.txt file with requirements
174 176
175 177 fixes
176 178 +++++
177 179
178 180 - fixes db session issues with celery when emailing admins
179 181 - #331 RhodeCode mangles repository names if the a repository group
180 182 contains the "full path" to the repositories
181 183 - #298 Conflicting e-mail addresses for LDAP and RhodeCode users
182 184 - DB session cleanup after hg protocol operations, fixes issues with
183 185 `mysql has gone away` errors
184 186 - #333 doc fixes for get_repo api function
185 187 - #271 rare JSON serialization problem with statistics enabled
186 188 - #337 Fixes issues with validation of repository name conflicting with
187 189 a group name. A proper message is now displayed.
188 190 - #292 made ldap_dn in user edit readonly, to get rid of confusion that field
189 191 doesn't work
190 192 - #316 fixes issues with web description in hgrc files
191 193
192 194 1.2.3 (**2011-11-02**)
193 195 ----------------------
194 196
195 197 news
196 198 ++++
197 199
198 200 - added option to manage repos group for non admin users
199 201 - added following API methods for get_users, create_user, get_users_groups,
200 202 get_users_group, create_users_group, add_user_to_users_groups, get_repos,
201 203 get_repo, create_repo, add_user_to_repo
202 204 - implements #237 added password confirmation for my account
203 205 and admin edit user.
204 206 - implements #291 email notification for global events are now sent to all
205 207 administrator users, and global config email.
206 208
207 209 fixes
208 210 +++++
209 211
210 212 - added option for passing auth method for smtp mailer
211 213 - #276 issue with adding a single user with id>10 to usergroups
212 214 - #277 fixes windows LDAP settings in which missing values breaks the ldap auth
213 215 - #288 fixes managing of repos in a group for non admin user
214 216
215 217 1.2.2 (**2011-10-17**)
216 218 ----------------------
217 219
218 220 news
219 221 ++++
220 222
221 223 - #226 repo groups are available by path instead of numerical id
222 224
223 225 fixes
224 226 +++++
225 227
226 228 - #259 Groups with the same name but with different parent group
227 229 - #260 Put repo in group, then move group to another group -> repo becomes unavailable
228 230 - #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
229 231 - #265 ldap save fails sometimes on converting attributes to booleans,
230 232 added getter and setter into model that will prevent from this on db model level
231 233 - fixed problems with timestamps issues #251 and #213
232 234 - fixes #266 RhodeCode allows to create repo with the same name and in
233 235 the same parent as group
234 236 - fixes #245 Rescan of the repositories on Windows
235 237 - fixes #248 cannot edit repos inside a group on windows
236 238 - fixes #219 forking problems on windows
237 239
238 240 1.2.1 (**2011-10-08**)
239 241 ----------------------
240 242
241 243 news
242 244 ++++
243 245
244 246
245 247 fixes
246 248 +++++
247 249
248 250 - fixed problems with basic auth and push problems
249 251 - gui fixes
250 252 - fixed logger
251 253
252 254 1.2.0 (**2011-10-07**)
253 255 ----------------------
254 256
255 257 news
256 258 ++++
257 259
258 260 - implemented #47 repository groups
259 261 - implemented #89 Can setup google analytics code from settings menu
260 262 - implemented #91 added nicer looking archive urls with more download options
261 263 like tags, branches
262 264 - implemented #44 into file browsing, and added follow branch option
263 265 - implemented #84 downloads can be enabled/disabled for each repository
264 266 - anonymous repository can be cloned without having to pass default:default
265 267 into clone url
266 268 - fixed #90 whoosh indexer can index chooses repositories passed in command
267 269 line
268 270 - extended journal with day aggregates and paging
269 271 - implemented #107 source code lines highlight ranges
270 272 - implemented #93 customizable changelog on combined revision ranges -
271 273 equivalent of githubs compare view
272 274 - implemented #108 extended and more powerful LDAP configuration
273 275 - implemented #56 users groups
274 276 - major code rewrites optimized codes for speed and memory usage
275 277 - raw and diff downloads are now in git format
276 278 - setup command checks for write access to given path
277 279 - fixed many issues with international characters and unicode. It uses utf8
278 280 decode with replace to provide less errors even with non utf8 encoded strings
279 281 - #125 added API KEY access to feeds
280 282 - #109 Repository can be created from external Mercurial link (aka. remote
281 283 repository, and manually updated (via pull) from admin panel
282 284 - beta git support - push/pull server + basic view for git repos
283 285 - added followers page and forks page
284 286 - server side file creation (with binary file upload interface)
285 287 and edition with commits powered by codemirror
286 288 - #111 file browser file finder, quick lookup files on whole file tree
287 289 - added quick login sliding menu into main page
288 290 - changelog uses lazy loading of affected files details, in some scenarios
289 291 this can improve speed of changelog page dramatically especially for
290 292 larger repositories.
291 293 - implements #214 added support for downloading subrepos in download menu.
292 294 - Added basic API for direct operations on rhodecode via JSON
293 295 - Implemented advanced hook management
294 296
295 297 fixes
296 298 +++++
297 299
298 300 - fixed file browser bug, when switching into given form revision the url was
299 301 not changing
300 302 - fixed propagation to error controller on simplehg and simplegit middlewares
301 303 - fixed error when trying to make a download on empty repository
302 304 - fixed problem with '[' chars in commit messages in journal
303 305 - fixed #99 Unicode errors, on file node paths with non utf-8 characters
304 306 - journal fork fixes
305 307 - removed issue with space inside renamed repository after deletion
306 308 - fixed strange issue on formencode imports
307 309 - fixed #126 Deleting repository on Windows, rename used incompatible chars.
308 310 - #150 fixes for errors on repositories mapped in db but corrupted in
309 311 filesystem
310 312 - fixed problem with ascendant characters in realm #181
311 313 - fixed problem with sqlite file based database connection pool
312 314 - whoosh indexer and code stats share the same dynamic extensions map
313 315 - fixes #188 - relationship delete of repo_to_perm entry on user removal
314 316 - fixes issue #189 Trending source files shows "show more" when no more exist
315 317 - fixes issue #197 Relative paths for pidlocks
316 318 - fixes issue #198 password will require only 3 chars now for login form
317 319 - fixes issue #199 wrong redirection for non admin users after creating a repository
318 320 - fixes issues #202, bad db constraint made impossible to attach same group
319 321 more than one time. Affects only mysql/postgres
320 322 - fixes #218 os.kill patch for windows was missing sig param
321 323 - improved rendering of dag (they are not trimmed anymore when number of
322 324 heads exceeds 5)
323 325
324 326 1.1.8 (**2011-04-12**)
325 327 ----------------------
326 328
327 329 news
328 330 ++++
329 331
330 332 - improved windows support
331 333
332 334 fixes
333 335 +++++
334 336
335 337 - fixed #140 freeze of python dateutil library, since new version is python2.x
336 338 incompatible
337 339 - setup-app will check for write permission in given path
338 340 - cleaned up license info issue #149
339 341 - fixes for issues #137,#116 and problems with unicode and accented characters.
340 342 - fixes crashes on gravatar, when passed in email as unicode
341 343 - fixed tooltip flickering problems
342 344 - fixed came_from redirection on windows
343 345 - fixed logging modules, and sql formatters
344 346 - windows fixes for os.kill issue #133
345 347 - fixes path splitting for windows issues #148
346 348 - fixed issue #143 wrong import on migration to 1.1.X
347 349 - fixed problems with displaying binary files, thanks to Thomas Waldmann
348 350 - removed name from archive files since it's breaking ui for long repo names
349 351 - fixed issue with archive headers sent to browser, thanks to Thomas Waldmann
350 352 - fixed compatibility for 1024px displays, and larger dpi settings, thanks to
351 353 Thomas Waldmann
352 354 - fixed issue #166 summary pager was skipping 10 revisions on second page
353 355
354 356
355 357 1.1.7 (**2011-03-23**)
356 358 ----------------------
357 359
358 360 news
359 361 ++++
360 362
361 363 fixes
362 364 +++++
363 365
364 366 - fixed (again) #136 installation support for FreeBSD
365 367
366 368
367 369 1.1.6 (**2011-03-21**)
368 370 ----------------------
369 371
370 372 news
371 373 ++++
372 374
373 375 fixes
374 376 +++++
375 377
376 378 - fixed #136 installation support for FreeBSD
377 379 - RhodeCode will check for python version during installation
378 380
379 381 1.1.5 (**2011-03-17**)
380 382 ----------------------
381 383
382 384 news
383 385 ++++
384 386
385 387 - basic windows support, by exchanging pybcrypt into sha256 for windows only
386 388 highly inspired by idea of mantis406
387 389
388 390 fixes
389 391 +++++
390 392
391 393 - fixed sorting by author in main page
392 394 - fixed crashes with diffs on binary files
393 395 - fixed #131 problem with boolean values for LDAP
394 396 - fixed #122 mysql problems thanks to striker69
395 397 - fixed problem with errors on calling raw/raw_files/annotate functions
396 398 with unknown revisions
397 399 - fixed returned rawfiles attachment names with international character
398 400 - cleaned out docs, big thanks to Jason Harris
399 401
400 402 1.1.4 (**2011-02-19**)
401 403 ----------------------
402 404
403 405 news
404 406 ++++
405 407
406 408 fixes
407 409 +++++
408 410
409 411 - fixed formencode import problem on settings page, that caused server crash
410 412 when that page was accessed as first after server start
411 413 - journal fixes
412 414 - fixed option to access repository just by entering http://server/<repo_name>
413 415
414 416 1.1.3 (**2011-02-16**)
415 417 ----------------------
416 418
417 419 news
418 420 ++++
419 421
420 422 - implemented #102 allowing the '.' character in username
421 423 - added option to access repository just by entering http://server/<repo_name>
422 424 - celery task ignores result for better performance
423 425
424 426 fixes
425 427 +++++
426 428
427 429 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
428 430 apollo13 and Johan Walles
429 431 - small fixes in journal
430 432 - fixed problems with getting setting for celery from .ini files
431 433 - registration, password reset and login boxes share the same title as main
432 434 application now
433 435 - fixed #113: to high permissions to fork repository
434 436 - fixed problem with '[' chars in commit messages in journal
435 437 - removed issue with space inside renamed repository after deletion
436 438 - db transaction fixes when filesystem repository creation failed
437 439 - fixed #106 relation issues on databases different than sqlite
438 440 - fixed static files paths links to use of url() method
439 441
440 442 1.1.2 (**2011-01-12**)
441 443 ----------------------
442 444
443 445 news
444 446 ++++
445 447
446 448
447 449 fixes
448 450 +++++
449 451
450 452 - fixes #98 protection against float division of percentage stats
451 453 - fixed graph bug
452 454 - forced webhelpers version since it was making troubles during installation
453 455
454 456 1.1.1 (**2011-01-06**)
455 457 ----------------------
456 458
457 459 news
458 460 ++++
459 461
460 462 - added force https option into ini files for easier https usage (no need to
461 463 set server headers with this options)
462 464 - small css updates
463 465
464 466 fixes
465 467 +++++
466 468
467 469 - fixed #96 redirect loop on files view on repositories without changesets
468 470 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
469 471 and server crashed with errors
470 472 - fixed large tooltips problems on main page
471 473 - fixed #92 whoosh indexer is more error proof
472 474
473 475 1.1.0 (**2010-12-18**)
474 476 ----------------------
475 477
476 478 news
477 479 ++++
478 480
479 481 - rewrite of internals for vcs >=0.1.10
480 482 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
481 483 with older clients
482 484 - anonymous access, authentication via ldap
483 485 - performance upgrade for cached repos list - each repository has its own
484 486 cache that's invalidated when needed.
485 487 - performance upgrades on repositories with large amount of commits (20K+)
486 488 - main page quick filter for filtering repositories
487 489 - user dashboards with ability to follow chosen repositories actions
488 490 - sends email to admin on new user registration
489 491 - added cache/statistics reset options into repository settings
490 492 - more detailed action logger (based on hooks) with pushed changesets lists
491 493 and options to disable those hooks from admin panel
492 494 - introduced new enhanced changelog for merges that shows more accurate results
493 495 - new improved and faster code stats (based on pygments lexers mapping tables,
494 496 showing up to 10 trending sources for each repository. Additionally stats
495 497 can be disabled in repository settings.
496 498 - gui optimizations, fixed application width to 1024px
497 499 - added cut off (for large files/changesets) limit into config files
498 500 - whoosh, celeryd, upgrade moved to paster command
499 501 - other than sqlite database backends can be used
500 502
501 503 fixes
502 504 +++++
503 505
504 506 - fixes #61 forked repo was showing only after cache expired
505 507 - fixes #76 no confirmation on user deletes
506 508 - fixes #66 Name field misspelled
507 509 - fixes #72 block user removal when he owns repositories
508 510 - fixes #69 added password confirmation fields
509 511 - fixes #87 RhodeCode crashes occasionally on updating repository owner
510 512 - fixes #82 broken annotations on files with more than 1 blank line at the end
511 513 - a lot of fixes and tweaks for file browser
512 514 - fixed detached session issues
513 515 - fixed when user had no repos he would see all repos listed in my account
514 516 - fixed ui() instance bug when global hgrc settings was loaded for server
515 517 instance and all hgrc options were merged with our db ui() object
516 518 - numerous small bugfixes
517 519
518 520 (special thanks for TkSoh for detailed feedback)
519 521
520 522
521 523 1.0.2 (**2010-11-12**)
522 524 ----------------------
523 525
524 526 news
525 527 ++++
526 528
527 529 - tested under python2.7
528 530 - bumped sqlalchemy and celery versions
529 531
530 532 fixes
531 533 +++++
532 534
533 535 - fixed #59 missing graph.js
534 536 - fixed repo_size crash when repository had broken symlinks
535 537 - fixed python2.5 crashes.
536 538
537 539
538 540 1.0.1 (**2010-11-10**)
539 541 ----------------------
540 542
541 543 news
542 544 ++++
543 545
544 546 - small css updated
545 547
546 548 fixes
547 549 +++++
548 550
549 551 - fixed #53 python2.5 incompatible enumerate calls
550 552 - fixed #52 disable mercurial extension for web
551 553 - fixed #51 deleting repositories don't delete it's dependent objects
552 554
553 555
554 556 1.0.0 (**2010-11-02**)
555 557 ----------------------
556 558
557 559 - security bugfix simplehg wasn't checking for permissions on commands
558 560 other than pull or push.
559 561 - fixed doubled messages after push or pull in admin journal
560 562 - templating and css corrections, fixed repo switcher on chrome, updated titles
561 563 - admin menu accessible from options menu on repository view
562 564 - permissions cached queries
563 565
564 566 1.0.0rc4 (**2010-10-12**)
565 567 --------------------------
566 568
567 569 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
568 570 - removed cache_manager settings from sqlalchemy meta
569 571 - added sqlalchemy cache settings to ini files
570 572 - validated password length and added second try of failure on paster setup-app
571 573 - fixed setup database destroy prompt even when there was no db
572 574
573 575
574 576 1.0.0rc3 (**2010-10-11**)
575 577 -------------------------
576 578
577 579 - fixed i18n during installation.
578 580
579 581 1.0.0rc2 (**2010-10-11**)
580 582 -------------------------
581 583
582 584 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
583 585 occure. After vcs is fixed it'll be put back again.
584 586 - templating/css rewrites, optimized css. No newline at end of file
@@ -1,660 +1,656
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.api
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 API controller for RhodeCode
7 7
8 8 :created_on: Aug 20, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2011-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
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import traceback
29 29 import logging
30 30
31 31 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
32 32 from rhodecode.lib.auth import HasPermissionAllDecorator, \
33 33 HasPermissionAnyDecorator, PasswordGenerator
34 34
35 35 from rhodecode.model.meta import Session
36 36 from rhodecode.model.scm import ScmModel
37 37 from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
38 38 from rhodecode.model.repo import RepoModel
39 39 from rhodecode.model.user import UserModel
40 40 from rhodecode.model.users_group import UsersGroupModel
41 41 from rhodecode.model.repos_group import ReposGroupModel
42 from rhodecode.lib.utils import map_groups
42 43
43 44
44 45 log = logging.getLogger(__name__)
45 46
46 47
47 48 class ApiController(JSONRPCController):
48 49 """
49 50 API Controller
50 51
51 52
52 53 Each method needs to have USER as argument this is then based on given
53 54 API_KEY propagated as instance of user object
54 55
55 56 Preferably this should be first argument also
56 57
57 58
58 59 Each function should also **raise** JSONRPCError for any
59 60 errors that happens
60 61
61 62 """
62 63
63 64 @HasPermissionAllDecorator('hg.admin')
64 65 def pull(self, apiuser, repo_name):
65 66 """
66 67 Dispatch pull action on given repo
67 68
68 69
69 70 :param user:
70 71 :param repo_name:
71 72 """
72 73
73 74 if Repository.is_valid(repo_name) is False:
74 75 raise JSONRPCError('Unknown repo "%s"' % repo_name)
75 76
76 77 try:
77 78 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
78 79 return 'Pulled from %s' % repo_name
79 80 except Exception:
80 81 raise JSONRPCError('Unable to pull changes from "%s"' % repo_name)
81 82
82 83 @HasPermissionAllDecorator('hg.admin')
83 84 def get_user(self, apiuser, userid):
84 85 """"
85 86 Get a user by username
86 87
87 88 :param apiuser:
88 89 :param username:
89 90 """
90 91
91 92 user = UserModel().get_user(userid)
92 93 if user is None:
93 94 return user
94 95
95 96 return dict(
96 97 id=user.user_id,
97 98 username=user.username,
98 99 firstname=user.name,
99 100 lastname=user.lastname,
100 101 email=user.email,
101 102 active=user.active,
102 103 admin=user.admin,
103 104 ldap_dn=user.ldap_dn
104 105 )
105 106
106 107 @HasPermissionAllDecorator('hg.admin')
107 108 def get_users(self, apiuser):
108 109 """"
109 110 Get all users
110 111
111 112 :param apiuser:
112 113 """
113 114
114 115 result = []
115 116 for user in User.getAll():
116 117 result.append(
117 118 dict(
118 119 id=user.user_id,
119 120 username=user.username,
120 121 firstname=user.name,
121 122 lastname=user.lastname,
122 123 email=user.email,
123 124 active=user.active,
124 125 admin=user.admin,
125 126 ldap_dn=user.ldap_dn
126 127 )
127 128 )
128 129 return result
129 130
130 131 @HasPermissionAllDecorator('hg.admin')
131 132 def create_user(self, apiuser, username, email, password, firstname=None,
132 133 lastname=None, active=True, admin=False, ldap_dn=None):
133 134 """
134 135 Create new user
135 136
136 137 :param apiuser:
137 138 :param username:
138 139 :param password:
139 140 :param email:
140 141 :param name:
141 142 :param lastname:
142 143 :param active:
143 144 :param admin:
144 145 :param ldap_dn:
145 146 """
146 147 if User.get_by_username(username):
147 148 raise JSONRPCError("user %s already exist" % username)
148 149
149 150 if User.get_by_email(email, case_insensitive=True):
150 151 raise JSONRPCError("email %s already exist" % email)
151 152
152 153 if ldap_dn:
153 154 # generate temporary password if ldap_dn
154 155 password = PasswordGenerator().gen_password(length=8)
155 156
156 157 try:
157 158 usr = UserModel().create_or_update(
158 159 username, password, email, firstname,
159 160 lastname, active, admin, ldap_dn
160 161 )
161 162 Session.commit()
162 163 return dict(
163 164 id=usr.user_id,
164 165 msg='created new user %s' % username
165 166 )
166 167 except Exception:
167 168 log.error(traceback.format_exc())
168 169 raise JSONRPCError('failed to create user %s' % username)
169 170
170 171 @HasPermissionAllDecorator('hg.admin')
171 172 def update_user(self, apiuser, userid, username, password, email,
172 173 firstname, lastname, active, admin, ldap_dn):
173 174 """
174 175 Updates given user
175 176
176 177 :param apiuser:
177 178 :param username:
178 179 :param password:
179 180 :param email:
180 181 :param name:
181 182 :param lastname:
182 183 :param active:
183 184 :param admin:
184 185 :param ldap_dn:
185 186 """
186 187 if not UserModel().get_user(userid):
187 188 raise JSONRPCError("user %s does not exist" % username)
188 189
189 190 try:
190 191 usr = UserModel().create_or_update(
191 192 username, password, email, firstname,
192 193 lastname, active, admin, ldap_dn
193 194 )
194 195 Session.commit()
195 196 return dict(
196 197 id=usr.user_id,
197 198 msg='updated user %s' % username
198 199 )
199 200 except Exception:
200 201 log.error(traceback.format_exc())
201 202 raise JSONRPCError('failed to update user %s' % username)
202 203
203 204 @HasPermissionAllDecorator('hg.admin')
204 205 def get_users_group(self, apiuser, group_name):
205 206 """"
206 207 Get users group by name
207 208
208 209 :param apiuser:
209 210 :param group_name:
210 211 """
211 212
212 213 users_group = UsersGroup.get_by_group_name(group_name)
213 214 if not users_group:
214 215 return None
215 216
216 217 members = []
217 218 for user in users_group.members:
218 219 user = user.user
219 220 members.append(dict(id=user.user_id,
220 221 username=user.username,
221 222 firstname=user.name,
222 223 lastname=user.lastname,
223 224 email=user.email,
224 225 active=user.active,
225 226 admin=user.admin,
226 227 ldap=user.ldap_dn))
227 228
228 229 return dict(id=users_group.users_group_id,
229 230 group_name=users_group.users_group_name,
230 231 active=users_group.users_group_active,
231 232 members=members)
232 233
233 234 @HasPermissionAllDecorator('hg.admin')
234 235 def get_users_groups(self, apiuser):
235 236 """"
236 237 Get all users groups
237 238
238 239 :param apiuser:
239 240 """
240 241
241 242 result = []
242 243 for users_group in UsersGroup.getAll():
243 244 members = []
244 245 for user in users_group.members:
245 246 user = user.user
246 247 members.append(dict(id=user.user_id,
247 248 username=user.username,
248 249 firstname=user.name,
249 250 lastname=user.lastname,
250 251 email=user.email,
251 252 active=user.active,
252 253 admin=user.admin,
253 254 ldap=user.ldap_dn))
254 255
255 256 result.append(dict(id=users_group.users_group_id,
256 257 group_name=users_group.users_group_name,
257 258 active=users_group.users_group_active,
258 259 members=members))
259 260 return result
260 261
261 262 @HasPermissionAllDecorator('hg.admin')
262 263 def create_users_group(self, apiuser, group_name, active=True):
263 264 """
264 265 Creates an new usergroup
265 266
266 267 :param group_name:
267 268 :param active:
268 269 """
269 270
270 271 if self.get_users_group(apiuser, group_name):
271 272 raise JSONRPCError("users group %s already exist" % group_name)
272 273
273 274 try:
274 275 ug = UsersGroupModel().create(name=group_name, active=active)
275 276 Session.commit()
276 277 return dict(id=ug.users_group_id,
277 278 msg='created new users group %s' % group_name)
278 279 except Exception:
279 280 log.error(traceback.format_exc())
280 281 raise JSONRPCError('failed to create group %s' % group_name)
281 282
282 283 @HasPermissionAllDecorator('hg.admin')
283 284 def add_user_to_users_group(self, apiuser, group_name, username):
284 285 """"
285 286 Add a user to a group
286 287
287 288 :param apiuser:
288 289 :param group_name:
289 290 :param username:
290 291 """
291 292
292 293 try:
293 294 users_group = UsersGroup.get_by_group_name(group_name)
294 295 if not users_group:
295 296 raise JSONRPCError('unknown users group %s' % group_name)
296 297
297 298 user = User.get_by_username(username)
298 299 if user is None:
299 300 raise JSONRPCError('unknown user %s' % username)
300 301
301 302 ugm = UsersGroupModel().add_user_to_group(users_group, user)
302 303 success = True if ugm != True else False
303 304 msg = 'added member %s to users group %s' % (username, group_name)
304 305 msg = msg if success else 'User is already in that group'
305 306 Session.commit()
306 307
307 308 return dict(
308 309 id=ugm.users_group_member_id if ugm != True else None,
309 310 success=success,
310 311 msg=msg
311 312 )
312 313 except Exception:
313 314 log.error(traceback.format_exc())
314 315 raise JSONRPCError('failed to add users group member')
315 316
316 317 @HasPermissionAllDecorator('hg.admin')
317 318 def remove_user_from_users_group(self, apiuser, group_name, username):
318 319 """
319 320 Remove user from a group
320 321
321 322 :param apiuser
322 323 :param group_name
323 324 :param username
324 325 """
325 326
326 327 try:
327 328 users_group = UsersGroup.get_by_group_name(group_name)
328 329 if not users_group:
329 330 raise JSONRPCError('unknown users group %s' % group_name)
330 331
331 332 user = User.get_by_username(username)
332 333 if user is None:
333 334 raise JSONRPCError('unknown user %s' % username)
334 335
335 336 success = UsersGroupModel().remove_user_from_group(users_group, user)
336 337 msg = 'removed member %s from users group %s' % (username, group_name)
337 338 msg = msg if success else "User wasn't in group"
338 339 Session.commit()
339 340 return dict(success=success, msg=msg)
340 341 except Exception:
341 342 log.error(traceback.format_exc())
342 343 raise JSONRPCError('failed to remove user from group')
343 344
344 345 @HasPermissionAnyDecorator('hg.admin')
345 346 def get_repo(self, apiuser, repoid):
346 347 """"
347 348 Get repository by name
348 349
349 350 :param apiuser:
350 351 :param repo_name:
351 352 """
352 353
353 354 repo = RepoModel().get_repo(repoid)
354 355 if repo is None:
355 356 raise JSONRPCError('unknown repository %s' % repo)
356 357
357 358 members = []
358 359 for user in repo.repo_to_perm:
359 360 perm = user.permission.permission_name
360 361 user = user.user
361 362 members.append(
362 363 dict(
363 364 type_="user",
364 365 id=user.user_id,
365 366 username=user.username,
366 367 firstname=user.name,
367 368 lastname=user.lastname,
368 369 email=user.email,
369 370 active=user.active,
370 371 admin=user.admin,
371 372 ldap=user.ldap_dn,
372 373 permission=perm
373 374 )
374 375 )
375 376 for users_group in repo.users_group_to_perm:
376 377 perm = users_group.permission.permission_name
377 378 users_group = users_group.users_group
378 379 members.append(
379 380 dict(
380 381 type_="users_group",
381 382 id=users_group.users_group_id,
382 383 name=users_group.users_group_name,
383 384 active=users_group.users_group_active,
384 385 permission=perm
385 386 )
386 387 )
387 388
388 389 return dict(
389 390 id=repo.repo_id,
390 391 repo_name=repo.repo_name,
391 392 type=repo.repo_type,
392 393 description=repo.description,
393 394 members=members
394 395 )
395 396
396 397 @HasPermissionAnyDecorator('hg.admin')
397 398 def get_repos(self, apiuser):
398 399 """"
399 400 Get all repositories
400 401
401 402 :param apiuser:
402 403 """
403 404
404 405 result = []
405 406 for repository in Repository.getAll():
406 407 result.append(
407 408 dict(
408 409 id=repository.repo_id,
409 410 repo_name=repository.repo_name,
410 411 type=repository.repo_type,
411 412 description=repository.description
412 413 )
413 414 )
414 415 return result
415 416
416 417 @HasPermissionAnyDecorator('hg.admin')
417 418 def get_repo_nodes(self, apiuser, repo_name, revision, root_path,
418 419 ret_type='all'):
419 420 """
420 421 returns a list of nodes and it's children
421 422 for a given path at given revision. It's possible to specify ret_type
422 423 to show only files or dirs
423 424
424 425 :param apiuser:
425 426 :param repo_name: name of repository
426 427 :param revision: revision for which listing should be done
427 428 :param root_path: path from which start displaying
428 429 :param ret_type: return type 'all|files|dirs' nodes
429 430 """
430 431 try:
431 432 _d, _f = ScmModel().get_nodes(repo_name, revision, root_path,
432 433 flat=False)
433 434 _map = {
434 435 'all': _d + _f,
435 436 'files': _f,
436 437 'dirs': _d,
437 438 }
438 439 return _map[ret_type]
439 440 except KeyError:
440 441 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
441 442 except Exception, e:
442 443 raise JSONRPCError(e)
443 444
444 445 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
445 446 def create_repo(self, apiuser, repo_name, owner_name, description='',
446 447 repo_type='hg', private=False, clone_uri=None):
447 448 """
448 449 Create repository, if clone_url is given it makes a remote clone
449 450
450 451 :param apiuser:
451 452 :param repo_name:
452 453 :param owner_name:
453 454 :param description:
454 455 :param repo_type:
455 456 :param private:
456 457 :param clone_uri:
457 458 """
458 459
459 460 try:
460 461 owner = User.get_by_username(owner_name)
461 462 if owner is None:
462 463 raise JSONRPCError('unknown user %s' % owner_name)
463 464
464 465 if Repository.get_by_repo_name(repo_name):
465 466 raise JSONRPCError("repo %s already exist" % repo_name)
466 467
467 groups = repo_name.split('/')
468 groups = repo_name.split(Repository.url_sep())
468 469 real_name = groups[-1]
469 groups = groups[:-1]
470 parent_id = None
471 for g in groups:
472 group = RepoGroup.get_by_group_name(g)
473 if not group:
474 group = ReposGroupModel().create(g, '', parent_id)
475 parent_id = group.group_id
470 # create structure of groups
471 group = map_groups(repo_name)
476 472
477 473 repo = RepoModel().create(
478 474 dict(
479 475 repo_name=real_name,
480 476 repo_name_full=repo_name,
481 477 description=description,
482 478 private=private,
483 479 repo_type=repo_type,
484 repo_group=parent_id,
480 repo_group=group.group_id if group else None,
485 481 clone_uri=clone_uri
486 482 ),
487 483 owner
488 484 )
489 485 Session.commit()
490 486
491 487 return dict(
492 488 id=repo.repo_id,
493 489 msg="Created new repository %s" % repo.repo_name
494 490 )
495 491
496 492 except Exception:
497 493 log.error(traceback.format_exc())
498 494 raise JSONRPCError('failed to create repository %s' % repo_name)
499 495
500 496 @HasPermissionAnyDecorator('hg.admin')
501 497 def delete_repo(self, apiuser, repo_name):
502 498 """
503 499 Deletes a given repository
504 500
505 501 :param repo_name:
506 502 """
507 503 if not Repository.get_by_repo_name(repo_name):
508 504 raise JSONRPCError("repo %s does not exist" % repo_name)
509 505 try:
510 506 RepoModel().delete(repo_name)
511 507 Session.commit()
512 508 return dict(
513 509 msg='Deleted repository %s' % repo_name
514 510 )
515 511 except Exception:
516 512 log.error(traceback.format_exc())
517 513 raise JSONRPCError('failed to delete repository %s' % repo_name)
518 514
519 515 @HasPermissionAnyDecorator('hg.admin')
520 516 def grant_user_permission(self, apiuser, repo_name, username, perm):
521 517 """
522 518 Grant permission for user on given repository, or update existing one
523 519 if found
524 520
525 521 :param repo_name:
526 522 :param username:
527 523 :param perm:
528 524 """
529 525
530 526 try:
531 527 repo = Repository.get_by_repo_name(repo_name)
532 528 if repo is None:
533 529 raise JSONRPCError('unknown repository %s' % repo)
534 530
535 531 user = User.get_by_username(username)
536 532 if user is None:
537 533 raise JSONRPCError('unknown user %s' % username)
538 534
539 535 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
540 536
541 537 Session.commit()
542 538 return dict(
543 539 msg='Granted perm: %s for user: %s in repo: %s' % (
544 540 perm, username, repo_name
545 541 )
546 542 )
547 543 except Exception:
548 544 log.error(traceback.format_exc())
549 545 raise JSONRPCError(
550 546 'failed to edit permission %(repo)s for %(user)s' % dict(
551 547 user=username, repo=repo_name
552 548 )
553 549 )
554 550
555 551 @HasPermissionAnyDecorator('hg.admin')
556 552 def revoke_user_permission(self, apiuser, repo_name, username):
557 553 """
558 554 Revoke permission for user on given repository
559 555
560 556 :param repo_name:
561 557 :param username:
562 558 """
563 559
564 560 try:
565 561 repo = Repository.get_by_repo_name(repo_name)
566 562 if repo is None:
567 563 raise JSONRPCError('unknown repository %s' % repo)
568 564
569 565 user = User.get_by_username(username)
570 566 if user is None:
571 567 raise JSONRPCError('unknown user %s' % username)
572 568
573 569 RepoModel().revoke_user_permission(repo=repo_name, user=username)
574 570
575 571 Session.commit()
576 572 return dict(
577 573 msg='Revoked perm for user: %s in repo: %s' % (
578 574 username, repo_name
579 575 )
580 576 )
581 577 except Exception:
582 578 log.error(traceback.format_exc())
583 579 raise JSONRPCError(
584 580 'failed to edit permission %(repo)s for %(user)s' % dict(
585 581 user=username, repo=repo_name
586 582 )
587 583 )
588 584
589 585 @HasPermissionAnyDecorator('hg.admin')
590 586 def grant_users_group_permission(self, apiuser, repo_name, group_name, perm):
591 587 """
592 588 Grant permission for users group on given repository, or update
593 589 existing one if found
594 590
595 591 :param repo_name:
596 592 :param group_name:
597 593 :param perm:
598 594 """
599 595
600 596 try:
601 597 repo = Repository.get_by_repo_name(repo_name)
602 598 if repo is None:
603 599 raise JSONRPCError('unknown repository %s' % repo)
604 600
605 601 user_group = UsersGroup.get_by_group_name(group_name)
606 602 if user_group is None:
607 603 raise JSONRPCError('unknown users group %s' % user_group)
608 604
609 605 RepoModel().grant_users_group_permission(repo=repo_name,
610 606 group_name=group_name,
611 607 perm=perm)
612 608
613 609 Session.commit()
614 610 return dict(
615 611 msg='Granted perm: %s for group: %s in repo: %s' % (
616 612 perm, group_name, repo_name
617 613 )
618 614 )
619 615 except Exception:
620 616 log.error(traceback.format_exc())
621 617 raise JSONRPCError(
622 618 'failed to edit permission %(repo)s for %(usersgr)s' % dict(
623 619 usersgr=group_name, repo=repo_name
624 620 )
625 621 )
626 622
627 623 @HasPermissionAnyDecorator('hg.admin')
628 624 def revoke_users_group_permission(self, apiuser, repo_name, group_name):
629 625 """
630 626 Revoke permission for users group on given repository
631 627
632 628 :param repo_name:
633 629 :param group_name:
634 630 """
635 631
636 632 try:
637 633 repo = Repository.get_by_repo_name(repo_name)
638 634 if repo is None:
639 635 raise JSONRPCError('unknown repository %s' % repo)
640 636
641 637 user_group = UsersGroup.get_by_group_name(group_name)
642 638 if user_group is None:
643 639 raise JSONRPCError('unknown users group %s' % user_group)
644 640
645 641 RepoModel().revoke_users_group_permission(repo=repo_name,
646 642 group_name=group_name)
647 643
648 644 Session.commit()
649 645 return dict(
650 646 msg='Revoked perm for group: %s in repo: %s' % (
651 647 group_name, repo_name
652 648 )
653 649 )
654 650 except Exception:
655 651 log.error(traceback.format_exc())
656 652 raise JSONRPCError(
657 653 'failed to edit permission %(repo)s for %(usersgr)s' % dict(
658 654 usersgr=group_name, repo=repo_name
659 655 )
660 656 )
@@ -1,662 +1,659
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Utilities library for RhodeCode
7 7
8 8 :created_on: Apr 18, 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 os
27 27 import re
28 28 import logging
29 29 import datetime
30 30 import traceback
31 31 import paste
32 32 import beaker
33 33 import tarfile
34 34 import shutil
35 35 from os.path import abspath
36 36 from os.path import dirname as dn, join as jn
37 37
38 38 from paste.script.command import Command, BadCommand
39 39
40 40 from mercurial import ui, config
41 41
42 42 from webhelpers.text import collapse, remove_formatting, strip_tags
43 43
44 44 from rhodecode.lib.vcs import get_backend
45 45 from rhodecode.lib.vcs.backends.base import BaseChangeset
46 46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 47 from rhodecode.lib.vcs.utils.helpers import get_scm
48 48 from rhodecode.lib.vcs.exceptions import VCSError
49 49
50 50 from rhodecode.lib.caching_query import FromCache
51 51
52 52 from rhodecode.model import meta
53 53 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
54 54 UserLog, RepoGroup, RhodeCodeSetting, UserRepoGroupToPerm
55 55 from rhodecode.model.meta import Session
56 56 from rhodecode.model.repos_group import ReposGroupModel
57 57 from rhodecode.lib.utils2 import safe_str, safe_unicode
58 58 from rhodecode.lib.vcs.utils.fakemod import create_module
59 59
60 60 log = logging.getLogger(__name__)
61 61
62 62 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
63 63
64 64
65 65 def recursive_replace(str_, replace=' '):
66 66 """
67 67 Recursive replace of given sign to just one instance
68 68
69 69 :param str_: given string
70 70 :param replace: char to find and replace multiple instances
71 71
72 72 Examples::
73 73 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
74 74 'Mighty-Mighty-Bo-sstones'
75 75 """
76 76
77 77 if str_.find(replace * 2) == -1:
78 78 return str_
79 79 else:
80 80 str_ = str_.replace(replace * 2, replace)
81 81 return recursive_replace(str_, replace)
82 82
83 83
84 84 def repo_name_slug(value):
85 85 """
86 86 Return slug of name of repository
87 87 This function is called on each creation/modification
88 88 of repository to prevent bad names in repo
89 89 """
90 90
91 91 slug = remove_formatting(value)
92 92 slug = strip_tags(slug)
93 93
94 94 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
95 95 slug = slug.replace(c, '-')
96 96 slug = recursive_replace(slug, '-')
97 97 slug = collapse(slug, '-')
98 98 return slug
99 99
100 100
101 101 def get_repo_slug(request):
102 102 _repo = request.environ['pylons.routes_dict'].get('repo_name')
103 103 if _repo:
104 104 _repo = _repo.rstrip('/')
105 105 return _repo
106 106
107 107
108 108 def get_repos_group_slug(request):
109 109 _group = request.environ['pylons.routes_dict'].get('group_name')
110 110 if _group:
111 111 _group = _group.rstrip('/')
112 112 return _group
113 113
114 114
115 115 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
116 116 """
117 117 Action logger for various actions made by users
118 118
119 119 :param user: user that made this action, can be a unique username string or
120 120 object containing user_id attribute
121 121 :param action: action to log, should be on of predefined unique actions for
122 122 easy translations
123 123 :param repo: string name of repository or object containing repo_id,
124 124 that action was made on
125 125 :param ipaddr: optional ip address from what the action was made
126 126 :param sa: optional sqlalchemy session
127 127
128 128 """
129 129
130 130 if not sa:
131 131 sa = meta.Session
132 132
133 133 try:
134 134 if hasattr(user, 'user_id'):
135 135 user_obj = user
136 136 elif isinstance(user, basestring):
137 137 user_obj = User.get_by_username(user)
138 138 else:
139 139 raise Exception('You have to provide user object or username')
140 140
141 141 if hasattr(repo, 'repo_id'):
142 142 repo_obj = Repository.get(repo.repo_id)
143 143 repo_name = repo_obj.repo_name
144 144 elif isinstance(repo, basestring):
145 145 repo_name = repo.lstrip('/')
146 146 repo_obj = Repository.get_by_repo_name(repo_name)
147 147 else:
148 148 raise Exception('You have to provide repository to action logger')
149 149
150 150 user_log = UserLog()
151 151 user_log.user_id = user_obj.user_id
152 152 user_log.action = action
153 153
154 154 user_log.repository_id = repo_obj.repo_id
155 155 user_log.repository_name = repo_name
156 156
157 157 user_log.action_date = datetime.datetime.now()
158 158 user_log.user_ip = ipaddr
159 159 sa.add(user_log)
160 160
161 161 log.info(
162 162 'Adding user %s, action %s on %s' % (user_obj, action,
163 163 safe_unicode(repo))
164 164 )
165 165 if commit:
166 166 sa.commit()
167 167 except:
168 168 log.error(traceback.format_exc())
169 169 raise
170 170
171 171
172 172 def get_repos(path, recursive=False):
173 173 """
174 174 Scans given path for repos and return (name,(type,path)) tuple
175 175
176 176 :param path: path to scan for repositories
177 177 :param recursive: recursive search and return names with subdirs in front
178 178 """
179 179
180 180 # remove ending slash for better results
181 181 path = path.rstrip(os.sep)
182 182
183 183 def _get_repos(p):
184 184 if not os.access(p, os.W_OK):
185 185 return
186 186 for dirpath in os.listdir(p):
187 187 if os.path.isfile(os.path.join(p, dirpath)):
188 188 continue
189 189 cur_path = os.path.join(p, dirpath)
190 190 try:
191 191 scm_info = get_scm(cur_path)
192 192 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
193 193 except VCSError:
194 194 if not recursive:
195 195 continue
196 196 #check if this dir containts other repos for recursive scan
197 197 rec_path = os.path.join(p, dirpath)
198 198 if os.path.isdir(rec_path):
199 199 for inner_scm in _get_repos(rec_path):
200 200 yield inner_scm
201 201
202 202 return _get_repos(path)
203 203
204 204
205 205 def is_valid_repo(repo_name, base_path):
206 206 """
207 207 Returns True if given path is a valid repository False otherwise
208 208
209 209 :param repo_name:
210 210 :param base_path:
211 211
212 212 :return True: if given path is a valid repository
213 213 """
214 214 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
215 215
216 216 try:
217 217 get_scm(full_path)
218 218 return True
219 219 except VCSError:
220 220 return False
221 221
222 222
223 223 def is_valid_repos_group(repos_group_name, base_path):
224 224 """
225 225 Returns True if given path is a repos group False otherwise
226 226
227 227 :param repo_name:
228 228 :param base_path:
229 229 """
230 230 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
231 231
232 232 # check if it's not a repo
233 233 if is_valid_repo(repos_group_name, base_path):
234 234 return False
235 235
236 236 # check if it's a valid path
237 237 if os.path.isdir(full_path):
238 238 return True
239 239
240 240 return False
241 241
242 242
243 243 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
244 244 while True:
245 245 ok = raw_input(prompt)
246 246 if ok in ('y', 'ye', 'yes'):
247 247 return True
248 248 if ok in ('n', 'no', 'nop', 'nope'):
249 249 return False
250 250 retries = retries - 1
251 251 if retries < 0:
252 252 raise IOError
253 253 print complaint
254 254
255 255 #propagated from mercurial documentation
256 256 ui_sections = ['alias', 'auth',
257 257 'decode/encode', 'defaults',
258 258 'diff', 'email',
259 259 'extensions', 'format',
260 260 'merge-patterns', 'merge-tools',
261 261 'hooks', 'http_proxy',
262 262 'smtp', 'patch',
263 263 'paths', 'profiling',
264 264 'server', 'trusted',
265 265 'ui', 'web', ]
266 266
267 267
268 268 def make_ui(read_from='file', path=None, checkpaths=True):
269 269 """
270 270 A function that will read python rc files or database
271 271 and make an mercurial ui object from read options
272 272
273 273 :param path: path to mercurial config file
274 274 :param checkpaths: check the path
275 275 :param read_from: read from 'file' or 'db'
276 276 """
277 277
278 278 baseui = ui.ui()
279 279
280 280 # clean the baseui object
281 281 baseui._ocfg = config.config()
282 282 baseui._ucfg = config.config()
283 283 baseui._tcfg = config.config()
284 284
285 285 if read_from == 'file':
286 286 if not os.path.isfile(path):
287 287 log.debug('hgrc file is not present at %s skipping...' % path)
288 288 return False
289 289 log.debug('reading hgrc from %s' % path)
290 290 cfg = config.config()
291 291 cfg.read(path)
292 292 for section in ui_sections:
293 293 for k, v in cfg.items(section):
294 294 log.debug('settings ui from file[%s]%s:%s' % (section, k, v))
295 295 baseui.setconfig(section, k, v)
296 296
297 297 elif read_from == 'db':
298 298 sa = meta.Session
299 299 ret = sa.query(RhodeCodeUi)\
300 300 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
301 301 .all()
302 302
303 303 hg_ui = ret
304 304 for ui_ in hg_ui:
305 305 if ui_.ui_active:
306 306 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
307 307 ui_.ui_key, ui_.ui_value)
308 308 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
309 309
310 310 meta.Session.remove()
311 311 return baseui
312 312
313 313
314 314 def set_rhodecode_config(config):
315 315 """
316 316 Updates pylons config with new settings from database
317 317
318 318 :param config:
319 319 """
320 320 hgsettings = RhodeCodeSetting.get_app_settings()
321 321
322 322 for k, v in hgsettings.items():
323 323 config[k] = v
324 324
325 325
326 326 def invalidate_cache(cache_key, *args):
327 327 """
328 328 Puts cache invalidation task into db for
329 329 further global cache invalidation
330 330 """
331 331
332 332 from rhodecode.model.scm import ScmModel
333 333
334 334 if cache_key.startswith('get_repo_cached_'):
335 335 name = cache_key.split('get_repo_cached_')[-1]
336 336 ScmModel().mark_for_invalidation(name)
337 337
338 338
339 339 class EmptyChangeset(BaseChangeset):
340 340 """
341 341 An dummy empty changeset. It's possible to pass hash when creating
342 342 an EmptyChangeset
343 343 """
344 344
345 345 def __init__(self, cs='0' * 40, repo=None, requested_revision=None,
346 346 alias=None):
347 347 self._empty_cs = cs
348 348 self.revision = -1
349 349 self.message = ''
350 350 self.author = ''
351 351 self.date = ''
352 352 self.repository = repo
353 353 self.requested_revision = requested_revision
354 354 self.alias = alias
355 355
356 356 @LazyProperty
357 357 def raw_id(self):
358 358 """
359 359 Returns raw string identifying this changeset, useful for web
360 360 representation.
361 361 """
362 362
363 363 return self._empty_cs
364 364
365 365 @LazyProperty
366 366 def branch(self):
367 367 return get_backend(self.alias).DEFAULT_BRANCH_NAME
368 368
369 369 @LazyProperty
370 370 def short_id(self):
371 371 return self.raw_id[:12]
372 372
373 373 def get_file_changeset(self, path):
374 374 return self
375 375
376 376 def get_file_content(self, path):
377 377 return u''
378 378
379 379 def get_file_size(self, path):
380 380 return 0
381 381
382 382
383 def map_groups(groups):
383 def map_groups(path):
384 384 """
385 Checks for groups existence, and creates groups structures.
386 It returns last group in structure
385 Given a full path to a repository, create all nested groups that this
386 repo is inside. This function creates parent-child relationships between
387 groups and creates default perms for all new groups.
387 388
388 :param groups: list of groups structure
389 :param paths: full path to repository
389 390 """
390 391 sa = meta.Session
391
392 groups = path.split(Repository.url_sep())
392 393 parent = None
393 394 group = None
394 395
395 396 # last element is repo in nested groups structure
396 397 groups = groups[:-1]
397 398 rgm = ReposGroupModel(sa)
398 399 for lvl, group_name in enumerate(groups):
399 400 group_name = '/'.join(groups[:lvl] + [group_name])
400 401 group = RepoGroup.get_by_group_name(group_name)
401 402 desc = '%s group' % group_name
402 403
403 # # WTF that doesn't work !?
404 # if group is None:
405 # group = rgm.create(group_name, desc, parent, just_db=True)
406 # sa.commit()
407
408 404 # skip folders that are now removed repos
409 405 if REMOVED_REPO_PAT.match(group_name):
410 406 break
411 407
412 408 if group is None:
413 log.debug('creating group level: %s group_name: %s' % (lvl, group_name))
409 log.debug('creating group level: %s group_name: %s' % (lvl,
410 group_name))
414 411 group = RepoGroup(group_name, parent)
415 412 group.group_description = desc
416 413 sa.add(group)
417 414 rgm._create_default_perms(group)
418 sa.commit()
415 sa.flush()
419 416 parent = group
420 417 return group
421 418
422 419
423 420 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
424 421 """
425 422 maps all repos given in initial_repo_list, non existing repositories
426 423 are created, if remove_obsolete is True it also check for db entries
427 424 that are not in initial_repo_list and removes them.
428 425
429 426 :param initial_repo_list: list of repositories found by scanning methods
430 427 :param remove_obsolete: check for obsolete entries in database
431 428 """
432 429 from rhodecode.model.repo import RepoModel
433 430 sa = meta.Session
434 431 rm = RepoModel()
435 432 user = sa.query(User).filter(User.admin == True).first()
436 433 if user is None:
437 434 raise Exception('Missing administrative account !')
438 435 added = []
439 436
440 437 for name, repo in initial_repo_list.items():
441 group = map_groups(name.split(Repository.url_sep()))
438 group = map_groups(name)
442 439 if not rm.get_by_repo_name(name, cache=False):
443 440 log.info('repository %s not found creating default' % name)
444 441 added.append(name)
445 442 form_data = {
446 443 'repo_name': name,
447 444 'repo_name_full': name,
448 445 'repo_type': repo.alias,
449 446 'description': repo.description \
450 447 if repo.description != 'unknown' else '%s repository' % name,
451 448 'private': False,
452 449 'group_id': getattr(group, 'group_id', None)
453 450 }
454 451 rm.create(form_data, user, just_db=True)
455 452 sa.commit()
456 453 removed = []
457 454 if remove_obsolete:
458 455 #remove from database those repositories that are not in the filesystem
459 456 for repo in sa.query(Repository).all():
460 457 if repo.repo_name not in initial_repo_list.keys():
461 458 removed.append(repo.repo_name)
462 459 sa.delete(repo)
463 460 sa.commit()
464 461
465 462 return added, removed
466 463
467 464
468 465 # set cache regions for beaker so celery can utilise it
469 466 def add_cache(settings):
470 467 cache_settings = {'regions': None}
471 468 for key in settings.keys():
472 469 for prefix in ['beaker.cache.', 'cache.']:
473 470 if key.startswith(prefix):
474 471 name = key.split(prefix)[1].strip()
475 472 cache_settings[name] = settings[key].strip()
476 473 if cache_settings['regions']:
477 474 for region in cache_settings['regions'].split(','):
478 475 region = region.strip()
479 476 region_settings = {}
480 477 for key, value in cache_settings.items():
481 478 if key.startswith(region):
482 479 region_settings[key.split('.')[1]] = value
483 480 region_settings['expire'] = int(region_settings.get('expire',
484 481 60))
485 482 region_settings.setdefault('lock_dir',
486 483 cache_settings.get('lock_dir'))
487 484 region_settings.setdefault('data_dir',
488 485 cache_settings.get('data_dir'))
489 486
490 487 if 'type' not in region_settings:
491 488 region_settings['type'] = cache_settings.get('type',
492 489 'memory')
493 490 beaker.cache.cache_regions[region] = region_settings
494 491
495 492
496 493 def load_rcextensions(root_path):
497 494 import rhodecode
498 495 from rhodecode.config import conf
499 496
500 497 path = os.path.join(root_path, 'rcextensions', '__init__.py')
501 498 if os.path.isfile(path):
502 499 rcext = create_module('rc', path)
503 500 EXT = rhodecode.EXTENSIONS = rcext
504 501 log.debug('Found rcextensions now loading %s...' % rcext)
505 502
506 503 # Additional mappings that are not present in the pygments lexers
507 504 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
508 505
509 506 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
510 507
511 508 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
512 509 log.debug('settings custom INDEX_EXTENSIONS')
513 510 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
514 511
515 512 #ADDITIONAL MAPPINGS
516 513 log.debug('adding extra into INDEX_EXTENSIONS')
517 514 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
518 515
519 516
520 517 #==============================================================================
521 518 # TEST FUNCTIONS AND CREATORS
522 519 #==============================================================================
523 520 def create_test_index(repo_location, config, full_index):
524 521 """
525 522 Makes default test index
526 523
527 524 :param config: test config
528 525 :param full_index:
529 526 """
530 527
531 528 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
532 529 from rhodecode.lib.pidlock import DaemonLock, LockHeld
533 530
534 531 repo_location = repo_location
535 532
536 533 index_location = os.path.join(config['app_conf']['index_dir'])
537 534 if not os.path.exists(index_location):
538 535 os.makedirs(index_location)
539 536
540 537 try:
541 538 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
542 539 WhooshIndexingDaemon(index_location=index_location,
543 540 repo_location=repo_location)\
544 541 .run(full_index=full_index)
545 542 l.release()
546 543 except LockHeld:
547 544 pass
548 545
549 546
550 547 def create_test_env(repos_test_path, config):
551 548 """
552 549 Makes a fresh database and
553 550 install test repository into tmp dir
554 551 """
555 552 from rhodecode.lib.db_manage import DbManage
556 553 from rhodecode.tests import HG_REPO, TESTS_TMP_PATH
557 554
558 555 # PART ONE create db
559 556 dbconf = config['sqlalchemy.db1.url']
560 557 log.debug('making test db %s' % dbconf)
561 558
562 559 # create test dir if it doesn't exist
563 560 if not os.path.isdir(repos_test_path):
564 561 log.debug('Creating testdir %s' % repos_test_path)
565 562 os.makedirs(repos_test_path)
566 563
567 564 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
568 565 tests=True)
569 566 dbmanage.create_tables(override=True)
570 567 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
571 568 dbmanage.create_default_user()
572 569 dbmanage.admin_prompt()
573 570 dbmanage.create_permissions()
574 571 dbmanage.populate_default_permissions()
575 572 Session.commit()
576 573 # PART TWO make test repo
577 574 log.debug('making test vcs repositories')
578 575
579 576 idx_path = config['app_conf']['index_dir']
580 577 data_path = config['app_conf']['cache_dir']
581 578
582 579 #clean index and data
583 580 if idx_path and os.path.exists(idx_path):
584 581 log.debug('remove %s' % idx_path)
585 582 shutil.rmtree(idx_path)
586 583
587 584 if data_path and os.path.exists(data_path):
588 585 log.debug('remove %s' % data_path)
589 586 shutil.rmtree(data_path)
590 587
591 588 #CREATE DEFAULT HG REPOSITORY
592 589 cur_dir = dn(dn(abspath(__file__)))
593 590 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
594 591 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
595 592 tar.close()
596 593
597 594
598 595 #==============================================================================
599 596 # PASTER COMMANDS
600 597 #==============================================================================
601 598 class BasePasterCommand(Command):
602 599 """
603 600 Abstract Base Class for paster commands.
604 601
605 602 The celery commands are somewhat aggressive about loading
606 603 celery.conf, and since our module sets the `CELERY_LOADER`
607 604 environment variable to our loader, we have to bootstrap a bit and
608 605 make sure we've had a chance to load the pylons config off of the
609 606 command line, otherwise everything fails.
610 607 """
611 608 min_args = 1
612 609 min_args_error = "Please provide a paster config file as an argument."
613 610 takes_config_file = 1
614 611 requires_config_file = True
615 612
616 613 def notify_msg(self, msg, log=False):
617 614 """Make a notification to user, additionally if logger is passed
618 615 it logs this action using given logger
619 616
620 617 :param msg: message that will be printed to user
621 618 :param log: logging instance, to use to additionally log this message
622 619
623 620 """
624 621 if log and isinstance(log, logging):
625 622 log(msg)
626 623
627 624 def run(self, args):
628 625 """
629 626 Overrides Command.run
630 627
631 628 Checks for a config file argument and loads it.
632 629 """
633 630 if len(args) < self.min_args:
634 631 raise BadCommand(
635 632 self.min_args_error % {'min_args': self.min_args,
636 633 'actual_args': len(args)})
637 634
638 635 # Decrement because we're going to lob off the first argument.
639 636 # @@ This is hacky
640 637 self.min_args -= 1
641 638 self.bootstrap_config(args[0])
642 639 self.update_parser()
643 640 return super(BasePasterCommand, self).run(args[1:])
644 641
645 642 def update_parser(self):
646 643 """
647 644 Abstract method. Allows for the class's parser to be updated
648 645 before the superclass's `run` method is called. Necessary to
649 646 allow options/arguments to be passed through to the underlying
650 647 celery command.
651 648 """
652 649 raise NotImplementedError("Abstract Method.")
653 650
654 651 def bootstrap_config(self, conf):
655 652 """
656 653 Loads the pylons configuration.
657 654 """
658 655 from pylons import config as pylonsconfig
659 656
660 657 self.path_to_ini_file = os.path.realpath(conf)
661 658 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
662 659 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
General Comments 0
You need to be logged in to leave comments. Login now