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