##// END OF EJS Templates
merge upstream
Aras Pranckevicius -
r1861:3f5be4db merge beta
parent child Browse files
Show More
@@ -1,6 +1,6 b''
1 =================================================
1 ========================
2 Welcome to RhodeCode (RhodiumCode) documentation!
2 RhodeCode documentation!
3 =================================================
3 ========================
4
4
5 ``RhodeCode`` is a fast and powerful management tool for Mercurial_ and GIT_
5 ``RhodeCode`` is a fast and powerful management tool for Mercurial_ and GIT_
6 with a built in push/pull server and full text search.
6 with a built in push/pull server and full text search.
@@ -102,7 +102,6 b' Incoming / Plans'
102 - pull requests and web based merges
102 - pull requests and web based merges
103 - per line file history
103 - per line file history
104 - SSH based authentication with server side key management
104 - SSH based authentication with server side key management
105 - Redmine and other bugtrackers integration
106 - Commit based built in wiki system
105 - Commit based built in wiki system
107 - More statistics and graph (global annotation + some more statistics)
106 - More statistics and graph (global annotation + some more statistics)
108 - Other advancements as development continues (or you can of course make
107 - Other advancements as development continues (or you can of course make
@@ -58,13 +58,34 b' container_auth_enabled = false'
58 proxypass_auth_enabled = false
58 proxypass_auth_enabled = false
59
59
60 ## overwrite schema of clone url
60 ## overwrite schema of clone url
61 # available vars:
61 ## available vars:
62 # scheme - http/https
62 ## scheme - http/https
63 # user - current user
63 ## user - current user
64 # pass - password
64 ## pass - password
65 # netloc - network location
65 ## netloc - network location
66 # path - usually repo_name
66 ## path - usually repo_name
67 # clone_uri = {scheme}://{user}{pass}{netloc}{path}
67
68 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
69
70 ## issue tracking mapping for commits messages
71 ## uncomment url_pat, issue_server, issue_prefix to enable
72
73
74 ## pattern to get the issues from commit messages
75 ## default one used here is #1234
76
77 #url_pat = (?:^#|\s#)(\w+)
78
79 ## server url to the issue, each {id} will be replaced with id
80 ## fetched from the regex
81
82 #issue_server = https://myissueserver.com/issue/{id}
83
84 ## prefix to add to link to indicate it's an url
85 ## #314 will be replaced by <issue_prefix><id>
86
87 #issue_prefix = #
88
68
89
69 ####################################
90 ####################################
70 ### CELERY CONFIG ####
91 ### CELERY CONFIG ####
@@ -10,6 +10,8 b" There's a single schema for calling all "
10 with JSON protocol both ways. An url to send API request in RhodeCode is
10 with JSON protocol both ways. An url to send API request in RhodeCode is
11 <your_server>/_admin/api
11 <your_server>/_admin/api
12
12
13 API ACCESS FOR WEB VIEWS
14 ++++++++++++++++++++++++
13
15
14 API access can also be turned on for each view decorated with `@LoginRequired`
16 API access can also be turned on for each view decorated with `@LoginRequired`
15 decorator. To enable API access simple change standard login decorator into
17 decorator. To enable API access simple change standard login decorator into
@@ -18,6 +20,9 b' by adding a GET parameter to url `?api_k'
18 enabled on RSS/ATOM feed views.
20 enabled on RSS/ATOM feed views.
19
21
20
22
23 API ACCESS
24 ++++++++++
25
21 All clients are required to send JSON-RPC spec JSON data::
26 All clients are required to send JSON-RPC spec JSON data::
22
27
23 {
28 {
@@ -69,15 +74,47 b' INPUT::'
69 api_key : "<api_key>"
74 api_key : "<api_key>"
70 method : "pull"
75 method : "pull"
71 args : {
76 args : {
72 "repo" : "<repo_name>"
77 "repo_name" : "<reponame>"
73 }
78 }
74
79
75 OUTPUT::
80 OUTPUT::
76
81
77 result : "Pulled from <repo_name>"
82 result : "Pulled from <reponame>"
78 error : null
83 error : null
79
84
80
85
86 get_user
87 --------
88
89 Get's an user by username, Returns empty result if user is not found.
90 This command can be executed only using api_key belonging to user with admin
91 rights.
92
93 INPUT::
94
95 api_key : "<api_key>"
96 method : "get_user"
97 args : {
98 "username" : "<username>"
99 }
100
101 OUTPUT::
102
103 result: None if user does not exist or
104 {
105 "id" : "<id>",
106 "username" : "<username>",
107 "firstname": "<firstname>",
108 "lastname" : "<lastname>",
109 "email" : "<email>",
110 "active" : "<bool>",
111 "admin" :  "<bool>",
112 "ldap" : "<ldap_dn>"
113 }
114
115 error: null
116
117
81 get_users
118 get_users
82 ---------
119 ---------
83
120
@@ -131,46 +168,11 b' INPUT::'
131 OUTPUT::
168 OUTPUT::
132
169
133 result: {
170 result: {
171 "id" : "<new_user_id>",
134 "msg" : "created new user <username>"
172 "msg" : "created new user <username>"
135 }
173 }
136 error: null
174 error: null
137
175
138 get_users_groups
139 ----------------
140
141 Lists all existing users groups. This command can be executed only using api_key
142 belonging to user with admin rights.
143
144 INPUT::
145
146 api_key : "<api_key>"
147 method : "get_users_groups"
148 args : { }
149
150 OUTPUT::
151
152 result : [
153 {
154 "id" : "<id>",
155 "name" : "<name>",
156 "active": "<bool>",
157 "members" : [
158 {
159 "id" : "<userid>",
160 "username" : "<username>",
161 "firstname": "<firstname>",
162 "lastname" : "<lastname>",
163 "email" : "<email>",
164 "active" : "<bool>",
165 "admin" :  "<bool>",
166 "ldap" : "<ldap_dn>"
167 },
168
169 ]
170 }
171 ]
172 error : null
173
174 get_users_group
176 get_users_group
175 ---------------
177 ---------------
176
178
@@ -189,24 +191,61 b' OUTPUT::'
189
191
190 result : None if group not exist
192 result : None if group not exist
191 {
193 {
192 "id" : "<id>",
194 "id" : "<id>",
193 "name" : "<name>",
195 "group_name" : "<groupname>",
194 "active": "<bool>",
196 "active": "<bool>",
195 "members" : [
197 "members" : [
196 { "id" : "<userid>",
198 { "id" : "<userid>",
197 "username" : "<username>",
199 "username" : "<username>",
198 "firstname": "<firstname>",
200 "firstname": "<firstname>",
199 "lastname" : "<lastname>",
201 "lastname" : "<lastname>",
200 "email" : "<email>",
202 "email" : "<email>",
201 "active" : "<bool>",
203 "active" : "<bool>",
202 "admin" :  "<bool>",
204 "admin" :  "<bool>",
203 "ldap" : "<ldap_dn>"
205 "ldap" : "<ldap_dn>"
204 },
206 },
205
207
206 ]
208 ]
207 }
209 }
208 error : null
210 error : null
209
211
212 get_users_groups
213 ----------------
214
215 Lists all existing users groups. This command can be executed only using
216 api_key belonging to user with admin rights.
217
218 INPUT::
219
220 api_key : "<api_key>"
221 method : "get_users_groups"
222 args : { }
223
224 OUTPUT::
225
226 result : [
227 {
228 "id" : "<id>",
229 "group_name" : "<groupname>",
230 "active": "<bool>",
231 "members" : [
232 {
233 "id" : "<userid>",
234 "username" : "<username>",
235 "firstname": "<firstname>",
236 "lastname" : "<lastname>",
237 "email" : "<email>",
238 "active" : "<bool>",
239 "admin" :  "<bool>",
240 "ldap" : "<ldap_dn>"
241 },
242
243 ]
244 }
245 ]
246 error : null
247
248
210 create_users_group
249 create_users_group
211 ------------------
250 ------------------
212
251
@@ -218,7 +257,7 b' INPUT::'
218 api_key : "<api_key>"
257 api_key : "<api_key>"
219 method : "create_users_group"
258 method : "create_users_group"
220 args: {
259 args: {
221 "name": "<name>",
260 "group_name": "<groupname>",
222 "active":"<bool> = True"
261 "active":"<bool> = True"
223 }
262 }
224
263
@@ -226,7 +265,7 b' OUTPUT::'
226
265
227 result: {
266 result: {
228 "id": "<newusersgroupid>",
267 "id": "<newusersgroupid>",
229 "msg": "created new users group <name>"
268 "msg": "created new users group <groupname>"
230 }
269 }
231 error: null
270 error: null
232
271
@@ -253,6 +292,51 b' OUTPUT::'
253 }
292 }
254 error: null
293 error: null
255
294
295 get_repo
296 --------
297
298 Gets an existing repository. This command can be executed only using api_key
299 belonging to user with admin rights
300
301 INPUT::
302
303 api_key : "<api_key>"
304 method : "get_repo"
305 args: {
306 "repo_name" : "<reponame>"
307 }
308
309 OUTPUT::
310
311 result: None if repository does not exist or
312 {
313 "id" : "<id>",
314 "repo_name" : "<reponame>"
315 "type" : "<type>",
316 "description" : "<description>",
317 "members" : [
318 { "id" : "<userid>",
319 "username" : "<username>",
320 "firstname": "<firstname>",
321 "lastname" : "<lastname>",
322 "email" : "<email>",
323 "active" : "<bool>",
324 "admin" :  "<bool>",
325 "ldap" : "<ldap_dn>",
326 "permission" : "repository.(read|write|admin)"
327 },
328
329 {
330 "id" : "<usersgroupid>",
331 "name" : "<usersgroupname>",
332 "active": "<bool>",
333 "permission" : "repository.(read|write|admin)"
334 },
335
336 ]
337 }
338 error: null
339
256 get_repos
340 get_repos
257 ---------
341 ---------
258
342
@@ -270,7 +354,7 b' OUTPUT::'
270 result: [
354 result: [
271 {
355 {
272 "id" : "<id>",
356 "id" : "<id>",
273 "name" : "<name>"
357 "repo_name" : "<reponame>"
274 "type" : "<type>",
358 "type" : "<type>",
275 "description" : "<description>"
359 "description" : "<description>"
276 },
360 },
@@ -278,57 +362,13 b' OUTPUT::'
278 ]
362 ]
279 error: null
363 error: null
280
364
281 get_repo
282 --------
283
284 Gets an existing repository. This command can be executed only using api_key
285 belonging to user with admin rights
286
287 INPUT::
288
289 api_key : "<api_key>"
290 method : "get_repo"
291 args: {
292 "name" : "<name>"
293 }
294
295 OUTPUT::
296
297 result: None if repository not exist
298 {
299 "id" : "<id>",
300 "name" : "<name>"
301 "type" : "<type>",
302 "description" : "<description>",
303 "members" : [
304 { "id" : "<userid>",
305 "username" : "<username>",
306 "firstname": "<firstname>",
307 "lastname" : "<lastname>",
308 "email" : "<email>",
309 "active" : "<bool>",
310 "admin" :  "<bool>",
311 "ldap" : "<ldap_dn>",
312 "permission" : "repository.(read|write|admin)"
313 },
314
315 {
316 "id" : "<usersgroupid>",
317 "name" : "<usersgroupname>",
318 "active": "<bool>",
319 "permission" : "repository.(read|write|admin)"
320 },
321
322 ]
323 }
324 error: null
325
365
326 get_repo_nodes
366 get_repo_nodes
327 --------------
367 --------------
328
368
329 returns a list of nodes and it's children in a flat list for a given path
369 returns a list of nodes and it's children in a flat list for a given path
330 at given revision. It's possible to specify ret_type to show only files or
370 at given revision. It's possible to specify ret_type to show only `files` or
331 dirs. This command can be executed only using api_key belonging to user
371 `dirs`. This command can be executed only using api_key belonging to user
332 with admin rights
372 with admin rights
333
373
334 INPUT::
374 INPUT::
@@ -336,7 +376,7 b' INPUT::'
336 api_key : "<api_key>"
376 api_key : "<api_key>"
337 method : "get_repo_nodes"
377 method : "get_repo_nodes"
338 args: {
378 args: {
339 "repo_name" : "<name>",
379 "repo_name" : "<reponame>",
340 "revision" : "<revision>",
380 "revision" : "<revision>",
341 "root_path" : "<root_path>",
381 "root_path" : "<root_path>",
342 "ret_type" : "<ret_type>" = 'all'
382 "ret_type" : "<ret_type>" = 'all'
@@ -369,7 +409,7 b' INPUT::'
369 api_key : "<api_key>"
409 api_key : "<api_key>"
370 method : "create_repo"
410 method : "create_repo"
371 args: {
411 args: {
372 "name" : "<name>",
412 "repo_name" : "<reponame>",
373 "owner_name" : "<ownername>",
413 "owner_name" : "<ownername>",
374 "description" : "<description> = ''",
414 "description" : "<description> = ''",
375 "repo_type" : "<type> = 'hg'",
415 "repo_type" : "<type> = 'hg'",
@@ -378,7 +418,10 b' INPUT::'
378
418
379 OUTPUT::
419 OUTPUT::
380
420
381 result: None
421 result: {
422 "id": "<newrepoid>",
423 "msg": "Created new repository <reponame>",
424 }
382 error: null
425 error: null
383
426
384 add_user_to_repo
427 add_user_to_repo
@@ -394,13 +437,15 b' INPUT::'
394 method : "add_user_to_repo"
437 method : "add_user_to_repo"
395 args: {
438 args: {
396 "repo_name" : "<reponame>",
439 "repo_name" : "<reponame>",
397 "username" : "<username>",
440 "username" : "<username>",
398 "perm" : "(None|repository.(read|write|admin))",
441 "perm" : "(None|repository.(read|write|admin))",
399 }
442 }
400
443
401 OUTPUT::
444 OUTPUT::
402
445
403 result: None
446 result: {
447 "msg" : "Added perm: <perm> for <username> in repo: <reponame>"
448 }
404 error: null
449 error: null
405
450
406 add_users_group_to_repo
451 add_users_group_to_repo
@@ -416,6 +461,12 b' INPUT::'
416 method : "add_users_group_to_repo"
461 method : "add_users_group_to_repo"
417 args: {
462 args: {
418 "repo_name" : "<reponame>",
463 "repo_name" : "<reponame>",
419 "group_name" : "<groupname>",
464 "group_name" : "<groupname>",
420 "perm" : "(None|repository.(read|write|admin))",
465 "perm" : "(None|repository.(read|write|admin))",
421 } No newline at end of file
466 }
467 OUTPUT::
468
469 result: {
470 "msg" : Added perm: <perm> for <groupname> in repo: <reponame>"
471 }
472
@@ -1,4 +1,4 b''
1 .. _api:
1 .. _indexapi:
2
2
3 API Reference
3 API Reference
4 =============
4 =============
@@ -6,14 +6,29 b' The :mod:`models` Module'
6 .. automodule:: rhodecode.model
6 .. automodule:: rhodecode.model
7 :members:
7 :members:
8
8
9 .. automodule:: rhodecode.model.comment
10 :members:
11
12 .. automodule:: rhodecode.model.notification
13 :members:
14
9 .. automodule:: rhodecode.model.permission
15 .. automodule:: rhodecode.model.permission
10 :members:
16 :members:
11
17
18 .. automodule:: rhodecode.model.repo_permission
19 :members:
20
12 .. automodule:: rhodecode.model.repo
21 .. automodule:: rhodecode.model.repo
13 :members:
22 :members:
14
23
24 .. automodule:: rhodecode.model.repos_group
25 :members:
26
15 .. automodule:: rhodecode.model.scm
27 .. automodule:: rhodecode.model.scm
16 :members:
28 :members:
17
29
18 .. automodule:: rhodecode.model.user
30 .. automodule:: rhodecode.model.user
19 :members:
31 :members:
32
33 .. automodule:: rhodecode.model.users_group
34 :members: No newline at end of file
@@ -33,7 +33,9 b' news'
33 - implements #330 api method for listing nodes ar particular revision
33 - implements #330 api method for listing nodes ar particular revision
34 - fixed #331 RhodeCode mangles repository names if the a repository group
34 - fixed #331 RhodeCode mangles repository names if the a repository group
35 contains the "full path" to the repositories
35 contains the "full path" to the repositories
36
36 - #73 added linking issues in commit messages to choosen issue tracker url
37 based on user defined regular expression
38
37 fixes
39 fixes
38 -----
40 -----
39
41
@@ -2,8 +2,8 b''
2
2
3 .. include:: ./../README.rst
3 .. include:: ./../README.rst
4
4
5 Documentation
5 Users Guide
6 -------------
6 -----------
7
7
8 **Installation:**
8 **Installation:**
9
9
@@ -23,7 +23,6 b' Documentation'
23 usage/enable_git
23 usage/enable_git
24 usage/statistics
24 usage/statistics
25 usage/backup
25 usage/backup
26 usage/api_key_access
27
26
28 **Develop**
27 **Develop**
29
28
@@ -36,7 +35,7 b' Documentation'
36 **API**
35 **API**
37
36
38 .. toctree::
37 .. toctree::
39 :maxdepth: 2
38 :maxdepth: 1
40
39
41 api/index
40 api/index
42
41
@@ -425,7 +425,25 b' the following in the [app:main] section '
425 forge the authentication header and could effectively become authenticated
425 forge the authentication header and could effectively become authenticated
426 using any account of their liking.
426 using any account of their liking.
427
427
428 Integration with Issue trackers
429 -------------------------------
428
430
431 RhodeCode provides a simple integration with issue trackers. It's possible
432 to define a regular expression that will fetch issue id stored in commit
433 messages and replace that with an url to this issue. To enable this simply
434 uncomment following variables in the ini file::
435
436 url_pat = (?:^#|\s#)(\w+)
437 issue_server = https://myissueserver.com/issue/{id}
438 issue_prefix = #
439
440 `url_pat` is the regular expression that will match issues, default given regex
441 will match issues in format of #<number> eg. #300.
442 Matched issues will be replace with the `issue_server` url replacing {id} with
443 id fetched from regex. Since the # is striped `issue_prefix` is added as a
444 prefix to url. `issue_prefix` can be something different than # if you pass
445 ISSUE- as issue prefix this will generate an url in format
446 `<a href="https://myissueserver.com/issue/300">ISSUE-300</a>`
429
447
430 Hook management
448 Hook management
431 ---------------
449 ---------------
@@ -58,13 +58,34 b' container_auth_enabled = false'
58 proxypass_auth_enabled = false
58 proxypass_auth_enabled = false
59
59
60 ## overwrite schema of clone url
60 ## overwrite schema of clone url
61 # available vars:
61 ## available vars:
62 # scheme - http/https
62 ## scheme - http/https
63 # user - current user
63 ## user - current user
64 # pass - password
64 ## pass - password
65 # netloc - network location
65 ## netloc - network location
66 # path - usually repo_name
66 ## path - usually repo_name
67 # clone_uri = {scheme}://{user}{pass}{netloc}{path}
67
68 #clone_uri = {scheme}://{user}{pass}{netloc}{path}
69
70 ## issue tracking mapping for commits messages
71 ## uncomment url_pat, issue_server, issue_prefix to enable
72
73
74 ## pattern to get the issues from commit messages
75 ## default one used here is #1234
76
77 #url_pat = (?:^#|\s#)(\w+)
78
79 ## server url to the issue, each {id} will be replaced with id
80 ## fetched from the regex
81
82 #issue_server = https://myissueserver.com/issue/{id}
83
84 ## prefix to add to link to indicate it's an url
85 ## #314 will be replaced by <issue_prefix><id>
86
87 #issue_prefix = #
88
68
89
69 ####################################
90 ####################################
70 ### CELERY CONFIG ####
91 ### CELERY CONFIG ####
@@ -58,14 +58,35 b' container_auth_enabled = false'
58 proxypass_auth_enabled = false
58 proxypass_auth_enabled = false
59
59
60 ## overwrite schema of clone url
60 ## overwrite schema of clone url
61 # available vars:
61 ## available vars:
62 # scheme - http/https
62 ## scheme - http/https
63 # user - current user
63 ## user - current user
64 # pass - password
64 ## pass - password
65 # netloc - network location
65 ## netloc - network location
66 # path - usually repo_name
66 ## path - usually repo_name
67
67 # clone_uri = {scheme}://{user}{pass}{netloc}{path}
68 # clone_uri = {scheme}://{user}{pass}{netloc}{path}
68
69
70 ## issue tracking mapping for commits messages
71 ## uncomment url_pat, issue_server, issue_prefix to enable
72
73
74 ## pattern to get the issues from commit messages
75 ## default one used here is #1234
76
77 #url_pat = (?:^#|\s#)(\w+)
78
79 ## server url to the issue, each {id} will be replaced with id
80 ## fetched from the regex
81
82 #issue_server = https://myissueserver.com/issue/{id}
83
84 ## prefix to add to link to indicate it's an url
85 ## #314 will be replaced by <issue_prefix><id>
86
87 #issue_prefix = #
88
89
69 ####################################
90 ####################################
70 ### CELERY CONFIG ####
91 ### CELERY CONFIG ####
71 ####################################
92 ####################################
@@ -64,23 +64,23 b' class ApiController(JSONRPCController):'
64 """
64 """
65
65
66 @HasPermissionAllDecorator('hg.admin')
66 @HasPermissionAllDecorator('hg.admin')
67 def pull(self, apiuser, repo):
67 def pull(self, apiuser, repo_name):
68 """
68 """
69 Dispatch pull action on given repo
69 Dispatch pull action on given repo
70
70
71
71
72 :param user:
72 :param user:
73 :param repo:
73 :param repo_name:
74 """
74 """
75
75
76 if Repository.is_valid(repo) is False:
76 if Repository.is_valid(repo_name) is False:
77 raise JSONRPCError('Unknown repo "%s"' % repo)
77 raise JSONRPCError('Unknown repo "%s"' % repo_name)
78
78
79 try:
79 try:
80 ScmModel().pull_changes(repo, self.rhodecode_user.username)
80 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
81 return 'Pulled from %s' % repo
81 return 'Pulled from %s' % repo_name
82 except Exception:
82 except Exception:
83 raise JSONRPCError('Unable to pull changes from "%s"' % repo)
83 raise JSONRPCError('Unable to pull changes from "%s"' % repo_name)
84
84
85 @HasPermissionAllDecorator('hg.admin')
85 @HasPermissionAllDecorator('hg.admin')
86 def get_user(self, apiuser, username):
86 def get_user(self, apiuser, username):
@@ -151,10 +151,15 b' class ApiController(JSONRPCController):'
151 raise JSONRPCError("user %s already exist" % username)
151 raise JSONRPCError("user %s already exist" % username)
152
152
153 try:
153 try:
154 UserModel().create_or_update(username, password, email, firstname,
154 usr = UserModel().create_or_update(
155 lastname, active, admin, ldap_dn)
155 username, password, email, firstname,
156 lastname, active, admin, ldap_dn
157 )
156 Session.commit()
158 Session.commit()
157 return dict(msg='created new user %s' % username)
159 return dict(
160 id=usr.user_id,
161 msg='created new user %s' % username
162 )
158 except Exception:
163 except Exception:
159 log.error(traceback.format_exc())
164 log.error(traceback.format_exc())
160 raise JSONRPCError('failed to create user %s' % username)
165 raise JSONRPCError('failed to create user %s' % username)
@@ -185,7 +190,7 b' class ApiController(JSONRPCController):'
185 ldap=user.ldap_dn))
190 ldap=user.ldap_dn))
186
191
187 return dict(id=users_group.users_group_id,
192 return dict(id=users_group.users_group_id,
188 name=users_group.users_group_name,
193 group_name=users_group.users_group_name,
189 active=users_group.users_group_active,
194 active=users_group.users_group_active,
190 members=members)
195 members=members)
191
196
@@ -212,31 +217,31 b' class ApiController(JSONRPCController):'
212 ldap=user.ldap_dn))
217 ldap=user.ldap_dn))
213
218
214 result.append(dict(id=users_group.users_group_id,
219 result.append(dict(id=users_group.users_group_id,
215 name=users_group.users_group_name,
220 group_name=users_group.users_group_name,
216 active=users_group.users_group_active,
221 active=users_group.users_group_active,
217 members=members))
222 members=members))
218 return result
223 return result
219
224
220 @HasPermissionAllDecorator('hg.admin')
225 @HasPermissionAllDecorator('hg.admin')
221 def create_users_group(self, apiuser, name, active=True):
226 def create_users_group(self, apiuser, group_name, active=True):
222 """
227 """
223 Creates an new usergroup
228 Creates an new usergroup
224
229
225 :param name:
230 :param group_name:
226 :param active:
231 :param active:
227 """
232 """
228
233
229 if self.get_users_group(apiuser, name):
234 if self.get_users_group(apiuser, group_name):
230 raise JSONRPCError("users group %s already exist" % name)
235 raise JSONRPCError("users group %s already exist" % group_name)
231
236
232 try:
237 try:
233 ug = UsersGroupModel().create(name=name, active=active)
238 ug = UsersGroupModel().create(name=group_name, active=active)
234 Session.commit()
239 Session.commit()
235 return dict(id=ug.users_group_id,
240 return dict(id=ug.users_group_id,
236 msg='created new users group %s' % name)
241 msg='created new users group %s' % group_name)
237 except Exception:
242 except Exception:
238 log.error(traceback.format_exc())
243 log.error(traceback.format_exc())
239 raise JSONRPCError('failed to create group %s' % name)
244 raise JSONRPCError('failed to create group %s' % group_name)
240
245
241 @HasPermissionAllDecorator('hg.admin')
246 @HasPermissionAllDecorator('hg.admin')
242 def add_user_to_users_group(self, apiuser, group_name, username):
247 def add_user_to_users_group(self, apiuser, group_name, username):
@@ -312,7 +317,7 b' class ApiController(JSONRPCController):'
312
317
313 return dict(
318 return dict(
314 id=repo.repo_id,
319 id=repo.repo_id,
315 name=repo.repo_name,
320 repo_name=repo.repo_name,
316 type=repo.repo_type,
321 type=repo.repo_type,
317 description=repo.description,
322 description=repo.description,
318 members=members
323 members=members
@@ -331,7 +336,7 b' class ApiController(JSONRPCController):'
331 result.append(
336 result.append(
332 dict(
337 dict(
333 id=repository.repo_id,
338 id=repository.repo_id,
334 name=repository.repo_name,
339 repo_name=repository.repo_name,
335 type=repository.repo_type,
340 type=repository.repo_type,
336 description=repository.description
341 description=repository.description
337 )
342 )
@@ -367,13 +372,13 b' class ApiController(JSONRPCController):'
367 raise JSONRPCError(e)
372 raise JSONRPCError(e)
368
373
369 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
374 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
370 def create_repo(self, apiuser, name, owner_name, description='',
375 def create_repo(self, apiuser, repo_name, owner_name, description='',
371 repo_type='hg', private=False):
376 repo_type='hg', private=False):
372 """
377 """
373 Create a repository
378 Create a repository
374
379
375 :param apiuser:
380 :param apiuser:
376 :param name:
381 :param repo_name:
377 :param description:
382 :param description:
378 :param type:
383 :param type:
379 :param private:
384 :param private:
@@ -386,10 +391,10 b' class ApiController(JSONRPCController):'
386 except NoResultFound:
391 except NoResultFound:
387 raise JSONRPCError('unknown user %s' % owner)
392 raise JSONRPCError('unknown user %s' % owner)
388
393
389 if self.get_repo(apiuser, name):
394 if Repository.get_by_repo_name(repo_name):
390 raise JSONRPCError("repo %s already exist" % name)
395 raise JSONRPCError("repo %s already exist" % repo_name)
391
396
392 groups = name.split('/')
397 groups = repo_name.split('/')
393 real_name = groups[-1]
398 real_name = groups[-1]
394 groups = groups[:-1]
399 groups = groups[:-1]
395 parent_id = None
400 parent_id = None
@@ -405,10 +410,10 b' class ApiController(JSONRPCController):'
405 )
410 )
406 parent_id = group.group_id
411 parent_id = group.group_id
407
412
408 RepoModel().create(
413 repo = RepoModel().create(
409 dict(
414 dict(
410 repo_name=real_name,
415 repo_name=real_name,
411 repo_name_full=name,
416 repo_name_full=repo_name,
412 description=description,
417 description=description,
413 private=private,
418 private=private,
414 repo_type=repo_type,
419 repo_type=repo_type,
@@ -418,9 +423,15 b' class ApiController(JSONRPCController):'
418 owner
423 owner
419 )
424 )
420 Session.commit()
425 Session.commit()
426
427 return dict(
428 id=repo.repo_id,
429 msg="Created new repository %s" % repo.repo_name
430 )
431
421 except Exception:
432 except Exception:
422 log.error(traceback.format_exc())
433 log.error(traceback.format_exc())
423 raise JSONRPCError('failed to create repository %s' % name)
434 raise JSONRPCError('failed to create repository %s' % repo_name)
424
435
425 @HasPermissionAnyDecorator('hg.admin')
436 @HasPermissionAnyDecorator('hg.admin')
426 def add_user_to_repo(self, apiuser, repo_name, username, perm):
437 def add_user_to_repo(self, apiuser, repo_name, username, perm):
@@ -97,6 +97,13 b' class DbManage(object):'
97 from rhodecode.lib.dbmigrate.migrate.exceptions import \
97 from rhodecode.lib.dbmigrate.migrate.exceptions import \
98 DatabaseNotControlledError
98 DatabaseNotControlledError
99
99
100 if 'sqlite' in self.dburi:
101 print (
102 '********************** WARNING **********************\n'
103 'Make sure your version of sqlite is at least 3.7.X. \n'
104 'Earlier versions are known to fail on some migrations\n'
105 '*****************************************************\n'
106 )
100 upgrade = ask_ok('You are about to perform database upgrade, make '
107 upgrade = ask_ok('You are about to perform database upgrade, make '
101 'sure You backed up your database before. '
108 'sure You backed up your database before. '
102 'Continue ? [y/n]')
109 'Continue ? [y/n]')
@@ -161,6 +168,9 b' class DbManage(object):'
161 print ('Adding ldap defaults')
168 print ('Adding ldap defaults')
162 self.klass.create_ldap_options(skip_existing=True)
169 self.klass.create_ldap_options(skip_existing=True)
163
170
171 def step_4(self):
172 print ('TODO:')
173
164 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
174 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
165
175
166 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
176 # CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
@@ -8,6 +8,7 b' import hashlib'
8 import StringIO
8 import StringIO
9 import urllib
9 import urllib
10 import math
10 import math
11 import logging
11
12
12 from datetime import datetime
13 from datetime import datetime
13 from pygments.formatters.html import HtmlFormatter
14 from pygments.formatters.html import HtmlFormatter
@@ -41,6 +42,8 b' from rhodecode.lib.utils import repo_nam'
41 from rhodecode.lib import str2bool, safe_unicode, safe_str, get_changeset_safe
42 from rhodecode.lib import str2bool, safe_unicode, safe_str, get_changeset_safe
42 from rhodecode.lib.markup_renderer import MarkupRenderer
43 from rhodecode.lib.markup_renderer import MarkupRenderer
43
44
45 log = logging.getLogger(__name__)
46
44
47
45 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
48 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
46 """
49 """
@@ -728,7 +731,7 b' def fancy_file_stats(stats):'
728 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
731 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
729
732
730
733
731 def urlify_text(text):
734 def urlify_text(text_):
732 import re
735 import re
733
736
734 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
737 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
@@ -738,8 +741,42 b' def urlify_text(text):'
738 url_full = match_obj.groups()[0]
741 url_full = match_obj.groups()[0]
739 return '<a href="%(url)s">%(url)s</a>' % ({'url':url_full})
742 return '<a href="%(url)s">%(url)s</a>' % ({'url':url_full})
740
743
741 return literal(url_pat.sub(url_func, text))
744 return literal(url_pat.sub(url_func, text_))
742
745
746 def urlify_commit(text_):
747 import re
748 import traceback
749
750 try:
751 conf = config['app_conf']
752
753 URL_PAT = re.compile(r'%s' % conf.get('url_pat'))
754
755 if URL_PAT:
756 ISSUE_SERVER = conf.get('issue_server')
757 ISSUE_PREFIX = conf.get('issue_prefix')
758 def url_func(match_obj):
759 issue_id = match_obj.groups()[0]
760 tmpl = (
761 '<a class="%(cls)s" href="%(url)s">'
762 ' %(issue-prefix)s%(id-repr)s'
763 '</a>'
764 )
765 return tmpl % (
766 {
767 'cls':'issue-tracker-link',
768 'url':ISSUE_SERVER.replace('{id}',issue_id),
769 'id-repr':issue_id,
770 'issue-prefix':ISSUE_PREFIX,
771 'serv':ISSUE_SERVER,
772 }
773 )
774 return literal(URL_PAT.sub(url_func, text_))
775 except:
776 log.error(traceback.format_exc())
777 pass
778
779 return text_
743
780
744 def rst(source):
781 def rst(source):
745 return literal('<div class="rst-block">%s</div>' %
782 return literal('<div class="rst-block">%s</div>' %
@@ -1,3 +1,4 b''
1
1
2
2 class InvalidMessage(RuntimeError):
3 class InvalidMessage(RuntimeError):
3 """
4 """
@@ -5,6 +6,7 b' class InvalidMessage(RuntimeError):'
5 as recipients or sender address.
6 as recipients or sender address.
6 """
7 """
7
8
9
8 class BadHeaders(RuntimeError):
10 class BadHeaders(RuntimeError):
9 """
11 """
10 Raised if message contains newlines in headers.
12 Raised if message contains newlines in headers.
@@ -45,6 +45,8 b' class Message(object):'
45 :param bcc: BCC list
45 :param bcc: BCC list
46 :param extra_headers: dict of extra email headers
46 :param extra_headers: dict of extra email headers
47 :param attachments: list of Attachment instances
47 :param attachments: list of Attachment instances
48 :param recipients_separator: alternative separator for any of
49 'From', 'To', 'Delivered-To', 'Cc', 'Bcc' fields
48 """
50 """
49
51
50 def __init__(self,
52 def __init__(self,
@@ -56,8 +58,8 b' class Message(object):'
56 cc=None,
58 cc=None,
57 bcc=None,
59 bcc=None,
58 extra_headers=None,
60 extra_headers=None,
59 attachments=None):
61 attachments=None,
60
62 recipients_separator="; "):
61
63
62 self.subject = subject or ''
64 self.subject = subject or ''
63 self.sender = sender
65 self.sender = sender
@@ -70,6 +72,8 b' class Message(object):'
70 self.bcc = bcc or []
72 self.bcc = bcc or []
71 self.extra_headers = extra_headers or {}
73 self.extra_headers = extra_headers or {}
72
74
75 self.recipients_separator = recipients_separator
76
73 @property
77 @property
74 def send_to(self):
78 def send_to(self):
75 return set(self.recipients) | set(self.bcc or ()) | set(self.cc or ())
79 return set(self.recipients) | set(self.bcc or ()) | set(self.cc or ())
@@ -92,7 +96,8 b' class Message(object):'
92 To=self.recipients,
96 To=self.recipients,
93 From=self.sender,
97 From=self.sender,
94 Body=self.body,
98 Body=self.body,
95 Html=self.html)
99 Html=self.html,
100 separator=self.recipients_separator)
96
101
97 if self.bcc:
102 if self.bcc:
98 response.base['Bcc'] = self.bcc
103 response.base['Bcc'] = self.bcc
@@ -141,12 +141,14 b' class MailResponse(object):'
141 MailResponse.to_message. This lets you change it and work with it, then
141 MailResponse.to_message. This lets you change it and work with it, then
142 send it out when it's ready.
142 send it out when it's ready.
143 """
143 """
144 def __init__(self, To=None, From=None, Subject=None, Body=None, Html=None):
144 def __init__(self, To=None, From=None, Subject=None, Body=None, Html=None,
145 separator="; "):
145 self.Body = Body
146 self.Body = Body
146 self.Html = Html
147 self.Html = Html
147 self.base = MailBase([('To', To), ('From', From), ('Subject', Subject)])
148 self.base = MailBase([('To', To), ('From', From), ('Subject', Subject)])
148 self.multipart = self.Body and self.Html
149 self.multipart = self.Body and self.Html
149 self.attachments = []
150 self.attachments = []
151 self.separator = separator
150
152
151 def __contains__(self, key):
153 def __contains__(self, key):
152 return self.base.__contains__(key)
154 return self.base.__contains__(key)
@@ -298,7 +300,7 b' class MailResponse(object):'
298 self.base.body = self.Html
300 self.base.body = self.Html
299 self.base.content_encoding['Content-Type'] = ('text/html', {})
301 self.base.content_encoding['Content-Type'] = ('text/html', {})
300
302
301 return to_message(self.base)
303 return to_message(self.base, separator=self.separator)
302
304
303 def all_parts(self):
305 def all_parts(self):
304 """
306 """
@@ -310,7 +312,7 b' class MailResponse(object):'
310 def keys(self):
312 def keys(self):
311 return self.base.keys()
313 return self.base.keys()
312
314
313 def to_message(mail):
315 def to_message(mail, separator="; "):
314 """
316 """
315 Given a MailBase message, this will construct a MIMEPart
317 Given a MailBase message, this will construct a MIMEPart
316 that is canonicalized for use with the Python email API.
318 that is canonicalized for use with the Python email API.
@@ -339,10 +341,16 b' def to_message(mail):'
339
341
340 for k in mail.keys():
342 for k in mail.keys():
341 if k in ADDRESS_HEADERS_WHITELIST:
343 if k in ADDRESS_HEADERS_WHITELIST:
342 out[k.encode('ascii')] = header_to_mime_encoding(mail[k])
344 out[k.encode('ascii')] = header_to_mime_encoding(
345 mail[k],
346 not_email=False,
347 separator=separator
348 )
343 else:
349 else:
344 out[k.encode('ascii')] = header_to_mime_encoding(mail[k],
350 out[k.encode('ascii')] = header_to_mime_encoding(
345 not_email=True)
351 mail[k],
352 not_email=True
353 )
346
354
347 out.extract_payload(mail)
355 out.extract_payload(mail)
348
356
@@ -403,12 +411,12 b' class MIMEPart(MIMEBase):'
403 self.is_multipart())
411 self.is_multipart())
404
412
405
413
406 def header_to_mime_encoding(value, not_email=False):
414 def header_to_mime_encoding(value, not_email=False, separator=", "):
407 if not value: return ""
415 if not value: return ""
408
416
409 encoder = Charset(DEFAULT_ENCODING)
417 encoder = Charset(DEFAULT_ENCODING)
410 if type(value) == list:
418 if type(value) == list:
411 return "; ".join(properly_encode_header(
419 return separator.join(properly_encode_header(
412 v, encoder, not_email) for v in value)
420 v, encoder, not_email) for v in value)
413 else:
421 else:
414 return properly_encode_header(value, encoder, not_email)
422 return properly_encode_header(value, encoder, not_email)
@@ -59,7 +59,8 b' class SmtpMailer(object):'
59
59
60 if isinstance(recipients, basestring):
60 if isinstance(recipients, basestring):
61 recipients = [recipients]
61 recipients = [recipients]
62 msg = Message(subject, recipients, body, html, self.mail_from)
62 msg = Message(subject, recipients, body, html, self.mail_from,
63 recipients_separator=", ")
63 raw_msg = msg.to_message()
64 raw_msg = msg.to_message()
64
65
65 if self.ssl:
66 if self.ssl:
@@ -152,12 +152,12 b' def get_repos(path, recursive=False):'
152 """
152 """
153 Scans given path for repos and return (name,(type,path)) tuple
153 Scans given path for repos and return (name,(type,path)) tuple
154
154
155 :param path: path to scann for repositories
155 :param path: path to scan for repositories
156 :param recursive: recursive search and return names with subdirs in front
156 :param recursive: recursive search and return names with subdirs in front
157 """
157 """
158
158
159 # remove ending slash for better results
159 # remove ending slash for better results
160 path = path.rstrip('/')
160 path = path.rstrip(os.sep)
161
161
162 def _get_repos(p):
162 def _get_repos(p):
163 if not os.access(p, os.W_OK):
163 if not os.access(p, os.W_OK):
@@ -1,7 +1,7 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.notification
3 rhodecode.model.notification
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Model for notifications
6 Model for notifications
7
7
@@ -48,7 +48,7 b''
48 <div class="left">
48 <div class="left">
49 <div>
49 <div>
50 ${h.checkbox(cs.short_id,class_="changeset_range")}
50 ${h.checkbox(cs.short_id,class_="changeset_range")}
51 <span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span>
51 <span class="tooltip" title="${cs.date}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
52 </div>
52 </div>
53 <div class="author">
53 <div class="author">
54 <div class="gravatar">
54 <div class="gravatar">
@@ -49,7 +49,7 b''
49 <span>${h.person(c.changeset.author)}</span><br/>
49 <span>${h.person(c.changeset.author)}</span><br/>
50 <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
50 <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
51 </div>
51 </div>
52 <div class="message">${h.wrap_paragraphs(c.changeset.message)}</div>
52 <div class="message">${h.urlify_commit(h.wrap_paragraphs(c.changeset.message))}</div>
53 </div>
53 </div>
54 <div class="right">
54 <div class="right">
55 <div class="changes">
55 <div class="changes">
@@ -95,7 +95,7 b''
95 <div class="cs_${change}">
95 <div class="cs_${change}">
96 <div class="node">
96 <div class="node">
97 %if change != 'removed':
97 %if change != 'removed':
98 ${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path))}
98 ${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path)+"_target")}
99 %else:
99 %else:
100 ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID('',filenode.path)))}
100 ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID('',filenode.path)))}
101 %endif
101 %endif
@@ -138,11 +138,15 b''
138 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
138 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
139 var show = 'none';
139 var show = 'none';
140 var target = e.currentTarget;
140 var target = e.currentTarget;
141 console.log(target);
141 if(target.checked){
142 if(target.checked){
142 var show = ''
143 var show = ''
143 }
144 }
145 console.log('aa')
144 var boxid = YUD.getAttribute(target,'id_for');
146 var boxid = YUD.getAttribute(target,'id_for');
147 console.log(boxid);
145 var comments = YUQ('#{0} .inline-comments'.format(boxid));
148 var comments = YUQ('#{0} .inline-comments'.format(boxid));
149 console.log(comments)
146 for(c in comments){
150 for(c in comments){
147 YUD.setStyle(comments[c],'display',show);
151 YUD.setStyle(comments[c],'display',show);
148 }
152 }
@@ -41,7 +41,7 b''
41 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
41 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
42 <td><div class="author">${h.person(cs.author)}</div></td>
42 <td><div class="author">${h.person(cs.author)}</div></td>
43 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
43 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
44 <td><div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div></td>
44 <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message))}</div></td>
45 </tr>
45 </tr>
46 %endfor
46 %endfor
47 </table>
47 </table>
@@ -7,8 +7,8 b''
7
7
8 %for change,filenode,diff,cs1,cs2,stat in changes:
8 %for change,filenode,diff,cs1,cs2,stat in changes:
9 %if change !='removed':
9 %if change !='removed':
10 <div id="${h.FID(filenode.changeset.raw_id,filenode.path)}" style="clear:both;height:90px;margin-top:-60px"></div>
10 <div id="${h.FID(filenode.changeset.raw_id,filenode.path)}_target" style="clear:both;height:90px;margin-top:-60px"></div>
11 <div class="diffblock margined comm">
11 <div id="${h.FID(filenode.changeset.raw_id,filenode.path)}" class="diffblock margined comm">
12 <div class="code-header">
12 <div class="code-header">
13 <div class="changeset_header">
13 <div class="changeset_header">
14 <div class="changeset_file">
14 <div class="changeset_file">
@@ -2,16 +2,19 b''
2 %if c.repo_changesets:
2 %if c.repo_changesets:
3 <table class="table_disp">
3 <table class="table_disp">
4 <tr>
4 <tr>
5 <th class="left">${_('commit message')}</th>
5 <th class="left">${_('revision')}</th>
6 <th class="left">${_('commit message')}</th>
6 <th class="left">${_('age')}</th>
7 <th class="left">${_('age')}</th>
7 <th class="left">${_('author')}</th>
8 <th class="left">${_('author')}</th>
8 <th class="left">${_('revision')}</th>
9 <th class="left">${_('branch')}</th>
9 <th class="left">${_('branch')}</th>
10 <th class="left">${_('tags')}</th>
10 <th class="left">${_('tags')}</th>
11 </tr>
11 </tr>
12 %for cnt,cs in enumerate(c.repo_changesets):
12 %for cnt,cs in enumerate(c.repo_changesets):
13 <tr class="parity${cnt%2}">
13 <tr class="parity${cnt%2}">
14 <td>
14 <td>
15 <div><pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}">r${cs.revision}:${h.short_id(cs.raw_id)}</a></pre></div>
16 </td>
17 <td>
15 ${h.link_to(h.truncate(cs.message,50),
18 ${h.link_to(h.truncate(cs.message,50),
16 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
19 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
17 title=cs.message)}
20 title=cs.message)}
@@ -20,7 +23,6 b''
20 ${h.age(cs.date)}</span>
23 ${h.age(cs.date)}</span>
21 </td>
24 </td>
22 <td title="${cs.author}">${h.person(cs.author)}</td>
25 <td title="${cs.author}">${h.person(cs.author)}</td>
23 <td><div><pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}">r${cs.revision}:${h.short_id(cs.raw_id)}</a></pre></div></td>
24 <td>
26 <td>
25 <span class="logtags">
27 <span class="logtags">
26 <span class="branchtag">${cs.branch}</span>
28 <span class="branchtag">${cs.branch}</span>
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now