##// END OF EJS Templates
Basic compare-view controller with ref parsing
marcink -
r2241:b2a2868d codereview
parent child Browse files
Show More
@@ -0,0 +1,98 b''
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.compare
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 compare controller for pylons showoing differences between two
7 repos, branches, bookmarks or tips
8
9 :created_on: May 6, 2012
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
13 """
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
18 #
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
23 #
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import logging
27 import traceback
28
29 from pylons import request, response, session, tmpl_context as c, url
30 from pylons.controllers.util import abort, redirect
31
32 from rhodecode.lib.base import BaseRepoController, render
33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from webob.exc import HTTPNotFound
35
36 log = logging.getLogger(__name__)
37
38
39 class CompareController(BaseRepoController):
40
41 @LoginRequired()
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 'repository.admin')
44 def __before__(self):
45 super(CompareController, self).__before__()
46
47 def _handle_ref(self, ref):
48 """
49 Parse the org...other string
50 Possible formats are `(branch|book|tag):<name>...(branch|book|tag):<othername>`
51 or using a repo <empty>...(repo:</rhodecode/path/to/other)
52
53
54 :param ref:
55 :type ref:
56 """
57 org_repo = c.rhodecode_repo.name
58
59 def org_parser(org):
60 _repo = org_repo
61 name, val = org.split(':')
62 return _repo, (name, val)
63
64 def other_parser(other):
65 _repo = org_repo
66 name, val = other.split(':')
67 if 'repo' in other:
68 _repo = val
69 name = 'branch'
70 val = c.rhodecode_repo.DEFAULT_BRANCH_NAME
71
72 return _repo, (name, val)
73
74 if '...' in ref:
75 try:
76 org, other = ref.split('...')
77 org_repo, org_ref = org_parser(org)
78 other_repo, other_ref = other_parser(other)
79 return org_repo, org_ref, other_repo, other_ref
80 except:
81 log.error(traceback.format_exc())
82
83 raise HTTPNotFound
84
85 def index(self, ref):
86
87 org_repo, org_ref, other_repo, other_ref = self._handle_ref(ref)
88 return '''
89 <pre>
90 REPO: %s
91 REF: %s
92
93 vs
94
95 REPO: %s
96 REF: %s
97 </pre>
98 ''' % (org_repo, org_ref, other_repo, other_ref)
@@ -0,0 +1,7 b''
1 from rhodecode.tests import *
2
3 class TestCompareController(TestController):
4
5 def test_index(self):
6 response = self.app.get(url(controller='compare', action='index'))
7 # Test response...
@@ -1,507 +1,512 b''
1 """
1 """
2 Routes configuration
2 Routes configuration
3
3
4 The more specific and detailed routes should be defined first so they
4 The more specific and detailed routes should be defined first so they
5 may take precedent over the more generic routes. For more information
5 may take precedent over the more generic routes. For more information
6 refer to the routes manual at http://routes.groovie.org/docs/
6 refer to the routes manual at http://routes.groovie.org/docs/
7 """
7 """
8 from __future__ import with_statement
8 from __future__ import with_statement
9 from routes import Mapper
9 from routes import Mapper
10
10
11 # prefix for non repository related links needs to be prefixed with `/`
11 # prefix for non repository related links needs to be prefixed with `/`
12 ADMIN_PREFIX = '/_admin'
12 ADMIN_PREFIX = '/_admin'
13
13
14
14
15 def make_map(config):
15 def make_map(config):
16 """Create, configure and return the routes Mapper"""
16 """Create, configure and return the routes Mapper"""
17 rmap = Mapper(directory=config['pylons.paths']['controllers'],
17 rmap = Mapper(directory=config['pylons.paths']['controllers'],
18 always_scan=config['debug'])
18 always_scan=config['debug'])
19 rmap.minimization = False
19 rmap.minimization = False
20 rmap.explicit = False
20 rmap.explicit = False
21
21
22 from rhodecode.lib.utils import is_valid_repo
22 from rhodecode.lib.utils import is_valid_repo
23 from rhodecode.lib.utils import is_valid_repos_group
23 from rhodecode.lib.utils import is_valid_repos_group
24
24
25 def check_repo(environ, match_dict):
25 def check_repo(environ, match_dict):
26 """
26 """
27 check for valid repository for proper 404 handling
27 check for valid repository for proper 404 handling
28
28
29 :param environ:
29 :param environ:
30 :param match_dict:
30 :param match_dict:
31 """
31 """
32 from rhodecode.model.db import Repository
32 from rhodecode.model.db import Repository
33 repo_name = match_dict.get('repo_name')
33 repo_name = match_dict.get('repo_name')
34
34
35 try:
35 try:
36 by_id = repo_name.split('_')
36 by_id = repo_name.split('_')
37 if len(by_id) == 2 and by_id[1].isdigit():
37 if len(by_id) == 2 and by_id[1].isdigit():
38 repo_name = Repository.get(by_id[1]).repo_name
38 repo_name = Repository.get(by_id[1]).repo_name
39 match_dict['repo_name'] = repo_name
39 match_dict['repo_name'] = repo_name
40 except:
40 except:
41 pass
41 pass
42
42
43 return is_valid_repo(repo_name, config['base_path'])
43 return is_valid_repo(repo_name, config['base_path'])
44
44
45 def check_group(environ, match_dict):
45 def check_group(environ, match_dict):
46 """
46 """
47 check for valid repositories group for proper 404 handling
47 check for valid repositories group for proper 404 handling
48
48
49 :param environ:
49 :param environ:
50 :param match_dict:
50 :param match_dict:
51 """
51 """
52 repos_group_name = match_dict.get('group_name')
52 repos_group_name = match_dict.get('group_name')
53
53
54 return is_valid_repos_group(repos_group_name, config['base_path'])
54 return is_valid_repos_group(repos_group_name, config['base_path'])
55
55
56 def check_int(environ, match_dict):
56 def check_int(environ, match_dict):
57 return match_dict.get('id').isdigit()
57 return match_dict.get('id').isdigit()
58
58
59 # The ErrorController route (handles 404/500 error pages); it should
59 # The ErrorController route (handles 404/500 error pages); it should
60 # likely stay at the top, ensuring it can always be resolved
60 # likely stay at the top, ensuring it can always be resolved
61 rmap.connect('/error/{action}', controller='error')
61 rmap.connect('/error/{action}', controller='error')
62 rmap.connect('/error/{action}/{id}', controller='error')
62 rmap.connect('/error/{action}/{id}', controller='error')
63
63
64 #==========================================================================
64 #==========================================================================
65 # CUSTOM ROUTES HERE
65 # CUSTOM ROUTES HERE
66 #==========================================================================
66 #==========================================================================
67
67
68 #MAIN PAGE
68 #MAIN PAGE
69 rmap.connect('home', '/', controller='home', action='index')
69 rmap.connect('home', '/', controller='home', action='index')
70 rmap.connect('repo_switcher', '/repos', controller='home',
70 rmap.connect('repo_switcher', '/repos', controller='home',
71 action='repo_switcher')
71 action='repo_switcher')
72 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
72 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
73 controller='home', action='branch_tag_switcher')
73 controller='home', action='branch_tag_switcher')
74 rmap.connect('bugtracker',
74 rmap.connect('bugtracker',
75 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
75 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
76 _static=True)
76 _static=True)
77 rmap.connect('rst_help',
77 rmap.connect('rst_help',
78 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
78 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
79 _static=True)
79 _static=True)
80 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
80 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
81
81
82 #ADMIN REPOSITORY REST ROUTES
82 #ADMIN REPOSITORY REST ROUTES
83 with rmap.submapper(path_prefix=ADMIN_PREFIX,
83 with rmap.submapper(path_prefix=ADMIN_PREFIX,
84 controller='admin/repos') as m:
84 controller='admin/repos') as m:
85 m.connect("repos", "/repos",
85 m.connect("repos", "/repos",
86 action="create", conditions=dict(method=["POST"]))
86 action="create", conditions=dict(method=["POST"]))
87 m.connect("repos", "/repos",
87 m.connect("repos", "/repos",
88 action="index", conditions=dict(method=["GET"]))
88 action="index", conditions=dict(method=["GET"]))
89 m.connect("formatted_repos", "/repos.{format}",
89 m.connect("formatted_repos", "/repos.{format}",
90 action="index",
90 action="index",
91 conditions=dict(method=["GET"]))
91 conditions=dict(method=["GET"]))
92 m.connect("new_repo", "/repos/new",
92 m.connect("new_repo", "/repos/new",
93 action="new", conditions=dict(method=["GET"]))
93 action="new", conditions=dict(method=["GET"]))
94 m.connect("formatted_new_repo", "/repos/new.{format}",
94 m.connect("formatted_new_repo", "/repos/new.{format}",
95 action="new", conditions=dict(method=["GET"]))
95 action="new", conditions=dict(method=["GET"]))
96 m.connect("/repos/{repo_name:.*}",
96 m.connect("/repos/{repo_name:.*}",
97 action="update", conditions=dict(method=["PUT"],
97 action="update", conditions=dict(method=["PUT"],
98 function=check_repo))
98 function=check_repo))
99 m.connect("/repos/{repo_name:.*}",
99 m.connect("/repos/{repo_name:.*}",
100 action="delete", conditions=dict(method=["DELETE"],
100 action="delete", conditions=dict(method=["DELETE"],
101 function=check_repo))
101 function=check_repo))
102 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
102 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
103 action="edit", conditions=dict(method=["GET"],
103 action="edit", conditions=dict(method=["GET"],
104 function=check_repo))
104 function=check_repo))
105 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
105 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
106 action="edit", conditions=dict(method=["GET"],
106 action="edit", conditions=dict(method=["GET"],
107 function=check_repo))
107 function=check_repo))
108 m.connect("repo", "/repos/{repo_name:.*}",
108 m.connect("repo", "/repos/{repo_name:.*}",
109 action="show", conditions=dict(method=["GET"],
109 action="show", conditions=dict(method=["GET"],
110 function=check_repo))
110 function=check_repo))
111 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
111 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
112 action="show", conditions=dict(method=["GET"],
112 action="show", conditions=dict(method=["GET"],
113 function=check_repo))
113 function=check_repo))
114 #ajax delete repo perm user
114 #ajax delete repo perm user
115 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
115 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
116 action="delete_perm_user",
116 action="delete_perm_user",
117 conditions=dict(method=["DELETE"], function=check_repo))
117 conditions=dict(method=["DELETE"], function=check_repo))
118
118
119 #ajax delete repo perm users_group
119 #ajax delete repo perm users_group
120 m.connect('delete_repo_users_group',
120 m.connect('delete_repo_users_group',
121 "/repos_delete_users_group/{repo_name:.*}",
121 "/repos_delete_users_group/{repo_name:.*}",
122 action="delete_perm_users_group",
122 action="delete_perm_users_group",
123 conditions=dict(method=["DELETE"], function=check_repo))
123 conditions=dict(method=["DELETE"], function=check_repo))
124
124
125 #settings actions
125 #settings actions
126 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
126 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
127 action="repo_stats", conditions=dict(method=["DELETE"],
127 action="repo_stats", conditions=dict(method=["DELETE"],
128 function=check_repo))
128 function=check_repo))
129 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
129 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
130 action="repo_cache", conditions=dict(method=["DELETE"],
130 action="repo_cache", conditions=dict(method=["DELETE"],
131 function=check_repo))
131 function=check_repo))
132 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*}",
132 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*}",
133 action="repo_public_journal", conditions=dict(method=["PUT"],
133 action="repo_public_journal", conditions=dict(method=["PUT"],
134 function=check_repo))
134 function=check_repo))
135 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
135 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
136 action="repo_pull", conditions=dict(method=["PUT"],
136 action="repo_pull", conditions=dict(method=["PUT"],
137 function=check_repo))
137 function=check_repo))
138 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*}",
138 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*}",
139 action="repo_as_fork", conditions=dict(method=["PUT"],
139 action="repo_as_fork", conditions=dict(method=["PUT"],
140 function=check_repo))
140 function=check_repo))
141
141
142 with rmap.submapper(path_prefix=ADMIN_PREFIX,
142 with rmap.submapper(path_prefix=ADMIN_PREFIX,
143 controller='admin/repos_groups') as m:
143 controller='admin/repos_groups') as m:
144 m.connect("repos_groups", "/repos_groups",
144 m.connect("repos_groups", "/repos_groups",
145 action="create", conditions=dict(method=["POST"]))
145 action="create", conditions=dict(method=["POST"]))
146 m.connect("repos_groups", "/repos_groups",
146 m.connect("repos_groups", "/repos_groups",
147 action="index", conditions=dict(method=["GET"]))
147 action="index", conditions=dict(method=["GET"]))
148 m.connect("formatted_repos_groups", "/repos_groups.{format}",
148 m.connect("formatted_repos_groups", "/repos_groups.{format}",
149 action="index", conditions=dict(method=["GET"]))
149 action="index", conditions=dict(method=["GET"]))
150 m.connect("new_repos_group", "/repos_groups/new",
150 m.connect("new_repos_group", "/repos_groups/new",
151 action="new", conditions=dict(method=["GET"]))
151 action="new", conditions=dict(method=["GET"]))
152 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
152 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
153 action="new", conditions=dict(method=["GET"]))
153 action="new", conditions=dict(method=["GET"]))
154 m.connect("update_repos_group", "/repos_groups/{id}",
154 m.connect("update_repos_group", "/repos_groups/{id}",
155 action="update", conditions=dict(method=["PUT"],
155 action="update", conditions=dict(method=["PUT"],
156 function=check_int))
156 function=check_int))
157 m.connect("delete_repos_group", "/repos_groups/{id}",
157 m.connect("delete_repos_group", "/repos_groups/{id}",
158 action="delete", conditions=dict(method=["DELETE"],
158 action="delete", conditions=dict(method=["DELETE"],
159 function=check_int))
159 function=check_int))
160 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
160 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
161 action="edit", conditions=dict(method=["GET"],
161 action="edit", conditions=dict(method=["GET"],
162 function=check_int))
162 function=check_int))
163 m.connect("formatted_edit_repos_group",
163 m.connect("formatted_edit_repos_group",
164 "/repos_groups/{id}.{format}/edit",
164 "/repos_groups/{id}.{format}/edit",
165 action="edit", conditions=dict(method=["GET"],
165 action="edit", conditions=dict(method=["GET"],
166 function=check_int))
166 function=check_int))
167 m.connect("repos_group", "/repos_groups/{id}",
167 m.connect("repos_group", "/repos_groups/{id}",
168 action="show", conditions=dict(method=["GET"],
168 action="show", conditions=dict(method=["GET"],
169 function=check_int))
169 function=check_int))
170 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
170 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
171 action="show", conditions=dict(method=["GET"],
171 action="show", conditions=dict(method=["GET"],
172 function=check_int))
172 function=check_int))
173 # ajax delete repos group perm user
173 # ajax delete repos group perm user
174 m.connect('delete_repos_group_user_perm',
174 m.connect('delete_repos_group_user_perm',
175 "/delete_repos_group_user_perm/{group_name:.*}",
175 "/delete_repos_group_user_perm/{group_name:.*}",
176 action="delete_repos_group_user_perm",
176 action="delete_repos_group_user_perm",
177 conditions=dict(method=["DELETE"], function=check_group))
177 conditions=dict(method=["DELETE"], function=check_group))
178
178
179 # ajax delete repos group perm users_group
179 # ajax delete repos group perm users_group
180 m.connect('delete_repos_group_users_group_perm',
180 m.connect('delete_repos_group_users_group_perm',
181 "/delete_repos_group_users_group_perm/{group_name:.*}",
181 "/delete_repos_group_users_group_perm/{group_name:.*}",
182 action="delete_repos_group_users_group_perm",
182 action="delete_repos_group_users_group_perm",
183 conditions=dict(method=["DELETE"], function=check_group))
183 conditions=dict(method=["DELETE"], function=check_group))
184
184
185 #ADMIN USER REST ROUTES
185 #ADMIN USER REST ROUTES
186 with rmap.submapper(path_prefix=ADMIN_PREFIX,
186 with rmap.submapper(path_prefix=ADMIN_PREFIX,
187 controller='admin/users') as m:
187 controller='admin/users') as m:
188 m.connect("users", "/users",
188 m.connect("users", "/users",
189 action="create", conditions=dict(method=["POST"]))
189 action="create", conditions=dict(method=["POST"]))
190 m.connect("users", "/users",
190 m.connect("users", "/users",
191 action="index", conditions=dict(method=["GET"]))
191 action="index", conditions=dict(method=["GET"]))
192 m.connect("formatted_users", "/users.{format}",
192 m.connect("formatted_users", "/users.{format}",
193 action="index", conditions=dict(method=["GET"]))
193 action="index", conditions=dict(method=["GET"]))
194 m.connect("new_user", "/users/new",
194 m.connect("new_user", "/users/new",
195 action="new", conditions=dict(method=["GET"]))
195 action="new", conditions=dict(method=["GET"]))
196 m.connect("formatted_new_user", "/users/new.{format}",
196 m.connect("formatted_new_user", "/users/new.{format}",
197 action="new", conditions=dict(method=["GET"]))
197 action="new", conditions=dict(method=["GET"]))
198 m.connect("update_user", "/users/{id}",
198 m.connect("update_user", "/users/{id}",
199 action="update", conditions=dict(method=["PUT"]))
199 action="update", conditions=dict(method=["PUT"]))
200 m.connect("delete_user", "/users/{id}",
200 m.connect("delete_user", "/users/{id}",
201 action="delete", conditions=dict(method=["DELETE"]))
201 action="delete", conditions=dict(method=["DELETE"]))
202 m.connect("edit_user", "/users/{id}/edit",
202 m.connect("edit_user", "/users/{id}/edit",
203 action="edit", conditions=dict(method=["GET"]))
203 action="edit", conditions=dict(method=["GET"]))
204 m.connect("formatted_edit_user",
204 m.connect("formatted_edit_user",
205 "/users/{id}.{format}/edit",
205 "/users/{id}.{format}/edit",
206 action="edit", conditions=dict(method=["GET"]))
206 action="edit", conditions=dict(method=["GET"]))
207 m.connect("user", "/users/{id}",
207 m.connect("user", "/users/{id}",
208 action="show", conditions=dict(method=["GET"]))
208 action="show", conditions=dict(method=["GET"]))
209 m.connect("formatted_user", "/users/{id}.{format}",
209 m.connect("formatted_user", "/users/{id}.{format}",
210 action="show", conditions=dict(method=["GET"]))
210 action="show", conditions=dict(method=["GET"]))
211
211
212 #EXTRAS USER ROUTES
212 #EXTRAS USER ROUTES
213 m.connect("user_perm", "/users_perm/{id}",
213 m.connect("user_perm", "/users_perm/{id}",
214 action="update_perm", conditions=dict(method=["PUT"]))
214 action="update_perm", conditions=dict(method=["PUT"]))
215
215
216 #ADMIN USERS REST ROUTES
216 #ADMIN USERS REST ROUTES
217 with rmap.submapper(path_prefix=ADMIN_PREFIX,
217 with rmap.submapper(path_prefix=ADMIN_PREFIX,
218 controller='admin/users_groups') as m:
218 controller='admin/users_groups') as m:
219 m.connect("users_groups", "/users_groups",
219 m.connect("users_groups", "/users_groups",
220 action="create", conditions=dict(method=["POST"]))
220 action="create", conditions=dict(method=["POST"]))
221 m.connect("users_groups", "/users_groups",
221 m.connect("users_groups", "/users_groups",
222 action="index", conditions=dict(method=["GET"]))
222 action="index", conditions=dict(method=["GET"]))
223 m.connect("formatted_users_groups", "/users_groups.{format}",
223 m.connect("formatted_users_groups", "/users_groups.{format}",
224 action="index", conditions=dict(method=["GET"]))
224 action="index", conditions=dict(method=["GET"]))
225 m.connect("new_users_group", "/users_groups/new",
225 m.connect("new_users_group", "/users_groups/new",
226 action="new", conditions=dict(method=["GET"]))
226 action="new", conditions=dict(method=["GET"]))
227 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
227 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
228 action="new", conditions=dict(method=["GET"]))
228 action="new", conditions=dict(method=["GET"]))
229 m.connect("update_users_group", "/users_groups/{id}",
229 m.connect("update_users_group", "/users_groups/{id}",
230 action="update", conditions=dict(method=["PUT"]))
230 action="update", conditions=dict(method=["PUT"]))
231 m.connect("delete_users_group", "/users_groups/{id}",
231 m.connect("delete_users_group", "/users_groups/{id}",
232 action="delete", conditions=dict(method=["DELETE"]))
232 action="delete", conditions=dict(method=["DELETE"]))
233 m.connect("edit_users_group", "/users_groups/{id}/edit",
233 m.connect("edit_users_group", "/users_groups/{id}/edit",
234 action="edit", conditions=dict(method=["GET"]))
234 action="edit", conditions=dict(method=["GET"]))
235 m.connect("formatted_edit_users_group",
235 m.connect("formatted_edit_users_group",
236 "/users_groups/{id}.{format}/edit",
236 "/users_groups/{id}.{format}/edit",
237 action="edit", conditions=dict(method=["GET"]))
237 action="edit", conditions=dict(method=["GET"]))
238 m.connect("users_group", "/users_groups/{id}",
238 m.connect("users_group", "/users_groups/{id}",
239 action="show", conditions=dict(method=["GET"]))
239 action="show", conditions=dict(method=["GET"]))
240 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
240 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
241 action="show", conditions=dict(method=["GET"]))
241 action="show", conditions=dict(method=["GET"]))
242
242
243 #EXTRAS USER ROUTES
243 #EXTRAS USER ROUTES
244 m.connect("users_group_perm", "/users_groups_perm/{id}",
244 m.connect("users_group_perm", "/users_groups_perm/{id}",
245 action="update_perm", conditions=dict(method=["PUT"]))
245 action="update_perm", conditions=dict(method=["PUT"]))
246
246
247 #ADMIN GROUP REST ROUTES
247 #ADMIN GROUP REST ROUTES
248 rmap.resource('group', 'groups',
248 rmap.resource('group', 'groups',
249 controller='admin/groups', path_prefix=ADMIN_PREFIX)
249 controller='admin/groups', path_prefix=ADMIN_PREFIX)
250
250
251 #ADMIN PERMISSIONS REST ROUTES
251 #ADMIN PERMISSIONS REST ROUTES
252 rmap.resource('permission', 'permissions',
252 rmap.resource('permission', 'permissions',
253 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
253 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
254
254
255 ##ADMIN LDAP SETTINGS
255 ##ADMIN LDAP SETTINGS
256 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
256 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
257 controller='admin/ldap_settings', action='ldap_settings',
257 controller='admin/ldap_settings', action='ldap_settings',
258 conditions=dict(method=["POST"]))
258 conditions=dict(method=["POST"]))
259
259
260 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
260 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
261 controller='admin/ldap_settings')
261 controller='admin/ldap_settings')
262
262
263 #ADMIN SETTINGS REST ROUTES
263 #ADMIN SETTINGS REST ROUTES
264 with rmap.submapper(path_prefix=ADMIN_PREFIX,
264 with rmap.submapper(path_prefix=ADMIN_PREFIX,
265 controller='admin/settings') as m:
265 controller='admin/settings') as m:
266 m.connect("admin_settings", "/settings",
266 m.connect("admin_settings", "/settings",
267 action="create", conditions=dict(method=["POST"]))
267 action="create", conditions=dict(method=["POST"]))
268 m.connect("admin_settings", "/settings",
268 m.connect("admin_settings", "/settings",
269 action="index", conditions=dict(method=["GET"]))
269 action="index", conditions=dict(method=["GET"]))
270 m.connect("formatted_admin_settings", "/settings.{format}",
270 m.connect("formatted_admin_settings", "/settings.{format}",
271 action="index", conditions=dict(method=["GET"]))
271 action="index", conditions=dict(method=["GET"]))
272 m.connect("admin_new_setting", "/settings/new",
272 m.connect("admin_new_setting", "/settings/new",
273 action="new", conditions=dict(method=["GET"]))
273 action="new", conditions=dict(method=["GET"]))
274 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
274 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
275 action="new", conditions=dict(method=["GET"]))
275 action="new", conditions=dict(method=["GET"]))
276 m.connect("/settings/{setting_id}",
276 m.connect("/settings/{setting_id}",
277 action="update", conditions=dict(method=["PUT"]))
277 action="update", conditions=dict(method=["PUT"]))
278 m.connect("/settings/{setting_id}",
278 m.connect("/settings/{setting_id}",
279 action="delete", conditions=dict(method=["DELETE"]))
279 action="delete", conditions=dict(method=["DELETE"]))
280 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
280 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
281 action="edit", conditions=dict(method=["GET"]))
281 action="edit", conditions=dict(method=["GET"]))
282 m.connect("formatted_admin_edit_setting",
282 m.connect("formatted_admin_edit_setting",
283 "/settings/{setting_id}.{format}/edit",
283 "/settings/{setting_id}.{format}/edit",
284 action="edit", conditions=dict(method=["GET"]))
284 action="edit", conditions=dict(method=["GET"]))
285 m.connect("admin_setting", "/settings/{setting_id}",
285 m.connect("admin_setting", "/settings/{setting_id}",
286 action="show", conditions=dict(method=["GET"]))
286 action="show", conditions=dict(method=["GET"]))
287 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
287 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
288 action="show", conditions=dict(method=["GET"]))
288 action="show", conditions=dict(method=["GET"]))
289 m.connect("admin_settings_my_account", "/my_account",
289 m.connect("admin_settings_my_account", "/my_account",
290 action="my_account", conditions=dict(method=["GET"]))
290 action="my_account", conditions=dict(method=["GET"]))
291 m.connect("admin_settings_my_account_update", "/my_account_update",
291 m.connect("admin_settings_my_account_update", "/my_account_update",
292 action="my_account_update", conditions=dict(method=["PUT"]))
292 action="my_account_update", conditions=dict(method=["PUT"]))
293 m.connect("admin_settings_create_repository", "/create_repository",
293 m.connect("admin_settings_create_repository", "/create_repository",
294 action="create_repository", conditions=dict(method=["GET"]))
294 action="create_repository", conditions=dict(method=["GET"]))
295
295
296 #NOTIFICATION REST ROUTES
296 #NOTIFICATION REST ROUTES
297 with rmap.submapper(path_prefix=ADMIN_PREFIX,
297 with rmap.submapper(path_prefix=ADMIN_PREFIX,
298 controller='admin/notifications') as m:
298 controller='admin/notifications') as m:
299 m.connect("notifications", "/notifications",
299 m.connect("notifications", "/notifications",
300 action="create", conditions=dict(method=["POST"]))
300 action="create", conditions=dict(method=["POST"]))
301 m.connect("notifications", "/notifications",
301 m.connect("notifications", "/notifications",
302 action="index", conditions=dict(method=["GET"]))
302 action="index", conditions=dict(method=["GET"]))
303 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
303 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
304 action="mark_all_read", conditions=dict(method=["GET"]))
304 action="mark_all_read", conditions=dict(method=["GET"]))
305 m.connect("formatted_notifications", "/notifications.{format}",
305 m.connect("formatted_notifications", "/notifications.{format}",
306 action="index", conditions=dict(method=["GET"]))
306 action="index", conditions=dict(method=["GET"]))
307 m.connect("new_notification", "/notifications/new",
307 m.connect("new_notification", "/notifications/new",
308 action="new", conditions=dict(method=["GET"]))
308 action="new", conditions=dict(method=["GET"]))
309 m.connect("formatted_new_notification", "/notifications/new.{format}",
309 m.connect("formatted_new_notification", "/notifications/new.{format}",
310 action="new", conditions=dict(method=["GET"]))
310 action="new", conditions=dict(method=["GET"]))
311 m.connect("/notification/{notification_id}",
311 m.connect("/notification/{notification_id}",
312 action="update", conditions=dict(method=["PUT"]))
312 action="update", conditions=dict(method=["PUT"]))
313 m.connect("/notification/{notification_id}",
313 m.connect("/notification/{notification_id}",
314 action="delete", conditions=dict(method=["DELETE"]))
314 action="delete", conditions=dict(method=["DELETE"]))
315 m.connect("edit_notification", "/notification/{notification_id}/edit",
315 m.connect("edit_notification", "/notification/{notification_id}/edit",
316 action="edit", conditions=dict(method=["GET"]))
316 action="edit", conditions=dict(method=["GET"]))
317 m.connect("formatted_edit_notification",
317 m.connect("formatted_edit_notification",
318 "/notification/{notification_id}.{format}/edit",
318 "/notification/{notification_id}.{format}/edit",
319 action="edit", conditions=dict(method=["GET"]))
319 action="edit", conditions=dict(method=["GET"]))
320 m.connect("notification", "/notification/{notification_id}",
320 m.connect("notification", "/notification/{notification_id}",
321 action="show", conditions=dict(method=["GET"]))
321 action="show", conditions=dict(method=["GET"]))
322 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
322 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
323 action="show", conditions=dict(method=["GET"]))
323 action="show", conditions=dict(method=["GET"]))
324
324
325 #ADMIN MAIN PAGES
325 #ADMIN MAIN PAGES
326 with rmap.submapper(path_prefix=ADMIN_PREFIX,
326 with rmap.submapper(path_prefix=ADMIN_PREFIX,
327 controller='admin/admin') as m:
327 controller='admin/admin') as m:
328 m.connect('admin_home', '', action='index')
328 m.connect('admin_home', '', action='index')
329 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
329 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
330 action='add_repo')
330 action='add_repo')
331
331
332 #==========================================================================
332 #==========================================================================
333 # API V2
333 # API V2
334 #==========================================================================
334 #==========================================================================
335 with rmap.submapper(path_prefix=ADMIN_PREFIX,
335 with rmap.submapper(path_prefix=ADMIN_PREFIX,
336 controller='api/api') as m:
336 controller='api/api') as m:
337 m.connect('api', '/api')
337 m.connect('api', '/api')
338
338
339 #USER JOURNAL
339 #USER JOURNAL
340 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
340 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
341
341
342 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
342 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
343 controller='journal', action="public_journal")
343 controller='journal', action="public_journal")
344
344
345 rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
345 rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
346 controller='journal', action="public_journal_rss")
346 controller='journal', action="public_journal_rss")
347
347
348 rmap.connect('public_journal_atom',
348 rmap.connect('public_journal_atom',
349 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
349 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
350 action="public_journal_atom")
350 action="public_journal_atom")
351
351
352 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
352 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
353 controller='journal', action='toggle_following',
353 controller='journal', action='toggle_following',
354 conditions=dict(method=["POST"]))
354 conditions=dict(method=["POST"]))
355
355
356 #SEARCH
356 #SEARCH
357 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
357 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
358 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
358 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
359 controller='search')
359 controller='search')
360
360
361 #LOGIN/LOGOUT/REGISTER/SIGN IN
361 #LOGIN/LOGOUT/REGISTER/SIGN IN
362 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
362 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
363 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
363 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
364 action='logout')
364 action='logout')
365
365
366 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
366 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
367 action='register')
367 action='register')
368
368
369 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
369 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
370 controller='login', action='password_reset')
370 controller='login', action='password_reset')
371
371
372 rmap.connect('reset_password_confirmation',
372 rmap.connect('reset_password_confirmation',
373 '%s/password_reset_confirmation' % ADMIN_PREFIX,
373 '%s/password_reset_confirmation' % ADMIN_PREFIX,
374 controller='login', action='password_reset_confirmation')
374 controller='login', action='password_reset_confirmation')
375
375
376 #FEEDS
376 #FEEDS
377 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
377 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
378 controller='feed', action='rss',
378 controller='feed', action='rss',
379 conditions=dict(function=check_repo))
379 conditions=dict(function=check_repo))
380
380
381 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
381 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
382 controller='feed', action='atom',
382 controller='feed', action='atom',
383 conditions=dict(function=check_repo))
383 conditions=dict(function=check_repo))
384
384
385 #==========================================================================
385 #==========================================================================
386 # REPOSITORY ROUTES
386 # REPOSITORY ROUTES
387 #==========================================================================
387 #==========================================================================
388 rmap.connect('summary_home', '/{repo_name:.*}',
388 rmap.connect('summary_home', '/{repo_name:.*}',
389 controller='summary',
389 controller='summary',
390 conditions=dict(function=check_repo))
390 conditions=dict(function=check_repo))
391
391
392 rmap.connect('repos_group_home', '/{group_name:.*}',
392 rmap.connect('repos_group_home', '/{group_name:.*}',
393 controller='admin/repos_groups', action="show_by_name",
393 controller='admin/repos_groups', action="show_by_name",
394 conditions=dict(function=check_group))
394 conditions=dict(function=check_group))
395
395
396 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
396 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
397 controller='changeset', revision='tip',
397 controller='changeset', revision='tip',
398 conditions=dict(function=check_repo))
398 conditions=dict(function=check_repo))
399
399
400 rmap.connect('changeset_comment',
400 rmap.connect('changeset_comment',
401 '/{repo_name:.*}/changeset/{revision}/comment',
401 '/{repo_name:.*}/changeset/{revision}/comment',
402 controller='changeset', revision='tip', action='comment',
402 controller='changeset', revision='tip', action='comment',
403 conditions=dict(function=check_repo))
403 conditions=dict(function=check_repo))
404
404
405 rmap.connect('changeset_comment_delete',
405 rmap.connect('changeset_comment_delete',
406 '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
406 '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
407 controller='changeset', action='delete_comment',
407 controller='changeset', action='delete_comment',
408 conditions=dict(function=check_repo, method=["DELETE"]))
408 conditions=dict(function=check_repo, method=["DELETE"]))
409
409
410 rmap.connect('raw_changeset_home',
410 rmap.connect('raw_changeset_home',
411 '/{repo_name:.*}/raw-changeset/{revision}',
411 '/{repo_name:.*}/raw-changeset/{revision}',
412 controller='changeset', action='raw_changeset',
412 controller='changeset', action='raw_changeset',
413 revision='tip', conditions=dict(function=check_repo))
413 revision='tip', conditions=dict(function=check_repo))
414
414
415 rmap.connect('compare_home',
416 '/{repo_name:.*}/compare/{ref:.*}',
417 controller='compare', action='index',
418 conditions=dict(function=check_repo))
419
415 rmap.connect('summary_home', '/{repo_name:.*}/summary',
420 rmap.connect('summary_home', '/{repo_name:.*}/summary',
416 controller='summary', conditions=dict(function=check_repo))
421 controller='summary', conditions=dict(function=check_repo))
417
422
418 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
423 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
419 controller='shortlog', conditions=dict(function=check_repo))
424 controller='shortlog', conditions=dict(function=check_repo))
420
425
421 rmap.connect('branches_home', '/{repo_name:.*}/branches',
426 rmap.connect('branches_home', '/{repo_name:.*}/branches',
422 controller='branches', conditions=dict(function=check_repo))
427 controller='branches', conditions=dict(function=check_repo))
423
428
424 rmap.connect('tags_home', '/{repo_name:.*}/tags',
429 rmap.connect('tags_home', '/{repo_name:.*}/tags',
425 controller='tags', conditions=dict(function=check_repo))
430 controller='tags', conditions=dict(function=check_repo))
426
431
427 rmap.connect('bookmarks_home', '/{repo_name:.*}/bookmarks',
432 rmap.connect('bookmarks_home', '/{repo_name:.*}/bookmarks',
428 controller='bookmarks', conditions=dict(function=check_repo))
433 controller='bookmarks', conditions=dict(function=check_repo))
429
434
430 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
435 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
431 controller='changelog', conditions=dict(function=check_repo))
436 controller='changelog', conditions=dict(function=check_repo))
432
437
433 rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
438 rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
434 controller='changelog', action='changelog_details',
439 controller='changelog', action='changelog_details',
435 conditions=dict(function=check_repo))
440 conditions=dict(function=check_repo))
436
441
437 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
442 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
438 controller='files', revision='tip', f_path='',
443 controller='files', revision='tip', f_path='',
439 conditions=dict(function=check_repo))
444 conditions=dict(function=check_repo))
440
445
441 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
446 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
442 controller='files', action='diff', revision='tip', f_path='',
447 controller='files', action='diff', revision='tip', f_path='',
443 conditions=dict(function=check_repo))
448 conditions=dict(function=check_repo))
444
449
445 rmap.connect('files_rawfile_home',
450 rmap.connect('files_rawfile_home',
446 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
451 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
447 controller='files', action='rawfile', revision='tip',
452 controller='files', action='rawfile', revision='tip',
448 f_path='', conditions=dict(function=check_repo))
453 f_path='', conditions=dict(function=check_repo))
449
454
450 rmap.connect('files_raw_home',
455 rmap.connect('files_raw_home',
451 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
456 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
452 controller='files', action='raw', revision='tip', f_path='',
457 controller='files', action='raw', revision='tip', f_path='',
453 conditions=dict(function=check_repo))
458 conditions=dict(function=check_repo))
454
459
455 rmap.connect('files_annotate_home',
460 rmap.connect('files_annotate_home',
456 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
461 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
457 controller='files', action='index', revision='tip',
462 controller='files', action='index', revision='tip',
458 f_path='', annotate=True, conditions=dict(function=check_repo))
463 f_path='', annotate=True, conditions=dict(function=check_repo))
459
464
460 rmap.connect('files_edit_home',
465 rmap.connect('files_edit_home',
461 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
466 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
462 controller='files', action='edit', revision='tip',
467 controller='files', action='edit', revision='tip',
463 f_path='', conditions=dict(function=check_repo))
468 f_path='', conditions=dict(function=check_repo))
464
469
465 rmap.connect('files_add_home',
470 rmap.connect('files_add_home',
466 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
471 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
467 controller='files', action='add', revision='tip',
472 controller='files', action='add', revision='tip',
468 f_path='', conditions=dict(function=check_repo))
473 f_path='', conditions=dict(function=check_repo))
469
474
470 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
475 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
471 controller='files', action='archivefile',
476 controller='files', action='archivefile',
472 conditions=dict(function=check_repo))
477 conditions=dict(function=check_repo))
473
478
474 rmap.connect('files_nodelist_home',
479 rmap.connect('files_nodelist_home',
475 '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
480 '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
476 controller='files', action='nodelist',
481 controller='files', action='nodelist',
477 conditions=dict(function=check_repo))
482 conditions=dict(function=check_repo))
478
483
479 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
484 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
480 controller='settings', action="delete",
485 controller='settings', action="delete",
481 conditions=dict(method=["DELETE"], function=check_repo))
486 conditions=dict(method=["DELETE"], function=check_repo))
482
487
483 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
488 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
484 controller='settings', action="update",
489 controller='settings', action="update",
485 conditions=dict(method=["PUT"], function=check_repo))
490 conditions=dict(method=["PUT"], function=check_repo))
486
491
487 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
492 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
488 controller='settings', action='index',
493 controller='settings', action='index',
489 conditions=dict(function=check_repo))
494 conditions=dict(function=check_repo))
490
495
491 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
496 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
492 controller='forks', action='fork_create',
497 controller='forks', action='fork_create',
493 conditions=dict(function=check_repo, method=["POST"]))
498 conditions=dict(function=check_repo, method=["POST"]))
494
499
495 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
500 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
496 controller='forks', action='fork',
501 controller='forks', action='fork',
497 conditions=dict(function=check_repo))
502 conditions=dict(function=check_repo))
498
503
499 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
504 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
500 controller='forks', action='forks',
505 controller='forks', action='forks',
501 conditions=dict(function=check_repo))
506 conditions=dict(function=check_repo))
502
507
503 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
508 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
504 controller='followers', action='followers',
509 controller='followers', action='followers',
505 conditions=dict(function=check_repo))
510 conditions=dict(function=check_repo))
506
511
507 return rmap
512 return rmap
General Comments 0
You need to be logged in to leave comments. Login now