##// END OF EJS Templates
consistent handling of grant/revoke of permissions widgets...
marcink -
r3715:25dbbdae beta
parent child Browse files
Show More
@@ -1,679 +1,669 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 if match_dict.get('f_path'):
35 if match_dict.get('f_path'):
36 #fix for multiple initial slashes that causes errors
36 #fix for multiple initial slashes that causes errors
37 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
37 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
38
38
39 try:
39 try:
40 by_id = repo_name.split('_')
40 by_id = repo_name.split('_')
41 if len(by_id) == 2 and by_id[1].isdigit() and by_id[0] == '':
41 if len(by_id) == 2 and by_id[1].isdigit() and by_id[0] == '':
42 repo_name = Repository.get(by_id[1]).repo_name
42 repo_name = Repository.get(by_id[1]).repo_name
43 match_dict['repo_name'] = repo_name
43 match_dict['repo_name'] = repo_name
44 except Exception:
44 except Exception:
45 pass
45 pass
46
46
47 return is_valid_repo(repo_name, config['base_path'])
47 return is_valid_repo(repo_name, config['base_path'])
48
48
49 def check_group(environ, match_dict):
49 def check_group(environ, match_dict):
50 """
50 """
51 check for valid repository group for proper 404 handling
51 check for valid repository group for proper 404 handling
52
52
53 :param environ:
53 :param environ:
54 :param match_dict:
54 :param match_dict:
55 """
55 """
56 repos_group_name = match_dict.get('group_name')
56 repos_group_name = match_dict.get('group_name')
57 return is_valid_repos_group(repos_group_name, config['base_path'])
57 return is_valid_repos_group(repos_group_name, config['base_path'])
58
58
59 def check_group_skip_path(environ, match_dict):
59 def check_group_skip_path(environ, match_dict):
60 """
60 """
61 check for valid repository group for proper 404 handling, but skips
61 check for valid repository group for proper 404 handling, but skips
62 verification of existing path
62 verification of existing path
63
63
64 :param environ:
64 :param environ:
65 :param match_dict:
65 :param match_dict:
66 """
66 """
67 repos_group_name = match_dict.get('group_name')
67 repos_group_name = match_dict.get('group_name')
68 return is_valid_repos_group(repos_group_name, config['base_path'],
68 return is_valid_repos_group(repos_group_name, config['base_path'],
69 skip_path_check=True)
69 skip_path_check=True)
70
70
71 def check_int(environ, match_dict):
71 def check_int(environ, match_dict):
72 return match_dict.get('id').isdigit()
72 return match_dict.get('id').isdigit()
73
73
74 # The ErrorController route (handles 404/500 error pages); it should
74 # The ErrorController route (handles 404/500 error pages); it should
75 # likely stay at the top, ensuring it can always be resolved
75 # likely stay at the top, ensuring it can always be resolved
76 rmap.connect('/error/{action}', controller='error')
76 rmap.connect('/error/{action}', controller='error')
77 rmap.connect('/error/{action}/{id}', controller='error')
77 rmap.connect('/error/{action}/{id}', controller='error')
78
78
79 #==========================================================================
79 #==========================================================================
80 # CUSTOM ROUTES HERE
80 # CUSTOM ROUTES HERE
81 #==========================================================================
81 #==========================================================================
82
82
83 #MAIN PAGE
83 #MAIN PAGE
84 rmap.connect('home', '/', controller='home', action='index')
84 rmap.connect('home', '/', controller='home', action='index')
85 rmap.connect('repo_switcher', '/repos', controller='home',
85 rmap.connect('repo_switcher', '/repos', controller='home',
86 action='repo_switcher')
86 action='repo_switcher')
87 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*?}',
87 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*?}',
88 controller='home', action='branch_tag_switcher')
88 controller='home', action='branch_tag_switcher')
89 rmap.connect('bugtracker',
89 rmap.connect('bugtracker',
90 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
90 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
91 _static=True)
91 _static=True)
92 rmap.connect('rst_help',
92 rmap.connect('rst_help',
93 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
93 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
94 _static=True)
94 _static=True)
95 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
95 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
96
96
97 #ADMIN REPOSITORY REST ROUTES
97 #ADMIN REPOSITORY REST ROUTES
98 with rmap.submapper(path_prefix=ADMIN_PREFIX,
98 with rmap.submapper(path_prefix=ADMIN_PREFIX,
99 controller='admin/repos') as m:
99 controller='admin/repos') as m:
100 m.connect("repos", "/repos",
100 m.connect("repos", "/repos",
101 action="create", conditions=dict(method=["POST"]))
101 action="create", conditions=dict(method=["POST"]))
102 m.connect("repos", "/repos",
102 m.connect("repos", "/repos",
103 action="index", conditions=dict(method=["GET"]))
103 action="index", conditions=dict(method=["GET"]))
104 m.connect("formatted_repos", "/repos.{format}",
104 m.connect("formatted_repos", "/repos.{format}",
105 action="index",
105 action="index",
106 conditions=dict(method=["GET"]))
106 conditions=dict(method=["GET"]))
107 m.connect("new_repo", "/create_repository",
107 m.connect("new_repo", "/create_repository",
108 action="create_repository", conditions=dict(method=["GET"]))
108 action="create_repository", conditions=dict(method=["GET"]))
109 m.connect("/repos/{repo_name:.*?}",
109 m.connect("/repos/{repo_name:.*?}",
110 action="update", conditions=dict(method=["PUT"],
110 action="update", conditions=dict(method=["PUT"],
111 function=check_repo))
111 function=check_repo))
112 m.connect("/repos/{repo_name:.*?}",
112 m.connect("/repos/{repo_name:.*?}",
113 action="delete", conditions=dict(method=["DELETE"],
113 action="delete", conditions=dict(method=["DELETE"],
114 function=check_repo))
114 function=check_repo))
115 m.connect("formatted_edit_repo", "/repos/{repo_name:.*?}.{format}/edit",
115 m.connect("formatted_edit_repo", "/repos/{repo_name:.*?}.{format}/edit",
116 action="edit", conditions=dict(method=["GET"],
116 action="edit", conditions=dict(method=["GET"],
117 function=check_repo))
117 function=check_repo))
118 m.connect("repo", "/repos/{repo_name:.*?}",
118 m.connect("repo", "/repos/{repo_name:.*?}",
119 action="show", conditions=dict(method=["GET"],
119 action="show", conditions=dict(method=["GET"],
120 function=check_repo))
120 function=check_repo))
121 m.connect("formatted_repo", "/repos/{repo_name:.*?}.{format}",
121 m.connect("formatted_repo", "/repos/{repo_name:.*?}.{format}",
122 action="show", conditions=dict(method=["GET"],
122 action="show", conditions=dict(method=["GET"],
123 function=check_repo))
123 function=check_repo))
124 #add repo perm member
124 #add repo perm member
125 m.connect('set_repo_perm_member', "/set_repo_perm_member/{repo_name:.*?}",
125 m.connect('set_repo_perm_member',
126 action="set_repo_perm_member",
126 "/repos/{repo_name:.*?}/grant_perm",
127 conditions=dict(method=["POST"], function=check_repo))
127 action="set_repo_perm_member",
128 conditions=dict(method=["POST"], function=check_repo))
128
129
129 #ajax delete repo perm user
130 #ajax delete repo perm user
130 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*?}",
131 m.connect('delete_repo_perm_member',
131 action="delete_perm_user",
132 "/repos/{repo_name:.*?}/revoke_perm",
132 conditions=dict(method=["DELETE"], function=check_repo))
133 action="delete_repo_perm_member",
133
134 #ajax delete repo perm users_group
135 m.connect('delete_repo_users_group',
136 "/repos_delete_users_group/{repo_name:.*?}",
137 action="delete_perm_users_group",
138 conditions=dict(method=["DELETE"], function=check_repo))
134 conditions=dict(method=["DELETE"], function=check_repo))
139
135
140 #settings actions
136 #settings actions
141 m.connect('repo_stats', "/repos_stats/{repo_name:.*?}",
137 m.connect('repo_stats', "/repos_stats/{repo_name:.*?}",
142 action="repo_stats", conditions=dict(method=["DELETE"],
138 action="repo_stats", conditions=dict(method=["DELETE"],
143 function=check_repo))
139 function=check_repo))
144 m.connect('repo_cache', "/repos_cache/{repo_name:.*?}",
140 m.connect('repo_cache', "/repos_cache/{repo_name:.*?}",
145 action="repo_cache", conditions=dict(method=["DELETE"],
141 action="repo_cache", conditions=dict(method=["DELETE"],
146 function=check_repo))
142 function=check_repo))
147 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*?}",
143 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*?}",
148 action="repo_public_journal", conditions=dict(method=["PUT"],
144 action="repo_public_journal", conditions=dict(method=["PUT"],
149 function=check_repo))
145 function=check_repo))
150 m.connect('repo_pull', "/repo_pull/{repo_name:.*?}",
146 m.connect('repo_pull', "/repo_pull/{repo_name:.*?}",
151 action="repo_pull", conditions=dict(method=["PUT"],
147 action="repo_pull", conditions=dict(method=["PUT"],
152 function=check_repo))
148 function=check_repo))
153 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*?}",
149 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*?}",
154 action="repo_as_fork", conditions=dict(method=["PUT"],
150 action="repo_as_fork", conditions=dict(method=["PUT"],
155 function=check_repo))
151 function=check_repo))
156 m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
152 m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
157 action="repo_locking", conditions=dict(method=["PUT"],
153 action="repo_locking", conditions=dict(method=["PUT"],
158 function=check_repo))
154 function=check_repo))
159 m.connect('toggle_locking', "/locking_toggle/{repo_name:.*?}",
155 m.connect('toggle_locking', "/locking_toggle/{repo_name:.*?}",
160 action="toggle_locking", conditions=dict(method=["GET"],
156 action="toggle_locking", conditions=dict(method=["GET"],
161 function=check_repo))
157 function=check_repo))
162
158
163 #repo fields
159 #repo fields
164 m.connect('create_repo_fields', "/repo_fields/{repo_name:.*?}/new",
160 m.connect('create_repo_fields', "/repo_fields/{repo_name:.*?}/new",
165 action="create_repo_field", conditions=dict(method=["PUT"],
161 action="create_repo_field", conditions=dict(method=["PUT"],
166 function=check_repo))
162 function=check_repo))
167
163
168 m.connect('delete_repo_fields', "/repo_fields/{repo_name:.*?}/{field_id}",
164 m.connect('delete_repo_fields', "/repo_fields/{repo_name:.*?}/{field_id}",
169 action="delete_repo_field", conditions=dict(method=["DELETE"],
165 action="delete_repo_field", conditions=dict(method=["DELETE"],
170 function=check_repo))
166 function=check_repo))
171
167
172 with rmap.submapper(path_prefix=ADMIN_PREFIX,
168 with rmap.submapper(path_prefix=ADMIN_PREFIX,
173 controller='admin/repos_groups') as m:
169 controller='admin/repos_groups') as m:
174 m.connect("repos_groups", "/repos_groups",
170 m.connect("repos_groups", "/repos_groups",
175 action="create", conditions=dict(method=["POST"]))
171 action="create", conditions=dict(method=["POST"]))
176 m.connect("repos_groups", "/repos_groups",
172 m.connect("repos_groups", "/repos_groups",
177 action="index", conditions=dict(method=["GET"]))
173 action="index", conditions=dict(method=["GET"]))
178 m.connect("formatted_repos_groups", "/repos_groups.{format}",
174 m.connect("formatted_repos_groups", "/repos_groups.{format}",
179 action="index", conditions=dict(method=["GET"]))
175 action="index", conditions=dict(method=["GET"]))
180 m.connect("new_repos_group", "/repos_groups/new",
176 m.connect("new_repos_group", "/repos_groups/new",
181 action="new", conditions=dict(method=["GET"]))
177 action="new", conditions=dict(method=["GET"]))
182 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
178 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
183 action="new", conditions=dict(method=["GET"]))
179 action="new", conditions=dict(method=["GET"]))
184 m.connect("update_repos_group", "/repos_groups/{group_name:.*?}",
180 m.connect("update_repos_group", "/repos_groups/{group_name:.*?}",
185 action="update", conditions=dict(method=["PUT"],
181 action="update", conditions=dict(method=["PUT"],
186 function=check_group))
182 function=check_group))
183 #add repo group perm member
184 m.connect('set_repo_group_perm_member',
185 "/repos_groups/{group_name:.*?}/grant_perm",
186 action="set_repo_group_perm_member",
187 conditions=dict(method=["POST"], function=check_group))
188
189 #ajax delete repo group perm
190 m.connect('delete_repo_group_perm_member',
191 "/repos_groups/{group_name:.*?}/revoke_perm",
192 action="delete_repo_group_perm_member",
193 conditions=dict(method=["DELETE"], function=check_group))
194
187 m.connect("delete_repos_group", "/repos_groups/{group_name:.*?}",
195 m.connect("delete_repos_group", "/repos_groups/{group_name:.*?}",
188 action="delete", conditions=dict(method=["DELETE"],
196 action="delete", conditions=dict(method=["DELETE"],
189 function=check_group_skip_path))
197 function=check_group_skip_path))
190 m.connect("edit_repos_group", "/repos_groups/{group_name:.*?}/edit",
198 m.connect("edit_repos_group", "/repos_groups/{group_name:.*?}/edit",
191 action="edit", conditions=dict(method=["GET"],
199 action="edit", conditions=dict(method=["GET"],
192 function=check_group))
200 function=check_group))
193 m.connect("formatted_edit_repos_group",
201 m.connect("formatted_edit_repos_group",
194 "/repos_groups/{group_name:.*?}.{format}/edit",
202 "/repos_groups/{group_name:.*?}.{format}/edit",
195 action="edit", conditions=dict(method=["GET"],
203 action="edit", conditions=dict(method=["GET"],
196 function=check_group))
204 function=check_group))
197 m.connect("repos_group", "/repos_groups/{group_name:.*?}",
205 m.connect("repos_group", "/repos_groups/{group_name:.*?}",
198 action="show", conditions=dict(method=["GET"],
206 action="show", conditions=dict(method=["GET"],
199 function=check_group))
207 function=check_group))
200 m.connect("formatted_repos_group", "/repos_groups/{group_name:.*?}.{format}",
208 m.connect("formatted_repos_group", "/repos_groups/{group_name:.*?}.{format}",
201 action="show", conditions=dict(method=["GET"],
209 action="show", conditions=dict(method=["GET"],
202 function=check_group))
210 function=check_group))
203
211
204 #add repo perm member
205 m.connect('set_repo_group_perm_member',
206 "/set_repo_group_perm_member/{group_name:.*?}",
207 action="set_repo_group_perm_member",
208 conditions=dict(method=["POST"], function=check_group))
209
210 # ajax delete repository group perm user
211 m.connect('delete_repos_group_user_perm',
212 "/delete_repos_group_user_perm/{group_name:.*?}",
213 action="delete_repos_group_user_perm",
214 conditions=dict(method=["DELETE"], function=check_group))
215
216 # ajax delete repository group perm users_group
217 m.connect('delete_repos_group_users_group_perm',
218 "/delete_repos_group_users_group_perm/{group_name:.*?}",
219 action="delete_repos_group_users_group_perm",
220 conditions=dict(method=["DELETE"], function=check_group))
221
222 #ADMIN USER REST ROUTES
212 #ADMIN USER REST ROUTES
223 with rmap.submapper(path_prefix=ADMIN_PREFIX,
213 with rmap.submapper(path_prefix=ADMIN_PREFIX,
224 controller='admin/users') as m:
214 controller='admin/users') as m:
225 m.connect("users", "/users",
215 m.connect("users", "/users",
226 action="create", conditions=dict(method=["POST"]))
216 action="create", conditions=dict(method=["POST"]))
227 m.connect("users", "/users",
217 m.connect("users", "/users",
228 action="index", conditions=dict(method=["GET"]))
218 action="index", conditions=dict(method=["GET"]))
229 m.connect("formatted_users", "/users.{format}",
219 m.connect("formatted_users", "/users.{format}",
230 action="index", conditions=dict(method=["GET"]))
220 action="index", conditions=dict(method=["GET"]))
231 m.connect("new_user", "/users/new",
221 m.connect("new_user", "/users/new",
232 action="new", conditions=dict(method=["GET"]))
222 action="new", conditions=dict(method=["GET"]))
233 m.connect("formatted_new_user", "/users/new.{format}",
223 m.connect("formatted_new_user", "/users/new.{format}",
234 action="new", conditions=dict(method=["GET"]))
224 action="new", conditions=dict(method=["GET"]))
235 m.connect("update_user", "/users/{id}",
225 m.connect("update_user", "/users/{id}",
236 action="update", conditions=dict(method=["PUT"]))
226 action="update", conditions=dict(method=["PUT"]))
237 m.connect("delete_user", "/users/{id}",
227 m.connect("delete_user", "/users/{id}",
238 action="delete", conditions=dict(method=["DELETE"]))
228 action="delete", conditions=dict(method=["DELETE"]))
239 m.connect("edit_user", "/users/{id}/edit",
229 m.connect("edit_user", "/users/{id}/edit",
240 action="edit", conditions=dict(method=["GET"]))
230 action="edit", conditions=dict(method=["GET"]))
241 m.connect("formatted_edit_user",
231 m.connect("formatted_edit_user",
242 "/users/{id}.{format}/edit",
232 "/users/{id}.{format}/edit",
243 action="edit", conditions=dict(method=["GET"]))
233 action="edit", conditions=dict(method=["GET"]))
244 m.connect("user", "/users/{id}",
234 m.connect("user", "/users/{id}",
245 action="show", conditions=dict(method=["GET"]))
235 action="show", conditions=dict(method=["GET"]))
246 m.connect("formatted_user", "/users/{id}.{format}",
236 m.connect("formatted_user", "/users/{id}.{format}",
247 action="show", conditions=dict(method=["GET"]))
237 action="show", conditions=dict(method=["GET"]))
248
238
249 #EXTRAS USER ROUTES
239 #EXTRAS USER ROUTES
250 m.connect("user_perm", "/users_perm/{id}",
240 m.connect("user_perm", "/users_perm/{id}",
251 action="update_perm", conditions=dict(method=["PUT"]))
241 action="update_perm", conditions=dict(method=["PUT"]))
252 m.connect("user_emails", "/users_emails/{id}",
242 m.connect("user_emails", "/users_emails/{id}",
253 action="add_email", conditions=dict(method=["PUT"]))
243 action="add_email", conditions=dict(method=["PUT"]))
254 m.connect("user_emails_delete", "/users_emails/{id}",
244 m.connect("user_emails_delete", "/users_emails/{id}",
255 action="delete_email", conditions=dict(method=["DELETE"]))
245 action="delete_email", conditions=dict(method=["DELETE"]))
256 m.connect("user_ips", "/users_ips/{id}",
246 m.connect("user_ips", "/users_ips/{id}",
257 action="add_ip", conditions=dict(method=["PUT"]))
247 action="add_ip", conditions=dict(method=["PUT"]))
258 m.connect("user_ips_delete", "/users_ips/{id}",
248 m.connect("user_ips_delete", "/users_ips/{id}",
259 action="delete_ip", conditions=dict(method=["DELETE"]))
249 action="delete_ip", conditions=dict(method=["DELETE"]))
260
250
261 #ADMIN USER GROUPS REST ROUTES
251 #ADMIN USER GROUPS REST ROUTES
262 with rmap.submapper(path_prefix=ADMIN_PREFIX,
252 with rmap.submapper(path_prefix=ADMIN_PREFIX,
263 controller='admin/users_groups') as m:
253 controller='admin/users_groups') as m:
264 m.connect("users_groups", "/users_groups",
254 m.connect("users_groups", "/users_groups",
265 action="create", conditions=dict(method=["POST"]))
255 action="create", conditions=dict(method=["POST"]))
266 m.connect("users_groups", "/users_groups",
256 m.connect("users_groups", "/users_groups",
267 action="index", conditions=dict(method=["GET"]))
257 action="index", conditions=dict(method=["GET"]))
268 m.connect("formatted_users_groups", "/users_groups.{format}",
258 m.connect("formatted_users_groups", "/users_groups.{format}",
269 action="index", conditions=dict(method=["GET"]))
259 action="index", conditions=dict(method=["GET"]))
270 m.connect("new_users_group", "/users_groups/new",
260 m.connect("new_users_group", "/users_groups/new",
271 action="new", conditions=dict(method=["GET"]))
261 action="new", conditions=dict(method=["GET"]))
272 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
262 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
273 action="new", conditions=dict(method=["GET"]))
263 action="new", conditions=dict(method=["GET"]))
274 m.connect("update_users_group", "/users_groups/{id}",
264 m.connect("update_users_group", "/users_groups/{id}",
275 action="update", conditions=dict(method=["PUT"]))
265 action="update", conditions=dict(method=["PUT"]))
276 m.connect("delete_users_group", "/users_groups/{id}",
266 m.connect("delete_users_group", "/users_groups/{id}",
277 action="delete", conditions=dict(method=["DELETE"]))
267 action="delete", conditions=dict(method=["DELETE"]))
278 m.connect("edit_users_group", "/users_groups/{id}/edit",
268 m.connect("edit_users_group", "/users_groups/{id}/edit",
279 action="edit", conditions=dict(method=["GET"]))
269 action="edit", conditions=dict(method=["GET"]))
280 m.connect("formatted_edit_users_group",
270 m.connect("formatted_edit_users_group",
281 "/users_groups/{id}.{format}/edit",
271 "/users_groups/{id}.{format}/edit",
282 action="edit", conditions=dict(method=["GET"]))
272 action="edit", conditions=dict(method=["GET"]))
283 m.connect("users_group", "/users_groups/{id}",
273 m.connect("users_group", "/users_groups/{id}",
284 action="show", conditions=dict(method=["GET"]))
274 action="show", conditions=dict(method=["GET"]))
285 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
275 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
286 action="show", conditions=dict(method=["GET"]))
276 action="show", conditions=dict(method=["GET"]))
287
277
288 #EXTRAS USER ROUTES
278 #EXTRAS USER ROUTES
289 # update
279 # update
290 m.connect("users_group_perm", "/users_groups/{id}/update_global_perm",
280 m.connect("users_group_perm", "/users_groups/{id}/update_global_perm",
291 action="update_perm", conditions=dict(method=["PUT"]))
281 action="update_perm", conditions=dict(method=["PUT"]))
292
282
293 #add user group perm member
283 #add user group perm member
294 m.connect('set_user_group_perm_member', "/users_groups/{id}/grant_perm",
284 m.connect('set_user_group_perm_member', "/users_groups/{id}/grant_perm",
295 action="set_user_group_perm_member",
285 action="set_user_group_perm_member",
296 conditions=dict(method=["POST"]))
286 conditions=dict(method=["POST"]))
297
287
298 #ajax delete user group perm
288 #ajax delete user group perm
299 m.connect('delete_user_group_perm_member', "/users_groups/{id}/revoke_perm",
289 m.connect('delete_user_group_perm_member', "/users_groups/{id}/revoke_perm",
300 action="delete_user_group_perm_member",
290 action="delete_user_group_perm_member",
301 conditions=dict(method=["DELETE"]))
291 conditions=dict(method=["DELETE"]))
302
292
303 #ADMIN GROUP REST ROUTES
293 #ADMIN GROUP REST ROUTES
304 rmap.resource('group', 'groups',
294 rmap.resource('group', 'groups',
305 controller='admin/groups', path_prefix=ADMIN_PREFIX)
295 controller='admin/groups', path_prefix=ADMIN_PREFIX)
306
296
307 #ADMIN PERMISSIONS REST ROUTES
297 #ADMIN PERMISSIONS REST ROUTES
308 rmap.resource('permission', 'permissions',
298 rmap.resource('permission', 'permissions',
309 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
299 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
310
300
311 #ADMIN DEFAULTS REST ROUTES
301 #ADMIN DEFAULTS REST ROUTES
312 rmap.resource('default', 'defaults',
302 rmap.resource('default', 'defaults',
313 controller='admin/defaults', path_prefix=ADMIN_PREFIX)
303 controller='admin/defaults', path_prefix=ADMIN_PREFIX)
314
304
315 ##ADMIN LDAP SETTINGS
305 ##ADMIN LDAP SETTINGS
316 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
306 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
317 controller='admin/ldap_settings', action='ldap_settings',
307 controller='admin/ldap_settings', action='ldap_settings',
318 conditions=dict(method=["POST"]))
308 conditions=dict(method=["POST"]))
319
309
320 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
310 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
321 controller='admin/ldap_settings')
311 controller='admin/ldap_settings')
322
312
323 #ADMIN SETTINGS REST ROUTES
313 #ADMIN SETTINGS REST ROUTES
324 with rmap.submapper(path_prefix=ADMIN_PREFIX,
314 with rmap.submapper(path_prefix=ADMIN_PREFIX,
325 controller='admin/settings') as m:
315 controller='admin/settings') as m:
326 m.connect("admin_settings", "/settings",
316 m.connect("admin_settings", "/settings",
327 action="create", conditions=dict(method=["POST"]))
317 action="create", conditions=dict(method=["POST"]))
328 m.connect("admin_settings", "/settings",
318 m.connect("admin_settings", "/settings",
329 action="index", conditions=dict(method=["GET"]))
319 action="index", conditions=dict(method=["GET"]))
330 m.connect("formatted_admin_settings", "/settings.{format}",
320 m.connect("formatted_admin_settings", "/settings.{format}",
331 action="index", conditions=dict(method=["GET"]))
321 action="index", conditions=dict(method=["GET"]))
332 m.connect("admin_new_setting", "/settings/new",
322 m.connect("admin_new_setting", "/settings/new",
333 action="new", conditions=dict(method=["GET"]))
323 action="new", conditions=dict(method=["GET"]))
334 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
324 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
335 action="new", conditions=dict(method=["GET"]))
325 action="new", conditions=dict(method=["GET"]))
336 m.connect("/settings/{setting_id}",
326 m.connect("/settings/{setting_id}",
337 action="update", conditions=dict(method=["PUT"]))
327 action="update", conditions=dict(method=["PUT"]))
338 m.connect("/settings/{setting_id}",
328 m.connect("/settings/{setting_id}",
339 action="delete", conditions=dict(method=["DELETE"]))
329 action="delete", conditions=dict(method=["DELETE"]))
340 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
330 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
341 action="edit", conditions=dict(method=["GET"]))
331 action="edit", conditions=dict(method=["GET"]))
342 m.connect("formatted_admin_edit_setting",
332 m.connect("formatted_admin_edit_setting",
343 "/settings/{setting_id}.{format}/edit",
333 "/settings/{setting_id}.{format}/edit",
344 action="edit", conditions=dict(method=["GET"]))
334 action="edit", conditions=dict(method=["GET"]))
345 m.connect("admin_setting", "/settings/{setting_id}",
335 m.connect("admin_setting", "/settings/{setting_id}",
346 action="show", conditions=dict(method=["GET"]))
336 action="show", conditions=dict(method=["GET"]))
347 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
337 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
348 action="show", conditions=dict(method=["GET"]))
338 action="show", conditions=dict(method=["GET"]))
349 m.connect("admin_settings_my_account", "/my_account",
339 m.connect("admin_settings_my_account", "/my_account",
350 action="my_account", conditions=dict(method=["GET"]))
340 action="my_account", conditions=dict(method=["GET"]))
351 m.connect("admin_settings_my_account_update", "/my_account_update",
341 m.connect("admin_settings_my_account_update", "/my_account_update",
352 action="my_account_update", conditions=dict(method=["PUT"]))
342 action="my_account_update", conditions=dict(method=["PUT"]))
353 m.connect("admin_settings_my_repos", "/my_account/repos",
343 m.connect("admin_settings_my_repos", "/my_account/repos",
354 action="my_account_my_repos", conditions=dict(method=["GET"]))
344 action="my_account_my_repos", conditions=dict(method=["GET"]))
355 m.connect("admin_settings_my_pullrequests", "/my_account/pull_requests",
345 m.connect("admin_settings_my_pullrequests", "/my_account/pull_requests",
356 action="my_account_my_pullrequests", conditions=dict(method=["GET"]))
346 action="my_account_my_pullrequests", conditions=dict(method=["GET"]))
357
347
358 #NOTIFICATION REST ROUTES
348 #NOTIFICATION REST ROUTES
359 with rmap.submapper(path_prefix=ADMIN_PREFIX,
349 with rmap.submapper(path_prefix=ADMIN_PREFIX,
360 controller='admin/notifications') as m:
350 controller='admin/notifications') as m:
361 m.connect("notifications", "/notifications",
351 m.connect("notifications", "/notifications",
362 action="create", conditions=dict(method=["POST"]))
352 action="create", conditions=dict(method=["POST"]))
363 m.connect("notifications", "/notifications",
353 m.connect("notifications", "/notifications",
364 action="index", conditions=dict(method=["GET"]))
354 action="index", conditions=dict(method=["GET"]))
365 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
355 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
366 action="mark_all_read", conditions=dict(method=["GET"]))
356 action="mark_all_read", conditions=dict(method=["GET"]))
367 m.connect("formatted_notifications", "/notifications.{format}",
357 m.connect("formatted_notifications", "/notifications.{format}",
368 action="index", conditions=dict(method=["GET"]))
358 action="index", conditions=dict(method=["GET"]))
369 m.connect("new_notification", "/notifications/new",
359 m.connect("new_notification", "/notifications/new",
370 action="new", conditions=dict(method=["GET"]))
360 action="new", conditions=dict(method=["GET"]))
371 m.connect("formatted_new_notification", "/notifications/new.{format}",
361 m.connect("formatted_new_notification", "/notifications/new.{format}",
372 action="new", conditions=dict(method=["GET"]))
362 action="new", conditions=dict(method=["GET"]))
373 m.connect("/notification/{notification_id}",
363 m.connect("/notification/{notification_id}",
374 action="update", conditions=dict(method=["PUT"]))
364 action="update", conditions=dict(method=["PUT"]))
375 m.connect("/notification/{notification_id}",
365 m.connect("/notification/{notification_id}",
376 action="delete", conditions=dict(method=["DELETE"]))
366 action="delete", conditions=dict(method=["DELETE"]))
377 m.connect("edit_notification", "/notification/{notification_id}/edit",
367 m.connect("edit_notification", "/notification/{notification_id}/edit",
378 action="edit", conditions=dict(method=["GET"]))
368 action="edit", conditions=dict(method=["GET"]))
379 m.connect("formatted_edit_notification",
369 m.connect("formatted_edit_notification",
380 "/notification/{notification_id}.{format}/edit",
370 "/notification/{notification_id}.{format}/edit",
381 action="edit", conditions=dict(method=["GET"]))
371 action="edit", conditions=dict(method=["GET"]))
382 m.connect("notification", "/notification/{notification_id}",
372 m.connect("notification", "/notification/{notification_id}",
383 action="show", conditions=dict(method=["GET"]))
373 action="show", conditions=dict(method=["GET"]))
384 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
374 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
385 action="show", conditions=dict(method=["GET"]))
375 action="show", conditions=dict(method=["GET"]))
386
376
387 #ADMIN MAIN PAGES
377 #ADMIN MAIN PAGES
388 with rmap.submapper(path_prefix=ADMIN_PREFIX,
378 with rmap.submapper(path_prefix=ADMIN_PREFIX,
389 controller='admin/admin') as m:
379 controller='admin/admin') as m:
390 m.connect('admin_home', '', action='index')
380 m.connect('admin_home', '', action='index')
391 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
381 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
392 action='add_repo')
382 action='add_repo')
393
383
394 #==========================================================================
384 #==========================================================================
395 # API V2
385 # API V2
396 #==========================================================================
386 #==========================================================================
397 with rmap.submapper(path_prefix=ADMIN_PREFIX,
387 with rmap.submapper(path_prefix=ADMIN_PREFIX,
398 controller='api/api') as m:
388 controller='api/api') as m:
399 m.connect('api', '/api')
389 m.connect('api', '/api')
400
390
401 #USER JOURNAL
391 #USER JOURNAL
402 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
392 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
403 controller='journal', action='index')
393 controller='journal', action='index')
404 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
394 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
405 controller='journal', action='journal_rss')
395 controller='journal', action='journal_rss')
406 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
396 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
407 controller='journal', action='journal_atom')
397 controller='journal', action='journal_atom')
408
398
409 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
399 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
410 controller='journal', action="public_journal")
400 controller='journal', action="public_journal")
411
401
412 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
402 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
413 controller='journal', action="public_journal_rss")
403 controller='journal', action="public_journal_rss")
414
404
415 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
405 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
416 controller='journal', action="public_journal_rss")
406 controller='journal', action="public_journal_rss")
417
407
418 rmap.connect('public_journal_atom',
408 rmap.connect('public_journal_atom',
419 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
409 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
420 action="public_journal_atom")
410 action="public_journal_atom")
421
411
422 rmap.connect('public_journal_atom_old',
412 rmap.connect('public_journal_atom_old',
423 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
413 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
424 action="public_journal_atom")
414 action="public_journal_atom")
425
415
426 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
416 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
427 controller='journal', action='toggle_following',
417 controller='journal', action='toggle_following',
428 conditions=dict(method=["POST"]))
418 conditions=dict(method=["POST"]))
429
419
430 #SEARCH
420 #SEARCH
431 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
421 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
432 rmap.connect('search_repo_admin', '%s/search/{repo_name:.*}' % ADMIN_PREFIX,
422 rmap.connect('search_repo_admin', '%s/search/{repo_name:.*}' % ADMIN_PREFIX,
433 controller='search',
423 controller='search',
434 conditions=dict(function=check_repo))
424 conditions=dict(function=check_repo))
435 rmap.connect('search_repo', '/{repo_name:.*?}/search',
425 rmap.connect('search_repo', '/{repo_name:.*?}/search',
436 controller='search',
426 controller='search',
437 conditions=dict(function=check_repo),
427 conditions=dict(function=check_repo),
438 )
428 )
439
429
440 #LOGIN/LOGOUT/REGISTER/SIGN IN
430 #LOGIN/LOGOUT/REGISTER/SIGN IN
441 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
431 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
442 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
432 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
443 action='logout')
433 action='logout')
444
434
445 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
435 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
446 action='register')
436 action='register')
447
437
448 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
438 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
449 controller='login', action='password_reset')
439 controller='login', action='password_reset')
450
440
451 rmap.connect('reset_password_confirmation',
441 rmap.connect('reset_password_confirmation',
452 '%s/password_reset_confirmation' % ADMIN_PREFIX,
442 '%s/password_reset_confirmation' % ADMIN_PREFIX,
453 controller='login', action='password_reset_confirmation')
443 controller='login', action='password_reset_confirmation')
454
444
455 #FEEDS
445 #FEEDS
456 rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
446 rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
457 controller='feed', action='rss',
447 controller='feed', action='rss',
458 conditions=dict(function=check_repo))
448 conditions=dict(function=check_repo))
459
449
460 rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
450 rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
461 controller='feed', action='atom',
451 controller='feed', action='atom',
462 conditions=dict(function=check_repo))
452 conditions=dict(function=check_repo))
463
453
464 #==========================================================================
454 #==========================================================================
465 # REPOSITORY ROUTES
455 # REPOSITORY ROUTES
466 #==========================================================================
456 #==========================================================================
467 rmap.connect('summary_home', '/{repo_name:.*?}',
457 rmap.connect('summary_home', '/{repo_name:.*?}',
468 controller='summary',
458 controller='summary',
469 conditions=dict(function=check_repo))
459 conditions=dict(function=check_repo))
470
460
471 rmap.connect('repo_size', '/{repo_name:.*?}/repo_size',
461 rmap.connect('repo_size', '/{repo_name:.*?}/repo_size',
472 controller='summary', action='repo_size',
462 controller='summary', action='repo_size',
473 conditions=dict(function=check_repo))
463 conditions=dict(function=check_repo))
474
464
475 rmap.connect('repos_group_home', '/{group_name:.*}',
465 rmap.connect('repos_group_home', '/{group_name:.*}',
476 controller='admin/repos_groups', action="show_by_name",
466 controller='admin/repos_groups', action="show_by_name",
477 conditions=dict(function=check_group))
467 conditions=dict(function=check_group))
478
468
479 rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision}',
469 rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision}',
480 controller='changeset', revision='tip',
470 controller='changeset', revision='tip',
481 conditions=dict(function=check_repo))
471 conditions=dict(function=check_repo))
482
472
483 # no longer user, but kept for routes to work
473 # no longer user, but kept for routes to work
484 rmap.connect("_edit_repo", "/{repo_name:.*?}/edit",
474 rmap.connect("_edit_repo", "/{repo_name:.*?}/edit",
485 controller='admin/repos', action="edit",
475 controller='admin/repos', action="edit",
486 conditions=dict(method=["GET"], function=check_repo)
476 conditions=dict(method=["GET"], function=check_repo)
487 )
477 )
488
478
489 rmap.connect("edit_repo", "/{repo_name:.*?}/settings",
479 rmap.connect("edit_repo", "/{repo_name:.*?}/settings",
490 controller='admin/repos', action="edit",
480 controller='admin/repos', action="edit",
491 conditions=dict(method=["GET"], function=check_repo)
481 conditions=dict(method=["GET"], function=check_repo)
492 )
482 )
493
483
494 #still working url for backward compat.
484 #still working url for backward compat.
495 rmap.connect('raw_changeset_home_depraced',
485 rmap.connect('raw_changeset_home_depraced',
496 '/{repo_name:.*?}/raw-changeset/{revision}',
486 '/{repo_name:.*?}/raw-changeset/{revision}',
497 controller='changeset', action='changeset_raw',
487 controller='changeset', action='changeset_raw',
498 revision='tip', conditions=dict(function=check_repo))
488 revision='tip', conditions=dict(function=check_repo))
499
489
500 ## new URLs
490 ## new URLs
501 rmap.connect('changeset_raw_home',
491 rmap.connect('changeset_raw_home',
502 '/{repo_name:.*?}/changeset-diff/{revision}',
492 '/{repo_name:.*?}/changeset-diff/{revision}',
503 controller='changeset', action='changeset_raw',
493 controller='changeset', action='changeset_raw',
504 revision='tip', conditions=dict(function=check_repo))
494 revision='tip', conditions=dict(function=check_repo))
505
495
506 rmap.connect('changeset_patch_home',
496 rmap.connect('changeset_patch_home',
507 '/{repo_name:.*?}/changeset-patch/{revision}',
497 '/{repo_name:.*?}/changeset-patch/{revision}',
508 controller='changeset', action='changeset_patch',
498 controller='changeset', action='changeset_patch',
509 revision='tip', conditions=dict(function=check_repo))
499 revision='tip', conditions=dict(function=check_repo))
510
500
511 rmap.connect('changeset_download_home',
501 rmap.connect('changeset_download_home',
512 '/{repo_name:.*?}/changeset-download/{revision}',
502 '/{repo_name:.*?}/changeset-download/{revision}',
513 controller='changeset', action='changeset_download',
503 controller='changeset', action='changeset_download',
514 revision='tip', conditions=dict(function=check_repo))
504 revision='tip', conditions=dict(function=check_repo))
515
505
516 rmap.connect('changeset_comment',
506 rmap.connect('changeset_comment',
517 '/{repo_name:.*?}/changeset/{revision}/comment',
507 '/{repo_name:.*?}/changeset/{revision}/comment',
518 controller='changeset', revision='tip', action='comment',
508 controller='changeset', revision='tip', action='comment',
519 conditions=dict(function=check_repo))
509 conditions=dict(function=check_repo))
520
510
521 rmap.connect('changeset_comment_preview',
511 rmap.connect('changeset_comment_preview',
522 '/{repo_name:.*?}/changeset/comment/preview',
512 '/{repo_name:.*?}/changeset/comment/preview',
523 controller='changeset', action='preview_comment',
513 controller='changeset', action='preview_comment',
524 conditions=dict(function=check_repo, method=["POST"]))
514 conditions=dict(function=check_repo, method=["POST"]))
525
515
526 rmap.connect('changeset_comment_delete',
516 rmap.connect('changeset_comment_delete',
527 '/{repo_name:.*?}/changeset/comment/{comment_id}/delete',
517 '/{repo_name:.*?}/changeset/comment/{comment_id}/delete',
528 controller='changeset', action='delete_comment',
518 controller='changeset', action='delete_comment',
529 conditions=dict(function=check_repo, method=["DELETE"]))
519 conditions=dict(function=check_repo, method=["DELETE"]))
530
520
531 rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
521 rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
532 controller='changeset', action='changeset_info')
522 controller='changeset', action='changeset_info')
533
523
534 rmap.connect('compare_url',
524 rmap.connect('compare_url',
535 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref:.*?}...{other_ref_type}@{other_ref:.*?}',
525 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref:.*?}...{other_ref_type}@{other_ref:.*?}',
536 controller='compare', action='index',
526 controller='compare', action='index',
537 conditions=dict(function=check_repo),
527 conditions=dict(function=check_repo),
538 requirements=dict(
528 requirements=dict(
539 org_ref_type='(branch|book|tag|rev|__other_ref_type__)',
529 org_ref_type='(branch|book|tag|rev|__other_ref_type__)',
540 other_ref_type='(branch|book|tag|rev|__org_ref_type__)')
530 other_ref_type='(branch|book|tag|rev|__org_ref_type__)')
541 )
531 )
542
532
543 rmap.connect('pullrequest_home',
533 rmap.connect('pullrequest_home',
544 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
534 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
545 action='index', conditions=dict(function=check_repo,
535 action='index', conditions=dict(function=check_repo,
546 method=["GET"]))
536 method=["GET"]))
547
537
548 rmap.connect('pullrequest',
538 rmap.connect('pullrequest',
549 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
539 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
550 action='create', conditions=dict(function=check_repo,
540 action='create', conditions=dict(function=check_repo,
551 method=["POST"]))
541 method=["POST"]))
552
542
553 rmap.connect('pullrequest_show',
543 rmap.connect('pullrequest_show',
554 '/{repo_name:.*?}/pull-request/{pull_request_id}',
544 '/{repo_name:.*?}/pull-request/{pull_request_id}',
555 controller='pullrequests',
545 controller='pullrequests',
556 action='show', conditions=dict(function=check_repo,
546 action='show', conditions=dict(function=check_repo,
557 method=["GET"]))
547 method=["GET"]))
558 rmap.connect('pullrequest_update',
548 rmap.connect('pullrequest_update',
559 '/{repo_name:.*?}/pull-request/{pull_request_id}',
549 '/{repo_name:.*?}/pull-request/{pull_request_id}',
560 controller='pullrequests',
550 controller='pullrequests',
561 action='update', conditions=dict(function=check_repo,
551 action='update', conditions=dict(function=check_repo,
562 method=["PUT"]))
552 method=["PUT"]))
563 rmap.connect('pullrequest_delete',
553 rmap.connect('pullrequest_delete',
564 '/{repo_name:.*?}/pull-request/{pull_request_id}',
554 '/{repo_name:.*?}/pull-request/{pull_request_id}',
565 controller='pullrequests',
555 controller='pullrequests',
566 action='delete', conditions=dict(function=check_repo,
556 action='delete', conditions=dict(function=check_repo,
567 method=["DELETE"]))
557 method=["DELETE"]))
568
558
569 rmap.connect('pullrequest_show_all',
559 rmap.connect('pullrequest_show_all',
570 '/{repo_name:.*?}/pull-request',
560 '/{repo_name:.*?}/pull-request',
571 controller='pullrequests',
561 controller='pullrequests',
572 action='show_all', conditions=dict(function=check_repo,
562 action='show_all', conditions=dict(function=check_repo,
573 method=["GET"]))
563 method=["GET"]))
574
564
575 rmap.connect('pullrequest_comment',
565 rmap.connect('pullrequest_comment',
576 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
566 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
577 controller='pullrequests',
567 controller='pullrequests',
578 action='comment', conditions=dict(function=check_repo,
568 action='comment', conditions=dict(function=check_repo,
579 method=["POST"]))
569 method=["POST"]))
580
570
581 rmap.connect('pullrequest_comment_delete',
571 rmap.connect('pullrequest_comment_delete',
582 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
572 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
583 controller='pullrequests', action='delete_comment',
573 controller='pullrequests', action='delete_comment',
584 conditions=dict(function=check_repo, method=["DELETE"]))
574 conditions=dict(function=check_repo, method=["DELETE"]))
585
575
586 rmap.connect('summary_home_summary', '/{repo_name:.*?}/summary',
576 rmap.connect('summary_home_summary', '/{repo_name:.*?}/summary',
587 controller='summary', conditions=dict(function=check_repo))
577 controller='summary', conditions=dict(function=check_repo))
588
578
589 rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
579 rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
590 controller='shortlog', conditions=dict(function=check_repo))
580 controller='shortlog', conditions=dict(function=check_repo))
591
581
592 rmap.connect('shortlog_file_home', '/{repo_name:.*?}/shortlog/{revision}/{f_path:.*}',
582 rmap.connect('shortlog_file_home', '/{repo_name:.*?}/shortlog/{revision}/{f_path:.*}',
593 controller='shortlog', f_path=None,
583 controller='shortlog', f_path=None,
594 conditions=dict(function=check_repo))
584 conditions=dict(function=check_repo))
595
585
596 rmap.connect('branches_home', '/{repo_name:.*?}/branches',
586 rmap.connect('branches_home', '/{repo_name:.*?}/branches',
597 controller='branches', conditions=dict(function=check_repo))
587 controller='branches', conditions=dict(function=check_repo))
598
588
599 rmap.connect('tags_home', '/{repo_name:.*?}/tags',
589 rmap.connect('tags_home', '/{repo_name:.*?}/tags',
600 controller='tags', conditions=dict(function=check_repo))
590 controller='tags', conditions=dict(function=check_repo))
601
591
602 rmap.connect('bookmarks_home', '/{repo_name:.*?}/bookmarks',
592 rmap.connect('bookmarks_home', '/{repo_name:.*?}/bookmarks',
603 controller='bookmarks', conditions=dict(function=check_repo))
593 controller='bookmarks', conditions=dict(function=check_repo))
604
594
605 rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
595 rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
606 controller='changelog', conditions=dict(function=check_repo))
596 controller='changelog', conditions=dict(function=check_repo))
607
597
608 rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
598 rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
609 controller='changelog', action='changelog_details',
599 controller='changelog', action='changelog_details',
610 conditions=dict(function=check_repo))
600 conditions=dict(function=check_repo))
611
601
612 rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
602 rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
613 controller='files', revision='tip', f_path='',
603 controller='files', revision='tip', f_path='',
614 conditions=dict(function=check_repo))
604 conditions=dict(function=check_repo))
615
605
616 rmap.connect('files_home_nopath', '/{repo_name:.*?}/files/{revision}',
606 rmap.connect('files_home_nopath', '/{repo_name:.*?}/files/{revision}',
617 controller='files', revision='tip', f_path='',
607 controller='files', revision='tip', f_path='',
618 conditions=dict(function=check_repo))
608 conditions=dict(function=check_repo))
619
609
620 rmap.connect('files_history_home',
610 rmap.connect('files_history_home',
621 '/{repo_name:.*?}/history/{revision}/{f_path:.*}',
611 '/{repo_name:.*?}/history/{revision}/{f_path:.*}',
622 controller='files', action='history', revision='tip', f_path='',
612 controller='files', action='history', revision='tip', f_path='',
623 conditions=dict(function=check_repo))
613 conditions=dict(function=check_repo))
624
614
625 rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
615 rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
626 controller='files', action='diff', revision='tip', f_path='',
616 controller='files', action='diff', revision='tip', f_path='',
627 conditions=dict(function=check_repo))
617 conditions=dict(function=check_repo))
628
618
629 rmap.connect('files_rawfile_home',
619 rmap.connect('files_rawfile_home',
630 '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
620 '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
631 controller='files', action='rawfile', revision='tip',
621 controller='files', action='rawfile', revision='tip',
632 f_path='', conditions=dict(function=check_repo))
622 f_path='', conditions=dict(function=check_repo))
633
623
634 rmap.connect('files_raw_home',
624 rmap.connect('files_raw_home',
635 '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
625 '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
636 controller='files', action='raw', revision='tip', f_path='',
626 controller='files', action='raw', revision='tip', f_path='',
637 conditions=dict(function=check_repo))
627 conditions=dict(function=check_repo))
638
628
639 rmap.connect('files_annotate_home',
629 rmap.connect('files_annotate_home',
640 '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
630 '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
641 controller='files', action='index', revision='tip',
631 controller='files', action='index', revision='tip',
642 f_path='', annotate=True, conditions=dict(function=check_repo))
632 f_path='', annotate=True, conditions=dict(function=check_repo))
643
633
644 rmap.connect('files_edit_home',
634 rmap.connect('files_edit_home',
645 '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
635 '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
646 controller='files', action='edit', revision='tip',
636 controller='files', action='edit', revision='tip',
647 f_path='', conditions=dict(function=check_repo))
637 f_path='', conditions=dict(function=check_repo))
648
638
649 rmap.connect('files_add_home',
639 rmap.connect('files_add_home',
650 '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
640 '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
651 controller='files', action='add', revision='tip',
641 controller='files', action='add', revision='tip',
652 f_path='', conditions=dict(function=check_repo))
642 f_path='', conditions=dict(function=check_repo))
653
643
654 rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
644 rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
655 controller='files', action='archivefile',
645 controller='files', action='archivefile',
656 conditions=dict(function=check_repo))
646 conditions=dict(function=check_repo))
657
647
658 rmap.connect('files_nodelist_home',
648 rmap.connect('files_nodelist_home',
659 '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
649 '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
660 controller='files', action='nodelist',
650 controller='files', action='nodelist',
661 conditions=dict(function=check_repo))
651 conditions=dict(function=check_repo))
662
652
663 rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
653 rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
664 controller='forks', action='fork_create',
654 controller='forks', action='fork_create',
665 conditions=dict(function=check_repo, method=["POST"]))
655 conditions=dict(function=check_repo, method=["POST"]))
666
656
667 rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
657 rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
668 controller='forks', action='fork',
658 controller='forks', action='fork',
669 conditions=dict(function=check_repo))
659 conditions=dict(function=check_repo))
670
660
671 rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
661 rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
672 controller='forks', action='forks',
662 controller='forks', action='forks',
673 conditions=dict(function=check_repo))
663 conditions=dict(function=check_repo))
674
664
675 rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
665 rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
676 controller='followers', action='followers',
666 controller='followers', action='followers',
677 conditions=dict(function=check_repo))
667 conditions=dict(function=check_repo))
678
668
679 return rmap
669 return rmap
@@ -1,580 +1,572 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Repositories controller for RhodeCode
6 Repositories controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 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 logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from webob.exc import HTTPInternalServerError, HTTPForbidden
31 from webob.exc import HTTPInternalServerError, HTTPForbidden
32 from pylons import request, session, tmpl_context as c, url
32 from pylons import request, session, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.exc import IntegrityError
35 from sqlalchemy.exc import IntegrityError
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
41 HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
41 HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.utils import action_logger, repo_name_slug
43 from rhodecode.lib.utils import action_logger, repo_name_slug
44 from rhodecode.lib.helpers import get_token
44 from rhodecode.lib.helpers import get_token
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
47 RhodeCodeSetting, RepositoryField
47 RhodeCodeSetting, RepositoryField
48 from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
48 from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
49 from rhodecode.model.scm import ScmModel, RepoGroupList
49 from rhodecode.model.scm import ScmModel, RepoGroupList
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from sqlalchemy.sql.expression import func
52 from sqlalchemy.sql.expression import func
53 from rhodecode.lib.exceptions import AttachedForksError
53 from rhodecode.lib.exceptions import AttachedForksError
54 from rhodecode.lib.utils2 import safe_int
54
55
55 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
56
57
57
58
58 class ReposController(BaseRepoController):
59 class ReposController(BaseRepoController):
59 """
60 """
60 REST Controller styled on the Atom Publishing Protocol"""
61 REST Controller styled on the Atom Publishing Protocol"""
61 # To properly map this controller, ensure your config/routing.py
62 # To properly map this controller, ensure your config/routing.py
62 # file has a resource setup:
63 # file has a resource setup:
63 # map.resource('repo', 'repos')
64 # map.resource('repo', 'repos')
64
65
65 @LoginRequired()
66 @LoginRequired()
66 def __before__(self):
67 def __before__(self):
67 super(ReposController, self).__before__()
68 super(ReposController, self).__before__()
68
69
69 def __load_defaults(self):
70 def __load_defaults(self):
70 acl_groups = RepoGroupList(RepoGroup.query().all(),
71 acl_groups = RepoGroupList(RepoGroup.query().all(),
71 perm_set=['group.write', 'group.admin'])
72 perm_set=['group.write', 'group.admin'])
72 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
73 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
73 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
74 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
74
75
75 repo_model = RepoModel()
76 repo_model = RepoModel()
76 c.users_array = repo_model.get_users_js()
77 c.users_array = repo_model.get_users_js()
77 c.users_groups_array = repo_model.get_users_groups_js()
78 c.users_groups_array = repo_model.get_users_groups_js()
78 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
79 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
79 c.landing_revs_choices = choices
80 c.landing_revs_choices = choices
80
81
81 def __load_data(self, repo_name=None):
82 def __load_data(self, repo_name=None):
82 """
83 """
83 Load defaults settings for edit, and update
84 Load defaults settings for edit, and update
84
85
85 :param repo_name:
86 :param repo_name:
86 """
87 """
87 self.__load_defaults()
88 self.__load_defaults()
88
89
89 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
90 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
90 repo = db_repo.scm_instance
91 repo = db_repo.scm_instance
91
92
92 if c.repo_info is None:
93 if c.repo_info is None:
93 h.not_mapped_error(repo_name)
94 h.not_mapped_error(repo_name)
94 return redirect(url('repos'))
95 return redirect(url('repos'))
95
96
96 ##override defaults for exact repo info here git/hg etc
97 ##override defaults for exact repo info here git/hg etc
97 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
98 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
98 c.landing_revs_choices = choices
99 c.landing_revs_choices = choices
99
100
100 c.default_user_id = User.get_by_username('default').user_id
101 c.default_user_id = User.get_by_username('default').user_id
101 c.in_public_journal = UserFollowing.query()\
102 c.in_public_journal = UserFollowing.query()\
102 .filter(UserFollowing.user_id == c.default_user_id)\
103 .filter(UserFollowing.user_id == c.default_user_id)\
103 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
104 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
104
105
105 if c.repo_info.stats:
106 if c.repo_info.stats:
106 # this is on what revision we ended up so we add +1 for count
107 # this is on what revision we ended up so we add +1 for count
107 last_rev = c.repo_info.stats.stat_on_revision + 1
108 last_rev = c.repo_info.stats.stat_on_revision + 1
108 else:
109 else:
109 last_rev = 0
110 last_rev = 0
110 c.stats_revision = last_rev
111 c.stats_revision = last_rev
111
112
112 c.repo_last_rev = repo.count() if repo.revisions else 0
113 c.repo_last_rev = repo.count() if repo.revisions else 0
113
114
114 if last_rev == 0 or c.repo_last_rev == 0:
115 if last_rev == 0 or c.repo_last_rev == 0:
115 c.stats_percentage = 0
116 c.stats_percentage = 0
116 else:
117 else:
117 c.stats_percentage = '%.2f' % ((float((last_rev)) /
118 c.stats_percentage = '%.2f' % ((float((last_rev)) /
118 c.repo_last_rev) * 100)
119 c.repo_last_rev) * 100)
119
120
120 c.repo_fields = RepositoryField.query()\
121 c.repo_fields = RepositoryField.query()\
121 .filter(RepositoryField.repository == db_repo).all()
122 .filter(RepositoryField.repository == db_repo).all()
122
123
123 defaults = RepoModel()._get_defaults(repo_name)
124 defaults = RepoModel()._get_defaults(repo_name)
124
125
125 c.repos_list = [('', _('--REMOVE FORK--'))]
126 c.repos_list = [('', _('--REMOVE FORK--'))]
126 c.repos_list += [(x.repo_id, x.repo_name) for x in
127 c.repos_list += [(x.repo_id, x.repo_name) for x in
127 Repository.query().order_by(Repository.repo_name).all()
128 Repository.query().order_by(Repository.repo_name).all()
128 if x.repo_id != c.repo_info.repo_id]
129 if x.repo_id != c.repo_info.repo_id]
129
130
130 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
131 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
131 return defaults
132 return defaults
132
133
133 @HasPermissionAllDecorator('hg.admin')
134 @HasPermissionAllDecorator('hg.admin')
134 def index(self, format='html'):
135 def index(self, format='html'):
135 """GET /repos: All items in the collection"""
136 """GET /repos: All items in the collection"""
136 # url('repos')
137 # url('repos')
137
138
138 c.repos_list = Repository.query()\
139 c.repos_list = Repository.query()\
139 .order_by(func.lower(Repository.repo_name))\
140 .order_by(func.lower(Repository.repo_name))\
140 .all()
141 .all()
141
142
142 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
143 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
143 admin=True,
144 admin=True,
144 super_user_actions=True)
145 super_user_actions=True)
145 #json used to render the grid
146 #json used to render the grid
146 c.data = json.dumps(repos_data)
147 c.data = json.dumps(repos_data)
147
148
148 return render('admin/repos/repos.html')
149 return render('admin/repos/repos.html')
149
150
150 @NotAnonymous()
151 @NotAnonymous()
151 def create(self):
152 def create(self):
152 """
153 """
153 POST /repos: Create a new item"""
154 POST /repos: Create a new item"""
154 # url('repos')
155 # url('repos')
155
156
156 self.__load_defaults()
157 self.__load_defaults()
157 form_result = {}
158 form_result = {}
158 try:
159 try:
159 form_result = RepoForm(repo_groups=c.repo_groups_choices,
160 form_result = RepoForm(repo_groups=c.repo_groups_choices,
160 landing_revs=c.landing_revs_choices)()\
161 landing_revs=c.landing_revs_choices)()\
161 .to_python(dict(request.POST))
162 .to_python(dict(request.POST))
162
163
163 new_repo = RepoModel().create(form_result,
164 new_repo = RepoModel().create(form_result,
164 self.rhodecode_user.user_id)
165 self.rhodecode_user.user_id)
165 if form_result['clone_uri']:
166 if form_result['clone_uri']:
166 h.flash(_('Created repository %s from %s') \
167 h.flash(_('Created repository %s from %s') \
167 % (form_result['repo_name'], form_result['clone_uri']),
168 % (form_result['repo_name'], form_result['clone_uri']),
168 category='success')
169 category='success')
169 else:
170 else:
170 repo_url = h.link_to(form_result['repo_name'],
171 repo_url = h.link_to(form_result['repo_name'],
171 h.url('summary_home', repo_name=form_result['repo_name_full']))
172 h.url('summary_home', repo_name=form_result['repo_name_full']))
172 h.flash(h.literal(_('Created repository %s') % repo_url),
173 h.flash(h.literal(_('Created repository %s') % repo_url),
173 category='success')
174 category='success')
174
175
175 if request.POST.get('user_created'):
176 if request.POST.get('user_created'):
176 # created by regular non admin user
177 # created by regular non admin user
177 action_logger(self.rhodecode_user, 'user_created_repo',
178 action_logger(self.rhodecode_user, 'user_created_repo',
178 form_result['repo_name_full'], self.ip_addr,
179 form_result['repo_name_full'], self.ip_addr,
179 self.sa)
180 self.sa)
180 else:
181 else:
181 action_logger(self.rhodecode_user, 'admin_created_repo',
182 action_logger(self.rhodecode_user, 'admin_created_repo',
182 form_result['repo_name_full'], self.ip_addr,
183 form_result['repo_name_full'], self.ip_addr,
183 self.sa)
184 self.sa)
184 Session().commit()
185 Session().commit()
185 except formencode.Invalid, errors:
186 except formencode.Invalid, errors:
186 return htmlfill.render(
187 return htmlfill.render(
187 render('admin/repos/repo_add.html'),
188 render('admin/repos/repo_add.html'),
188 defaults=errors.value,
189 defaults=errors.value,
189 errors=errors.error_dict or {},
190 errors=errors.error_dict or {},
190 prefix_error=False,
191 prefix_error=False,
191 encoding="UTF-8")
192 encoding="UTF-8")
192
193
193 except Exception:
194 except Exception:
194 log.error(traceback.format_exc())
195 log.error(traceback.format_exc())
195 msg = _('Error creating repository %s') \
196 msg = _('Error creating repository %s') \
196 % form_result.get('repo_name')
197 % form_result.get('repo_name')
197 h.flash(msg, category='error')
198 h.flash(msg, category='error')
198 if c.rhodecode_user.is_admin:
199 if c.rhodecode_user.is_admin:
199 return redirect(url('repos'))
200 return redirect(url('repos'))
200 return redirect(url('home'))
201 return redirect(url('home'))
201 #redirect to our new repo !
202 #redirect to our new repo !
202 return redirect(url('summary_home', repo_name=new_repo.repo_name))
203 return redirect(url('summary_home', repo_name=new_repo.repo_name))
203
204
204 @NotAnonymous()
205 @NotAnonymous()
205 def create_repository(self):
206 def create_repository(self):
206 """GET /_admin/create_repository: Form to create a new item"""
207 """GET /_admin/create_repository: Form to create a new item"""
207 new_repo = request.GET.get('repo', '')
208 new_repo = request.GET.get('repo', '')
208 parent_group = request.GET.get('parent_group')
209 parent_group = request.GET.get('parent_group')
209 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
210 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
210 #you're not super admin nor have global create permissions,
211 #you're not super admin nor have global create permissions,
211 #but maybe you have at least write permission to a parent group ?
212 #but maybe you have at least write permission to a parent group ?
212 _gr = RepoGroup.get(parent_group)
213 _gr = RepoGroup.get(parent_group)
213 gr_name = _gr.group_name if _gr else None
214 gr_name = _gr.group_name if _gr else None
214 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
215 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
215 raise HTTPForbidden
216 raise HTTPForbidden
216
217
217 acl_groups = RepoGroupList(RepoGroup.query().all(),
218 acl_groups = RepoGroupList(RepoGroup.query().all(),
218 perm_set=['group.write', 'group.admin'])
219 perm_set=['group.write', 'group.admin'])
219 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
220 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
220 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
221 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
221 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
222 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
222
223
223 c.new_repo = repo_name_slug(new_repo)
224 c.new_repo = repo_name_slug(new_repo)
224
225
225 ## apply the defaults from defaults page
226 ## apply the defaults from defaults page
226 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
227 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
227 if parent_group:
228 if parent_group:
228 defaults.update({'repo_group': parent_group})
229 defaults.update({'repo_group': parent_group})
229
230
230 return htmlfill.render(
231 return htmlfill.render(
231 render('admin/repos/repo_add.html'),
232 render('admin/repos/repo_add.html'),
232 defaults=defaults,
233 defaults=defaults,
233 errors={},
234 errors={},
234 prefix_error=False,
235 prefix_error=False,
235 encoding="UTF-8"
236 encoding="UTF-8"
236 )
237 )
237
238
238 @HasRepoPermissionAllDecorator('repository.admin')
239 @HasRepoPermissionAllDecorator('repository.admin')
239 def update(self, repo_name):
240 def update(self, repo_name):
240 """
241 """
241 PUT /repos/repo_name: Update an existing item"""
242 PUT /repos/repo_name: Update an existing item"""
242 # Forms posted to this method should contain a hidden field:
243 # Forms posted to this method should contain a hidden field:
243 # <input type="hidden" name="_method" value="PUT" />
244 # <input type="hidden" name="_method" value="PUT" />
244 # Or using helpers:
245 # Or using helpers:
245 # h.form(url('repo', repo_name=ID),
246 # h.form(url('repo', repo_name=ID),
246 # method='put')
247 # method='put')
247 # url('repo', repo_name=ID)
248 # url('repo', repo_name=ID)
248 self.__load_defaults()
249 self.__load_defaults()
249 repo_model = RepoModel()
250 repo_model = RepoModel()
250 changed_name = repo_name
251 changed_name = repo_name
251 #override the choices with extracted revisions !
252 #override the choices with extracted revisions !
252 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
253 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
253 c.landing_revs_choices = choices
254 c.landing_revs_choices = choices
254 repo = Repository.get_by_repo_name(repo_name)
255 repo = Repository.get_by_repo_name(repo_name)
255 _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
256 _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
256 'repo_group': repo.group.get_dict() \
257 'repo_group': repo.group.get_dict() \
257 if repo.group else {}},
258 if repo.group else {}},
258 repo_groups=c.repo_groups_choices,
259 repo_groups=c.repo_groups_choices,
259 landing_revs=c.landing_revs_choices)()
260 landing_revs=c.landing_revs_choices)()
260 try:
261 try:
261 form_result = _form.to_python(dict(request.POST))
262 form_result = _form.to_python(dict(request.POST))
262 repo = repo_model.update(repo_name, **form_result)
263 repo = repo_model.update(repo_name, **form_result)
263 ScmModel().mark_for_invalidation(repo_name)
264 ScmModel().mark_for_invalidation(repo_name)
264 h.flash(_('Repository %s updated successfully') % repo_name,
265 h.flash(_('Repository %s updated successfully') % repo_name,
265 category='success')
266 category='success')
266 changed_name = repo.repo_name
267 changed_name = repo.repo_name
267 action_logger(self.rhodecode_user, 'admin_updated_repo',
268 action_logger(self.rhodecode_user, 'admin_updated_repo',
268 changed_name, self.ip_addr, self.sa)
269 changed_name, self.ip_addr, self.sa)
269 Session().commit()
270 Session().commit()
270 except formencode.Invalid, errors:
271 except formencode.Invalid, errors:
271 defaults = self.__load_data(repo_name)
272 defaults = self.__load_data(repo_name)
272 defaults.update(errors.value)
273 defaults.update(errors.value)
273 return htmlfill.render(
274 return htmlfill.render(
274 render('admin/repos/repo_edit.html'),
275 render('admin/repos/repo_edit.html'),
275 defaults=defaults,
276 defaults=defaults,
276 errors=errors.error_dict or {},
277 errors=errors.error_dict or {},
277 prefix_error=False,
278 prefix_error=False,
278 encoding="UTF-8")
279 encoding="UTF-8")
279
280
280 except Exception:
281 except Exception:
281 log.error(traceback.format_exc())
282 log.error(traceback.format_exc())
282 h.flash(_('Error occurred during update of repository %s') \
283 h.flash(_('Error occurred during update of repository %s') \
283 % repo_name, category='error')
284 % repo_name, category='error')
284 return redirect(url('edit_repo', repo_name=changed_name))
285 return redirect(url('edit_repo', repo_name=changed_name))
285
286
286 @HasRepoPermissionAllDecorator('repository.admin')
287 @HasRepoPermissionAllDecorator('repository.admin')
287 def delete(self, repo_name):
288 def delete(self, repo_name):
288 """
289 """
289 DELETE /repos/repo_name: Delete an existing item"""
290 DELETE /repos/repo_name: Delete an existing item"""
290 # Forms posted to this method should contain a hidden field:
291 # Forms posted to this method should contain a hidden field:
291 # <input type="hidden" name="_method" value="DELETE" />
292 # <input type="hidden" name="_method" value="DELETE" />
292 # Or using helpers:
293 # Or using helpers:
293 # h.form(url('repo', repo_name=ID),
294 # h.form(url('repo', repo_name=ID),
294 # method='delete')
295 # method='delete')
295 # url('repo', repo_name=ID)
296 # url('repo', repo_name=ID)
296
297
297 repo_model = RepoModel()
298 repo_model = RepoModel()
298 repo = repo_model.get_by_repo_name(repo_name)
299 repo = repo_model.get_by_repo_name(repo_name)
299 if not repo:
300 if not repo:
300 h.not_mapped_error(repo_name)
301 h.not_mapped_error(repo_name)
301 return redirect(url('repos'))
302 return redirect(url('repos'))
302 try:
303 try:
303 _forks = repo.forks.count()
304 _forks = repo.forks.count()
304 handle_forks = None
305 handle_forks = None
305 if _forks and request.POST.get('forks'):
306 if _forks and request.POST.get('forks'):
306 do = request.POST['forks']
307 do = request.POST['forks']
307 if do == 'detach_forks':
308 if do == 'detach_forks':
308 handle_forks = 'detach'
309 handle_forks = 'detach'
309 h.flash(_('Detached %s forks') % _forks, category='success')
310 h.flash(_('Detached %s forks') % _forks, category='success')
310 elif do == 'delete_forks':
311 elif do == 'delete_forks':
311 handle_forks = 'delete'
312 handle_forks = 'delete'
312 h.flash(_('Deleted %s forks') % _forks, category='success')
313 h.flash(_('Deleted %s forks') % _forks, category='success')
313 repo_model.delete(repo, forks=handle_forks)
314 repo_model.delete(repo, forks=handle_forks)
314 action_logger(self.rhodecode_user, 'admin_deleted_repo',
315 action_logger(self.rhodecode_user, 'admin_deleted_repo',
315 repo_name, self.ip_addr, self.sa)
316 repo_name, self.ip_addr, self.sa)
316 ScmModel().mark_for_invalidation(repo_name)
317 ScmModel().mark_for_invalidation(repo_name)
317 h.flash(_('Deleted repository %s') % repo_name, category='success')
318 h.flash(_('Deleted repository %s') % repo_name, category='success')
318 Session().commit()
319 Session().commit()
319 except AttachedForksError:
320 except AttachedForksError:
320 h.flash(_('Cannot delete %s it still contains attached forks')
321 h.flash(_('Cannot delete %s it still contains attached forks')
321 % repo_name, category='warning')
322 % repo_name, category='warning')
322
323
323 except Exception:
324 except Exception:
324 log.error(traceback.format_exc())
325 log.error(traceback.format_exc())
325 h.flash(_('An error occurred during deletion of %s') % repo_name,
326 h.flash(_('An error occurred during deletion of %s') % repo_name,
326 category='error')
327 category='error')
327
328
328 return redirect(url('repos'))
329 return redirect(url('repos'))
329
330
330 @HasRepoPermissionAllDecorator('repository.admin')
331 @HasRepoPermissionAllDecorator('repository.admin')
331 def set_repo_perm_member(self, repo_name):
332 def set_repo_perm_member(self, repo_name):
332 form = RepoPermsForm()().to_python(request.POST)
333 form = RepoPermsForm()().to_python(request.POST)
333 RepoModel()._update_permissions(repo_name, form['perms_new'],
334 RepoModel()._update_permissions(repo_name, form['perms_new'],
334 form['perms_updates'])
335 form['perms_updates'])
335 #TODO: implement this
336 #TODO: implement this
336 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
337 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
337 # repo_name, self.ip_addr, self.sa)
338 # repo_name, self.ip_addr, self.sa)
338 Session().commit()
339 Session().commit()
339 h.flash(_('Repository permissions updated'), category='success')
340 h.flash(_('Repository permissions updated'), category='success')
340 return redirect(url('edit_repo', repo_name=repo_name))
341 return redirect(url('edit_repo', repo_name=repo_name))
341
342
342 @HasRepoPermissionAllDecorator('repository.admin')
343 @HasRepoPermissionAllDecorator('repository.admin')
343 def delete_perm_user(self, repo_name):
344 def delete_repo_perm_member(self, repo_name):
344 """
345 """
345 DELETE an existing repository permission user
346 DELETE an existing repository permission user
346
347
347 :param repo_name:
348 :param repo_name:
348 """
349 """
349 try:
350 try:
350 RepoModel().revoke_user_permission(repo=repo_name,
351 obj_type = request.POST.get('obj_type')
351 user=request.POST['user_id'])
352 obj_id = None
353 if obj_type == 'user':
354 obj_id = safe_int(request.POST.get('user_id'))
355 elif obj_type == 'user_group':
356 obj_id = safe_int(request.POST.get('user_group_id'))
357
358 if obj_type == 'user':
359 RepoModel().revoke_user_permission(repo=repo_name, user=obj_id)
360 elif obj_type == 'user_group':
361 RepoModel().revoke_users_group_permission(
362 repo=repo_name, group_name=obj_id
363 )
352 #TODO: implement this
364 #TODO: implement this
353 #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
365 #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
354 # repo_name, self.ip_addr, self.sa)
366 # repo_name, self.ip_addr, self.sa)
355 Session().commit()
367 Session().commit()
356 except Exception:
368 except Exception:
357 log.error(traceback.format_exc())
369 log.error(traceback.format_exc())
358 h.flash(_('An error occurred during deletion of repository user'),
370 h.flash(_('An error occurred during revoking of permission'),
359 category='error')
360 raise HTTPInternalServerError()
361
362 @HasRepoPermissionAllDecorator('repository.admin')
363 def delete_perm_users_group(self, repo_name):
364 """
365 DELETE an existing repository permission user group
366
367 :param repo_name:
368 """
369
370 try:
371 RepoModel().revoke_users_group_permission(
372 repo=repo_name, group_name=request.POST['users_group_id']
373 )
374 Session().commit()
375 except Exception:
376 log.error(traceback.format_exc())
377 h.flash(_('An error occurred during deletion of repository'
378 ' user groups'),
379 category='error')
371 category='error')
380 raise HTTPInternalServerError()
372 raise HTTPInternalServerError()
381
373
382 @HasRepoPermissionAllDecorator('repository.admin')
374 @HasRepoPermissionAllDecorator('repository.admin')
383 def repo_stats(self, repo_name):
375 def repo_stats(self, repo_name):
384 """
376 """
385 DELETE an existing repository statistics
377 DELETE an existing repository statistics
386
378
387 :param repo_name:
379 :param repo_name:
388 """
380 """
389
381
390 try:
382 try:
391 RepoModel().delete_stats(repo_name)
383 RepoModel().delete_stats(repo_name)
392 Session().commit()
384 Session().commit()
393 except Exception, e:
385 except Exception, e:
394 log.error(traceback.format_exc())
386 log.error(traceback.format_exc())
395 h.flash(_('An error occurred during deletion of repository stats'),
387 h.flash(_('An error occurred during deletion of repository stats'),
396 category='error')
388 category='error')
397 return redirect(url('edit_repo', repo_name=repo_name))
389 return redirect(url('edit_repo', repo_name=repo_name))
398
390
399 @HasRepoPermissionAllDecorator('repository.admin')
391 @HasRepoPermissionAllDecorator('repository.admin')
400 def repo_cache(self, repo_name):
392 def repo_cache(self, repo_name):
401 """
393 """
402 INVALIDATE existing repository cache
394 INVALIDATE existing repository cache
403
395
404 :param repo_name:
396 :param repo_name:
405 """
397 """
406
398
407 try:
399 try:
408 ScmModel().mark_for_invalidation(repo_name)
400 ScmModel().mark_for_invalidation(repo_name)
409 Session().commit()
401 Session().commit()
410 except Exception, e:
402 except Exception, e:
411 log.error(traceback.format_exc())
403 log.error(traceback.format_exc())
412 h.flash(_('An error occurred during cache invalidation'),
404 h.flash(_('An error occurred during cache invalidation'),
413 category='error')
405 category='error')
414 return redirect(url('edit_repo', repo_name=repo_name))
406 return redirect(url('edit_repo', repo_name=repo_name))
415
407
416 @HasRepoPermissionAllDecorator('repository.admin')
408 @HasRepoPermissionAllDecorator('repository.admin')
417 def repo_locking(self, repo_name):
409 def repo_locking(self, repo_name):
418 """
410 """
419 Unlock repository when it is locked !
411 Unlock repository when it is locked !
420
412
421 :param repo_name:
413 :param repo_name:
422 """
414 """
423
415
424 try:
416 try:
425 repo = Repository.get_by_repo_name(repo_name)
417 repo = Repository.get_by_repo_name(repo_name)
426 if request.POST.get('set_lock'):
418 if request.POST.get('set_lock'):
427 Repository.lock(repo, c.rhodecode_user.user_id)
419 Repository.lock(repo, c.rhodecode_user.user_id)
428 elif request.POST.get('set_unlock'):
420 elif request.POST.get('set_unlock'):
429 Repository.unlock(repo)
421 Repository.unlock(repo)
430 except Exception, e:
422 except Exception, e:
431 log.error(traceback.format_exc())
423 log.error(traceback.format_exc())
432 h.flash(_('An error occurred during unlocking'),
424 h.flash(_('An error occurred during unlocking'),
433 category='error')
425 category='error')
434 return redirect(url('edit_repo', repo_name=repo_name))
426 return redirect(url('edit_repo', repo_name=repo_name))
435
427
436 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
428 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
437 def toggle_locking(self, repo_name):
429 def toggle_locking(self, repo_name):
438 """
430 """
439 Toggle locking of repository by simple GET call to url
431 Toggle locking of repository by simple GET call to url
440
432
441 :param repo_name:
433 :param repo_name:
442 """
434 """
443
435
444 try:
436 try:
445 repo = Repository.get_by_repo_name(repo_name)
437 repo = Repository.get_by_repo_name(repo_name)
446
438
447 if repo.enable_locking:
439 if repo.enable_locking:
448 if repo.locked[0]:
440 if repo.locked[0]:
449 Repository.unlock(repo)
441 Repository.unlock(repo)
450 action = _('Unlocked')
442 action = _('Unlocked')
451 else:
443 else:
452 Repository.lock(repo, c.rhodecode_user.user_id)
444 Repository.lock(repo, c.rhodecode_user.user_id)
453 action = _('Locked')
445 action = _('Locked')
454
446
455 h.flash(_('Repository has been %s') % action,
447 h.flash(_('Repository has been %s') % action,
456 category='success')
448 category='success')
457 except Exception, e:
449 except Exception, e:
458 log.error(traceback.format_exc())
450 log.error(traceback.format_exc())
459 h.flash(_('An error occurred during unlocking'),
451 h.flash(_('An error occurred during unlocking'),
460 category='error')
452 category='error')
461 return redirect(url('summary_home', repo_name=repo_name))
453 return redirect(url('summary_home', repo_name=repo_name))
462
454
463 @HasRepoPermissionAllDecorator('repository.admin')
455 @HasRepoPermissionAllDecorator('repository.admin')
464 def repo_public_journal(self, repo_name):
456 def repo_public_journal(self, repo_name):
465 """
457 """
466 Set's this repository to be visible in public journal,
458 Set's this repository to be visible in public journal,
467 in other words assing default user to follow this repo
459 in other words assing default user to follow this repo
468
460
469 :param repo_name:
461 :param repo_name:
470 """
462 """
471
463
472 cur_token = request.POST.get('auth_token')
464 cur_token = request.POST.get('auth_token')
473 token = get_token()
465 token = get_token()
474 if cur_token == token:
466 if cur_token == token:
475 try:
467 try:
476 repo_id = Repository.get_by_repo_name(repo_name).repo_id
468 repo_id = Repository.get_by_repo_name(repo_name).repo_id
477 user_id = User.get_by_username('default').user_id
469 user_id = User.get_by_username('default').user_id
478 self.scm_model.toggle_following_repo(repo_id, user_id)
470 self.scm_model.toggle_following_repo(repo_id, user_id)
479 h.flash(_('Updated repository visibility in public journal'),
471 h.flash(_('Updated repository visibility in public journal'),
480 category='success')
472 category='success')
481 Session().commit()
473 Session().commit()
482 except Exception:
474 except Exception:
483 h.flash(_('An error occurred during setting this'
475 h.flash(_('An error occurred during setting this'
484 ' repository in public journal'),
476 ' repository in public journal'),
485 category='error')
477 category='error')
486
478
487 else:
479 else:
488 h.flash(_('Token mismatch'), category='error')
480 h.flash(_('Token mismatch'), category='error')
489 return redirect(url('edit_repo', repo_name=repo_name))
481 return redirect(url('edit_repo', repo_name=repo_name))
490
482
491 @HasRepoPermissionAllDecorator('repository.admin')
483 @HasRepoPermissionAllDecorator('repository.admin')
492 def repo_pull(self, repo_name):
484 def repo_pull(self, repo_name):
493 """
485 """
494 Runs task to update given repository with remote changes,
486 Runs task to update given repository with remote changes,
495 ie. make pull on remote location
487 ie. make pull on remote location
496
488
497 :param repo_name:
489 :param repo_name:
498 """
490 """
499 try:
491 try:
500 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
492 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
501 h.flash(_('Pulled from remote location'), category='success')
493 h.flash(_('Pulled from remote location'), category='success')
502 except Exception, e:
494 except Exception, e:
503 h.flash(_('An error occurred during pull from remote location'),
495 h.flash(_('An error occurred during pull from remote location'),
504 category='error')
496 category='error')
505
497
506 return redirect(url('edit_repo', repo_name=repo_name))
498 return redirect(url('edit_repo', repo_name=repo_name))
507
499
508 @HasRepoPermissionAllDecorator('repository.admin')
500 @HasRepoPermissionAllDecorator('repository.admin')
509 def repo_as_fork(self, repo_name):
501 def repo_as_fork(self, repo_name):
510 """
502 """
511 Mark given repository as a fork of another
503 Mark given repository as a fork of another
512
504
513 :param repo_name:
505 :param repo_name:
514 """
506 """
515 try:
507 try:
516 fork_id = request.POST.get('id_fork_of')
508 fork_id = request.POST.get('id_fork_of')
517 repo = ScmModel().mark_as_fork(repo_name, fork_id,
509 repo = ScmModel().mark_as_fork(repo_name, fork_id,
518 self.rhodecode_user.username)
510 self.rhodecode_user.username)
519 fork = repo.fork.repo_name if repo.fork else _('Nothing')
511 fork = repo.fork.repo_name if repo.fork else _('Nothing')
520 Session().commit()
512 Session().commit()
521 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
513 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
522 category='success')
514 category='success')
523 except Exception, e:
515 except Exception, e:
524 log.error(traceback.format_exc())
516 log.error(traceback.format_exc())
525 h.flash(_('An error occurred during this operation'),
517 h.flash(_('An error occurred during this operation'),
526 category='error')
518 category='error')
527
519
528 return redirect(url('edit_repo', repo_name=repo_name))
520 return redirect(url('edit_repo', repo_name=repo_name))
529
521
530 @HasPermissionAllDecorator('hg.admin')
522 @HasPermissionAllDecorator('hg.admin')
531 def show(self, repo_name, format='html'):
523 def show(self, repo_name, format='html'):
532 """GET /repos/repo_name: Show a specific item"""
524 """GET /repos/repo_name: Show a specific item"""
533 # url('repo', repo_name=ID)
525 # url('repo', repo_name=ID)
534
526
535 @HasRepoPermissionAllDecorator('repository.admin')
527 @HasRepoPermissionAllDecorator('repository.admin')
536 def edit(self, repo_name, format='html'):
528 def edit(self, repo_name, format='html'):
537 """GET /repos/repo_name/edit: Form to edit an existing item"""
529 """GET /repos/repo_name/edit: Form to edit an existing item"""
538 # url('edit_repo', repo_name=ID)
530 # url('edit_repo', repo_name=ID)
539 defaults = self.__load_data(repo_name)
531 defaults = self.__load_data(repo_name)
540
532
541 return htmlfill.render(
533 return htmlfill.render(
542 render('admin/repos/repo_edit.html'),
534 render('admin/repos/repo_edit.html'),
543 defaults=defaults,
535 defaults=defaults,
544 encoding="UTF-8",
536 encoding="UTF-8",
545 force_defaults=False
537 force_defaults=False
546 )
538 )
547
539
548 @HasPermissionAllDecorator('hg.admin')
540 @HasPermissionAllDecorator('hg.admin')
549 def create_repo_field(self, repo_name):
541 def create_repo_field(self, repo_name):
550 try:
542 try:
551 form_result = RepoFieldForm()().to_python(dict(request.POST))
543 form_result = RepoFieldForm()().to_python(dict(request.POST))
552 new_field = RepositoryField()
544 new_field = RepositoryField()
553 new_field.repository = Repository.get_by_repo_name(repo_name)
545 new_field.repository = Repository.get_by_repo_name(repo_name)
554 new_field.field_key = form_result['new_field_key']
546 new_field.field_key = form_result['new_field_key']
555 new_field.field_type = form_result['new_field_type'] # python type
547 new_field.field_type = form_result['new_field_type'] # python type
556 new_field.field_value = form_result['new_field_value'] # set initial blank value
548 new_field.field_value = form_result['new_field_value'] # set initial blank value
557 new_field.field_desc = form_result['new_field_desc']
549 new_field.field_desc = form_result['new_field_desc']
558 new_field.field_label = form_result['new_field_label']
550 new_field.field_label = form_result['new_field_label']
559 Session().add(new_field)
551 Session().add(new_field)
560 Session().commit()
552 Session().commit()
561
553
562 except Exception, e:
554 except Exception, e:
563 log.error(traceback.format_exc())
555 log.error(traceback.format_exc())
564 msg = _('An error occurred during creation of field')
556 msg = _('An error occurred during creation of field')
565 if isinstance(e, formencode.Invalid):
557 if isinstance(e, formencode.Invalid):
566 msg += ". " + e.msg
558 msg += ". " + e.msg
567 h.flash(msg, category='error')
559 h.flash(msg, category='error')
568 return redirect(url('edit_repo', repo_name=repo_name))
560 return redirect(url('edit_repo', repo_name=repo_name))
569
561
570 @HasPermissionAllDecorator('hg.admin')
562 @HasPermissionAllDecorator('hg.admin')
571 def delete_repo_field(self, repo_name, field_id):
563 def delete_repo_field(self, repo_name, field_id):
572 field = RepositoryField.get_or_404(field_id)
564 field = RepositoryField.get_or_404(field_id)
573 try:
565 try:
574 Session().delete(field)
566 Session().delete(field)
575 Session().commit()
567 Session().commit()
576 except Exception, e:
568 except Exception, e:
577 log.error(traceback.format_exc())
569 log.error(traceback.format_exc())
578 msg = _('An error occurred during removal of field')
570 msg = _('An error occurred during removal of field')
579 h.flash(msg, category='error')
571 h.flash(msg, category='error')
580 return redirect(url('edit_repo', repo_name=repo_name))
572 return redirect(url('edit_repo', repo_name=repo_name))
@@ -1,412 +1,404 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos_groups
3 rhodecode.controllers.admin.repos_groups
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository groups controller for RhodeCode
6 Repository groups controller for RhodeCode
7
7
8 :created_on: Mar 23, 2010
8 :created_on: Mar 23, 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 logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31
31
32 from pylons import request, tmpl_context as c, url
32 from pylons import request, tmpl_context as c, url
33 from pylons.controllers.util import abort, redirect
33 from pylons.controllers.util import abort, redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 from sqlalchemy.exc import IntegrityError
36 from sqlalchemy.exc import IntegrityError
37
37
38 import rhodecode
38 import rhodecode
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib.compat import json
40 from rhodecode.lib.compat import json
41 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\
41 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\
42 HasReposGroupPermissionAnyDecorator, HasReposGroupPermissionAll,\
42 HasReposGroupPermissionAnyDecorator, HasReposGroupPermissionAll,\
43 HasPermissionAll
43 HasPermissionAll
44 from rhodecode.lib.base import BaseController, render
44 from rhodecode.lib.base import BaseController, render
45 from rhodecode.model.db import RepoGroup, Repository
45 from rhodecode.model.db import RepoGroup, Repository
46 from rhodecode.model.scm import RepoGroupList
46 from rhodecode.model.scm import RepoGroupList
47 from rhodecode.model.repos_group import ReposGroupModel
47 from rhodecode.model.repos_group import ReposGroupModel
48 from rhodecode.model.forms import ReposGroupForm, RepoGroupPermsForm
48 from rhodecode.model.forms import ReposGroupForm, RepoGroupPermsForm
49 from rhodecode.model.meta import Session
49 from rhodecode.model.meta import Session
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from webob.exc import HTTPInternalServerError, HTTPNotFound
51 from webob.exc import HTTPInternalServerError, HTTPNotFound
52 from rhodecode.lib.utils2 import str2bool, safe_int
52 from rhodecode.lib.utils2 import str2bool, safe_int
53 from sqlalchemy.sql.expression import func
53 from sqlalchemy.sql.expression import func
54
54
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class ReposGroupsController(BaseController):
59 class ReposGroupsController(BaseController):
60 """REST Controller styled on the Atom Publishing Protocol"""
60 """REST Controller styled on the Atom Publishing Protocol"""
61 # To properly map this controller, ensure your config/routing.py
61 # To properly map this controller, ensure your config/routing.py
62 # file has a resource setup:
62 # file has a resource setup:
63 # map.resource('repos_group', 'repos_groups')
63 # map.resource('repos_group', 'repos_groups')
64
64
65 @LoginRequired()
65 @LoginRequired()
66 def __before__(self):
66 def __before__(self):
67 super(ReposGroupsController, self).__before__()
67 super(ReposGroupsController, self).__before__()
68
68
69 def __load_defaults(self, allow_empty_group=False, exclude_group_ids=[]):
69 def __load_defaults(self, allow_empty_group=False, exclude_group_ids=[]):
70 if HasPermissionAll('hg.admin')('group edit'):
70 if HasPermissionAll('hg.admin')('group edit'):
71 #we're global admin, we're ok and we can create TOP level groups
71 #we're global admin, we're ok and we can create TOP level groups
72 allow_empty_group = True
72 allow_empty_group = True
73
73
74 #override the choices for this form, we need to filter choices
74 #override the choices for this form, we need to filter choices
75 #and display only those we have ADMIN right
75 #and display only those we have ADMIN right
76 groups_with_admin_rights = RepoGroupList(RepoGroup.query().all(),
76 groups_with_admin_rights = RepoGroupList(RepoGroup.query().all(),
77 perm_set=['group.admin'])
77 perm_set=['group.admin'])
78 c.repo_groups = RepoGroup.groups_choices(groups=groups_with_admin_rights,
78 c.repo_groups = RepoGroup.groups_choices(groups=groups_with_admin_rights,
79 show_empty_group=allow_empty_group)
79 show_empty_group=allow_empty_group)
80 # exclude filtered ids
80 # exclude filtered ids
81 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
81 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
82 c.repo_groups)
82 c.repo_groups)
83 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
83 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
84 repo_model = RepoModel()
84 repo_model = RepoModel()
85 c.users_array = repo_model.get_users_js()
85 c.users_array = repo_model.get_users_js()
86 c.users_groups_array = repo_model.get_users_groups_js()
86 c.users_groups_array = repo_model.get_users_groups_js()
87
87
88 def __load_data(self, group_id):
88 def __load_data(self, group_id):
89 """
89 """
90 Load defaults settings for edit, and update
90 Load defaults settings for edit, and update
91
91
92 :param group_id:
92 :param group_id:
93 """
93 """
94 repo_group = RepoGroup.get_or_404(group_id)
94 repo_group = RepoGroup.get_or_404(group_id)
95 data = repo_group.get_dict()
95 data = repo_group.get_dict()
96 data['group_name'] = repo_group.name
96 data['group_name'] = repo_group.name
97
97
98 # fill repository group users
98 # fill repository group users
99 for p in repo_group.repo_group_to_perm:
99 for p in repo_group.repo_group_to_perm:
100 data.update({'u_perm_%s' % p.user.username:
100 data.update({'u_perm_%s' % p.user.username:
101 p.permission.permission_name})
101 p.permission.permission_name})
102
102
103 # fill repository group groups
103 # fill repository group groups
104 for p in repo_group.users_group_to_perm:
104 for p in repo_group.users_group_to_perm:
105 data.update({'g_perm_%s' % p.users_group.users_group_name:
105 data.update({'g_perm_%s' % p.users_group.users_group_name:
106 p.permission.permission_name})
106 p.permission.permission_name})
107
107
108 return data
108 return data
109
109
110 def _revoke_perms_on_yourself(self, form_result):
110 def _revoke_perms_on_yourself(self, form_result):
111 _up = filter(lambda u: c.rhodecode_user.username == u[0],
111 _up = filter(lambda u: c.rhodecode_user.username == u[0],
112 form_result['perms_updates'])
112 form_result['perms_updates'])
113 _new = filter(lambda u: c.rhodecode_user.username == u[0],
113 _new = filter(lambda u: c.rhodecode_user.username == u[0],
114 form_result['perms_new'])
114 form_result['perms_new'])
115 if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin':
115 if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin':
116 return True
116 return True
117 return False
117 return False
118
118
119 def index(self, format='html'):
119 def index(self, format='html'):
120 """GET /repos_groups: All items in the collection"""
120 """GET /repos_groups: All items in the collection"""
121 # url('repos_groups')
121 # url('repos_groups')
122 group_iter = RepoGroupList(RepoGroup.query().all(),
122 group_iter = RepoGroupList(RepoGroup.query().all(),
123 perm_set=['group.admin'])
123 perm_set=['group.admin'])
124 sk = lambda g: g.parents[0].group_name if g.parents else g.group_name
124 sk = lambda g: g.parents[0].group_name if g.parents else g.group_name
125 c.groups = sorted(group_iter, key=sk)
125 c.groups = sorted(group_iter, key=sk)
126 return render('admin/repos_groups/repos_groups_show.html')
126 return render('admin/repos_groups/repos_groups_show.html')
127
127
128 def create(self):
128 def create(self):
129 """POST /repos_groups: Create a new item"""
129 """POST /repos_groups: Create a new item"""
130 # url('repos_groups')
130 # url('repos_groups')
131
131
132 self.__load_defaults()
132 self.__load_defaults()
133
133
134 # permissions for can create group based on parent_id are checked
134 # permissions for can create group based on parent_id are checked
135 # here in the Form
135 # here in the Form
136 repos_group_form = ReposGroupForm(available_groups=
136 repos_group_form = ReposGroupForm(available_groups=
137 map(lambda k: unicode(k[0]), c.repo_groups))()
137 map(lambda k: unicode(k[0]), c.repo_groups))()
138 try:
138 try:
139 form_result = repos_group_form.to_python(dict(request.POST))
139 form_result = repos_group_form.to_python(dict(request.POST))
140 ReposGroupModel().create(
140 ReposGroupModel().create(
141 group_name=form_result['group_name'],
141 group_name=form_result['group_name'],
142 group_description=form_result['group_description'],
142 group_description=form_result['group_description'],
143 parent=form_result['group_parent_id'],
143 parent=form_result['group_parent_id'],
144 owner=self.rhodecode_user.user_id
144 owner=self.rhodecode_user.user_id
145 )
145 )
146 Session().commit()
146 Session().commit()
147 h.flash(_('Created repository group %s') \
147 h.flash(_('Created repository group %s') \
148 % form_result['group_name'], category='success')
148 % form_result['group_name'], category='success')
149 #TODO: in futureaction_logger(, '', '', '', self.sa)
149 #TODO: in futureaction_logger(, '', '', '', self.sa)
150 except formencode.Invalid, errors:
150 except formencode.Invalid, errors:
151 return htmlfill.render(
151 return htmlfill.render(
152 render('admin/repos_groups/repos_groups_add.html'),
152 render('admin/repos_groups/repos_groups_add.html'),
153 defaults=errors.value,
153 defaults=errors.value,
154 errors=errors.error_dict or {},
154 errors=errors.error_dict or {},
155 prefix_error=False,
155 prefix_error=False,
156 encoding="UTF-8")
156 encoding="UTF-8")
157 except Exception:
157 except Exception:
158 log.error(traceback.format_exc())
158 log.error(traceback.format_exc())
159 h.flash(_('Error occurred during creation of repository group %s') \
159 h.flash(_('Error occurred during creation of repository group %s') \
160 % request.POST.get('group_name'), category='error')
160 % request.POST.get('group_name'), category='error')
161 parent_group_id = form_result['group_parent_id']
161 parent_group_id = form_result['group_parent_id']
162 #TODO: maybe we should get back to the main view, not the admin one
162 #TODO: maybe we should get back to the main view, not the admin one
163 return redirect(url('repos_groups', parent_group=parent_group_id))
163 return redirect(url('repos_groups', parent_group=parent_group_id))
164
164
165 def new(self, format='html'):
165 def new(self, format='html'):
166 """GET /repos_groups/new: Form to create a new item"""
166 """GET /repos_groups/new: Form to create a new item"""
167 # url('new_repos_group')
167 # url('new_repos_group')
168 if HasPermissionAll('hg.admin')('group create'):
168 if HasPermissionAll('hg.admin')('group create'):
169 #we're global admin, we're ok and we can create TOP level groups
169 #we're global admin, we're ok and we can create TOP level groups
170 pass
170 pass
171 else:
171 else:
172 # we pass in parent group into creation form, thus we know
172 # we pass in parent group into creation form, thus we know
173 # what would be the group, we can check perms here !
173 # what would be the group, we can check perms here !
174 group_id = safe_int(request.GET.get('parent_group'))
174 group_id = safe_int(request.GET.get('parent_group'))
175 group = RepoGroup.get(group_id) if group_id else None
175 group = RepoGroup.get(group_id) if group_id else None
176 group_name = group.group_name if group else None
176 group_name = group.group_name if group else None
177 if HasReposGroupPermissionAll('group.admin')(group_name, 'group create'):
177 if HasReposGroupPermissionAll('group.admin')(group_name, 'group create'):
178 pass
178 pass
179 else:
179 else:
180 return abort(403)
180 return abort(403)
181
181
182 self.__load_defaults()
182 self.__load_defaults()
183 return render('admin/repos_groups/repos_groups_add.html')
183 return render('admin/repos_groups/repos_groups_add.html')
184
184
185 @HasReposGroupPermissionAnyDecorator('group.admin')
185 @HasReposGroupPermissionAnyDecorator('group.admin')
186 def update(self, group_name):
186 def update(self, group_name):
187 """PUT /repos_groups/group_name: Update an existing item"""
187 """PUT /repos_groups/group_name: Update an existing item"""
188 # Forms posted to this method should contain a hidden field:
188 # Forms posted to this method should contain a hidden field:
189 # <input type="hidden" name="_method" value="PUT" />
189 # <input type="hidden" name="_method" value="PUT" />
190 # Or using helpers:
190 # Or using helpers:
191 # h.form(url('repos_group', group_name=GROUP_NAME),
191 # h.form(url('repos_group', group_name=GROUP_NAME),
192 # method='put')
192 # method='put')
193 # url('repos_group', group_name=GROUP_NAME)
193 # url('repos_group', group_name=GROUP_NAME)
194
194
195 c.repos_group = ReposGroupModel()._get_repo_group(group_name)
195 c.repos_group = ReposGroupModel()._get_repo_group(group_name)
196 if HasPermissionAll('hg.admin')('group edit'):
196 if HasPermissionAll('hg.admin')('group edit'):
197 #we're global admin, we're ok and we can create TOP level groups
197 #we're global admin, we're ok and we can create TOP level groups
198 allow_empty_group = True
198 allow_empty_group = True
199 elif not c.repos_group.parent_group:
199 elif not c.repos_group.parent_group:
200 allow_empty_group = True
200 allow_empty_group = True
201 else:
201 else:
202 allow_empty_group = False
202 allow_empty_group = False
203 self.__load_defaults(allow_empty_group=allow_empty_group,
203 self.__load_defaults(allow_empty_group=allow_empty_group,
204 exclude_group_ids=[c.repos_group.group_id])
204 exclude_group_ids=[c.repos_group.group_id])
205
205
206 repos_group_form = ReposGroupForm(
206 repos_group_form = ReposGroupForm(
207 edit=True,
207 edit=True,
208 old_data=c.repos_group.get_dict(),
208 old_data=c.repos_group.get_dict(),
209 available_groups=c.repo_groups_choices,
209 available_groups=c.repo_groups_choices,
210 can_create_in_root=allow_empty_group,
210 can_create_in_root=allow_empty_group,
211 )()
211 )()
212 try:
212 try:
213 form_result = repos_group_form.to_python(dict(request.POST))
213 form_result = repos_group_form.to_python(dict(request.POST))
214 if not c.rhodecode_user.is_admin:
214 if not c.rhodecode_user.is_admin:
215 if self._revoke_perms_on_yourself(form_result):
215 if self._revoke_perms_on_yourself(form_result):
216 msg = _('Cannot revoke permission for yourself as admin')
216 msg = _('Cannot revoke permission for yourself as admin')
217 h.flash(msg, category='warning')
217 h.flash(msg, category='warning')
218 raise Exception('revoke admin permission on self')
218 raise Exception('revoke admin permission on self')
219
219
220 new_gr = ReposGroupModel().update(group_name, form_result)
220 new_gr = ReposGroupModel().update(group_name, form_result)
221 Session().commit()
221 Session().commit()
222 h.flash(_('Updated repository group %s') \
222 h.flash(_('Updated repository group %s') \
223 % form_result['group_name'], category='success')
223 % form_result['group_name'], category='success')
224 # we now have new name !
224 # we now have new name !
225 group_name = new_gr.group_name
225 group_name = new_gr.group_name
226 #TODO: in future action_logger(, '', '', '', self.sa)
226 #TODO: in future action_logger(, '', '', '', self.sa)
227 except formencode.Invalid, errors:
227 except formencode.Invalid, errors:
228
228
229 return htmlfill.render(
229 return htmlfill.render(
230 render('admin/repos_groups/repos_groups_edit.html'),
230 render('admin/repos_groups/repos_groups_edit.html'),
231 defaults=errors.value,
231 defaults=errors.value,
232 errors=errors.error_dict or {},
232 errors=errors.error_dict or {},
233 prefix_error=False,
233 prefix_error=False,
234 encoding="UTF-8")
234 encoding="UTF-8")
235 except Exception:
235 except Exception:
236 log.error(traceback.format_exc())
236 log.error(traceback.format_exc())
237 h.flash(_('Error occurred during update of repository group %s') \
237 h.flash(_('Error occurred during update of repository group %s') \
238 % request.POST.get('group_name'), category='error')
238 % request.POST.get('group_name'), category='error')
239
239
240 return redirect(url('edit_repos_group', group_name=group_name))
240 return redirect(url('edit_repos_group', group_name=group_name))
241
241
242 @HasReposGroupPermissionAnyDecorator('group.admin')
242 @HasReposGroupPermissionAnyDecorator('group.admin')
243 def delete(self, group_name):
243 def delete(self, group_name):
244 """DELETE /repos_groups/group_name: Delete an existing item"""
244 """DELETE /repos_groups/group_name: Delete an existing item"""
245 # Forms posted to this method should contain a hidden field:
245 # Forms posted to this method should contain a hidden field:
246 # <input type="hidden" name="_method" value="DELETE" />
246 # <input type="hidden" name="_method" value="DELETE" />
247 # Or using helpers:
247 # Or using helpers:
248 # h.form(url('repos_group', group_name=GROUP_NAME),
248 # h.form(url('repos_group', group_name=GROUP_NAME),
249 # method='delete')
249 # method='delete')
250 # url('repos_group', group_name=GROUP_NAME)
250 # url('repos_group', group_name=GROUP_NAME)
251
251
252 gr = c.repos_group = ReposGroupModel()._get_repo_group(group_name)
252 gr = c.repos_group = ReposGroupModel()._get_repo_group(group_name)
253 repos = gr.repositories.all()
253 repos = gr.repositories.all()
254 if repos:
254 if repos:
255 h.flash(_('This group contains %s repositores and cannot be '
255 h.flash(_('This group contains %s repositores and cannot be '
256 'deleted') % len(repos), category='warning')
256 'deleted') % len(repos), category='warning')
257 return redirect(url('repos_groups'))
257 return redirect(url('repos_groups'))
258
258
259 children = gr.children.all()
259 children = gr.children.all()
260 if children:
260 if children:
261 h.flash(_('This group contains %s subgroups and cannot be deleted'
261 h.flash(_('This group contains %s subgroups and cannot be deleted'
262 % (len(children))), category='warning')
262 % (len(children))), category='warning')
263 return redirect(url('repos_groups'))
263 return redirect(url('repos_groups'))
264
264
265 try:
265 try:
266 ReposGroupModel().delete(group_name)
266 ReposGroupModel().delete(group_name)
267 Session().commit()
267 Session().commit()
268 h.flash(_('Removed repository group %s') % group_name,
268 h.flash(_('Removed repository group %s') % group_name,
269 category='success')
269 category='success')
270 #TODO: in future action_logger(, '', '', '', self.sa)
270 #TODO: in future action_logger(, '', '', '', self.sa)
271 except Exception:
271 except Exception:
272 log.error(traceback.format_exc())
272 log.error(traceback.format_exc())
273 h.flash(_('Error occurred during deletion of repos '
273 h.flash(_('Error occurred during deletion of repos '
274 'group %s') % group_name, category='error')
274 'group %s') % group_name, category='error')
275
275
276 return redirect(url('repos_groups'))
276 return redirect(url('repos_groups'))
277
277
278 @HasReposGroupPermissionAnyDecorator('group.admin')
278 @HasReposGroupPermissionAnyDecorator('group.admin')
279 def set_repo_group_perm_member(self, group_name):
279 def set_repo_group_perm_member(self, group_name):
280 c.repos_group = ReposGroupModel()._get_repo_group(group_name)
280 c.repos_group = ReposGroupModel()._get_repo_group(group_name)
281 form = RepoGroupPermsForm()().to_python(request.POST)
281 form = RepoGroupPermsForm()().to_python(request.POST)
282
282
283 recursive = form['recursive']
283 recursive = form['recursive']
284 # iterate over all members(if in recursive mode) of this groups and
284 # iterate over all members(if in recursive mode) of this groups and
285 # set the permissions !
285 # set the permissions !
286 # this can be potentially heavy operation
286 # this can be potentially heavy operation
287 ReposGroupModel()._update_permissions(c.repos_group, form['perms_new'],
287 ReposGroupModel()._update_permissions(c.repos_group, form['perms_new'],
288 form['perms_updates'], recursive)
288 form['perms_updates'], recursive)
289 #TODO: implement this
289 #TODO: implement this
290 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
290 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
291 # repo_name, self.ip_addr, self.sa)
291 # repo_name, self.ip_addr, self.sa)
292 Session().commit()
292 Session().commit()
293 h.flash(_('Repository Group permissions updated'), category='success')
293 h.flash(_('Repository Group permissions updated'), category='success')
294 return redirect(url('edit_repos_group', group_name=group_name))
294 return redirect(url('edit_repos_group', group_name=group_name))
295
295
296 @HasReposGroupPermissionAnyDecorator('group.admin')
296 @HasReposGroupPermissionAnyDecorator('group.admin')
297 def delete_repos_group_user_perm(self, group_name):
297 def delete_repo_group_perm_member(self, group_name):
298 """
298 """
299 DELETE an existing repository group permission user
299 DELETE an existing repository group permission user
300
300
301 :param group_name:
301 :param group_name:
302 """
302 """
303 try:
303 try:
304 obj_type = request.POST.get('obj_type')
305 obj_id = None
306 if obj_type == 'user':
307 obj_id = safe_int(request.POST.get('user_id'))
308 elif obj_type == 'user_group':
309 obj_id = safe_int(request.POST.get('user_group_id'))
310
304 if not c.rhodecode_user.is_admin:
311 if not c.rhodecode_user.is_admin:
305 if c.rhodecode_user.user_id == safe_int(request.POST['user_id']):
312 if obj_type == 'user' and c.rhodecode_user.user_id == obj_id:
306 msg = _('Cannot revoke permission for yourself as admin')
313 msg = _('Cannot revoke permission for yourself as admin')
307 h.flash(msg, category='warning')
314 h.flash(msg, category='warning')
308 raise Exception('revoke admin permission on self')
315 raise Exception('revoke admin permission on self')
309 recursive = str2bool(request.POST.get('recursive', False))
316 recursive = str2bool(request.POST.get('recursive', False))
310 ReposGroupModel().delete_permission(
317 if obj_type == 'user':
311 repos_group=group_name, obj=request.POST['user_id'],
318 ReposGroupModel().delete_permission(
312 obj_type='user', recursive=recursive
319 repos_group=group_name, obj=obj_id,
313 )
320 obj_type='user', recursive=recursive
321 )
322 elif obj_type == 'user_group':
323 ReposGroupModel().delete_permission(
324 repos_group=group_name, obj=obj_id,
325 obj_type='users_group', recursive=recursive
326 )
327
314 Session().commit()
328 Session().commit()
315 except Exception:
329 except Exception:
316 log.error(traceback.format_exc())
330 log.error(traceback.format_exc())
317 h.flash(_('An error occurred during deletion of group user'),
331 h.flash(_('An error occurred during revoking of permission'),
318 category='error')
319 raise HTTPInternalServerError()
320
321 @HasReposGroupPermissionAnyDecorator('group.admin')
322 def delete_repos_group_users_group_perm(self, group_name):
323 """
324 DELETE an existing repository group permission user group
325
326 :param group_name:
327 """
328
329 try:
330 recursive = str2bool(request.POST.get('recursive', False))
331 ReposGroupModel().delete_permission(
332 repos_group=group_name, obj=request.POST['users_group_id'],
333 obj_type='users_group', recursive=recursive
334 )
335 Session().commit()
336 except Exception:
337 log.error(traceback.format_exc())
338 h.flash(_('An error occurred during deletion of group'
339 ' user groups'),
340 category='error')
332 category='error')
341 raise HTTPInternalServerError()
333 raise HTTPInternalServerError()
342
334
343 def show_by_name(self, group_name):
335 def show_by_name(self, group_name):
344 """
336 """
345 This is a proxy that does a lookup group_name -> id, and shows
337 This is a proxy that does a lookup group_name -> id, and shows
346 the group by id view instead
338 the group by id view instead
347 """
339 """
348 group_name = group_name.rstrip('/')
340 group_name = group_name.rstrip('/')
349 id_ = RepoGroup.get_by_group_name(group_name)
341 id_ = RepoGroup.get_by_group_name(group_name)
350 if id_:
342 if id_:
351 return self.show(id_.group_id)
343 return self.show(id_.group_id)
352 raise HTTPNotFound
344 raise HTTPNotFound
353
345
354 @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
346 @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
355 'group.admin')
347 'group.admin')
356 def show(self, group_name, format='html'):
348 def show(self, group_name, format='html'):
357 """GET /repos_groups/group_name: Show a specific item"""
349 """GET /repos_groups/group_name: Show a specific item"""
358 # url('repos_group', group_name=GROUP_NAME)
350 # url('repos_group', group_name=GROUP_NAME)
359
351
360 c.group = c.repos_group = ReposGroupModel()._get_repo_group(group_name)
352 c.group = c.repos_group = ReposGroupModel()._get_repo_group(group_name)
361 c.group_repos = c.group.repositories.all()
353 c.group_repos = c.group.repositories.all()
362
354
363 #overwrite our cached list with current filter
355 #overwrite our cached list with current filter
364 gr_filter = c.group_repos
356 gr_filter = c.group_repos
365 c.repo_cnt = 0
357 c.repo_cnt = 0
366
358
367 groups = RepoGroup.query().order_by(RepoGroup.group_name)\
359 groups = RepoGroup.query().order_by(RepoGroup.group_name)\
368 .filter(RepoGroup.group_parent_id == c.group.group_id).all()
360 .filter(RepoGroup.group_parent_id == c.group.group_id).all()
369 c.groups = self.scm_model.get_repos_groups(groups)
361 c.groups = self.scm_model.get_repos_groups(groups)
370
362
371 if not c.visual.lightweight_dashboard:
363 if not c.visual.lightweight_dashboard:
372 c.repos_list = self.scm_model.get_repos(all_repos=gr_filter)
364 c.repos_list = self.scm_model.get_repos(all_repos=gr_filter)
373 ## lightweight version of dashboard
365 ## lightweight version of dashboard
374 else:
366 else:
375 c.repos_list = Repository.query()\
367 c.repos_list = Repository.query()\
376 .filter(Repository.group_id == c.group.group_id)\
368 .filter(Repository.group_id == c.group.group_id)\
377 .order_by(func.lower(Repository.repo_name))\
369 .order_by(func.lower(Repository.repo_name))\
378 .all()
370 .all()
379
371
380 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
372 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
381 admin=False)
373 admin=False)
382 #json used to render the grid
374 #json used to render the grid
383 c.data = json.dumps(repos_data)
375 c.data = json.dumps(repos_data)
384
376
385 return render('admin/repos_groups/repos_groups.html')
377 return render('admin/repos_groups/repos_groups.html')
386
378
387 @HasReposGroupPermissionAnyDecorator('group.admin')
379 @HasReposGroupPermissionAnyDecorator('group.admin')
388 def edit(self, group_name, format='html'):
380 def edit(self, group_name, format='html'):
389 """GET /repos_groups/group_name/edit: Form to edit an existing item"""
381 """GET /repos_groups/group_name/edit: Form to edit an existing item"""
390 # url('edit_repos_group', group_name=GROUP_NAME)
382 # url('edit_repos_group', group_name=GROUP_NAME)
391
383
392 c.repos_group = ReposGroupModel()._get_repo_group(group_name)
384 c.repos_group = ReposGroupModel()._get_repo_group(group_name)
393 #we can only allow moving empty group if it's already a top-level
385 #we can only allow moving empty group if it's already a top-level
394 #group, ie has no parents, or we're admin
386 #group, ie has no parents, or we're admin
395 if HasPermissionAll('hg.admin')('group edit'):
387 if HasPermissionAll('hg.admin')('group edit'):
396 #we're global admin, we're ok and we can create TOP level groups
388 #we're global admin, we're ok and we can create TOP level groups
397 allow_empty_group = True
389 allow_empty_group = True
398 elif not c.repos_group.parent_group:
390 elif not c.repos_group.parent_group:
399 allow_empty_group = True
391 allow_empty_group = True
400 else:
392 else:
401 allow_empty_group = False
393 allow_empty_group = False
402
394
403 self.__load_defaults(allow_empty_group=allow_empty_group,
395 self.__load_defaults(allow_empty_group=allow_empty_group,
404 exclude_group_ids=[c.repos_group.group_id])
396 exclude_group_ids=[c.repos_group.group_id])
405 defaults = self.__load_data(c.repos_group.group_id)
397 defaults = self.__load_data(c.repos_group.group_id)
406
398
407 return htmlfill.render(
399 return htmlfill.render(
408 render('admin/repos_groups/repos_groups_edit.html'),
400 render('admin/repos_groups/repos_groups_edit.html'),
409 defaults=defaults,
401 defaults=defaults,
410 encoding="UTF-8",
402 encoding="UTF-8",
411 force_defaults=False
403 force_defaults=False
412 )
404 )
@@ -1,367 +1,367 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users_groups
3 rhodecode.controllers.admin.users_groups
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 User Groups crud controller for pylons
6 User Groups crud controller for pylons
7
7
8 :created_on: Jan 25, 2011
8 :created_on: Jan 25, 2011
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 logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, session, tmpl_context as c, url, config
31 from pylons import request, session, tmpl_context as c, url, config
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.lib.exceptions import UserGroupsAssignedException
36 from rhodecode.lib.exceptions import UserGroupsAssignedException
37 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
37 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
39 HasUserGroupPermissionAnyDecorator
39 HasUserGroupPermissionAnyDecorator
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.model.scm import UserGroupList
41 from rhodecode.model.scm import UserGroupList
42 from rhodecode.model.users_group import UserGroupModel
42 from rhodecode.model.users_group import UserGroupModel
43 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.db import User, UserGroup, UserGroupToPerm,\
44 from rhodecode.model.db import User, UserGroup, UserGroupToPerm,\
45 UserGroupRepoToPerm, UserGroupRepoGroupToPerm
45 UserGroupRepoToPerm, UserGroupRepoGroupToPerm
46 from rhodecode.model.forms import UserGroupForm, UserGroupPermsForm
46 from rhodecode.model.forms import UserGroupForm, UserGroupPermsForm
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.lib.utils import action_logger
48 from rhodecode.lib.utils import action_logger
49 from sqlalchemy.orm import joinedload
49 from sqlalchemy.orm import joinedload
50 from webob.exc import HTTPInternalServerError
50 from webob.exc import HTTPInternalServerError
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class UsersGroupsController(BaseController):
55 class UsersGroupsController(BaseController):
56 """REST Controller styled on the Atom Publishing Protocol"""
56 """REST Controller styled on the Atom Publishing Protocol"""
57 # To properly map this controller, ensure your config/routing.py
57 # To properly map this controller, ensure your config/routing.py
58 # file has a resource setup:
58 # file has a resource setup:
59 # map.resource('users_group', 'users_groups')
59 # map.resource('users_group', 'users_groups')
60
60
61 @LoginRequired()
61 @LoginRequired()
62 def __before__(self):
62 def __before__(self):
63 super(UsersGroupsController, self).__before__()
63 super(UsersGroupsController, self).__before__()
64 c.available_permissions = config['available_permissions']
64 c.available_permissions = config['available_permissions']
65
65
66 def __load_data(self, user_group_id):
66 def __load_data(self, user_group_id):
67 ugroup_repo_perms = UserGroupRepoToPerm.query()\
67 ugroup_repo_perms = UserGroupRepoToPerm.query()\
68 .options(joinedload(UserGroupRepoToPerm.permission))\
68 .options(joinedload(UserGroupRepoToPerm.permission))\
69 .options(joinedload(UserGroupRepoToPerm.repository))\
69 .options(joinedload(UserGroupRepoToPerm.repository))\
70 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
70 .filter(UserGroupRepoToPerm.users_group_id == user_group_id)\
71 .all()
71 .all()
72
72
73 for gr in ugroup_repo_perms:
73 for gr in ugroup_repo_perms:
74 c.users_group.permissions['repositories'][gr.repository.repo_name] \
74 c.users_group.permissions['repositories'][gr.repository.repo_name] \
75 = gr.permission.permission_name
75 = gr.permission.permission_name
76
76
77 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
77 ugroup_group_perms = UserGroupRepoGroupToPerm.query()\
78 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
78 .options(joinedload(UserGroupRepoGroupToPerm.permission))\
79 .options(joinedload(UserGroupRepoGroupToPerm.group))\
79 .options(joinedload(UserGroupRepoGroupToPerm.group))\
80 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
80 .filter(UserGroupRepoGroupToPerm.users_group_id == user_group_id)\
81 .all()
81 .all()
82
82
83 for gr in ugroup_group_perms:
83 for gr in ugroup_group_perms:
84 c.users_group.permissions['repositories_groups'][gr.group.group_name] \
84 c.users_group.permissions['repositories_groups'][gr.group.group_name] \
85 = gr.permission.permission_name
85 = gr.permission.permission_name
86
86
87 c.group_members_obj = sorted((x.user for x in c.users_group.members),
87 c.group_members_obj = sorted((x.user for x in c.users_group.members),
88 key=lambda u: u.username.lower())
88 key=lambda u: u.username.lower())
89
89
90 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
90 c.group_members = [(x.user_id, x.username) for x in c.group_members_obj]
91 c.available_members = sorted(((x.user_id, x.username) for x in
91 c.available_members = sorted(((x.user_id, x.username) for x in
92 User.query().all()),
92 User.query().all()),
93 key=lambda u: u[1].lower())
93 key=lambda u: u[1].lower())
94 repo_model = RepoModel()
94 repo_model = RepoModel()
95 c.users_array = repo_model.get_users_js()
95 c.users_array = repo_model.get_users_js()
96
96
97 # commented out due to not now supporting assignment for user group
97 # commented out due to not now supporting assignment for user group
98 # on user group
98 # on user group
99 c.users_groups_array = "[]" # repo_model.get_users_groups_js()
99 c.users_groups_array = "[]" # repo_model.get_users_groups_js()
100 c.available_permissions = config['available_permissions']
100 c.available_permissions = config['available_permissions']
101
101
102 def __load_defaults(self, user_group_id):
102 def __load_defaults(self, user_group_id):
103 """
103 """
104 Load defaults settings for edit, and update
104 Load defaults settings for edit, and update
105
105
106 :param user_group_id:
106 :param user_group_id:
107 """
107 """
108 user_group = UserGroup.get_or_404(user_group_id)
108 user_group = UserGroup.get_or_404(user_group_id)
109 data = user_group.get_dict()
109 data = user_group.get_dict()
110
110
111 ug_model = UserGroupModel()
111 ug_model = UserGroupModel()
112
112
113 data.update({
113 data.update({
114 'create_repo_perm': ug_model.has_perm(user_group,
114 'create_repo_perm': ug_model.has_perm(user_group,
115 'hg.create.repository'),
115 'hg.create.repository'),
116 'fork_repo_perm': ug_model.has_perm(user_group,
116 'fork_repo_perm': ug_model.has_perm(user_group,
117 'hg.fork.repository'),
117 'hg.fork.repository'),
118 })
118 })
119
119
120 # fill user group users
120 # fill user group users
121 for p in user_group.user_user_group_to_perm:
121 for p in user_group.user_user_group_to_perm:
122 data.update({'u_perm_%s' % p.user.username:
122 data.update({'u_perm_%s' % p.user.username:
123 p.permission.permission_name})
123 p.permission.permission_name})
124
124
125 return data
125 return data
126
126
127 def index(self, format='html'):
127 def index(self, format='html'):
128 """GET /users_groups: All items in the collection"""
128 """GET /users_groups: All items in the collection"""
129 # url('users_groups')
129 # url('users_groups')
130
130
131 group_iter = UserGroupList(UserGroup().query().all(),
131 group_iter = UserGroupList(UserGroup().query().all(),
132 perm_set=['usergroup.admin'])
132 perm_set=['usergroup.admin'])
133 sk = lambda g: g.users_group_name
133 sk = lambda g: g.users_group_name
134 c.users_groups_list = sorted(group_iter, key=sk)
134 c.users_groups_list = sorted(group_iter, key=sk)
135 return render('admin/users_groups/users_groups.html')
135 return render('admin/users_groups/users_groups.html')
136
136
137 @HasPermissionAllDecorator('hg.admin')
137 @HasPermissionAllDecorator('hg.admin')
138 def create(self):
138 def create(self):
139 """POST /users_groups: Create a new item"""
139 """POST /users_groups: Create a new item"""
140 # url('users_groups')
140 # url('users_groups')
141
141
142 users_group_form = UserGroupForm()()
142 users_group_form = UserGroupForm()()
143 try:
143 try:
144 form_result = users_group_form.to_python(dict(request.POST))
144 form_result = users_group_form.to_python(dict(request.POST))
145 UserGroupModel().create(name=form_result['users_group_name'],
145 UserGroupModel().create(name=form_result['users_group_name'],
146 owner=self.rhodecode_user.user_id,
146 owner=self.rhodecode_user.user_id,
147 active=form_result['users_group_active'])
147 active=form_result['users_group_active'])
148
148
149 gr = form_result['users_group_name']
149 gr = form_result['users_group_name']
150 action_logger(self.rhodecode_user,
150 action_logger(self.rhodecode_user,
151 'admin_created_users_group:%s' % gr,
151 'admin_created_users_group:%s' % gr,
152 None, self.ip_addr, self.sa)
152 None, self.ip_addr, self.sa)
153 h.flash(_('Created user group %s') % gr, category='success')
153 h.flash(_('Created user group %s') % gr, category='success')
154 Session().commit()
154 Session().commit()
155 except formencode.Invalid, errors:
155 except formencode.Invalid, errors:
156 return htmlfill.render(
156 return htmlfill.render(
157 render('admin/users_groups/users_group_add.html'),
157 render('admin/users_groups/users_group_add.html'),
158 defaults=errors.value,
158 defaults=errors.value,
159 errors=errors.error_dict or {},
159 errors=errors.error_dict or {},
160 prefix_error=False,
160 prefix_error=False,
161 encoding="UTF-8")
161 encoding="UTF-8")
162 except Exception:
162 except Exception:
163 log.error(traceback.format_exc())
163 log.error(traceback.format_exc())
164 h.flash(_('Error occurred during creation of user group %s') \
164 h.flash(_('Error occurred during creation of user group %s') \
165 % request.POST.get('users_group_name'), category='error')
165 % request.POST.get('users_group_name'), category='error')
166
166
167 return redirect(url('users_groups'))
167 return redirect(url('users_groups'))
168
168
169 @HasPermissionAllDecorator('hg.admin')
169 @HasPermissionAllDecorator('hg.admin')
170 def new(self, format='html'):
170 def new(self, format='html'):
171 """GET /users_groups/new: Form to create a new item"""
171 """GET /users_groups/new: Form to create a new item"""
172 # url('new_users_group')
172 # url('new_users_group')
173 return render('admin/users_groups/users_group_add.html')
173 return render('admin/users_groups/users_group_add.html')
174
174
175 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
175 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
176 def update(self, id):
176 def update(self, id):
177 """PUT /users_groups/id: Update an existing item"""
177 """PUT /users_groups/id: Update an existing item"""
178 # Forms posted to this method should contain a hidden field:
178 # Forms posted to this method should contain a hidden field:
179 # <input type="hidden" name="_method" value="PUT" />
179 # <input type="hidden" name="_method" value="PUT" />
180 # Or using helpers:
180 # Or using helpers:
181 # h.form(url('users_group', id=ID),
181 # h.form(url('users_group', id=ID),
182 # method='put')
182 # method='put')
183 # url('users_group', id=ID)
183 # url('users_group', id=ID)
184
184
185 c.users_group = UserGroup.get_or_404(id)
185 c.users_group = UserGroup.get_or_404(id)
186 self.__load_data(id)
186 self.__load_data(id)
187
187
188 available_members = [safe_unicode(x[0]) for x in c.available_members]
188 available_members = [safe_unicode(x[0]) for x in c.available_members]
189
189
190 users_group_form = UserGroupForm(edit=True,
190 users_group_form = UserGroupForm(edit=True,
191 old_data=c.users_group.get_dict(),
191 old_data=c.users_group.get_dict(),
192 available_members=available_members)()
192 available_members=available_members)()
193
193
194 try:
194 try:
195 form_result = users_group_form.to_python(request.POST)
195 form_result = users_group_form.to_python(request.POST)
196 UserGroupModel().update(c.users_group, form_result)
196 UserGroupModel().update(c.users_group, form_result)
197 gr = form_result['users_group_name']
197 gr = form_result['users_group_name']
198 action_logger(self.rhodecode_user,
198 action_logger(self.rhodecode_user,
199 'admin_updated_users_group:%s' % gr,
199 'admin_updated_users_group:%s' % gr,
200 None, self.ip_addr, self.sa)
200 None, self.ip_addr, self.sa)
201 h.flash(_('Updated user group %s') % gr, category='success')
201 h.flash(_('Updated user group %s') % gr, category='success')
202 Session().commit()
202 Session().commit()
203 except formencode.Invalid, errors:
203 except formencode.Invalid, errors:
204 ug_model = UserGroupModel()
204 ug_model = UserGroupModel()
205 defaults = errors.value
205 defaults = errors.value
206 e = errors.error_dict or {}
206 e = errors.error_dict or {}
207 defaults.update({
207 defaults.update({
208 'create_repo_perm': ug_model.has_perm(id,
208 'create_repo_perm': ug_model.has_perm(id,
209 'hg.create.repository'),
209 'hg.create.repository'),
210 'fork_repo_perm': ug_model.has_perm(id,
210 'fork_repo_perm': ug_model.has_perm(id,
211 'hg.fork.repository'),
211 'hg.fork.repository'),
212 '_method': 'put'
212 '_method': 'put'
213 })
213 })
214
214
215 return htmlfill.render(
215 return htmlfill.render(
216 render('admin/users_groups/users_group_edit.html'),
216 render('admin/users_groups/users_group_edit.html'),
217 defaults=defaults,
217 defaults=defaults,
218 errors=e,
218 errors=e,
219 prefix_error=False,
219 prefix_error=False,
220 encoding="UTF-8")
220 encoding="UTF-8")
221 except Exception:
221 except Exception:
222 log.error(traceback.format_exc())
222 log.error(traceback.format_exc())
223 h.flash(_('Error occurred during update of user group %s') \
223 h.flash(_('Error occurred during update of user group %s') \
224 % request.POST.get('users_group_name'), category='error')
224 % request.POST.get('users_group_name'), category='error')
225
225
226 return redirect(url('edit_users_group', id=id))
226 return redirect(url('edit_users_group', id=id))
227
227
228 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
228 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
229 def delete(self, id):
229 def delete(self, id):
230 """DELETE /users_groups/id: Delete an existing item"""
230 """DELETE /users_groups/id: Delete an existing item"""
231 # Forms posted to this method should contain a hidden field:
231 # Forms posted to this method should contain a hidden field:
232 # <input type="hidden" name="_method" value="DELETE" />
232 # <input type="hidden" name="_method" value="DELETE" />
233 # Or using helpers:
233 # Or using helpers:
234 # h.form(url('users_group', id=ID),
234 # h.form(url('users_group', id=ID),
235 # method='delete')
235 # method='delete')
236 # url('users_group', id=ID)
236 # url('users_group', id=ID)
237 usr_gr = UserGroup.get_or_404(id)
237 usr_gr = UserGroup.get_or_404(id)
238 try:
238 try:
239 UserGroupModel().delete(usr_gr)
239 UserGroupModel().delete(usr_gr)
240 Session().commit()
240 Session().commit()
241 h.flash(_('Successfully deleted user group'), category='success')
241 h.flash(_('Successfully deleted user group'), category='success')
242 except UserGroupsAssignedException, e:
242 except UserGroupsAssignedException, e:
243 h.flash(e, category='error')
243 h.flash(e, category='error')
244 except Exception:
244 except Exception:
245 log.error(traceback.format_exc())
245 log.error(traceback.format_exc())
246 h.flash(_('An error occurred during deletion of user group'),
246 h.flash(_('An error occurred during deletion of user group'),
247 category='error')
247 category='error')
248 return redirect(url('users_groups'))
248 return redirect(url('users_groups'))
249
249
250 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
250 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
251 def set_user_group_perm_member(self, id):
251 def set_user_group_perm_member(self, id):
252 """
252 """
253 grant permission for given usergroup
253 grant permission for given usergroup
254
254
255 :param id:
255 :param id:
256 """
256 """
257 user_group = UserGroup.get_or_404(id)
257 user_group = UserGroup.get_or_404(id)
258 form = UserGroupPermsForm()().to_python(request.POST)
258 form = UserGroupPermsForm()().to_python(request.POST)
259
259
260 # set the permissions !
260 # set the permissions !
261 UserGroupModel()._update_permissions(user_group, form['perms_new'],
261 UserGroupModel()._update_permissions(user_group, form['perms_new'],
262 form['perms_updates'])
262 form['perms_updates'])
263 #TODO: implement this
263 #TODO: implement this
264 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
264 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
265 # repo_name, self.ip_addr, self.sa)
265 # repo_name, self.ip_addr, self.sa)
266 Session().commit()
266 Session().commit()
267 h.flash(_('User Group permissions updated'), category='success')
267 h.flash(_('User Group permissions updated'), category='success')
268 return redirect(url('edit_users_group', id=id))
268 return redirect(url('edit_users_group', id=id))
269
269
270 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
270 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
271 def delete_user_group_perm_member(self, id):
271 def delete_user_group_perm_member(self, id):
272 """
272 """
273 DELETE an existing repository group permission user
273 DELETE an existing repository group permission user
274
274
275 :param group_name:
275 :param group_name:
276 """
276 """
277 try:
277 try:
278 obj_type = request.POST.get('obj_type')
278 obj_type = request.POST.get('obj_type')
279 obj_id = None
279 obj_id = None
280 if obj_type == 'user':
280 if obj_type == 'user':
281 obj_id = safe_int(request.POST.get('user_id'))
281 obj_id = safe_int(request.POST.get('user_id'))
282 elif obj_type == 'user_group':
282 elif obj_type == 'user_group':
283 obj_id = safe_int(request.POST.get('user_group_id'))
283 obj_id = safe_int(request.POST.get('user_group_id'))
284
284
285 if not c.rhodecode_user.is_admin:
285 if not c.rhodecode_user.is_admin:
286 if obj_type == 'user' and c.rhodecode_user.user_id == obj_id:
286 if obj_type == 'user' and c.rhodecode_user.user_id == obj_id:
287 msg = _('Cannot revoke permission for yourself as admin')
287 msg = _('Cannot revoke permission for yourself as admin')
288 h.flash(msg, category='warning')
288 h.flash(msg, category='warning')
289 raise Exception('revoke admin permission on self')
289 raise Exception('revoke admin permission on self')
290 if obj_type == 'user':
290 if obj_type == 'user':
291 UserGroupModel().revoke_user_permission(user_group=id,
291 UserGroupModel().revoke_user_permission(user_group=id,
292 user=obj_id)
292 user=obj_id)
293 elif obj_type == 'user_group':
293 elif obj_type == 'user_group':
294 pass
294 pass
295 Session().commit()
295 Session().commit()
296 except Exception:
296 except Exception:
297 log.error(traceback.format_exc())
297 log.error(traceback.format_exc())
298 h.flash(_('An error occurred during deletion of group user'),
298 h.flash(_('An error occurred during revoking of permission'),
299 category='error')
299 category='error')
300 raise HTTPInternalServerError()
300 raise HTTPInternalServerError()
301
301
302 def show(self, id, format='html'):
302 def show(self, id, format='html'):
303 """GET /users_groups/id: Show a specific item"""
303 """GET /users_groups/id: Show a specific item"""
304 # url('users_group', id=ID)
304 # url('users_group', id=ID)
305
305
306 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
306 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
307 def edit(self, id, format='html'):
307 def edit(self, id, format='html'):
308 """GET /users_groups/id/edit: Form to edit an existing item"""
308 """GET /users_groups/id/edit: Form to edit an existing item"""
309 # url('edit_users_group', id=ID)
309 # url('edit_users_group', id=ID)
310
310
311 c.users_group = UserGroup.get_or_404(id)
311 c.users_group = UserGroup.get_or_404(id)
312 self.__load_data(id)
312 self.__load_data(id)
313
313
314 defaults = self.__load_defaults(id)
314 defaults = self.__load_defaults(id)
315
315
316 return htmlfill.render(
316 return htmlfill.render(
317 render('admin/users_groups/users_group_edit.html'),
317 render('admin/users_groups/users_group_edit.html'),
318 defaults=defaults,
318 defaults=defaults,
319 encoding="UTF-8",
319 encoding="UTF-8",
320 force_defaults=False
320 force_defaults=False
321 )
321 )
322
322
323 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
323 @HasUserGroupPermissionAnyDecorator('usergroup.admin')
324 def update_perm(self, id):
324 def update_perm(self, id):
325 """PUT /users_perm/id: Update an existing item"""
325 """PUT /users_perm/id: Update an existing item"""
326 # url('users_group_perm', id=ID, method='put')
326 # url('users_group_perm', id=ID, method='put')
327
327
328 users_group = UserGroup.get_or_404(id)
328 users_group = UserGroup.get_or_404(id)
329 grant_create_perm = str2bool(request.POST.get('create_repo_perm'))
329 grant_create_perm = str2bool(request.POST.get('create_repo_perm'))
330 grant_fork_perm = str2bool(request.POST.get('fork_repo_perm'))
330 grant_fork_perm = str2bool(request.POST.get('fork_repo_perm'))
331 inherit_perms = str2bool(request.POST.get('inherit_default_permissions'))
331 inherit_perms = str2bool(request.POST.get('inherit_default_permissions'))
332
332
333 usergroup_model = UserGroupModel()
333 usergroup_model = UserGroupModel()
334
334
335 try:
335 try:
336 users_group.inherit_default_permissions = inherit_perms
336 users_group.inherit_default_permissions = inherit_perms
337 Session().add(users_group)
337 Session().add(users_group)
338
338
339 if grant_create_perm:
339 if grant_create_perm:
340 usergroup_model.revoke_perm(id, 'hg.create.none')
340 usergroup_model.revoke_perm(id, 'hg.create.none')
341 usergroup_model.grant_perm(id, 'hg.create.repository')
341 usergroup_model.grant_perm(id, 'hg.create.repository')
342 h.flash(_("Granted 'repository create' permission to user group"),
342 h.flash(_("Granted 'repository create' permission to user group"),
343 category='success')
343 category='success')
344 else:
344 else:
345 usergroup_model.revoke_perm(id, 'hg.create.repository')
345 usergroup_model.revoke_perm(id, 'hg.create.repository')
346 usergroup_model.grant_perm(id, 'hg.create.none')
346 usergroup_model.grant_perm(id, 'hg.create.none')
347 h.flash(_("Revoked 'repository create' permission to user group"),
347 h.flash(_("Revoked 'repository create' permission to user group"),
348 category='success')
348 category='success')
349
349
350 if grant_fork_perm:
350 if grant_fork_perm:
351 usergroup_model.revoke_perm(id, 'hg.fork.none')
351 usergroup_model.revoke_perm(id, 'hg.fork.none')
352 usergroup_model.grant_perm(id, 'hg.fork.repository')
352 usergroup_model.grant_perm(id, 'hg.fork.repository')
353 h.flash(_("Granted 'repository fork' permission to user group"),
353 h.flash(_("Granted 'repository fork' permission to user group"),
354 category='success')
354 category='success')
355 else:
355 else:
356 usergroup_model.revoke_perm(id, 'hg.fork.repository')
356 usergroup_model.revoke_perm(id, 'hg.fork.repository')
357 usergroup_model.grant_perm(id, 'hg.fork.none')
357 usergroup_model.grant_perm(id, 'hg.fork.none')
358 h.flash(_("Revoked 'repository fork' permission to user group"),
358 h.flash(_("Revoked 'repository fork' permission to user group"),
359 category='success')
359 category='success')
360
360
361 Session().commit()
361 Session().commit()
362 except Exception:
362 except Exception:
363 log.error(traceback.format_exc())
363 log.error(traceback.format_exc())
364 h.flash(_('An error occurred during permissions saving'),
364 h.flash(_('An error occurred during permissions saving'),
365 category='error')
365 category='error')
366
366
367 return redirect(url('edit_users_group', id=id))
367 return redirect(url('edit_users_group', id=id))
@@ -1,2195 +1,2229 b''
1 /**
1 /**
2 RhodeCode JS Files
2 RhodeCode JS Files
3 **/
3 **/
4
4
5 if (typeof console == "undefined" || typeof console.log == "undefined"){
5 if (typeof console == "undefined" || typeof console.log == "undefined"){
6 console = { log: function() {} }
6 console = { log: function() {} }
7 }
7 }
8
8
9
9
10 var str_repeat = function(i, m) {
10 var str_repeat = function(i, m) {
11 for (var o = []; m > 0; o[--m] = i);
11 for (var o = []; m > 0; o[--m] = i);
12 return o.join('');
12 return o.join('');
13 };
13 };
14
14
15 /**
15 /**
16 * INJECT .format function into String
16 * INJECT .format function into String
17 * Usage: "My name is {0} {1}".format("Johny","Bravo")
17 * Usage: "My name is {0} {1}".format("Johny","Bravo")
18 * Return "My name is Johny Bravo"
18 * Return "My name is Johny Bravo"
19 * Inspired by https://gist.github.com/1049426
19 * Inspired by https://gist.github.com/1049426
20 */
20 */
21 String.prototype.format = function() {
21 String.prototype.format = function() {
22
22
23 function format() {
23 function format() {
24 var str = this;
24 var str = this;
25 var len = arguments.length+1;
25 var len = arguments.length+1;
26 var safe = undefined;
26 var safe = undefined;
27 var arg = undefined;
27 var arg = undefined;
28
28
29 // For each {0} {1} {n...} replace with the argument in that position. If
29 // For each {0} {1} {n...} replace with the argument in that position. If
30 // the argument is an object or an array it will be stringified to JSON.
30 // the argument is an object or an array it will be stringified to JSON.
31 for (var i=0; i < len; arg = arguments[i++]) {
31 for (var i=0; i < len; arg = arguments[i++]) {
32 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
32 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
33 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
33 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
34 }
34 }
35 return str;
35 return str;
36 }
36 }
37
37
38 // Save a reference of what may already exist under the property native.
38 // Save a reference of what may already exist under the property native.
39 // Allows for doing something like: if("".format.native) { /* use native */ }
39 // Allows for doing something like: if("".format.native) { /* use native */ }
40 format.native = String.prototype.format;
40 format.native = String.prototype.format;
41
41
42 // Replace the prototype property
42 // Replace the prototype property
43 return format;
43 return format;
44
44
45 }();
45 }();
46
46
47 String.prototype.strip = function(char) {
47 String.prototype.strip = function(char) {
48 if(char === undefined){
48 if(char === undefined){
49 char = '\\s';
49 char = '\\s';
50 }
50 }
51 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
51 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
52 }
52 }
53 String.prototype.lstrip = function(char) {
53 String.prototype.lstrip = function(char) {
54 if(char === undefined){
54 if(char === undefined){
55 char = '\\s';
55 char = '\\s';
56 }
56 }
57 return this.replace(new RegExp('^'+char+'+'),'');
57 return this.replace(new RegExp('^'+char+'+'),'');
58 }
58 }
59 String.prototype.rstrip = function(char) {
59 String.prototype.rstrip = function(char) {
60 if(char === undefined){
60 if(char === undefined){
61 char = '\\s';
61 char = '\\s';
62 }
62 }
63 return this.replace(new RegExp(''+char+'+$'),'');
63 return this.replace(new RegExp(''+char+'+$'),'');
64 }
64 }
65
65
66
66
67 if(!Array.prototype.indexOf) {
67 if(!Array.prototype.indexOf) {
68 Array.prototype.indexOf = function(needle) {
68 Array.prototype.indexOf = function(needle) {
69 for(var i = 0; i < this.length; i++) {
69 for(var i = 0; i < this.length; i++) {
70 if(this[i] === needle) {
70 if(this[i] === needle) {
71 return i;
71 return i;
72 }
72 }
73 }
73 }
74 return -1;
74 return -1;
75 };
75 };
76 }
76 }
77
77
78 // IE(CRAP) doesn't support previousElementSibling
78 // IE(CRAP) doesn't support previousElementSibling
79 var prevElementSibling = function( el ) {
79 var prevElementSibling = function( el ) {
80 if( el.previousElementSibling ) {
80 if( el.previousElementSibling ) {
81 return el.previousElementSibling;
81 return el.previousElementSibling;
82 } else {
82 } else {
83 while( el = el.previousSibling ) {
83 while( el = el.previousSibling ) {
84 if( el.nodeType === 1 ) return el;
84 if( el.nodeType === 1 ) return el;
85 }
85 }
86 }
86 }
87 }
87 }
88
88
89 /**
89 /**
90 * SmartColorGenerator
90 * SmartColorGenerator
91 *
91 *
92 *usage::
92 *usage::
93 * var CG = new ColorGenerator();
93 * var CG = new ColorGenerator();
94 * var col = CG.getColor(key); //returns array of RGB
94 * var col = CG.getColor(key); //returns array of RGB
95 * 'rgb({0})'.format(col.join(',')
95 * 'rgb({0})'.format(col.join(',')
96 *
96 *
97 * @returns {ColorGenerator}
97 * @returns {ColorGenerator}
98 */
98 */
99 var ColorGenerator = function(){
99 var ColorGenerator = function(){
100 this.GOLDEN_RATIO = 0.618033988749895;
100 this.GOLDEN_RATIO = 0.618033988749895;
101 this.CURRENT_RATIO = 0.22717784590367374 // this can be random
101 this.CURRENT_RATIO = 0.22717784590367374 // this can be random
102 this.HSV_1 = 0.75;//saturation
102 this.HSV_1 = 0.75;//saturation
103 this.HSV_2 = 0.95;
103 this.HSV_2 = 0.95;
104 this.color;
104 this.color;
105 this.cacheColorMap = {};
105 this.cacheColorMap = {};
106 };
106 };
107
107
108 ColorGenerator.prototype = {
108 ColorGenerator.prototype = {
109 getColor:function(key){
109 getColor:function(key){
110 if(this.cacheColorMap[key] !== undefined){
110 if(this.cacheColorMap[key] !== undefined){
111 return this.cacheColorMap[key];
111 return this.cacheColorMap[key];
112 }
112 }
113 else{
113 else{
114 this.cacheColorMap[key] = this.generateColor();
114 this.cacheColorMap[key] = this.generateColor();
115 return this.cacheColorMap[key];
115 return this.cacheColorMap[key];
116 }
116 }
117 },
117 },
118 _hsvToRgb:function(h,s,v){
118 _hsvToRgb:function(h,s,v){
119 if (s == 0.0)
119 if (s == 0.0)
120 return [v, v, v];
120 return [v, v, v];
121 i = parseInt(h * 6.0)
121 i = parseInt(h * 6.0)
122 f = (h * 6.0) - i
122 f = (h * 6.0) - i
123 p = v * (1.0 - s)
123 p = v * (1.0 - s)
124 q = v * (1.0 - s * f)
124 q = v * (1.0 - s * f)
125 t = v * (1.0 - s * (1.0 - f))
125 t = v * (1.0 - s * (1.0 - f))
126 i = i % 6
126 i = i % 6
127 if (i == 0)
127 if (i == 0)
128 return [v, t, p]
128 return [v, t, p]
129 if (i == 1)
129 if (i == 1)
130 return [q, v, p]
130 return [q, v, p]
131 if (i == 2)
131 if (i == 2)
132 return [p, v, t]
132 return [p, v, t]
133 if (i == 3)
133 if (i == 3)
134 return [p, q, v]
134 return [p, q, v]
135 if (i == 4)
135 if (i == 4)
136 return [t, p, v]
136 return [t, p, v]
137 if (i == 5)
137 if (i == 5)
138 return [v, p, q]
138 return [v, p, q]
139 },
139 },
140 generateColor:function(){
140 generateColor:function(){
141 this.CURRENT_RATIO = this.CURRENT_RATIO+this.GOLDEN_RATIO;
141 this.CURRENT_RATIO = this.CURRENT_RATIO+this.GOLDEN_RATIO;
142 this.CURRENT_RATIO = this.CURRENT_RATIO %= 1;
142 this.CURRENT_RATIO = this.CURRENT_RATIO %= 1;
143 HSV_tuple = [this.CURRENT_RATIO, this.HSV_1, this.HSV_2]
143 HSV_tuple = [this.CURRENT_RATIO, this.HSV_1, this.HSV_2]
144 RGB_tuple = this._hsvToRgb(HSV_tuple[0],HSV_tuple[1],HSV_tuple[2]);
144 RGB_tuple = this._hsvToRgb(HSV_tuple[0],HSV_tuple[1],HSV_tuple[2]);
145 function toRgb(v){
145 function toRgb(v){
146 return ""+parseInt(v*256)
146 return ""+parseInt(v*256)
147 }
147 }
148 return [toRgb(RGB_tuple[0]),toRgb(RGB_tuple[1]),toRgb(RGB_tuple[2])];
148 return [toRgb(RGB_tuple[0]),toRgb(RGB_tuple[1]),toRgb(RGB_tuple[2])];
149
149
150 }
150 }
151 }
151 }
152
152
153 /**
153 /**
154 * PyRoutesJS
154 * PyRoutesJS
155 *
155 *
156 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
156 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
157 */
157 */
158 var pyroutes = (function() {
158 var pyroutes = (function() {
159 // access global map defined in special file pyroutes
159 // access global map defined in special file pyroutes
160 var matchlist = PROUTES_MAP;
160 var matchlist = PROUTES_MAP;
161 var sprintf = (function() {
161 var sprintf = (function() {
162 function get_type(variable) {
162 function get_type(variable) {
163 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
163 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
164 }
164 }
165 function str_repeat(input, multiplier) {
165 function str_repeat(input, multiplier) {
166 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
166 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
167 return output.join('');
167 return output.join('');
168 }
168 }
169
169
170 var str_format = function() {
170 var str_format = function() {
171 if (!str_format.cache.hasOwnProperty(arguments[0])) {
171 if (!str_format.cache.hasOwnProperty(arguments[0])) {
172 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
172 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
173 }
173 }
174 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
174 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
175 };
175 };
176
176
177 str_format.format = function(parse_tree, argv) {
177 str_format.format = function(parse_tree, argv) {
178 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
178 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
179 for (i = 0; i < tree_length; i++) {
179 for (i = 0; i < tree_length; i++) {
180 node_type = get_type(parse_tree[i]);
180 node_type = get_type(parse_tree[i]);
181 if (node_type === 'string') {
181 if (node_type === 'string') {
182 output.push(parse_tree[i]);
182 output.push(parse_tree[i]);
183 }
183 }
184 else if (node_type === 'array') {
184 else if (node_type === 'array') {
185 match = parse_tree[i]; // convenience purposes only
185 match = parse_tree[i]; // convenience purposes only
186 if (match[2]) { // keyword argument
186 if (match[2]) { // keyword argument
187 arg = argv[cursor];
187 arg = argv[cursor];
188 for (k = 0; k < match[2].length; k++) {
188 for (k = 0; k < match[2].length; k++) {
189 if (!arg.hasOwnProperty(match[2][k])) {
189 if (!arg.hasOwnProperty(match[2][k])) {
190 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
190 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
191 }
191 }
192 arg = arg[match[2][k]];
192 arg = arg[match[2][k]];
193 }
193 }
194 }
194 }
195 else if (match[1]) { // positional argument (explicit)
195 else if (match[1]) { // positional argument (explicit)
196 arg = argv[match[1]];
196 arg = argv[match[1]];
197 }
197 }
198 else { // positional argument (implicit)
198 else { // positional argument (implicit)
199 arg = argv[cursor++];
199 arg = argv[cursor++];
200 }
200 }
201
201
202 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
202 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
203 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
203 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
204 }
204 }
205 switch (match[8]) {
205 switch (match[8]) {
206 case 'b': arg = arg.toString(2); break;
206 case 'b': arg = arg.toString(2); break;
207 case 'c': arg = String.fromCharCode(arg); break;
207 case 'c': arg = String.fromCharCode(arg); break;
208 case 'd': arg = parseInt(arg, 10); break;
208 case 'd': arg = parseInt(arg, 10); break;
209 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
209 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
210 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
210 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
211 case 'o': arg = arg.toString(8); break;
211 case 'o': arg = arg.toString(8); break;
212 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
212 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
213 case 'u': arg = Math.abs(arg); break;
213 case 'u': arg = Math.abs(arg); break;
214 case 'x': arg = arg.toString(16); break;
214 case 'x': arg = arg.toString(16); break;
215 case 'X': arg = arg.toString(16).toUpperCase(); break;
215 case 'X': arg = arg.toString(16).toUpperCase(); break;
216 }
216 }
217 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
217 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
218 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
218 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
219 pad_length = match[6] - String(arg).length;
219 pad_length = match[6] - String(arg).length;
220 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
220 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
221 output.push(match[5] ? arg + pad : pad + arg);
221 output.push(match[5] ? arg + pad : pad + arg);
222 }
222 }
223 }
223 }
224 return output.join('');
224 return output.join('');
225 };
225 };
226
226
227 str_format.cache = {};
227 str_format.cache = {};
228
228
229 str_format.parse = function(fmt) {
229 str_format.parse = function(fmt) {
230 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
230 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
231 while (_fmt) {
231 while (_fmt) {
232 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
232 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
233 parse_tree.push(match[0]);
233 parse_tree.push(match[0]);
234 }
234 }
235 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
235 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
236 parse_tree.push('%');
236 parse_tree.push('%');
237 }
237 }
238 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
238 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
239 if (match[2]) {
239 if (match[2]) {
240 arg_names |= 1;
240 arg_names |= 1;
241 var field_list = [], replacement_field = match[2], field_match = [];
241 var field_list = [], replacement_field = match[2], field_match = [];
242 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
242 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
243 field_list.push(field_match[1]);
243 field_list.push(field_match[1]);
244 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
244 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
245 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
245 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
246 field_list.push(field_match[1]);
246 field_list.push(field_match[1]);
247 }
247 }
248 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
248 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
249 field_list.push(field_match[1]);
249 field_list.push(field_match[1]);
250 }
250 }
251 else {
251 else {
252 throw('[sprintf] huh?');
252 throw('[sprintf] huh?');
253 }
253 }
254 }
254 }
255 }
255 }
256 else {
256 else {
257 throw('[sprintf] huh?');
257 throw('[sprintf] huh?');
258 }
258 }
259 match[2] = field_list;
259 match[2] = field_list;
260 }
260 }
261 else {
261 else {
262 arg_names |= 2;
262 arg_names |= 2;
263 }
263 }
264 if (arg_names === 3) {
264 if (arg_names === 3) {
265 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
265 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
266 }
266 }
267 parse_tree.push(match);
267 parse_tree.push(match);
268 }
268 }
269 else {
269 else {
270 throw('[sprintf] huh?');
270 throw('[sprintf] huh?');
271 }
271 }
272 _fmt = _fmt.substring(match[0].length);
272 _fmt = _fmt.substring(match[0].length);
273 }
273 }
274 return parse_tree;
274 return parse_tree;
275 };
275 };
276
276
277 return str_format;
277 return str_format;
278 })();
278 })();
279
279
280 var vsprintf = function(fmt, argv) {
280 var vsprintf = function(fmt, argv) {
281 argv.unshift(fmt);
281 argv.unshift(fmt);
282 return sprintf.apply(null, argv);
282 return sprintf.apply(null, argv);
283 };
283 };
284 return {
284 return {
285 'url': function(route_name, params) {
285 'url': function(route_name, params) {
286 var result = route_name;
286 var result = route_name;
287 if (typeof(params) != 'object'){
287 if (typeof(params) != 'object'){
288 params = {};
288 params = {};
289 }
289 }
290 if (matchlist.hasOwnProperty(route_name)) {
290 if (matchlist.hasOwnProperty(route_name)) {
291 var route = matchlist[route_name];
291 var route = matchlist[route_name];
292 // param substitution
292 // param substitution
293 for(var i=0; i < route[1].length; i++) {
293 for(var i=0; i < route[1].length; i++) {
294
294
295 if (!params.hasOwnProperty(route[1][i]))
295 if (!params.hasOwnProperty(route[1][i]))
296 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
296 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
297 }
297 }
298 result = sprintf(route[0], params);
298 result = sprintf(route[0], params);
299
299
300 var ret = [];
300 var ret = [];
301 //extra params => GET
301 //extra params => GET
302 for(param in params){
302 for(param in params){
303 if (route[1].indexOf(param) == -1){
303 if (route[1].indexOf(param) == -1){
304 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
304 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
305 }
305 }
306 }
306 }
307 var _parts = ret.join("&");
307 var _parts = ret.join("&");
308 if(_parts){
308 if(_parts){
309 result = result +'?'+ _parts
309 result = result +'?'+ _parts
310 }
310 }
311 }
311 }
312
312
313 return result;
313 return result;
314 },
314 },
315 'register': function(route_name, route_tmpl, req_params) {
315 'register': function(route_name, route_tmpl, req_params) {
316 if (typeof(req_params) != 'object') {
316 if (typeof(req_params) != 'object') {
317 req_params = [];
317 req_params = [];
318 }
318 }
319 //fix escape
319 //fix escape
320 route_tmpl = unescape(route_tmpl);
320 route_tmpl = unescape(route_tmpl);
321 keys = [];
321 keys = [];
322 for (o in req_params){
322 for (o in req_params){
323 keys.push(req_params[o])
323 keys.push(req_params[o])
324 }
324 }
325 matchlist[route_name] = [
325 matchlist[route_name] = [
326 route_tmpl,
326 route_tmpl,
327 keys
327 keys
328 ]
328 ]
329 },
329 },
330 '_routes': function(){
330 '_routes': function(){
331 return matchlist;
331 return matchlist;
332 }
332 }
333 }
333 }
334 })();
334 })();
335
335
336
336
337
337
338 /**
338 /**
339 * GLOBAL YUI Shortcuts
339 * GLOBAL YUI Shortcuts
340 */
340 */
341 var YUC = YAHOO.util.Connect;
341 var YUC = YAHOO.util.Connect;
342 var YUD = YAHOO.util.Dom;
342 var YUD = YAHOO.util.Dom;
343 var YUE = YAHOO.util.Event;
343 var YUE = YAHOO.util.Event;
344 var YUQ = YAHOO.util.Selector.query;
344 var YUQ = YAHOO.util.Selector.query;
345
345
346 // defines if push state is enabled for this browser ?
346 // defines if push state is enabled for this browser ?
347 var push_state_enabled = Boolean(
347 var push_state_enabled = Boolean(
348 window.history && window.history.pushState && window.history.replaceState
348 window.history && window.history.pushState && window.history.replaceState
349 && !( /* disable for versions of iOS before version 4.3 (8F190) */
349 && !( /* disable for versions of iOS before version 4.3 (8F190) */
350 (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent)
350 (/ Mobile\/([1-7][a-z]|(8([abcde]|f(1[0-8]))))/i).test(navigator.userAgent)
351 /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
351 /* disable for the mercury iOS browser, or at least older versions of the webkit engine */
352 || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent)
352 || (/AppleWebKit\/5([0-2]|3[0-2])/i).test(navigator.userAgent)
353 )
353 )
354 );
354 );
355
355
356 var _run_callbacks = function(callbacks){
356 var _run_callbacks = function(callbacks){
357 if (callbacks !== undefined){
357 if (callbacks !== undefined){
358 var _l = callbacks.length;
358 var _l = callbacks.length;
359 for (var i=0;i<_l;i++){
359 for (var i=0;i<_l;i++){
360 var func = callbacks[i];
360 var func = callbacks[i];
361 if(typeof(func)=='function'){
361 if(typeof(func)=='function'){
362 try{
362 try{
363 func();
363 func();
364 }catch (err){};
364 }catch (err){};
365 }
365 }
366 }
366 }
367 }
367 }
368 }
368 }
369
369
370 /**
370 /**
371 * turns objects into GET query string
372 */
373 var toQueryString = function(o) {
374 if(typeof o !== 'object') {
375 return false;
376 }
377 var _p, _qs = [];
378 for(_p in o) {
379 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
380 }
381 return _qs.join('&');
382 };
383
384 /**
371 * Partial Ajax Implementation
385 * Partial Ajax Implementation
372 *
386 *
373 * @param url: defines url to make partial request
387 * @param url: defines url to make partial request
374 * @param container: defines id of container to input partial result
388 * @param container: defines id of container to input partial result
375 * @param s_call: success callback function that takes o as arg
389 * @param s_call: success callback function that takes o as arg
376 * o.tId
390 * o.tId
377 * o.status
391 * o.status
378 * o.statusText
392 * o.statusText
379 * o.getResponseHeader[ ]
393 * o.getResponseHeader[ ]
380 * o.getAllResponseHeaders
394 * o.getAllResponseHeaders
381 * o.responseText
395 * o.responseText
382 * o.responseXML
396 * o.responseXML
383 * o.argument
397 * o.argument
384 * @param f_call: failure callback
398 * @param f_call: failure callback
385 * @param args arguments
399 * @param args arguments
386 */
400 */
387 function ypjax(url,container,s_call,f_call,args){
401 function ypjax(url,container,s_call,f_call,args){
388 var method='GET';
402 var method='GET';
389 if(args===undefined){
403 if(args===undefined){
390 args=null;
404 args=null;
391 }
405 }
392
406
393 // Set special header for partial ajax == HTTP_X_PARTIAL_XHR
407 // Set special header for partial ajax == HTTP_X_PARTIAL_XHR
394 YUC.initHeader('X-PARTIAL-XHR',true);
408 YUC.initHeader('X-PARTIAL-XHR',true);
395
409
396 // wrapper of passed callback
410 // wrapper of passed callback
397 var s_wrapper = (function(o){
411 var s_wrapper = (function(o){
398 return function(o){
412 return function(o){
399 YUD.get(container).innerHTML=o.responseText;
413 YUD.get(container).innerHTML=o.responseText;
400 YUD.setStyle(container,'opacity','1.0');
414 YUD.setStyle(container,'opacity','1.0');
401 //execute the given original callback
415 //execute the given original callback
402 if (s_call !== undefined){
416 if (s_call !== undefined){
403 s_call(o);
417 s_call(o);
404 }
418 }
405 }
419 }
406 })()
420 })()
407 YUD.setStyle(container,'opacity','0.3');
421 YUD.setStyle(container,'opacity','0.3');
408 YUC.asyncRequest(method,url,{
422 YUC.asyncRequest(method,url,{
409 success:s_wrapper,
423 success:s_wrapper,
410 failure:function(o){
424 failure:function(o){
411 console.log(o);
425 console.log(o);
412 YUD.get(container).innerHTML='<span class="error_red">ERROR: {0}</span>'.format(o.status);
426 YUD.get(container).innerHTML='<span class="error_red">ERROR: {0}</span>'.format(o.status);
413 YUD.setStyle(container,'opacity','1.0');
427 YUD.setStyle(container,'opacity','1.0');
414 },
428 },
415 cache:false
429 cache:false
416 },args);
430 },args);
417
431
418 };
432 };
419
433
420 var ajaxGET = function(url,success) {
434 var ajaxGET = function(url,success) {
421 // Set special header for ajax == HTTP_X_PARTIAL_XHR
435 // Set special header for ajax == HTTP_X_PARTIAL_XHR
422 YUC.initHeader('X-PARTIAL-XHR',true);
436 YUC.initHeader('X-PARTIAL-XHR',true);
423
437
424 var sUrl = url;
438 var sUrl = url;
425 var callback = {
439 var callback = {
426 success: success,
440 success: success,
427 failure: function (o) {
441 failure: function (o) {
428 if (o.status != 0) {
442 if (o.status != 0) {
429 alert("error: " + o.statusText);
443 alert("error: " + o.statusText);
430 };
444 };
431 },
445 },
432 };
446 };
433
447
434 var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
448 var request = YAHOO.util.Connect.asyncRequest('GET', sUrl, callback);
435 return request;
449 return request;
436 };
450 };
437
451
438
452
439
453
440 var ajaxPOST = function(url,postData,success) {
454 var ajaxPOST = function(url,postData,success) {
441 // Set special header for ajax == HTTP_X_PARTIAL_XHR
455 // Set special header for ajax == HTTP_X_PARTIAL_XHR
442 YUC.initHeader('X-PARTIAL-XHR',true);
456 YUC.initHeader('X-PARTIAL-XHR',true);
443
457
444 var toQueryString = function(o) {
445 if(typeof o !== 'object') {
446 return false;
447 }
448 var _p, _qs = [];
449 for(_p in o) {
450 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
451 }
452 return _qs.join('&');
453 };
454
455 var sUrl = url;
458 var sUrl = url;
456 var callback = {
459 var callback = {
457 success: success,
460 success: success,
458 failure: function (o) {
461 failure: function (o) {
459 alert("error");
462 alert("error");
460 },
463 },
461 };
464 };
462 var postData = toQueryString(postData);
465 var postData = toQueryString(postData);
463 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
466 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
464 return request;
467 return request;
465 };
468 };
466
469
467
470
468 /**
471 /**
469 * tooltip activate
472 * tooltip activate
470 */
473 */
471 var tooltip_activate = function(){
474 var tooltip_activate = function(){
472 yt = YAHOO.yuitip.main;
475 yt = YAHOO.yuitip.main;
473 YUE.onDOMReady(yt.init);
476 YUE.onDOMReady(yt.init);
474 };
477 };
475
478
476 /**
479 /**
477 * show more
480 * show more
478 */
481 */
479 var show_more_event = function(){
482 var show_more_event = function(){
480 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
483 YUE.on(YUD.getElementsByClassName('show_more'),'click',function(e){
481 var el = e.target;
484 var el = e.target;
482 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
485 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
483 YUD.setStyle(el.parentNode,'display','none');
486 YUD.setStyle(el.parentNode,'display','none');
484 });
487 });
485 };
488 };
486
489
487 /**
490 /**
488 * show changeset tooltip
491 * show changeset tooltip
489 */
492 */
490 var show_changeset_tooltip = function(){
493 var show_changeset_tooltip = function(){
491 YUE.on(YUD.getElementsByClassName('lazy-cs'), 'mouseover', function(e){
494 YUE.on(YUD.getElementsByClassName('lazy-cs'), 'mouseover', function(e){
492 var target = e.currentTarget;
495 var target = e.currentTarget;
493 var rid = YUD.getAttribute(target,'raw_id');
496 var rid = YUD.getAttribute(target,'raw_id');
494 var repo_name = YUD.getAttribute(target,'repo_name');
497 var repo_name = YUD.getAttribute(target,'repo_name');
495 var ttid = 'tt-'+rid;
498 var ttid = 'tt-'+rid;
496 var success = function(o){
499 var success = function(o){
497 var json = JSON.parse(o.responseText);
500 var json = JSON.parse(o.responseText);
498 YUD.addClass(target,'tooltip')
501 YUD.addClass(target,'tooltip')
499 YUD.setAttribute(target, 'title',json['message']);
502 YUD.setAttribute(target, 'title',json['message']);
500 YAHOO.yuitip.main.show_yuitip(e, target);
503 YAHOO.yuitip.main.show_yuitip(e, target);
501 }
504 }
502 if(rid && !YUD.hasClass(target, 'tooltip')){
505 if(rid && !YUD.hasClass(target, 'tooltip')){
503 YUD.setAttribute(target,'id',ttid);
506 YUD.setAttribute(target,'id',ttid);
504 YUD.setAttribute(target, 'title',_TM['loading...']);
507 YUD.setAttribute(target, 'title',_TM['loading...']);
505 YAHOO.yuitip.main.set_listeners(target);
508 YAHOO.yuitip.main.set_listeners(target);
506 YAHOO.yuitip.main.show_yuitip(e, target);
509 YAHOO.yuitip.main.show_yuitip(e, target);
507 var url = pyroutes.url('changeset_info', {"repo_name":repo_name, "revision": rid});
510 var url = pyroutes.url('changeset_info', {"repo_name":repo_name, "revision": rid});
508 ajaxGET(url, success)
511 ajaxGET(url, success)
509 }
512 }
510 });
513 });
511 };
514 };
512
515
513 var onSuccessFollow = function(target){
516 var onSuccessFollow = function(target){
514 var f = YUD.get(target);
517 var f = YUD.get(target);
515 var f_cnt = YUD.get('current_followers_count');
518 var f_cnt = YUD.get('current_followers_count');
516
519
517 if(YUD.hasClass(f, 'follow')){
520 if(YUD.hasClass(f, 'follow')){
518 f.setAttribute('class','following');
521 f.setAttribute('class','following');
519 f.setAttribute('title',_TM['Stop following this repository']);
522 f.setAttribute('title',_TM['Stop following this repository']);
520
523
521 if(f_cnt){
524 if(f_cnt){
522 var cnt = Number(f_cnt.innerHTML)+1;
525 var cnt = Number(f_cnt.innerHTML)+1;
523 f_cnt.innerHTML = cnt;
526 f_cnt.innerHTML = cnt;
524 }
527 }
525 }
528 }
526 else{
529 else{
527 f.setAttribute('class','follow');
530 f.setAttribute('class','follow');
528 f.setAttribute('title',_TM['Start following this repository']);
531 f.setAttribute('title',_TM['Start following this repository']);
529 if(f_cnt){
532 if(f_cnt){
530 var cnt = Number(f_cnt.innerHTML)-1;
533 var cnt = Number(f_cnt.innerHTML)-1;
531 f_cnt.innerHTML = cnt;
534 f_cnt.innerHTML = cnt;
532 }
535 }
533 }
536 }
534 }
537 }
535
538
536 var toggleFollowingUser = function(target,fallows_user_id,token,user_id){
539 var toggleFollowingUser = function(target,fallows_user_id,token,user_id){
537 args = 'follows_user_id='+fallows_user_id;
540 args = 'follows_user_id='+fallows_user_id;
538 args+= '&amp;auth_token='+token;
541 args+= '&amp;auth_token='+token;
539 if(user_id != undefined){
542 if(user_id != undefined){
540 args+="&amp;user_id="+user_id;
543 args+="&amp;user_id="+user_id;
541 }
544 }
542 YUC.asyncRequest('POST',TOGGLE_FOLLOW_URL,{
545 YUC.asyncRequest('POST',TOGGLE_FOLLOW_URL,{
543 success:function(o){
546 success:function(o){
544 onSuccessFollow(target);
547 onSuccessFollow(target);
545 }
548 }
546 },args);
549 },args);
547 return false;
550 return false;
548 }
551 }
549
552
550 var toggleFollowingRepo = function(target,fallows_repo_id,token,user_id){
553 var toggleFollowingRepo = function(target,fallows_repo_id,token,user_id){
551
554
552 args = 'follows_repo_id='+fallows_repo_id;
555 args = 'follows_repo_id='+fallows_repo_id;
553 args+= '&amp;auth_token='+token;
556 args+= '&amp;auth_token='+token;
554 if(user_id != undefined){
557 if(user_id != undefined){
555 args+="&amp;user_id="+user_id;
558 args+="&amp;user_id="+user_id;
556 }
559 }
557 YUC.asyncRequest('POST',TOGGLE_FOLLOW_URL,{
560 YUC.asyncRequest('POST',TOGGLE_FOLLOW_URL,{
558 success:function(o){
561 success:function(o){
559 onSuccessFollow(target);
562 onSuccessFollow(target);
560 }
563 }
561 },args);
564 },args);
562 return false;
565 return false;
563 }
566 }
564
567
565 var showRepoSize = function(target, repo_name, token){
568 var showRepoSize = function(target, repo_name, token){
566 var args= 'auth_token='+token;
569 var args= 'auth_token='+token;
567
570
568 if(!YUD.hasClass(target, 'loaded')){
571 if(!YUD.hasClass(target, 'loaded')){
569 YUD.get(target).innerHTML = _TM['Loading ...'];
572 YUD.get(target).innerHTML = _TM['Loading ...'];
570 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
573 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
571 YUC.asyncRequest('POST',url,{
574 YUC.asyncRequest('POST',url,{
572 success:function(o){
575 success:function(o){
573 YUD.get(target).innerHTML = JSON.parse(o.responseText);
576 YUD.get(target).innerHTML = JSON.parse(o.responseText);
574 YUD.addClass(target, 'loaded');
577 YUD.addClass(target, 'loaded');
575 }
578 }
576 },args);
579 },args);
577 }
580 }
578 return false;
581 return false;
579 }
582 }
580
583
581 /**
584 /**
582 * TOOLTIP IMPL.
585 * TOOLTIP IMPL.
583 */
586 */
584 YAHOO.namespace('yuitip');
587 YAHOO.namespace('yuitip');
585 YAHOO.yuitip.main = {
588 YAHOO.yuitip.main = {
586
589
587 $: YAHOO.util.Dom.get,
590 $: YAHOO.util.Dom.get,
588
591
589 bgColor: '#000',
592 bgColor: '#000',
590 speed: 0.3,
593 speed: 0.3,
591 opacity: 0.9,
594 opacity: 0.9,
592 offset: [15,15],
595 offset: [15,15],
593 useAnim: false,
596 useAnim: false,
594 maxWidth: 600,
597 maxWidth: 600,
595 add_links: false,
598 add_links: false,
596 yuitips: [],
599 yuitips: [],
597
600
598 set_listeners: function(tt){
601 set_listeners: function(tt){
599 YUE.on(tt, 'mouseover', yt.show_yuitip, tt);
602 YUE.on(tt, 'mouseover', yt.show_yuitip, tt);
600 YUE.on(tt, 'mousemove', yt.move_yuitip, tt);
603 YUE.on(tt, 'mousemove', yt.move_yuitip, tt);
601 YUE.on(tt, 'mouseout', yt.close_yuitip, tt);
604 YUE.on(tt, 'mouseout', yt.close_yuitip, tt);
602 },
605 },
603
606
604 init: function(){
607 init: function(){
605 yt.tipBox = yt.$('tip-box');
608 yt.tipBox = yt.$('tip-box');
606 if(!yt.tipBox){
609 if(!yt.tipBox){
607 yt.tipBox = document.createElement('div');
610 yt.tipBox = document.createElement('div');
608 document.body.appendChild(yt.tipBox);
611 document.body.appendChild(yt.tipBox);
609 yt.tipBox.id = 'tip-box';
612 yt.tipBox.id = 'tip-box';
610 }
613 }
611
614
612 YUD.setStyle(yt.tipBox, 'display', 'none');
615 YUD.setStyle(yt.tipBox, 'display', 'none');
613 YUD.setStyle(yt.tipBox, 'position', 'absolute');
616 YUD.setStyle(yt.tipBox, 'position', 'absolute');
614 if(yt.maxWidth !== null){
617 if(yt.maxWidth !== null){
615 YUD.setStyle(yt.tipBox, 'max-width', yt.maxWidth+'px');
618 YUD.setStyle(yt.tipBox, 'max-width', yt.maxWidth+'px');
616 }
619 }
617
620
618 var yuitips = YUD.getElementsByClassName('tooltip');
621 var yuitips = YUD.getElementsByClassName('tooltip');
619
622
620 if(yt.add_links === true){
623 if(yt.add_links === true){
621 var links = document.getElementsByTagName('a');
624 var links = document.getElementsByTagName('a');
622 var linkLen = links.length;
625 var linkLen = links.length;
623 for(i=0;i<linkLen;i++){
626 for(i=0;i<linkLen;i++){
624 yuitips.push(links[i]);
627 yuitips.push(links[i]);
625 }
628 }
626 }
629 }
627
630
628 var yuiLen = yuitips.length;
631 var yuiLen = yuitips.length;
629
632
630 for(i=0;i<yuiLen;i++){
633 for(i=0;i<yuiLen;i++){
631 yt.set_listeners(yuitips[i]);
634 yt.set_listeners(yuitips[i]);
632 }
635 }
633 },
636 },
634
637
635 show_yuitip: function(e, el){
638 show_yuitip: function(e, el){
636 YUE.stopEvent(e);
639 YUE.stopEvent(e);
637 if(el.tagName.toLowerCase() === 'img'){
640 if(el.tagName.toLowerCase() === 'img'){
638 yt.tipText = el.alt ? el.alt : '';
641 yt.tipText = el.alt ? el.alt : '';
639 } else {
642 } else {
640 yt.tipText = el.title ? el.title : '';
643 yt.tipText = el.title ? el.title : '';
641 }
644 }
642
645
643 if(yt.tipText !== ''){
646 if(yt.tipText !== ''){
644 // save org title
647 // save org title
645 YUD.setAttribute(el, 'tt_title', yt.tipText);
648 YUD.setAttribute(el, 'tt_title', yt.tipText);
646 // reset title to not show org tooltips
649 // reset title to not show org tooltips
647 YUD.setAttribute(el, 'title', '');
650 YUD.setAttribute(el, 'title', '');
648
651
649 yt.tipBox.innerHTML = yt.tipText;
652 yt.tipBox.innerHTML = yt.tipText;
650 YUD.setStyle(yt.tipBox, 'display', 'block');
653 YUD.setStyle(yt.tipBox, 'display', 'block');
651 if(yt.useAnim === true){
654 if(yt.useAnim === true){
652 YUD.setStyle(yt.tipBox, 'opacity', '0');
655 YUD.setStyle(yt.tipBox, 'opacity', '0');
653 var newAnim = new YAHOO.util.Anim(yt.tipBox,
656 var newAnim = new YAHOO.util.Anim(yt.tipBox,
654 {
657 {
655 opacity: { to: yt.opacity }
658 opacity: { to: yt.opacity }
656 }, yt.speed, YAHOO.util.Easing.easeOut
659 }, yt.speed, YAHOO.util.Easing.easeOut
657 );
660 );
658 newAnim.animate();
661 newAnim.animate();
659 }
662 }
660 }
663 }
661 },
664 },
662
665
663 move_yuitip: function(e, el){
666 move_yuitip: function(e, el){
664 YUE.stopEvent(e);
667 YUE.stopEvent(e);
665 var movePos = YUE.getXY(e);
668 var movePos = YUE.getXY(e);
666 YUD.setStyle(yt.tipBox, 'top', (movePos[1] + yt.offset[1]) + 'px');
669 YUD.setStyle(yt.tipBox, 'top', (movePos[1] + yt.offset[1]) + 'px');
667 YUD.setStyle(yt.tipBox, 'left', (movePos[0] + yt.offset[0]) + 'px');
670 YUD.setStyle(yt.tipBox, 'left', (movePos[0] + yt.offset[0]) + 'px');
668 },
671 },
669
672
670 close_yuitip: function(e, el){
673 close_yuitip: function(e, el){
671 YUE.stopEvent(e);
674 YUE.stopEvent(e);
672
675
673 if(yt.useAnim === true){
676 if(yt.useAnim === true){
674 var newAnim = new YAHOO.util.Anim(yt.tipBox,
677 var newAnim = new YAHOO.util.Anim(yt.tipBox,
675 {
678 {
676 opacity: { to: 0 }
679 opacity: { to: 0 }
677 }, yt.speed, YAHOO.util.Easing.easeOut
680 }, yt.speed, YAHOO.util.Easing.easeOut
678 );
681 );
679 newAnim.animate();
682 newAnim.animate();
680 } else {
683 } else {
681 YUD.setStyle(yt.tipBox, 'display', 'none');
684 YUD.setStyle(yt.tipBox, 'display', 'none');
682 }
685 }
683 YUD.setAttribute(el,'title', YUD.getAttribute(el, 'tt_title'));
686 YUD.setAttribute(el,'title', YUD.getAttribute(el, 'tt_title'));
684 }
687 }
685 }
688 }
686
689
687 /**
690 /**
688 * Quick filter widget
691 * Quick filter widget
689 *
692 *
690 * @param target: filter input target
693 * @param target: filter input target
691 * @param nodes: list of nodes in html we want to filter.
694 * @param nodes: list of nodes in html we want to filter.
692 * @param display_element function that takes current node from nodes and
695 * @param display_element function that takes current node from nodes and
693 * does hide or show based on the node
696 * does hide or show based on the node
694 *
697 *
695 */
698 */
696 var q_filter = function(target,nodes,display_element){
699 var q_filter = function(target,nodes,display_element){
697
700
698 var nodes = nodes;
701 var nodes = nodes;
699 var q_filter_field = YUD.get(target);
702 var q_filter_field = YUD.get(target);
700 var F = YAHOO.namespace(target);
703 var F = YAHOO.namespace(target);
701
704
702 YUE.on(q_filter_field,'keyup',function(e){
705 YUE.on(q_filter_field,'keyup',function(e){
703 clearTimeout(F.filterTimeout);
706 clearTimeout(F.filterTimeout);
704 F.filterTimeout = setTimeout(F.updateFilter,600);
707 F.filterTimeout = setTimeout(F.updateFilter,600);
705 });
708 });
706
709
707 F.filterTimeout = null;
710 F.filterTimeout = null;
708
711
709 var show_node = function(node){
712 var show_node = function(node){
710 YUD.setStyle(node,'display','')
713 YUD.setStyle(node,'display','')
711 }
714 }
712 var hide_node = function(node){
715 var hide_node = function(node){
713 YUD.setStyle(node,'display','none');
716 YUD.setStyle(node,'display','none');
714 }
717 }
715
718
716 F.updateFilter = function() {
719 F.updateFilter = function() {
717 // Reset timeout
720 // Reset timeout
718 F.filterTimeout = null;
721 F.filterTimeout = null;
719
722
720 var obsolete = [];
723 var obsolete = [];
721
724
722 var req = q_filter_field.value.toLowerCase();
725 var req = q_filter_field.value.toLowerCase();
723
726
724 var l = nodes.length;
727 var l = nodes.length;
725 var i;
728 var i;
726 var showing = 0;
729 var showing = 0;
727
730
728 for (i=0;i<l;i++ ){
731 for (i=0;i<l;i++ ){
729 var n = nodes[i];
732 var n = nodes[i];
730 var target_element = display_element(n)
733 var target_element = display_element(n)
731 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
734 if(req && n.innerHTML.toLowerCase().indexOf(req) == -1){
732 hide_node(target_element);
735 hide_node(target_element);
733 }
736 }
734 else{
737 else{
735 show_node(target_element);
738 show_node(target_element);
736 showing+=1;
739 showing+=1;
737 }
740 }
738 }
741 }
739
742
740 // if repo_count is set update the number
743 // if repo_count is set update the number
741 var cnt = YUD.get('repo_count');
744 var cnt = YUD.get('repo_count');
742 if(cnt){
745 if(cnt){
743 YUD.get('repo_count').innerHTML = showing;
746 YUD.get('repo_count').innerHTML = showing;
744 }
747 }
745
748
746 }
749 }
747 };
750 };
748
751
749 var tableTr = function(cls, body){
752 var tableTr = function(cls, body){
750 var _el = document.createElement('div');
753 var _el = document.createElement('div');
751 var cont = new YAHOO.util.Element(body);
754 var cont = new YAHOO.util.Element(body);
752 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
755 var comment_id = fromHTML(body).children[0].id.split('comment-')[1];
753 var id = 'comment-tr-{0}'.format(comment_id);
756 var id = 'comment-tr-{0}'.format(comment_id);
754 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
757 var _html = ('<table><tbody><tr id="{0}" class="{1}">'+
755 '<td class="lineno-inline new-inline"></td>'+
758 '<td class="lineno-inline new-inline"></td>'+
756 '<td class="lineno-inline old-inline"></td>'+
759 '<td class="lineno-inline old-inline"></td>'+
757 '<td>{2}</td>'+
760 '<td>{2}</td>'+
758 '</tr></tbody></table>').format(id, cls, body);
761 '</tr></tbody></table>').format(id, cls, body);
759 _el.innerHTML = _html;
762 _el.innerHTML = _html;
760 return _el.children[0].children[0].children[0];
763 return _el.children[0].children[0].children[0];
761 };
764 };
762
765
763 /** comments **/
766 /** comments **/
764 var removeInlineForm = function(form) {
767 var removeInlineForm = function(form) {
765 form.parentNode.removeChild(form);
768 form.parentNode.removeChild(form);
766 };
769 };
767
770
768 var createInlineForm = function(parent_tr, f_path, line) {
771 var createInlineForm = function(parent_tr, f_path, line) {
769 var tmpl = YUD.get('comment-inline-form-template').innerHTML;
772 var tmpl = YUD.get('comment-inline-form-template').innerHTML;
770 tmpl = tmpl.format(f_path, line);
773 tmpl = tmpl.format(f_path, line);
771 var form = tableTr('comment-form-inline',tmpl)
774 var form = tableTr('comment-form-inline',tmpl)
772
775
773 // create event for hide button
776 // create event for hide button
774 form = new YAHOO.util.Element(form);
777 form = new YAHOO.util.Element(form);
775 var form_hide_button = new YAHOO.util.Element(YUD.getElementsByClassName('hide-inline-form',null,form)[0]);
778 var form_hide_button = new YAHOO.util.Element(YUD.getElementsByClassName('hide-inline-form',null,form)[0]);
776 form_hide_button.on('click', function(e) {
779 form_hide_button.on('click', function(e) {
777 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
780 var newtr = e.currentTarget.parentNode.parentNode.parentNode.parentNode.parentNode;
778 if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
781 if(YUD.hasClass(newtr.nextElementSibling,'inline-comments-button')){
779 YUD.setStyle(newtr.nextElementSibling,'display','');
782 YUD.setStyle(newtr.nextElementSibling,'display','');
780 }
783 }
781 removeInlineForm(newtr);
784 removeInlineForm(newtr);
782 YUD.removeClass(parent_tr, 'form-open');
785 YUD.removeClass(parent_tr, 'form-open');
783 YUD.removeClass(parent_tr, 'hl-comment');
786 YUD.removeClass(parent_tr, 'hl-comment');
784
787
785 });
788 });
786
789
787 return form
790 return form
788 };
791 };
789
792
790 /**
793 /**
791 * Inject inline comment for on given TR this tr should be always an .line
794 * Inject inline comment for on given TR this tr should be always an .line
792 * tr containing the line. Code will detect comment, and always put the comment
795 * tr containing the line. Code will detect comment, and always put the comment
793 * block at the very bottom
796 * block at the very bottom
794 */
797 */
795 var injectInlineForm = function(tr){
798 var injectInlineForm = function(tr){
796 if(!YUD.hasClass(tr, 'line')){
799 if(!YUD.hasClass(tr, 'line')){
797 return
800 return
798 }
801 }
799 var submit_url = AJAX_COMMENT_URL;
802 var submit_url = AJAX_COMMENT_URL;
800 var _td = YUD.getElementsByClassName('code',null,tr)[0];
803 var _td = YUD.getElementsByClassName('code',null,tr)[0];
801 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(_td,'no-comment')){
804 if(YUD.hasClass(tr,'form-open') || YUD.hasClass(tr,'context') || YUD.hasClass(_td,'no-comment')){
802 return
805 return
803 }
806 }
804 YUD.addClass(tr,'form-open');
807 YUD.addClass(tr,'form-open');
805 YUD.addClass(tr,'hl-comment');
808 YUD.addClass(tr,'hl-comment');
806 var node = YUD.getElementsByClassName('full_f_path',null,tr.parentNode.parentNode.parentNode)[0];
809 var node = YUD.getElementsByClassName('full_f_path',null,tr.parentNode.parentNode.parentNode)[0];
807 var f_path = YUD.getAttribute(node,'path');
810 var f_path = YUD.getAttribute(node,'path');
808 var lineno = getLineNo(tr);
811 var lineno = getLineNo(tr);
809 var form = createInlineForm(tr, f_path, lineno, submit_url);
812 var form = createInlineForm(tr, f_path, lineno, submit_url);
810
813
811 var parent = tr;
814 var parent = tr;
812 while (1){
815 while (1){
813 var n = parent.nextElementSibling;
816 var n = parent.nextElementSibling;
814 // next element are comments !
817 // next element are comments !
815 if(YUD.hasClass(n,'inline-comments')){
818 if(YUD.hasClass(n,'inline-comments')){
816 parent = n;
819 parent = n;
817 }
820 }
818 else{
821 else{
819 break;
822 break;
820 }
823 }
821 }
824 }
822 YUD.insertAfter(form,parent);
825 YUD.insertAfter(form,parent);
823 var f = YUD.get(form);
826 var f = YUD.get(form);
824 var overlay = YUD.getElementsByClassName('overlay',null,f)[0];
827 var overlay = YUD.getElementsByClassName('overlay',null,f)[0];
825 var _form = YUD.getElementsByClassName('inline-form',null,f)[0];
828 var _form = YUD.getElementsByClassName('inline-form',null,f)[0];
826
829
827 YUE.on(YUD.get(_form), 'submit',function(e){
830 YUE.on(YUD.get(_form), 'submit',function(e){
828 YUE.preventDefault(e);
831 YUE.preventDefault(e);
829
832
830 //ajax submit
833 //ajax submit
831 var text = YUD.get('text_'+lineno).value;
834 var text = YUD.get('text_'+lineno).value;
832 var postData = {
835 var postData = {
833 'text':text,
836 'text':text,
834 'f_path':f_path,
837 'f_path':f_path,
835 'line':lineno
838 'line':lineno
836 };
839 };
837
840
838 if(lineno === undefined){
841 if(lineno === undefined){
839 alert('missing line !');
842 alert('missing line !');
840 return
843 return
841 }
844 }
842 if(f_path === undefined){
845 if(f_path === undefined){
843 alert('missing file path !');
846 alert('missing file path !');
844 return
847 return
845 }
848 }
846
849
847 if(text == ""){
850 if(text == ""){
848 return
851 return
849 }
852 }
850
853
851 var success = function(o){
854 var success = function(o){
852 YUD.removeClass(tr, 'form-open');
855 YUD.removeClass(tr, 'form-open');
853 removeInlineForm(f);
856 removeInlineForm(f);
854 var json_data = JSON.parse(o.responseText);
857 var json_data = JSON.parse(o.responseText);
855 renderInlineComment(json_data);
858 renderInlineComment(json_data);
856 };
859 };
857
860
858 if (YUD.hasClass(overlay,'overlay')){
861 if (YUD.hasClass(overlay,'overlay')){
859 var w = _form.offsetWidth;
862 var w = _form.offsetWidth;
860 var h = _form.offsetHeight;
863 var h = _form.offsetHeight;
861 YUD.setStyle(overlay,'width',w+'px');
864 YUD.setStyle(overlay,'width',w+'px');
862 YUD.setStyle(overlay,'height',h+'px');
865 YUD.setStyle(overlay,'height',h+'px');
863 }
866 }
864 YUD.addClass(overlay, 'submitting');
867 YUD.addClass(overlay, 'submitting');
865
868
866 ajaxPOST(submit_url, postData, success);
869 ajaxPOST(submit_url, postData, success);
867 });
870 });
868
871
869 YUE.on('preview-btn_'+lineno, 'click', function(e){
872 YUE.on('preview-btn_'+lineno, 'click', function(e){
870 var _text = YUD.get('text_'+lineno).value;
873 var _text = YUD.get('text_'+lineno).value;
871 if(!_text){
874 if(!_text){
872 return
875 return
873 }
876 }
874 var post_data = {'text': _text};
877 var post_data = {'text': _text};
875 YUD.addClass('preview-box_'+lineno, 'unloaded');
878 YUD.addClass('preview-box_'+lineno, 'unloaded');
876 YUD.get('preview-box_'+lineno).innerHTML = _TM['Loading ...'];
879 YUD.get('preview-box_'+lineno).innerHTML = _TM['Loading ...'];
877 YUD.setStyle('edit-container_'+lineno, 'display', 'none');
880 YUD.setStyle('edit-container_'+lineno, 'display', 'none');
878 YUD.setStyle('preview-container_'+lineno, 'display', '');
881 YUD.setStyle('preview-container_'+lineno, 'display', '');
879
882
880 var url = pyroutes.url('changeset_comment_preview', {'repo_name': REPO_NAME});
883 var url = pyroutes.url('changeset_comment_preview', {'repo_name': REPO_NAME});
881 ajaxPOST(url,post_data,function(o){
884 ajaxPOST(url,post_data,function(o){
882 YUD.get('preview-box_'+lineno).innerHTML = o.responseText;
885 YUD.get('preview-box_'+lineno).innerHTML = o.responseText;
883 YUD.removeClass('preview-box_'+lineno, 'unloaded');
886 YUD.removeClass('preview-box_'+lineno, 'unloaded');
884 })
887 })
885 })
888 })
886 YUE.on('edit-btn_'+lineno, 'click', function(e){
889 YUE.on('edit-btn_'+lineno, 'click', function(e){
887 YUD.setStyle('edit-container_'+lineno, 'display', '');
890 YUD.setStyle('edit-container_'+lineno, 'display', '');
888 YUD.setStyle('preview-container_'+lineno, 'display', 'none');
891 YUD.setStyle('preview-container_'+lineno, 'display', 'none');
889 })
892 })
890
893
891
894
892 setTimeout(function(){
895 setTimeout(function(){
893 // callbacks
896 // callbacks
894 tooltip_activate();
897 tooltip_activate();
895 MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
898 MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno,
896 _USERS_AC_DATA, _GROUPS_AC_DATA);
899 _USERS_AC_DATA, _GROUPS_AC_DATA);
897 var _e = YUD.get('text_'+lineno);
900 var _e = YUD.get('text_'+lineno);
898 if(_e){
901 if(_e){
899 _e.focus();
902 _e.focus();
900 }
903 }
901 },10)
904 },10)
902 };
905 };
903
906
904 var deleteComment = function(comment_id){
907 var deleteComment = function(comment_id){
905 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
908 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
906 var postData = {'_method':'delete'};
909 var postData = {'_method':'delete'};
907 var success = function(o){
910 var success = function(o){
908 var n = YUD.get('comment-tr-'+comment_id);
911 var n = YUD.get('comment-tr-'+comment_id);
909 var root = prevElementSibling(prevElementSibling(n));
912 var root = prevElementSibling(prevElementSibling(n));
910 n.parentNode.removeChild(n);
913 n.parentNode.removeChild(n);
911
914
912 // scann nodes, and attach add button to last one only for TR
915 // scann nodes, and attach add button to last one only for TR
913 // which are the inline comments
916 // which are the inline comments
914 if(root && root.tagName == 'TR'){
917 if(root && root.tagName == 'TR'){
915 placeAddButton(root);
918 placeAddButton(root);
916 }
919 }
917 }
920 }
918 ajaxPOST(url,postData,success);
921 ajaxPOST(url,postData,success);
919 }
922 }
920
923
921 var createInlineAddButton = function(tr){
924 var createInlineAddButton = function(tr){
922
925
923 var label = TRANSLATION_MAP['Add another comment'];
926 var label = TRANSLATION_MAP['Add another comment'];
924
927
925 var html_el = document.createElement('div');
928 var html_el = document.createElement('div');
926 YUD.addClass(html_el, 'add-comment');
929 YUD.addClass(html_el, 'add-comment');
927 html_el.innerHTML = '<span class="ui-btn">{0}</span>'.format(label);
930 html_el.innerHTML = '<span class="ui-btn">{0}</span>'.format(label);
928
931
929 var add = new YAHOO.util.Element(html_el);
932 var add = new YAHOO.util.Element(html_el);
930 add.on('click', function(e) {
933 add.on('click', function(e) {
931 injectInlineForm(tr);
934 injectInlineForm(tr);
932 });
935 });
933 return add;
936 return add;
934 };
937 };
935
938
936 var getLineNo = function(tr) {
939 var getLineNo = function(tr) {
937 var line;
940 var line;
938 var o = tr.children[0].id.split('_');
941 var o = tr.children[0].id.split('_');
939 var n = tr.children[1].id.split('_');
942 var n = tr.children[1].id.split('_');
940
943
941 if (n.length >= 2) {
944 if (n.length >= 2) {
942 line = n[n.length-1];
945 line = n[n.length-1];
943 } else if (o.length >= 2) {
946 } else if (o.length >= 2) {
944 line = o[o.length-1];
947 line = o[o.length-1];
945 }
948 }
946
949
947 return line
950 return line
948 };
951 };
949
952
950 var placeAddButton = function(target_tr){
953 var placeAddButton = function(target_tr){
951 if(!target_tr){
954 if(!target_tr){
952 return
955 return
953 }
956 }
954 var last_node = target_tr;
957 var last_node = target_tr;
955 //scann
958 //scann
956 while (1){
959 while (1){
957 var n = last_node.nextElementSibling;
960 var n = last_node.nextElementSibling;
958 // next element are comments !
961 // next element are comments !
959 if(YUD.hasClass(n,'inline-comments')){
962 if(YUD.hasClass(n,'inline-comments')){
960 last_node = n;
963 last_node = n;
961 //also remove the comment button from previous
964 //also remove the comment button from previous
962 var comment_add_buttons = YUD.getElementsByClassName('add-comment',null,last_node);
965 var comment_add_buttons = YUD.getElementsByClassName('add-comment',null,last_node);
963 for(var i=0;i<comment_add_buttons.length;i++){
966 for(var i=0;i<comment_add_buttons.length;i++){
964 var b = comment_add_buttons[i];
967 var b = comment_add_buttons[i];
965 b.parentNode.removeChild(b);
968 b.parentNode.removeChild(b);
966 }
969 }
967 }
970 }
968 else{
971 else{
969 break;
972 break;
970 }
973 }
971 }
974 }
972
975
973 var add = createInlineAddButton(target_tr);
976 var add = createInlineAddButton(target_tr);
974 // get the comment div
977 // get the comment div
975 var comment_block = YUD.getElementsByClassName('comment',null,last_node)[0];
978 var comment_block = YUD.getElementsByClassName('comment',null,last_node)[0];
976 // attach add button
979 // attach add button
977 YUD.insertAfter(add,comment_block);
980 YUD.insertAfter(add,comment_block);
978 }
981 }
979
982
980 /**
983 /**
981 * Places the inline comment into the changeset block in proper line position
984 * Places the inline comment into the changeset block in proper line position
982 */
985 */
983 var placeInline = function(target_container,lineno,html){
986 var placeInline = function(target_container,lineno,html){
984 var lineid = "{0}_{1}".format(target_container,lineno);
987 var lineid = "{0}_{1}".format(target_container,lineno);
985 var target_line = YUD.get(lineid);
988 var target_line = YUD.get(lineid);
986 var comment = new YAHOO.util.Element(tableTr('inline-comments',html))
989 var comment = new YAHOO.util.Element(tableTr('inline-comments',html))
987
990
988 // check if there are comments already !
991 // check if there are comments already !
989 var parent = target_line.parentNode;
992 var parent = target_line.parentNode;
990 var root_parent = parent;
993 var root_parent = parent;
991 while (1){
994 while (1){
992 var n = parent.nextElementSibling;
995 var n = parent.nextElementSibling;
993 // next element are comments !
996 // next element are comments !
994 if(YUD.hasClass(n,'inline-comments')){
997 if(YUD.hasClass(n,'inline-comments')){
995 parent = n;
998 parent = n;
996 }
999 }
997 else{
1000 else{
998 break;
1001 break;
999 }
1002 }
1000 }
1003 }
1001 // put in the comment at the bottom
1004 // put in the comment at the bottom
1002 YUD.insertAfter(comment,parent);
1005 YUD.insertAfter(comment,parent);
1003
1006
1004 // scann nodes, and attach add button to last one
1007 // scann nodes, and attach add button to last one
1005 placeAddButton(root_parent);
1008 placeAddButton(root_parent);
1006
1009
1007 return target_line;
1010 return target_line;
1008 }
1011 }
1009
1012
1010 /**
1013 /**
1011 * make a single inline comment and place it inside
1014 * make a single inline comment and place it inside
1012 */
1015 */
1013 var renderInlineComment = function(json_data){
1016 var renderInlineComment = function(json_data){
1014 try{
1017 try{
1015 var html = json_data['rendered_text'];
1018 var html = json_data['rendered_text'];
1016 var lineno = json_data['line_no'];
1019 var lineno = json_data['line_no'];
1017 var target_id = json_data['target_id'];
1020 var target_id = json_data['target_id'];
1018 placeInline(target_id, lineno, html);
1021 placeInline(target_id, lineno, html);
1019
1022
1020 }catch(e){
1023 }catch(e){
1021 console.log(e);
1024 console.log(e);
1022 }
1025 }
1023 }
1026 }
1024
1027
1025 /**
1028 /**
1026 * Iterates over all the inlines, and places them inside proper blocks of data
1029 * Iterates over all the inlines, and places them inside proper blocks of data
1027 */
1030 */
1028 var renderInlineComments = function(file_comments){
1031 var renderInlineComments = function(file_comments){
1029 for (f in file_comments){
1032 for (f in file_comments){
1030 // holding all comments for a FILE
1033 // holding all comments for a FILE
1031 var box = file_comments[f];
1034 var box = file_comments[f];
1032
1035
1033 var target_id = YUD.getAttribute(box,'target_id');
1036 var target_id = YUD.getAttribute(box,'target_id');
1034 // actually comments with line numbers
1037 // actually comments with line numbers
1035 var comments = box.children;
1038 var comments = box.children;
1036 for(var i=0; i<comments.length; i++){
1039 for(var i=0; i<comments.length; i++){
1037 var data = {
1040 var data = {
1038 'rendered_text': comments[i].outerHTML,
1041 'rendered_text': comments[i].outerHTML,
1039 'line_no': YUD.getAttribute(comments[i],'line'),
1042 'line_no': YUD.getAttribute(comments[i],'line'),
1040 'target_id': target_id
1043 'target_id': target_id
1041 }
1044 }
1042 renderInlineComment(data);
1045 renderInlineComment(data);
1043 }
1046 }
1044 }
1047 }
1045 }
1048 }
1046
1049
1047 var fileBrowserListeners = function(current_url, node_list_url, url_base){
1050 var fileBrowserListeners = function(current_url, node_list_url, url_base){
1048 var current_url_branch = +"?branch=__BRANCH__";
1051 var current_url_branch = +"?branch=__BRANCH__";
1049
1052
1050 YUE.on('stay_at_branch','click',function(e){
1053 YUE.on('stay_at_branch','click',function(e){
1051 if(e.target.checked){
1054 if(e.target.checked){
1052 var uri = current_url_branch;
1055 var uri = current_url_branch;
1053 uri = uri.replace('__BRANCH__',e.target.value);
1056 uri = uri.replace('__BRANCH__',e.target.value);
1054 window.location = uri;
1057 window.location = uri;
1055 }
1058 }
1056 else{
1059 else{
1057 window.location = current_url;
1060 window.location = current_url;
1058 }
1061 }
1059 })
1062 })
1060
1063
1061 var n_filter = YUD.get('node_filter');
1064 var n_filter = YUD.get('node_filter');
1062 var F = YAHOO.namespace('node_filter');
1065 var F = YAHOO.namespace('node_filter');
1063
1066
1064 F.filterTimeout = null;
1067 F.filterTimeout = null;
1065 var nodes = null;
1068 var nodes = null;
1066
1069
1067 F.initFilter = function(){
1070 F.initFilter = function(){
1068 YUD.setStyle('node_filter_box_loading','display','');
1071 YUD.setStyle('node_filter_box_loading','display','');
1069 YUD.setStyle('search_activate_id','display','none');
1072 YUD.setStyle('search_activate_id','display','none');
1070 YUD.setStyle('add_node_id','display','none');
1073 YUD.setStyle('add_node_id','display','none');
1071 YUC.initHeader('X-PARTIAL-XHR',true);
1074 YUC.initHeader('X-PARTIAL-XHR',true);
1072 YUC.asyncRequest('GET', node_list_url, {
1075 YUC.asyncRequest('GET', node_list_url, {
1073 success:function(o){
1076 success:function(o){
1074 nodes = JSON.parse(o.responseText).nodes;
1077 nodes = JSON.parse(o.responseText).nodes;
1075 YUD.setStyle('node_filter_box_loading','display','none');
1078 YUD.setStyle('node_filter_box_loading','display','none');
1076 YUD.setStyle('node_filter_box','display','');
1079 YUD.setStyle('node_filter_box','display','');
1077 n_filter.focus();
1080 n_filter.focus();
1078 if(YUD.hasClass(n_filter,'init')){
1081 if(YUD.hasClass(n_filter,'init')){
1079 n_filter.value = '';
1082 n_filter.value = '';
1080 YUD.removeClass(n_filter,'init');
1083 YUD.removeClass(n_filter,'init');
1081 }
1084 }
1082 },
1085 },
1083 failure:function(o){
1086 failure:function(o){
1084 console.log('failed to load');
1087 console.log('failed to load');
1085 }
1088 }
1086 },null);
1089 },null);
1087 }
1090 }
1088
1091
1089 F.updateFilter = function(e) {
1092 F.updateFilter = function(e) {
1090
1093
1091 return function(){
1094 return function(){
1092 // Reset timeout
1095 // Reset timeout
1093 F.filterTimeout = null;
1096 F.filterTimeout = null;
1094 var query = e.target.value.toLowerCase();
1097 var query = e.target.value.toLowerCase();
1095 var match = [];
1098 var match = [];
1096 var matches = 0;
1099 var matches = 0;
1097 var matches_max = 20;
1100 var matches_max = 20;
1098 if (query != ""){
1101 if (query != ""){
1099 for(var i=0;i<nodes.length;i++){
1102 for(var i=0;i<nodes.length;i++){
1100
1103
1101 var pos = nodes[i].name.toLowerCase().indexOf(query)
1104 var pos = nodes[i].name.toLowerCase().indexOf(query)
1102 if(query && pos != -1){
1105 if(query && pos != -1){
1103
1106
1104 matches++
1107 matches++
1105 //show only certain amount to not kill browser
1108 //show only certain amount to not kill browser
1106 if (matches > matches_max){
1109 if (matches > matches_max){
1107 break;
1110 break;
1108 }
1111 }
1109
1112
1110 var n = nodes[i].name;
1113 var n = nodes[i].name;
1111 var t = nodes[i].type;
1114 var t = nodes[i].type;
1112 var n_hl = n.substring(0,pos)
1115 var n_hl = n.substring(0,pos)
1113 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
1116 +"<b>{0}</b>".format(n.substring(pos,pos+query.length))
1114 +n.substring(pos+query.length)
1117 +n.substring(pos+query.length)
1115 var new_url = url_base.replace('__FPATH__',n);
1118 var new_url = url_base.replace('__FPATH__',n);
1116 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
1119 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
1117 }
1120 }
1118 if(match.length >= matches_max){
1121 if(match.length >= matches_max){
1119 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
1122 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
1120 }
1123 }
1121 }
1124 }
1122 }
1125 }
1123 if(query != ""){
1126 if(query != ""){
1124 YUD.setStyle('tbody','display','none');
1127 YUD.setStyle('tbody','display','none');
1125 YUD.setStyle('tbody_filtered','display','');
1128 YUD.setStyle('tbody_filtered','display','');
1126
1129
1127 if (match.length==0){
1130 if (match.length==0){
1128 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
1131 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
1129 }
1132 }
1130
1133
1131 YUD.get('tbody_filtered').innerHTML = match.join("");
1134 YUD.get('tbody_filtered').innerHTML = match.join("");
1132 }
1135 }
1133 else{
1136 else{
1134 YUD.setStyle('tbody','display','');
1137 YUD.setStyle('tbody','display','');
1135 YUD.setStyle('tbody_filtered','display','none');
1138 YUD.setStyle('tbody_filtered','display','none');
1136 }
1139 }
1137
1140
1138 }
1141 }
1139 };
1142 };
1140
1143
1141 YUE.on(YUD.get('filter_activate'),'click',function(){
1144 YUE.on(YUD.get('filter_activate'),'click',function(){
1142 F.initFilter();
1145 F.initFilter();
1143 })
1146 })
1144 YUE.on(n_filter,'click',function(){
1147 YUE.on(n_filter,'click',function(){
1145 if(YUD.hasClass(n_filter,'init')){
1148 if(YUD.hasClass(n_filter,'init')){
1146 n_filter.value = '';
1149 n_filter.value = '';
1147 YUD.removeClass(n_filter,'init');
1150 YUD.removeClass(n_filter,'init');
1148 }
1151 }
1149 });
1152 });
1150 YUE.on(n_filter,'keyup',function(e){
1153 YUE.on(n_filter,'keyup',function(e){
1151 clearTimeout(F.filterTimeout);
1154 clearTimeout(F.filterTimeout);
1152 F.filterTimeout = setTimeout(F.updateFilter(e),600);
1155 F.filterTimeout = setTimeout(F.updateFilter(e),600);
1153 });
1156 });
1154 };
1157 };
1155
1158
1156
1159
1157 var initCodeMirror = function(textAreadId,resetUrl){
1160 var initCodeMirror = function(textAreadId,resetUrl){
1158 var myCodeMirror = CodeMirror.fromTextArea(YUD.get(textAreadId),{
1161 var myCodeMirror = CodeMirror.fromTextArea(YUD.get(textAreadId),{
1159 mode: "null",
1162 mode: "null",
1160 lineNumbers:true
1163 lineNumbers:true
1161 });
1164 });
1162 YUE.on('reset','click',function(e){
1165 YUE.on('reset','click',function(e){
1163 window.location=resetUrl
1166 window.location=resetUrl
1164 });
1167 });
1165
1168
1166 YUE.on('file_enable','click',function(){
1169 YUE.on('file_enable','click',function(){
1167 YUD.setStyle('editor_container','display','');
1170 YUD.setStyle('editor_container','display','');
1168 YUD.setStyle('upload_file_container','display','none');
1171 YUD.setStyle('upload_file_container','display','none');
1169 YUD.setStyle('filename_container','display','');
1172 YUD.setStyle('filename_container','display','');
1170 });
1173 });
1171
1174
1172 YUE.on('upload_file_enable','click',function(){
1175 YUE.on('upload_file_enable','click',function(){
1173 YUD.setStyle('editor_container','display','none');
1176 YUD.setStyle('editor_container','display','none');
1174 YUD.setStyle('upload_file_container','display','');
1177 YUD.setStyle('upload_file_container','display','');
1175 YUD.setStyle('filename_container','display','none');
1178 YUD.setStyle('filename_container','display','none');
1176 });
1179 });
1177 };
1180 };
1178
1181
1179
1182
1180
1183
1181 var getIdentNode = function(n){
1184 var getIdentNode = function(n){
1182 //iterate thru nodes untill matched interesting node !
1185 //iterate thru nodes untill matched interesting node !
1183
1186
1184 if (typeof n == 'undefined'){
1187 if (typeof n == 'undefined'){
1185 return -1
1188 return -1
1186 }
1189 }
1187
1190
1188 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
1191 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
1189 return n
1192 return n
1190 }
1193 }
1191 else{
1194 else{
1192 return getIdentNode(n.parentNode);
1195 return getIdentNode(n.parentNode);
1193 }
1196 }
1194 };
1197 };
1195
1198
1196 var getSelectionLink = function(e) {
1199 var getSelectionLink = function(e) {
1197
1200
1198 //get selection from start/to nodes
1201 //get selection from start/to nodes
1199 if (typeof window.getSelection != "undefined") {
1202 if (typeof window.getSelection != "undefined") {
1200 s = window.getSelection();
1203 s = window.getSelection();
1201
1204
1202 from = getIdentNode(s.anchorNode);
1205 from = getIdentNode(s.anchorNode);
1203 till = getIdentNode(s.focusNode);
1206 till = getIdentNode(s.focusNode);
1204
1207
1205 f_int = parseInt(from.id.replace('L',''));
1208 f_int = parseInt(from.id.replace('L',''));
1206 t_int = parseInt(till.id.replace('L',''));
1209 t_int = parseInt(till.id.replace('L',''));
1207
1210
1208 if (f_int > t_int){
1211 if (f_int > t_int){
1209 //highlight from bottom
1212 //highlight from bottom
1210 offset = -35;
1213 offset = -35;
1211 ranges = [t_int,f_int];
1214 ranges = [t_int,f_int];
1212
1215
1213 }
1216 }
1214 else{
1217 else{
1215 //highligth from top
1218 //highligth from top
1216 offset = 35;
1219 offset = 35;
1217 ranges = [f_int,t_int];
1220 ranges = [f_int,t_int];
1218 }
1221 }
1219 // if we select more than 2 lines
1222 // if we select more than 2 lines
1220 if (ranges[0] != ranges[1]){
1223 if (ranges[0] != ranges[1]){
1221 if(YUD.get('linktt') == null){
1224 if(YUD.get('linktt') == null){
1222 hl_div = document.createElement('div');
1225 hl_div = document.createElement('div');
1223 hl_div.id = 'linktt';
1226 hl_div.id = 'linktt';
1224 }
1227 }
1225 hl_div.innerHTML = '';
1228 hl_div.innerHTML = '';
1226
1229
1227 anchor = '#L'+ranges[0]+'-'+ranges[1];
1230 anchor = '#L'+ranges[0]+'-'+ranges[1];
1228 var link = document.createElement('a');
1231 var link = document.createElement('a');
1229 link.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
1232 link.href = location.href.substring(0,location.href.indexOf('#'))+anchor;
1230 link.innerHTML = _TM['Selection link'];
1233 link.innerHTML = _TM['Selection link'];
1231 hl_div.appendChild(link);
1234 hl_div.appendChild(link);
1232 YUD.get('body').appendChild(hl_div);
1235 YUD.get('body').appendChild(hl_div);
1233
1236
1234 xy = YUD.getXY(till.id);
1237 xy = YUD.getXY(till.id);
1235
1238
1236 YUD.addClass('linktt', 'hl-tip-box');
1239 YUD.addClass('linktt', 'hl-tip-box');
1237 YUD.setStyle('linktt','top',xy[1]+offset+'px');
1240 YUD.setStyle('linktt','top',xy[1]+offset+'px');
1238 YUD.setStyle('linktt','left',xy[0]+'px');
1241 YUD.setStyle('linktt','left',xy[0]+'px');
1239 YUD.setStyle('linktt','visibility','visible');
1242 YUD.setStyle('linktt','visibility','visible');
1240
1243
1241 }
1244 }
1242 else{
1245 else{
1243 YUD.setStyle('linktt','visibility','hidden');
1246 YUD.setStyle('linktt','visibility','hidden');
1244 }
1247 }
1245 }
1248 }
1246 };
1249 };
1247
1250
1248 var deleteNotification = function(url, notification_id,callbacks){
1251 var deleteNotification = function(url, notification_id,callbacks){
1249 var callback = {
1252 var callback = {
1250 success:function(o){
1253 success:function(o){
1251 var obj = YUD.get(String("notification_"+notification_id));
1254 var obj = YUD.get(String("notification_"+notification_id));
1252 if(obj.parentNode !== undefined){
1255 if(obj.parentNode !== undefined){
1253 obj.parentNode.removeChild(obj);
1256 obj.parentNode.removeChild(obj);
1254 }
1257 }
1255 _run_callbacks(callbacks);
1258 _run_callbacks(callbacks);
1256 },
1259 },
1257 failure:function(o){
1260 failure:function(o){
1258 alert("error");
1261 alert("error");
1259 },
1262 },
1260 };
1263 };
1261 var postData = '_method=delete';
1264 var postData = '_method=delete';
1262 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1265 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1263 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1266 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1264 callback, postData);
1267 callback, postData);
1265 };
1268 };
1266
1269
1267 var readNotification = function(url, notification_id,callbacks){
1270 var readNotification = function(url, notification_id,callbacks){
1268 var callback = {
1271 var callback = {
1269 success:function(o){
1272 success:function(o){
1270 var obj = YUD.get(String("notification_"+notification_id));
1273 var obj = YUD.get(String("notification_"+notification_id));
1271 YUD.removeClass(obj, 'unread');
1274 YUD.removeClass(obj, 'unread');
1272 var r_button = YUD.getElementsByClassName('read-notification',null,obj.children[0])[0];
1275 var r_button = YUD.getElementsByClassName('read-notification',null,obj.children[0])[0];
1273
1276
1274 if(r_button.parentNode !== undefined){
1277 if(r_button.parentNode !== undefined){
1275 r_button.parentNode.removeChild(r_button);
1278 r_button.parentNode.removeChild(r_button);
1276 }
1279 }
1277 _run_callbacks(callbacks);
1280 _run_callbacks(callbacks);
1278 },
1281 },
1279 failure:function(o){
1282 failure:function(o){
1280 alert("error");
1283 alert("error");
1281 },
1284 },
1282 };
1285 };
1283 var postData = '_method=put';
1286 var postData = '_method=put';
1284 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1287 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1285 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1288 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl,
1286 callback, postData);
1289 callback, postData);
1287 };
1290 };
1288
1291
1289 /** MEMBERS AUTOCOMPLETE WIDGET **/
1292 /** MEMBERS AUTOCOMPLETE WIDGET **/
1290
1293
1291 var MembersAutoComplete = function (divid, cont, users_list, groups_list) {
1294 var MembersAutoComplete = function (divid, cont, users_list, groups_list) {
1292 var myUsers = users_list;
1295 var myUsers = users_list;
1293 var myGroups = groups_list;
1296 var myGroups = groups_list;
1294
1297
1295 // Define a custom search function for the DataSource of users
1298 // Define a custom search function for the DataSource of users
1296 var matchUsers = function (sQuery) {
1299 var matchUsers = function (sQuery) {
1297 // Case insensitive matching
1300 // Case insensitive matching
1298 var query = sQuery.toLowerCase();
1301 var query = sQuery.toLowerCase();
1299 var i = 0;
1302 var i = 0;
1300 var l = myUsers.length;
1303 var l = myUsers.length;
1301 var matches = [];
1304 var matches = [];
1302
1305
1303 // Match against each name of each contact
1306 // Match against each name of each contact
1304 for (; i < l; i++) {
1307 for (; i < l; i++) {
1305 contact = myUsers[i];
1308 contact = myUsers[i];
1306 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1309 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1307 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1310 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1308 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1311 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1309 matches[matches.length] = contact;
1312 matches[matches.length] = contact;
1310 }
1313 }
1311 }
1314 }
1312 return matches;
1315 return matches;
1313 };
1316 };
1314
1317
1315 // Define a custom search function for the DataSource of userGroups
1318 // Define a custom search function for the DataSource of userGroups
1316 var matchGroups = function (sQuery) {
1319 var matchGroups = function (sQuery) {
1317 // Case insensitive matching
1320 // Case insensitive matching
1318 var query = sQuery.toLowerCase();
1321 var query = sQuery.toLowerCase();
1319 var i = 0;
1322 var i = 0;
1320 var l = myGroups.length;
1323 var l = myGroups.length;
1321 var matches = [];
1324 var matches = [];
1322
1325
1323 // Match against each name of each contact
1326 // Match against each name of each contact
1324 for (; i < l; i++) {
1327 for (; i < l; i++) {
1325 matched_group = myGroups[i];
1328 matched_group = myGroups[i];
1326 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1329 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1327 matches[matches.length] = matched_group;
1330 matches[matches.length] = matched_group;
1328 }
1331 }
1329 }
1332 }
1330 return matches;
1333 return matches;
1331 };
1334 };
1332
1335
1333 //match all
1336 //match all
1334 var matchAll = function (sQuery) {
1337 var matchAll = function (sQuery) {
1335 u = matchUsers(sQuery);
1338 u = matchUsers(sQuery);
1336 g = matchGroups(sQuery);
1339 g = matchGroups(sQuery);
1337 return u.concat(g);
1340 return u.concat(g);
1338 };
1341 };
1339
1342
1340 // DataScheme for members
1343 // DataScheme for members
1341 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
1344 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
1342 memberDS.responseSchema = {
1345 memberDS.responseSchema = {
1343 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
1346 fields: ["id", "fname", "lname", "nname", "grname", "grmembers", "gravatar_lnk"]
1344 };
1347 };
1345
1348
1346 // DataScheme for owner
1349 // DataScheme for owner
1347 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1350 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1348 ownerDS.responseSchema = {
1351 ownerDS.responseSchema = {
1349 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1352 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1350 };
1353 };
1351
1354
1352 // Instantiate AutoComplete for perms
1355 // Instantiate AutoComplete for perms
1353 var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS);
1356 var membersAC = new YAHOO.widget.AutoComplete(divid, cont, memberDS);
1354 membersAC.useShadow = false;
1357 membersAC.useShadow = false;
1355 membersAC.resultTypeList = false;
1358 membersAC.resultTypeList = false;
1356 membersAC.animVert = false;
1359 membersAC.animVert = false;
1357 membersAC.animHoriz = false;
1360 membersAC.animHoriz = false;
1358 membersAC.animSpeed = 0.1;
1361 membersAC.animSpeed = 0.1;
1359
1362
1360 // Instantiate AutoComplete for owner
1363 // Instantiate AutoComplete for owner
1361 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
1364 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
1362 ownerAC.useShadow = false;
1365 ownerAC.useShadow = false;
1363 ownerAC.resultTypeList = false;
1366 ownerAC.resultTypeList = false;
1364 ownerAC.animVert = false;
1367 ownerAC.animVert = false;
1365 ownerAC.animHoriz = false;
1368 ownerAC.animHoriz = false;
1366 ownerAC.animSpeed = 0.1;
1369 ownerAC.animSpeed = 0.1;
1367
1370
1368 // Helper highlight function for the formatter
1371 // Helper highlight function for the formatter
1369 var highlightMatch = function (full, snippet, matchindex) {
1372 var highlightMatch = function (full, snippet, matchindex) {
1370 return full.substring(0, matchindex)
1373 return full.substring(0, matchindex)
1371 + "<span class='match'>"
1374 + "<span class='match'>"
1372 + full.substr(matchindex, snippet.length)
1375 + full.substr(matchindex, snippet.length)
1373 + "</span>" + full.substring(matchindex + snippet.length);
1376 + "</span>" + full.substring(matchindex + snippet.length);
1374 };
1377 };
1375
1378
1376 // Custom formatter to highlight the matching letters
1379 // Custom formatter to highlight the matching letters
1377 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
1380 var custom_formatter = function (oResultData, sQuery, sResultMatch) {
1378 var query = sQuery.toLowerCase();
1381 var query = sQuery.toLowerCase();
1379 var _gravatar = function(res, em, group){
1382 var _gravatar = function(res, em, group){
1380 if (group !== undefined){
1383 if (group !== undefined){
1381 em = '/images/icons/group.png'
1384 em = '/images/icons/group.png'
1382 }
1385 }
1383 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1386 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1384 return tmpl.format(em,res)
1387 return tmpl.format(em,res)
1385 }
1388 }
1386 // group
1389 // group
1387 if (oResultData.grname != undefined) {
1390 if (oResultData.grname != undefined) {
1388 var grname = oResultData.grname;
1391 var grname = oResultData.grname;
1389 var grmembers = oResultData.grmembers;
1392 var grmembers = oResultData.grmembers;
1390 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1393 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1391 var grprefix = "{0}: ".format(_TM['Group']);
1394 var grprefix = "{0}: ".format(_TM['Group']);
1392 var grsuffix = " (" + grmembers + " )";
1395 var grsuffix = " (" + grmembers + " )";
1393 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1396 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1394
1397
1395 if (grnameMatchIndex > -1) {
1398 if (grnameMatchIndex > -1) {
1396 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
1399 return _gravatar(grprefix + highlightMatch(grname, query, grnameMatchIndex) + grsuffix,null,true);
1397 }
1400 }
1398 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
1401 return _gravatar(grprefix + oResultData.grname + grsuffix, null,true);
1399 // Users
1402 // Users
1400 } else if (oResultData.nname != undefined) {
1403 } else if (oResultData.nname != undefined) {
1401 var fname = oResultData.fname || "";
1404 var fname = oResultData.fname || "";
1402 var lname = oResultData.lname || "";
1405 var lname = oResultData.lname || "";
1403 var nname = oResultData.nname;
1406 var nname = oResultData.nname;
1404
1407
1405 // Guard against null value
1408 // Guard against null value
1406 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1409 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1407 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1410 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1408 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1411 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1409 displayfname, displaylname, displaynname;
1412 displayfname, displaylname, displaynname;
1410
1413
1411 if (fnameMatchIndex > -1) {
1414 if (fnameMatchIndex > -1) {
1412 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1415 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1413 } else {
1416 } else {
1414 displayfname = fname;
1417 displayfname = fname;
1415 }
1418 }
1416
1419
1417 if (lnameMatchIndex > -1) {
1420 if (lnameMatchIndex > -1) {
1418 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1421 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1419 } else {
1422 } else {
1420 displaylname = lname;
1423 displaylname = lname;
1421 }
1424 }
1422
1425
1423 if (nnameMatchIndex > -1) {
1426 if (nnameMatchIndex > -1) {
1424 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1427 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1425 } else {
1428 } else {
1426 displaynname = nname ? "(" + nname + ")" : "";
1429 displaynname = nname ? "(" + nname + ")" : "";
1427 }
1430 }
1428
1431
1429 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1432 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1430 } else {
1433 } else {
1431 return '';
1434 return '';
1432 }
1435 }
1433 };
1436 };
1434 membersAC.formatResult = custom_formatter;
1437 membersAC.formatResult = custom_formatter;
1435 ownerAC.formatResult = custom_formatter;
1438 ownerAC.formatResult = custom_formatter;
1436
1439
1437 var myHandler = function (sType, aArgs) {
1440 var myHandler = function (sType, aArgs) {
1438 var nextId = divid.split('perm_new_member_name_')[1];
1441 var nextId = divid.split('perm_new_member_name_')[1];
1439 var myAC = aArgs[0]; // reference back to the AC instance
1442 var myAC = aArgs[0]; // reference back to the AC instance
1440 var elLI = aArgs[1]; // reference to the selected LI element
1443 var elLI = aArgs[1]; // reference to the selected LI element
1441 var oData = aArgs[2]; // object literal of selected item's result data
1444 var oData = aArgs[2]; // object literal of selected item's result data
1442 //fill the autocomplete with value
1445 //fill the autocomplete with value
1443 if (oData.nname != undefined) {
1446 if (oData.nname != undefined) {
1444 //users
1447 //users
1445 myAC.getInputEl().value = oData.nname;
1448 myAC.getInputEl().value = oData.nname;
1446 YUD.get('perm_new_member_type_'+nextId).value = 'user';
1449 YUD.get('perm_new_member_type_'+nextId).value = 'user';
1447 } else {
1450 } else {
1448 //groups
1451 //groups
1449 myAC.getInputEl().value = oData.grname;
1452 myAC.getInputEl().value = oData.grname;
1450 YUD.get('perm_new_member_type_'+nextId).value = 'users_group';
1453 YUD.get('perm_new_member_type_'+nextId).value = 'users_group';
1451 }
1454 }
1452 };
1455 };
1453
1456
1454 membersAC.itemSelectEvent.subscribe(myHandler);
1457 membersAC.itemSelectEvent.subscribe(myHandler);
1455 if(ownerAC.itemSelectEvent){
1458 if(ownerAC.itemSelectEvent){
1456 ownerAC.itemSelectEvent.subscribe(myHandler);
1459 ownerAC.itemSelectEvent.subscribe(myHandler);
1457 }
1460 }
1458
1461
1459 return {
1462 return {
1460 memberDS: memberDS,
1463 memberDS: memberDS,
1461 ownerDS: ownerDS,
1464 ownerDS: ownerDS,
1462 membersAC: membersAC,
1465 membersAC: membersAC,
1463 ownerAC: ownerAC,
1466 ownerAC: ownerAC,
1464 };
1467 };
1465 }
1468 }
1466
1469
1467
1470
1468 var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
1471 var MentionsAutoComplete = function (divid, cont, users_list, groups_list) {
1469 var myUsers = users_list;
1472 var myUsers = users_list;
1470 var myGroups = groups_list;
1473 var myGroups = groups_list;
1471
1474
1472 // Define a custom search function for the DataSource of users
1475 // Define a custom search function for the DataSource of users
1473 var matchUsers = function (sQuery) {
1476 var matchUsers = function (sQuery) {
1474 var org_sQuery = sQuery;
1477 var org_sQuery = sQuery;
1475 if(this.mentionQuery == null){
1478 if(this.mentionQuery == null){
1476 return []
1479 return []
1477 }
1480 }
1478 sQuery = this.mentionQuery;
1481 sQuery = this.mentionQuery;
1479 // Case insensitive matching
1482 // Case insensitive matching
1480 var query = sQuery.toLowerCase();
1483 var query = sQuery.toLowerCase();
1481 var i = 0;
1484 var i = 0;
1482 var l = myUsers.length;
1485 var l = myUsers.length;
1483 var matches = [];
1486 var matches = [];
1484
1487
1485 // Match against each name of each contact
1488 // Match against each name of each contact
1486 for (; i < l; i++) {
1489 for (; i < l; i++) {
1487 contact = myUsers[i];
1490 contact = myUsers[i];
1488 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1491 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1489 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1492 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1490 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1493 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1491 matches[matches.length] = contact;
1494 matches[matches.length] = contact;
1492 }
1495 }
1493 }
1496 }
1494 return matches
1497 return matches
1495 };
1498 };
1496
1499
1497 //match all
1500 //match all
1498 var matchAll = function (sQuery) {
1501 var matchAll = function (sQuery) {
1499 u = matchUsers(sQuery);
1502 u = matchUsers(sQuery);
1500 return u
1503 return u
1501 };
1504 };
1502
1505
1503 // DataScheme for owner
1506 // DataScheme for owner
1504 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1507 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1505
1508
1506 ownerDS.responseSchema = {
1509 ownerDS.responseSchema = {
1507 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1510 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1508 };
1511 };
1509
1512
1510 // Instantiate AutoComplete for mentions
1513 // Instantiate AutoComplete for mentions
1511 var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1514 var ownerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1512 ownerAC.useShadow = false;
1515 ownerAC.useShadow = false;
1513 ownerAC.resultTypeList = false;
1516 ownerAC.resultTypeList = false;
1514 ownerAC.suppressInputUpdate = true;
1517 ownerAC.suppressInputUpdate = true;
1515 ownerAC.animVert = false;
1518 ownerAC.animVert = false;
1516 ownerAC.animHoriz = false;
1519 ownerAC.animHoriz = false;
1517 ownerAC.animSpeed = 0.1;
1520 ownerAC.animSpeed = 0.1;
1518
1521
1519 // Helper highlight function for the formatter
1522 // Helper highlight function for the formatter
1520 var highlightMatch = function (full, snippet, matchindex) {
1523 var highlightMatch = function (full, snippet, matchindex) {
1521 return full.substring(0, matchindex)
1524 return full.substring(0, matchindex)
1522 + "<span class='match'>"
1525 + "<span class='match'>"
1523 + full.substr(matchindex, snippet.length)
1526 + full.substr(matchindex, snippet.length)
1524 + "</span>" + full.substring(matchindex + snippet.length);
1527 + "</span>" + full.substring(matchindex + snippet.length);
1525 };
1528 };
1526
1529
1527 // Custom formatter to highlight the matching letters
1530 // Custom formatter to highlight the matching letters
1528 ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1531 ownerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1529 var org_sQuery = sQuery;
1532 var org_sQuery = sQuery;
1530 if(this.dataSource.mentionQuery != null){
1533 if(this.dataSource.mentionQuery != null){
1531 sQuery = this.dataSource.mentionQuery;
1534 sQuery = this.dataSource.mentionQuery;
1532 }
1535 }
1533
1536
1534 var query = sQuery.toLowerCase();
1537 var query = sQuery.toLowerCase();
1535 var _gravatar = function(res, em, group){
1538 var _gravatar = function(res, em, group){
1536 if (group !== undefined){
1539 if (group !== undefined){
1537 em = '/images/icons/group.png'
1540 em = '/images/icons/group.png'
1538 }
1541 }
1539 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1542 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1540 return tmpl.format(em,res)
1543 return tmpl.format(em,res)
1541 }
1544 }
1542 if (oResultData.nname != undefined) {
1545 if (oResultData.nname != undefined) {
1543 var fname = oResultData.fname || "";
1546 var fname = oResultData.fname || "";
1544 var lname = oResultData.lname || "";
1547 var lname = oResultData.lname || "";
1545 var nname = oResultData.nname;
1548 var nname = oResultData.nname;
1546
1549
1547 // Guard against null value
1550 // Guard against null value
1548 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1551 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1549 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1552 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1550 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1553 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1551 displayfname, displaylname, displaynname;
1554 displayfname, displaylname, displaynname;
1552
1555
1553 if (fnameMatchIndex > -1) {
1556 if (fnameMatchIndex > -1) {
1554 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1557 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1555 } else {
1558 } else {
1556 displayfname = fname;
1559 displayfname = fname;
1557 }
1560 }
1558
1561
1559 if (lnameMatchIndex > -1) {
1562 if (lnameMatchIndex > -1) {
1560 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1563 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1561 } else {
1564 } else {
1562 displaylname = lname;
1565 displaylname = lname;
1563 }
1566 }
1564
1567
1565 if (nnameMatchIndex > -1) {
1568 if (nnameMatchIndex > -1) {
1566 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1569 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1567 } else {
1570 } else {
1568 displaynname = nname ? "(" + nname + ")" : "";
1571 displaynname = nname ? "(" + nname + ")" : "";
1569 }
1572 }
1570
1573
1571 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1574 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1572 } else {
1575 } else {
1573 return '';
1576 return '';
1574 }
1577 }
1575 };
1578 };
1576
1579
1577 if(ownerAC.itemSelectEvent){
1580 if(ownerAC.itemSelectEvent){
1578 ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1581 ownerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1579
1582
1580 var myAC = aArgs[0]; // reference back to the AC instance
1583 var myAC = aArgs[0]; // reference back to the AC instance
1581 var elLI = aArgs[1]; // reference to the selected LI element
1584 var elLI = aArgs[1]; // reference to the selected LI element
1582 var oData = aArgs[2]; // object literal of selected item's result data
1585 var oData = aArgs[2]; // object literal of selected item's result data
1583 //fill the autocomplete with value
1586 //fill the autocomplete with value
1584 if (oData.nname != undefined) {
1587 if (oData.nname != undefined) {
1585 //users
1588 //users
1586 //Replace the mention name with replaced
1589 //Replace the mention name with replaced
1587 var re = new RegExp();
1590 var re = new RegExp();
1588 var org = myAC.getInputEl().value;
1591 var org = myAC.getInputEl().value;
1589 var chunks = myAC.dataSource.chunks
1592 var chunks = myAC.dataSource.chunks
1590 // replace middle chunk(the search term) with actuall match
1593 // replace middle chunk(the search term) with actuall match
1591 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1594 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1592 '@'+oData.nname+' ');
1595 '@'+oData.nname+' ');
1593 myAC.getInputEl().value = chunks.join('')
1596 myAC.getInputEl().value = chunks.join('')
1594 YUD.get(myAC.getInputEl()).focus(); // Y U NO WORK !?
1597 YUD.get(myAC.getInputEl()).focus(); // Y U NO WORK !?
1595 } else {
1598 } else {
1596 //groups
1599 //groups
1597 myAC.getInputEl().value = oData.grname;
1600 myAC.getInputEl().value = oData.grname;
1598 YUD.get('perm_new_member_type').value = 'users_group';
1601 YUD.get('perm_new_member_type').value = 'users_group';
1599 }
1602 }
1600 });
1603 });
1601 }
1604 }
1602
1605
1603 // in this keybuffer we will gather current value of search !
1606 // in this keybuffer we will gather current value of search !
1604 // since we need to get this just when someone does `@` then we do the
1607 // since we need to get this just when someone does `@` then we do the
1605 // search
1608 // search
1606 ownerAC.dataSource.chunks = [];
1609 ownerAC.dataSource.chunks = [];
1607 ownerAC.dataSource.mentionQuery = null;
1610 ownerAC.dataSource.mentionQuery = null;
1608
1611
1609 ownerAC.get_mention = function(msg, max_pos) {
1612 ownerAC.get_mention = function(msg, max_pos) {
1610 var org = msg;
1613 var org = msg;
1611 var re = new RegExp('(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)$')
1614 var re = new RegExp('(?:^@|\s@)([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)$')
1612 var chunks = [];
1615 var chunks = [];
1613
1616
1614
1617
1615 // cut first chunk until curret pos
1618 // cut first chunk until curret pos
1616 var to_max = msg.substr(0, max_pos);
1619 var to_max = msg.substr(0, max_pos);
1617 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1620 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1618 var msg2 = to_max.substr(at_pos);
1621 var msg2 = to_max.substr(at_pos);
1619
1622
1620 chunks.push(org.substr(0,at_pos))// prefix chunk
1623 chunks.push(org.substr(0,at_pos))// prefix chunk
1621 chunks.push(msg2) // search chunk
1624 chunks.push(msg2) // search chunk
1622 chunks.push(org.substr(max_pos)) // postfix chunk
1625 chunks.push(org.substr(max_pos)) // postfix chunk
1623
1626
1624 // clean up msg2 for filtering and regex match
1627 // clean up msg2 for filtering and regex match
1625 var msg2 = msg2.lstrip(' ').lstrip('\n');
1628 var msg2 = msg2.lstrip(' ').lstrip('\n');
1626
1629
1627 if(re.test(msg2)){
1630 if(re.test(msg2)){
1628 var unam = re.exec(msg2)[1];
1631 var unam = re.exec(msg2)[1];
1629 return [unam, chunks];
1632 return [unam, chunks];
1630 }
1633 }
1631 return [null, null];
1634 return [null, null];
1632 };
1635 };
1633
1636
1634 if (ownerAC.textboxKeyUpEvent){
1637 if (ownerAC.textboxKeyUpEvent){
1635 ownerAC.textboxKeyUpEvent.subscribe(function(type, args){
1638 ownerAC.textboxKeyUpEvent.subscribe(function(type, args){
1636
1639
1637 var ac_obj = args[0];
1640 var ac_obj = args[0];
1638 var currentMessage = args[1];
1641 var currentMessage = args[1];
1639 var currentCaretPosition = args[0]._elTextbox.selectionStart;
1642 var currentCaretPosition = args[0]._elTextbox.selectionStart;
1640
1643
1641 var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
1644 var unam = ownerAC.get_mention(currentMessage, currentCaretPosition);
1642 var curr_search = null;
1645 var curr_search = null;
1643 if(unam[0]){
1646 if(unam[0]){
1644 curr_search = unam[0];
1647 curr_search = unam[0];
1645 }
1648 }
1646
1649
1647 ownerAC.dataSource.chunks = unam[1];
1650 ownerAC.dataSource.chunks = unam[1];
1648 ownerAC.dataSource.mentionQuery = curr_search;
1651 ownerAC.dataSource.mentionQuery = curr_search;
1649
1652
1650 })
1653 })
1651 }
1654 }
1652 return {
1655 return {
1653 ownerDS: ownerDS,
1656 ownerDS: ownerDS,
1654 ownerAC: ownerAC,
1657 ownerAC: ownerAC,
1655 };
1658 };
1656 }
1659 }
1657
1660
1658 var addReviewMember = function(id,fname,lname,nname,gravatar_link){
1661 var addReviewMember = function(id,fname,lname,nname,gravatar_link){
1659 var members = YUD.get('review_members');
1662 var members = YUD.get('review_members');
1660 var tmpl = '<li id="reviewer_{2}">'+
1663 var tmpl = '<li id="reviewer_{2}">'+
1661 '<div class="reviewers_member">'+
1664 '<div class="reviewers_member">'+
1662 '<div class="gravatar"><img alt="gravatar" src="{0}"/> </div>'+
1665 '<div class="gravatar"><img alt="gravatar" src="{0}"/> </div>'+
1663 '<div style="float:left">{1}</div>'+
1666 '<div style="float:left">{1}</div>'+
1664 '<input type="hidden" value="{2}" name="review_members" />'+
1667 '<input type="hidden" value="{2}" name="review_members" />'+
1665 '<span class="delete_icon action_button" onclick="removeReviewMember({2})"></span>'+
1668 '<span class="delete_icon action_button" onclick="removeReviewMember({2})"></span>'+
1666 '</div>'+
1669 '</div>'+
1667 '</li>' ;
1670 '</li>' ;
1668 var displayname = "{0} {1} ({2})".format(fname,lname,nname);
1671 var displayname = "{0} {1} ({2})".format(fname,lname,nname);
1669 var element = tmpl.format(gravatar_link,displayname,id);
1672 var element = tmpl.format(gravatar_link,displayname,id);
1670 // check if we don't have this ID already in
1673 // check if we don't have this ID already in
1671 var ids = [];
1674 var ids = [];
1672 var _els = YUQ('#review_members li');
1675 var _els = YUQ('#review_members li');
1673 for (el in _els){
1676 for (el in _els){
1674 ids.push(_els[el].id)
1677 ids.push(_els[el].id)
1675 }
1678 }
1676 if(ids.indexOf('reviewer_'+id) == -1){
1679 if(ids.indexOf('reviewer_'+id) == -1){
1677 //only add if it's not there
1680 //only add if it's not there
1678 members.innerHTML += element;
1681 members.innerHTML += element;
1679 }
1682 }
1680
1683
1681 }
1684 }
1682
1685
1683 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1686 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1684 var el = YUD.get('reviewer_{0}'.format(reviewer_id));
1687 var el = YUD.get('reviewer_{0}'.format(reviewer_id));
1685 if (el.parentNode !== undefined){
1688 if (el.parentNode !== undefined){
1686 el.parentNode.removeChild(el);
1689 el.parentNode.removeChild(el);
1687 }
1690 }
1688 }
1691 }
1689
1692
1690 var updateReviewers = function(reviewers_ids, repo_name, pull_request_id){
1693 var updateReviewers = function(reviewers_ids, repo_name, pull_request_id){
1691 if (reviewers_ids === undefined){
1694 if (reviewers_ids === undefined){
1692 var reviewers_ids = [];
1695 var reviewers_ids = [];
1693 var ids = YUQ('#review_members input');
1696 var ids = YUQ('#review_members input');
1694 for(var i=0; i<ids.length;i++){
1697 for(var i=0; i<ids.length;i++){
1695 var id = ids[i].value
1698 var id = ids[i].value
1696 reviewers_ids.push(id);
1699 reviewers_ids.push(id);
1697 }
1700 }
1698 }
1701 }
1699 var url = pyroutes.url('pullrequest_update', {"repo_name":repo_name,
1702 var url = pyroutes.url('pullrequest_update', {"repo_name":repo_name,
1700 "pull_request_id": pull_request_id});
1703 "pull_request_id": pull_request_id});
1701 var postData = {'_method':'put',
1704 var postData = {'_method':'put',
1702 'reviewers_ids': reviewers_ids};
1705 'reviewers_ids': reviewers_ids};
1703 var success = function(o){
1706 var success = function(o){
1704 window.location.reload();
1707 window.location.reload();
1705 }
1708 }
1706 ajaxPOST(url,postData,success);
1709 ajaxPOST(url,postData,success);
1707 }
1710 }
1708
1711
1709 var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
1712 var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) {
1710 var myUsers = users_list;
1713 var myUsers = users_list;
1711 var myGroups = groups_list;
1714 var myGroups = groups_list;
1712
1715
1713 // Define a custom search function for the DataSource of users
1716 // Define a custom search function for the DataSource of users
1714 var matchUsers = function (sQuery) {
1717 var matchUsers = function (sQuery) {
1715 // Case insensitive matching
1718 // Case insensitive matching
1716 var query = sQuery.toLowerCase();
1719 var query = sQuery.toLowerCase();
1717 var i = 0;
1720 var i = 0;
1718 var l = myUsers.length;
1721 var l = myUsers.length;
1719 var matches = [];
1722 var matches = [];
1720
1723
1721 // Match against each name of each contact
1724 // Match against each name of each contact
1722 for (; i < l; i++) {
1725 for (; i < l; i++) {
1723 contact = myUsers[i];
1726 contact = myUsers[i];
1724 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1727 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1725 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1728 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1726 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1729 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1727 matches[matches.length] = contact;
1730 matches[matches.length] = contact;
1728 }
1731 }
1729 }
1732 }
1730 return matches;
1733 return matches;
1731 };
1734 };
1732
1735
1733 // Define a custom search function for the DataSource of userGroups
1736 // Define a custom search function for the DataSource of userGroups
1734 var matchGroups = function (sQuery) {
1737 var matchGroups = function (sQuery) {
1735 // Case insensitive matching
1738 // Case insensitive matching
1736 var query = sQuery.toLowerCase();
1739 var query = sQuery.toLowerCase();
1737 var i = 0;
1740 var i = 0;
1738 var l = myGroups.length;
1741 var l = myGroups.length;
1739 var matches = [];
1742 var matches = [];
1740
1743
1741 // Match against each name of each contact
1744 // Match against each name of each contact
1742 for (; i < l; i++) {
1745 for (; i < l; i++) {
1743 matched_group = myGroups[i];
1746 matched_group = myGroups[i];
1744 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1747 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1745 matches[matches.length] = matched_group;
1748 matches[matches.length] = matched_group;
1746 }
1749 }
1747 }
1750 }
1748 return matches;
1751 return matches;
1749 };
1752 };
1750
1753
1751 //match all
1754 //match all
1752 var matchAll = function (sQuery) {
1755 var matchAll = function (sQuery) {
1753 u = matchUsers(sQuery);
1756 u = matchUsers(sQuery);
1754 return u
1757 return u
1755 };
1758 };
1756
1759
1757 // DataScheme for owner
1760 // DataScheme for owner
1758 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1761 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
1759
1762
1760 ownerDS.responseSchema = {
1763 ownerDS.responseSchema = {
1761 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1764 fields: ["id", "fname", "lname", "nname", "gravatar_lnk"]
1762 };
1765 };
1763
1766
1764 // Instantiate AutoComplete for mentions
1767 // Instantiate AutoComplete for mentions
1765 var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1768 var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS);
1766 reviewerAC.useShadow = false;
1769 reviewerAC.useShadow = false;
1767 reviewerAC.resultTypeList = false;
1770 reviewerAC.resultTypeList = false;
1768 reviewerAC.suppressInputUpdate = true;
1771 reviewerAC.suppressInputUpdate = true;
1769 reviewerAC.animVert = false;
1772 reviewerAC.animVert = false;
1770 reviewerAC.animHoriz = false;
1773 reviewerAC.animHoriz = false;
1771 reviewerAC.animSpeed = 0.1;
1774 reviewerAC.animSpeed = 0.1;
1772
1775
1773 // Helper highlight function for the formatter
1776 // Helper highlight function for the formatter
1774 var highlightMatch = function (full, snippet, matchindex) {
1777 var highlightMatch = function (full, snippet, matchindex) {
1775 return full.substring(0, matchindex)
1778 return full.substring(0, matchindex)
1776 + "<span class='match'>"
1779 + "<span class='match'>"
1777 + full.substr(matchindex, snippet.length)
1780 + full.substr(matchindex, snippet.length)
1778 + "</span>" + full.substring(matchindex + snippet.length);
1781 + "</span>" + full.substring(matchindex + snippet.length);
1779 };
1782 };
1780
1783
1781 // Custom formatter to highlight the matching letters
1784 // Custom formatter to highlight the matching letters
1782 reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1785 reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1783 var org_sQuery = sQuery;
1786 var org_sQuery = sQuery;
1784 if(this.dataSource.mentionQuery != null){
1787 if(this.dataSource.mentionQuery != null){
1785 sQuery = this.dataSource.mentionQuery;
1788 sQuery = this.dataSource.mentionQuery;
1786 }
1789 }
1787
1790
1788 var query = sQuery.toLowerCase();
1791 var query = sQuery.toLowerCase();
1789 var _gravatar = function(res, em, group){
1792 var _gravatar = function(res, em, group){
1790 if (group !== undefined){
1793 if (group !== undefined){
1791 em = '/images/icons/group.png'
1794 em = '/images/icons/group.png'
1792 }
1795 }
1793 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1796 tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>'
1794 return tmpl.format(em,res)
1797 return tmpl.format(em,res)
1795 }
1798 }
1796 if (oResultData.nname != undefined) {
1799 if (oResultData.nname != undefined) {
1797 var fname = oResultData.fname || "";
1800 var fname = oResultData.fname || "";
1798 var lname = oResultData.lname || "";
1801 var lname = oResultData.lname || "";
1799 var nname = oResultData.nname;
1802 var nname = oResultData.nname;
1800
1803
1801 // Guard against null value
1804 // Guard against null value
1802 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1805 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1803 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1806 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1804 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1807 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1805 displayfname, displaylname, displaynname;
1808 displayfname, displaylname, displaynname;
1806
1809
1807 if (fnameMatchIndex > -1) {
1810 if (fnameMatchIndex > -1) {
1808 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1811 displayfname = highlightMatch(fname, query, fnameMatchIndex);
1809 } else {
1812 } else {
1810 displayfname = fname;
1813 displayfname = fname;
1811 }
1814 }
1812
1815
1813 if (lnameMatchIndex > -1) {
1816 if (lnameMatchIndex > -1) {
1814 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1817 displaylname = highlightMatch(lname, query, lnameMatchIndex);
1815 } else {
1818 } else {
1816 displaylname = lname;
1819 displaylname = lname;
1817 }
1820 }
1818
1821
1819 if (nnameMatchIndex > -1) {
1822 if (nnameMatchIndex > -1) {
1820 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1823 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
1821 } else {
1824 } else {
1822 displaynname = nname ? "(" + nname + ")" : "";
1825 displaynname = nname ? "(" + nname + ")" : "";
1823 }
1826 }
1824
1827
1825 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1828 return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk);
1826 } else {
1829 } else {
1827 return '';
1830 return '';
1828 }
1831 }
1829 };
1832 };
1830
1833
1831 //members cache to catch duplicates
1834 //members cache to catch duplicates
1832 reviewerAC.dataSource.cache = [];
1835 reviewerAC.dataSource.cache = [];
1833 // hack into select event
1836 // hack into select event
1834 if(reviewerAC.itemSelectEvent){
1837 if(reviewerAC.itemSelectEvent){
1835 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1838 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1836
1839
1837 var myAC = aArgs[0]; // reference back to the AC instance
1840 var myAC = aArgs[0]; // reference back to the AC instance
1838 var elLI = aArgs[1]; // reference to the selected LI element
1841 var elLI = aArgs[1]; // reference to the selected LI element
1839 var oData = aArgs[2]; // object literal of selected item's result data
1842 var oData = aArgs[2]; // object literal of selected item's result data
1840
1843
1841 //fill the autocomplete with value
1844 //fill the autocomplete with value
1842
1845
1843 if (oData.nname != undefined) {
1846 if (oData.nname != undefined) {
1844 addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
1847 addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
1845 oData.gravatar_lnk);
1848 oData.gravatar_lnk);
1846 myAC.dataSource.cache.push(oData.id);
1849 myAC.dataSource.cache.push(oData.id);
1847 YUD.get('user').value = ''
1850 YUD.get('user').value = ''
1848 }
1851 }
1849 });
1852 });
1850 }
1853 }
1851 return {
1854 return {
1852 ownerDS: ownerDS,
1855 ownerDS: ownerDS,
1853 reviewerAC: reviewerAC,
1856 reviewerAC: reviewerAC,
1854 };
1857 };
1855 }
1858 }
1856
1859
1857 /**
1860 /**
1858 * QUICK REPO MENU
1861 * QUICK REPO MENU
1859 */
1862 */
1860 var quick_repo_menu = function(){
1863 var quick_repo_menu = function(){
1861 YUE.on(YUQ('.quick_repo_menu'),'mouseenter',function(e){
1864 YUE.on(YUQ('.quick_repo_menu'),'mouseenter',function(e){
1862 var menu = e.currentTarget.firstElementChild.firstElementChild;
1865 var menu = e.currentTarget.firstElementChild.firstElementChild;
1863 if(YUD.hasClass(menu,'hidden')){
1866 if(YUD.hasClass(menu,'hidden')){
1864 YUD.replaceClass(e.currentTarget,'hidden', 'active');
1867 YUD.replaceClass(e.currentTarget,'hidden', 'active');
1865 YUD.replaceClass(menu, 'hidden', 'active');
1868 YUD.replaceClass(menu, 'hidden', 'active');
1866 }
1869 }
1867 })
1870 })
1868 YUE.on(YUQ('.quick_repo_menu'),'mouseleave',function(e){
1871 YUE.on(YUQ('.quick_repo_menu'),'mouseleave',function(e){
1869 var menu = e.currentTarget.firstElementChild.firstElementChild;
1872 var menu = e.currentTarget.firstElementChild.firstElementChild;
1870 if(YUD.hasClass(menu,'active')){
1873 if(YUD.hasClass(menu,'active')){
1871 YUD.replaceClass(e.currentTarget, 'active', 'hidden');
1874 YUD.replaceClass(e.currentTarget, 'active', 'hidden');
1872 YUD.replaceClass(menu, 'active', 'hidden');
1875 YUD.replaceClass(menu, 'active', 'hidden');
1873 }
1876 }
1874 })
1877 })
1875 };
1878 };
1876
1879
1877
1880
1878 /**
1881 /**
1879 * TABLE SORTING
1882 * TABLE SORTING
1880 */
1883 */
1881
1884
1882 // returns a node from given html;
1885 // returns a node from given html;
1883 var fromHTML = function(html){
1886 var fromHTML = function(html){
1884 var _html = document.createElement('element');
1887 var _html = document.createElement('element');
1885 _html.innerHTML = html;
1888 _html.innerHTML = html;
1886 return _html;
1889 return _html;
1887 }
1890 }
1888 var get_rev = function(node){
1891 var get_rev = function(node){
1889 var n = node.firstElementChild.firstElementChild;
1892 var n = node.firstElementChild.firstElementChild;
1890
1893
1891 if (n===null){
1894 if (n===null){
1892 return -1
1895 return -1
1893 }
1896 }
1894 else{
1897 else{
1895 out = n.firstElementChild.innerHTML.split(':')[0].replace('r','');
1898 out = n.firstElementChild.innerHTML.split(':')[0].replace('r','');
1896 return parseInt(out);
1899 return parseInt(out);
1897 }
1900 }
1898 }
1901 }
1899
1902
1900 var get_name = function(node){
1903 var get_name = function(node){
1901 var name = node.firstElementChild.children[2].innerHTML;
1904 var name = node.firstElementChild.children[2].innerHTML;
1902 return name
1905 return name
1903 }
1906 }
1904 var get_group_name = function(node){
1907 var get_group_name = function(node){
1905 var name = node.firstElementChild.children[1].innerHTML;
1908 var name = node.firstElementChild.children[1].innerHTML;
1906 return name
1909 return name
1907 }
1910 }
1908 var get_date = function(node){
1911 var get_date = function(node){
1909 var date_ = YUD.getAttribute(node.firstElementChild,'date');
1912 var date_ = YUD.getAttribute(node.firstElementChild,'date');
1910 return date_
1913 return date_
1911 }
1914 }
1912
1915
1913 var get_age = function(node){
1916 var get_age = function(node){
1914 return node
1917 return node
1915 }
1918 }
1916
1919
1917 var get_link = function(node){
1920 var get_link = function(node){
1918 return node.firstElementChild.text;
1921 return node.firstElementChild.text;
1919 }
1922 }
1920
1923
1921 var revisionSort = function(a, b, desc, field) {
1924 var revisionSort = function(a, b, desc, field) {
1922
1925
1923 var a_ = fromHTML(a.getData(field));
1926 var a_ = fromHTML(a.getData(field));
1924 var b_ = fromHTML(b.getData(field));
1927 var b_ = fromHTML(b.getData(field));
1925
1928
1926 // extract revisions from string nodes
1929 // extract revisions from string nodes
1927 a_ = get_rev(a_)
1930 a_ = get_rev(a_)
1928 b_ = get_rev(b_)
1931 b_ = get_rev(b_)
1929
1932
1930 var comp = YAHOO.util.Sort.compare;
1933 var comp = YAHOO.util.Sort.compare;
1931 var compState = comp(a_, b_, desc);
1934 var compState = comp(a_, b_, desc);
1932 return compState;
1935 return compState;
1933 };
1936 };
1934 var ageSort = function(a, b, desc, field) {
1937 var ageSort = function(a, b, desc, field) {
1935 var a_ = fromHTML(a.getData(field));
1938 var a_ = fromHTML(a.getData(field));
1936 var b_ = fromHTML(b.getData(field));
1939 var b_ = fromHTML(b.getData(field));
1937
1940
1938 // extract name from table
1941 // extract name from table
1939 a_ = get_date(a_)
1942 a_ = get_date(a_)
1940 b_ = get_date(b_)
1943 b_ = get_date(b_)
1941
1944
1942 var comp = YAHOO.util.Sort.compare;
1945 var comp = YAHOO.util.Sort.compare;
1943 var compState = comp(a_, b_, desc);
1946 var compState = comp(a_, b_, desc);
1944 return compState;
1947 return compState;
1945 };
1948 };
1946
1949
1947 var lastLoginSort = function(a, b, desc, field) {
1950 var lastLoginSort = function(a, b, desc, field) {
1948 var a_ = a.getData('last_login_raw') || 0;
1951 var a_ = a.getData('last_login_raw') || 0;
1949 var b_ = b.getData('last_login_raw') || 0;
1952 var b_ = b.getData('last_login_raw') || 0;
1950
1953
1951 var comp = YAHOO.util.Sort.compare;
1954 var comp = YAHOO.util.Sort.compare;
1952 var compState = comp(a_, b_, desc);
1955 var compState = comp(a_, b_, desc);
1953 return compState;
1956 return compState;
1954 };
1957 };
1955
1958
1956 var nameSort = function(a, b, desc, field) {
1959 var nameSort = function(a, b, desc, field) {
1957 var a_ = fromHTML(a.getData(field));
1960 var a_ = fromHTML(a.getData(field));
1958 var b_ = fromHTML(b.getData(field));
1961 var b_ = fromHTML(b.getData(field));
1959
1962
1960 // extract name from table
1963 // extract name from table
1961 a_ = get_name(a_)
1964 a_ = get_name(a_)
1962 b_ = get_name(b_)
1965 b_ = get_name(b_)
1963
1966
1964 var comp = YAHOO.util.Sort.compare;
1967 var comp = YAHOO.util.Sort.compare;
1965 var compState = comp(a_, b_, desc);
1968 var compState = comp(a_, b_, desc);
1966 return compState;
1969 return compState;
1967 };
1970 };
1968
1971
1969 var permNameSort = function(a, b, desc, field) {
1972 var permNameSort = function(a, b, desc, field) {
1970 var a_ = fromHTML(a.getData(field));
1973 var a_ = fromHTML(a.getData(field));
1971 var b_ = fromHTML(b.getData(field));
1974 var b_ = fromHTML(b.getData(field));
1972 // extract name from table
1975 // extract name from table
1973
1976
1974 a_ = a_.children[0].innerHTML;
1977 a_ = a_.children[0].innerHTML;
1975 b_ = b_.children[0].innerHTML;
1978 b_ = b_.children[0].innerHTML;
1976
1979
1977 var comp = YAHOO.util.Sort.compare;
1980 var comp = YAHOO.util.Sort.compare;
1978 var compState = comp(a_, b_, desc);
1981 var compState = comp(a_, b_, desc);
1979 return compState;
1982 return compState;
1980 };
1983 };
1981
1984
1982 var groupNameSort = function(a, b, desc, field) {
1985 var groupNameSort = function(a, b, desc, field) {
1983 var a_ = fromHTML(a.getData(field));
1986 var a_ = fromHTML(a.getData(field));
1984 var b_ = fromHTML(b.getData(field));
1987 var b_ = fromHTML(b.getData(field));
1985
1988
1986 // extract name from table
1989 // extract name from table
1987 a_ = get_group_name(a_)
1990 a_ = get_group_name(a_)
1988 b_ = get_group_name(b_)
1991 b_ = get_group_name(b_)
1989
1992
1990 var comp = YAHOO.util.Sort.compare;
1993 var comp = YAHOO.util.Sort.compare;
1991 var compState = comp(a_, b_, desc);
1994 var compState = comp(a_, b_, desc);
1992 return compState;
1995 return compState;
1993 };
1996 };
1994 var dateSort = function(a, b, desc, field) {
1997 var dateSort = function(a, b, desc, field) {
1995 var a_ = fromHTML(a.getData(field));
1998 var a_ = fromHTML(a.getData(field));
1996 var b_ = fromHTML(b.getData(field));
1999 var b_ = fromHTML(b.getData(field));
1997
2000
1998 // extract name from table
2001 // extract name from table
1999 a_ = get_date(a_)
2002 a_ = get_date(a_)
2000 b_ = get_date(b_)
2003 b_ = get_date(b_)
2001
2004
2002 var comp = YAHOO.util.Sort.compare;
2005 var comp = YAHOO.util.Sort.compare;
2003 var compState = comp(a_, b_, desc);
2006 var compState = comp(a_, b_, desc);
2004 return compState;
2007 return compState;
2005 };
2008 };
2006
2009
2007 var usernamelinkSort = function(a, b, desc, field) {
2010 var usernamelinkSort = function(a, b, desc, field) {
2008 var a_ = fromHTML(a.getData(field));
2011 var a_ = fromHTML(a.getData(field));
2009 var b_ = fromHTML(b.getData(field));
2012 var b_ = fromHTML(b.getData(field));
2010
2013
2011 // extract url text from string nodes
2014 // extract url text from string nodes
2012 a_ = get_link(a_)
2015 a_ = get_link(a_)
2013 b_ = get_link(b_)
2016 b_ = get_link(b_)
2014 var comp = YAHOO.util.Sort.compare;
2017 var comp = YAHOO.util.Sort.compare;
2015 var compState = comp(a_, b_, desc);
2018 var compState = comp(a_, b_, desc);
2016 return compState;
2019 return compState;
2017 }
2020 }
2018
2021
2019 var addPermAction = function(_html, users_list, groups_list){
2022 var addPermAction = function(_html, users_list, groups_list){
2020 var elmts = YUD.getElementsByClassName('last_new_member');
2023 var elmts = YUD.getElementsByClassName('last_new_member');
2021 var last_node = elmts[elmts.length-1];
2024 var last_node = elmts[elmts.length-1];
2022 if (last_node){
2025 if (last_node){
2023 var next_id = (YUD.getElementsByClassName('new_members')).length;
2026 var next_id = (YUD.getElementsByClassName('new_members')).length;
2024 _html = _html.format(next_id);
2027 _html = _html.format(next_id);
2025 last_node.innerHTML = _html;
2028 last_node.innerHTML = _html;
2026 YUD.setStyle(last_node, 'display', '');
2029 YUD.setStyle(last_node, 'display', '');
2027 YUD.removeClass(last_node, 'last_new_member');
2030 YUD.removeClass(last_node, 'last_new_member');
2028 MembersAutoComplete("perm_new_member_name_"+next_id,
2031 MembersAutoComplete("perm_new_member_name_"+next_id,
2029 "perm_container_"+next_id, users_list, groups_list);
2032 "perm_container_"+next_id, users_list, groups_list);
2030 //create new last NODE
2033 //create new last NODE
2031 var el = document.createElement('tr');
2034 var el = document.createElement('tr');
2032 el.id = 'add_perm_input';
2035 el.id = 'add_perm_input';
2033 YUD.addClass(el,'last_new_member');
2036 YUD.addClass(el,'last_new_member');
2034 YUD.addClass(el,'new_members');
2037 YUD.addClass(el,'new_members');
2035 YUD.insertAfter(el, last_node);
2038 YUD.insertAfter(el, last_node);
2036 }
2039 }
2037 }
2040 }
2041 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
2042 var callback = {
2043 success: function (o) {
2044 var tr = YUD.get(String(field_id));
2045 tr.parentNode.removeChild(tr);
2046 },
2047 failure: function (o) {
2048 alert(_TM['Failed to remoke permission'] + ": " + o.status);
2049 },
2050 };
2051 query_params = {
2052 '_method': 'delete'
2053 }
2054 // put extra data into POST
2055 if (extra_data !== undefined && (typeof extra_data === 'object')){
2056 for(k in extra_data){
2057 query_params[k] = extra_data[k];
2058 }
2059 }
2038
2060
2061 if (obj_type=='user'){
2062 query_params['user_id'] = obj_id;
2063 query_params['obj_type'] = 'user';
2064 }
2065 else if (obj_type=='user_group'){
2066 query_params['user_group_id'] = obj_id;
2067 query_params['obj_type'] = 'user_group';
2068 }
2069
2070 var request = YAHOO.util.Connect.asyncRequest('POST', url, callback,
2071 toQueryString(query_params));
2072 };
2039 /* Multi selectors */
2073 /* Multi selectors */
2040
2074
2041 var MultiSelectWidget = function(selected_id, available_id, form_id){
2075 var MultiSelectWidget = function(selected_id, available_id, form_id){
2042
2076
2043
2077
2044 //definition of containers ID's
2078 //definition of containers ID's
2045 var selected_container = selected_id;
2079 var selected_container = selected_id;
2046 var available_container = available_id;
2080 var available_container = available_id;
2047
2081
2048 //temp container for selected storage.
2082 //temp container for selected storage.
2049 var cache = new Array();
2083 var cache = new Array();
2050 var av_cache = new Array();
2084 var av_cache = new Array();
2051 var c = YUD.get(selected_container);
2085 var c = YUD.get(selected_container);
2052 var ac = YUD.get(available_container);
2086 var ac = YUD.get(available_container);
2053
2087
2054 //get only selected options for further fullfilment
2088 //get only selected options for further fullfilment
2055 for(var i = 0;node =c.options[i];i++){
2089 for(var i = 0;node =c.options[i];i++){
2056 if(node.selected){
2090 if(node.selected){
2057 //push selected to my temp storage left overs :)
2091 //push selected to my temp storage left overs :)
2058 cache.push(node);
2092 cache.push(node);
2059 }
2093 }
2060 }
2094 }
2061
2095
2062 //get all available options to cache
2096 //get all available options to cache
2063 for(var i = 0;node =ac.options[i];i++){
2097 for(var i = 0;node =ac.options[i];i++){
2064 //push selected to my temp storage left overs :)
2098 //push selected to my temp storage left overs :)
2065 av_cache.push(node);
2099 av_cache.push(node);
2066 }
2100 }
2067
2101
2068 //fill available only with those not in chosen
2102 //fill available only with those not in chosen
2069 ac.options.length=0;
2103 ac.options.length=0;
2070 tmp_cache = new Array();
2104 tmp_cache = new Array();
2071
2105
2072 for(var i = 0;node = av_cache[i];i++){
2106 for(var i = 0;node = av_cache[i];i++){
2073 var add = true;
2107 var add = true;
2074 for(var i2 = 0;node_2 = cache[i2];i2++){
2108 for(var i2 = 0;node_2 = cache[i2];i2++){
2075 if(node.value == node_2.value){
2109 if(node.value == node_2.value){
2076 add=false;
2110 add=false;
2077 break;
2111 break;
2078 }
2112 }
2079 }
2113 }
2080 if(add){
2114 if(add){
2081 tmp_cache.push(new Option(node.text, node.value, false, false));
2115 tmp_cache.push(new Option(node.text, node.value, false, false));
2082 }
2116 }
2083 }
2117 }
2084
2118
2085 for(var i = 0;node = tmp_cache[i];i++){
2119 for(var i = 0;node = tmp_cache[i];i++){
2086 ac.options[i] = node;
2120 ac.options[i] = node;
2087 }
2121 }
2088
2122
2089 function prompts_action_callback(e){
2123 function prompts_action_callback(e){
2090
2124
2091 var chosen = YUD.get(selected_container);
2125 var chosen = YUD.get(selected_container);
2092 var available = YUD.get(available_container);
2126 var available = YUD.get(available_container);
2093
2127
2094 //get checked and unchecked options from field
2128 //get checked and unchecked options from field
2095 function get_checked(from_field){
2129 function get_checked(from_field){
2096 //temp container for storage.
2130 //temp container for storage.
2097 var sel_cache = new Array();
2131 var sel_cache = new Array();
2098 var oth_cache = new Array();
2132 var oth_cache = new Array();
2099
2133
2100 for(var i = 0;node = from_field.options[i];i++){
2134 for(var i = 0;node = from_field.options[i];i++){
2101 if(node.selected){
2135 if(node.selected){
2102 //push selected fields :)
2136 //push selected fields :)
2103 sel_cache.push(node);
2137 sel_cache.push(node);
2104 }
2138 }
2105 else{
2139 else{
2106 oth_cache.push(node)
2140 oth_cache.push(node)
2107 }
2141 }
2108 }
2142 }
2109
2143
2110 return [sel_cache,oth_cache]
2144 return [sel_cache,oth_cache]
2111 }
2145 }
2112
2146
2113 //fill the field with given options
2147 //fill the field with given options
2114 function fill_with(field,options){
2148 function fill_with(field,options){
2115 //clear firtst
2149 //clear firtst
2116 field.options.length=0;
2150 field.options.length=0;
2117 for(var i = 0;node = options[i];i++){
2151 for(var i = 0;node = options[i];i++){
2118 field.options[i]=new Option(node.text, node.value,
2152 field.options[i]=new Option(node.text, node.value,
2119 false, false);
2153 false, false);
2120 }
2154 }
2121
2155
2122 }
2156 }
2123 //adds to current field
2157 //adds to current field
2124 function add_to(field,options){
2158 function add_to(field,options){
2125 for(var i = 0;node = options[i];i++){
2159 for(var i = 0;node = options[i];i++){
2126 field.appendChild(new Option(node.text, node.value,
2160 field.appendChild(new Option(node.text, node.value,
2127 false, false));
2161 false, false));
2128 }
2162 }
2129 }
2163 }
2130
2164
2131 // add action
2165 // add action
2132 if (this.id=='add_element'){
2166 if (this.id=='add_element'){
2133 var c = get_checked(available);
2167 var c = get_checked(available);
2134 add_to(chosen,c[0]);
2168 add_to(chosen,c[0]);
2135 fill_with(available,c[1]);
2169 fill_with(available,c[1]);
2136 }
2170 }
2137 // remove action
2171 // remove action
2138 if (this.id=='remove_element'){
2172 if (this.id=='remove_element'){
2139 var c = get_checked(chosen);
2173 var c = get_checked(chosen);
2140 add_to(available,c[0]);
2174 add_to(available,c[0]);
2141 fill_with(chosen,c[1]);
2175 fill_with(chosen,c[1]);
2142 }
2176 }
2143 // add all elements
2177 // add all elements
2144 if(this.id=='add_all_elements'){
2178 if(this.id=='add_all_elements'){
2145 for(var i=0; node = available.options[i];i++){
2179 for(var i=0; node = available.options[i];i++){
2146 chosen.appendChild(new Option(node.text,
2180 chosen.appendChild(new Option(node.text,
2147 node.value, false, false));
2181 node.value, false, false));
2148 }
2182 }
2149 available.options.length = 0;
2183 available.options.length = 0;
2150 }
2184 }
2151 //remove all elements
2185 //remove all elements
2152 if(this.id=='remove_all_elements'){
2186 if(this.id=='remove_all_elements'){
2153 for(var i=0; node = chosen.options[i];i++){
2187 for(var i=0; node = chosen.options[i];i++){
2154 available.appendChild(new Option(node.text,
2188 available.appendChild(new Option(node.text,
2155 node.value, false, false));
2189 node.value, false, false));
2156 }
2190 }
2157 chosen.options.length = 0;
2191 chosen.options.length = 0;
2158 }
2192 }
2159
2193
2160 }
2194 }
2161
2195
2162 YUE.addListener(['add_element','remove_element',
2196 YUE.addListener(['add_element','remove_element',
2163 'add_all_elements','remove_all_elements'],'click',
2197 'add_all_elements','remove_all_elements'],'click',
2164 prompts_action_callback)
2198 prompts_action_callback)
2165 if (form_id !== undefined) {
2199 if (form_id !== undefined) {
2166 YUE.addListener(form_id,'submit',function(){
2200 YUE.addListener(form_id,'submit',function(){
2167 var chosen = YUD.get(selected_container);
2201 var chosen = YUD.get(selected_container);
2168 for (var i = 0; i < chosen.options.length; i++) {
2202 for (var i = 0; i < chosen.options.length; i++) {
2169 chosen.options[i].selected = 'selected';
2203 chosen.options[i].selected = 'selected';
2170 }
2204 }
2171 });
2205 });
2172 }
2206 }
2173 }
2207 }
2174
2208
2175
2209
2176 // global hooks after DOM is loaded
2210 // global hooks after DOM is loaded
2177
2211
2178 YUE.onDOMReady(function(){
2212 YUE.onDOMReady(function(){
2179 YUE.on(YUQ('.diff-collapse-button'), 'click', function(e){
2213 YUE.on(YUQ('.diff-collapse-button'), 'click', function(e){
2180 var button = e.currentTarget;
2214 var button = e.currentTarget;
2181 var t = YUD.get(button).getAttribute('target');
2215 var t = YUD.get(button).getAttribute('target');
2182 console.log(t);
2216 console.log(t);
2183 if(YUD.hasClass(t, 'hidden')){
2217 if(YUD.hasClass(t, 'hidden')){
2184 YUD.removeClass(t, 'hidden');
2218 YUD.removeClass(t, 'hidden');
2185 YUD.get(button).innerHTML = "&uarr; {0} &uarr;".format(_TM['Collapse diff']);
2219 YUD.get(button).innerHTML = "&uarr; {0} &uarr;".format(_TM['Collapse diff']);
2186 }
2220 }
2187 else if(!YUD.hasClass(t, 'hidden')){
2221 else if(!YUD.hasClass(t, 'hidden')){
2188 YUD.addClass(t, 'hidden');
2222 YUD.addClass(t, 'hidden');
2189 YUD.get(button).innerHTML = "&darr; {0} &darr;".format(_TM['Expand diff']);
2223 YUD.get(button).innerHTML = "&darr; {0} &darr;".format(_TM['Expand diff']);
2190 }
2224 }
2191 });
2225 });
2192
2226
2193
2227
2194
2228
2195 });
2229 });
@@ -1,122 +1,103 b''
1 <table id="permissions_manage" class="noborder">
1 <table id="permissions_manage" class="noborder">
2 <tr>
2 <tr>
3 <td>${_('none')}</td>
3 <td>${_('none')}</td>
4 <td>${_('read')}</td>
4 <td>${_('read')}</td>
5 <td>${_('write')}</td>
5 <td>${_('write')}</td>
6 <td>${_('admin')}</td>
6 <td>${_('admin')}</td>
7 <td>${_('member')}</td>
7 <td>${_('member')}</td>
8 <td></td>
8 <td></td>
9 </tr>
9 </tr>
10 ## USERS
10 ## USERS
11 %for r2p in c.repo_info.repo_to_perm:
11 %for r2p in c.repo_info.repo_to_perm:
12 %if r2p.user.username =='default' and c.repo_info.private:
12 %if r2p.user.username =='default' and c.repo_info.private:
13 <tr>
13 <tr>
14 <td colspan="4">
14 <td colspan="4">
15 <span class="private_repo_msg">
15 <span class="private_repo_msg">
16 ${_('private repository')}
16 ${_('private repository')}
17 </span>
17 </span>
18 </td>
18 </td>
19 <td class="private_repo_msg"><img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${_('default')}</td>
19 <td class="private_repo_msg"><img style="vertical-align:bottom" src="${h.url('/images/icons/user.png')}"/>${_('default')}</td>
20 </tr>
20 </tr>
21 %else:
21 %else:
22 <tr id="id${id(r2p.user.username)}">
22 <tr id="id${id(r2p.user.username)}">
23 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.none')}</td>
23 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.none')}</td>
24 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.read')}</td>
24 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.read')}</td>
25 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
25 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
26 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
26 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
27 <td style="white-space: nowrap;">
27 <td style="white-space: nowrap;">
28 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
28 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
29 </td>
29 </td>
30 <td>
30 <td>
31 %if r2p.user.username !='default':
31 %if r2p.user.username !='default':
32 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
32 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
33 ${_('revoke')}
33 ${_('revoke')}
34 </span>
34 </span>
35 %endif
35 %endif
36 </td>
36 </td>
37 </tr>
37 </tr>
38 %endif
38 %endif
39 %endfor
39 %endfor
40
40
41 ## USER GROUPS
41 ## USER GROUPS
42 %for g2p in c.repo_info.users_group_to_perm:
42 %for g2p in c.repo_info.users_group_to_perm:
43 <tr id="id${id(g2p.users_group.users_group_name)}">
43 <tr id="id${id(g2p.users_group.users_group_name)}">
44 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.none')}</td>
44 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.none')}</td>
45 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.read')}</td>
45 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.read')}</td>
46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
48 <td style="white-space: nowrap;">
48 <td style="white-space: nowrap;">
49 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>
49 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>
50 %if h.HasPermissionAny('hg.admin')():
50 %if h.HasPermissionAny('hg.admin')():
51 <a href="${h.url('edit_users_group',id=g2p.users_group.users_group_id)}">${g2p.users_group.users_group_name}</a>
51 <a href="${h.url('edit_users_group',id=g2p.users_group.users_group_id)}">${g2p.users_group.users_group_name}</a>
52 %else:
52 %else:
53 ${g2p.users_group.users_group_name}
53 ${g2p.users_group.users_group_name}
54 %endif
54 %endif
55 </td>
55 </td>
56 <td>
56 <td>
57 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${g2p.users_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.users_group.users_group_name)}')">
57 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${g2p.users_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.users_group.users_group_name)}')">
58 ${_('revoke')}
58 ${_('revoke')}
59 </span>
59 </span>
60 </td>
60 </td>
61 </tr>
61 </tr>
62 %endfor
62 %endfor
63 <%
63 <%
64 _tmpl = h.literal("""' \
64 _tmpl = h.literal("""' \
65 <td><input type="radio" value="repository.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
65 <td><input type="radio" value="repository.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
66 <td><input type="radio" value="repository.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
66 <td><input type="radio" value="repository.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
67 <td><input type="radio" value="repository.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
67 <td><input type="radio" value="repository.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
68 <td><input type="radio" value="repository.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
68 <td><input type="radio" value="repository.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
69 <td class="ac"> \
69 <td class="ac"> \
70 <div class="perm_ac" id="perm_ac_{0}"> \
70 <div class="perm_ac" id="perm_ac_{0}"> \
71 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
71 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
72 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
72 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
73 <div id="perm_container_{0}"></div> \
73 <div id="perm_container_{0}"></div> \
74 </div> \
74 </div> \
75 </td> \
75 </td> \
76 <td></td>'""")
76 <td></td>'""")
77 %>
77 %>
78 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
78 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
79 <tr class="new_members last_new_member" id="add_perm_input"></tr>
79 <tr class="new_members last_new_member" id="add_perm_input"></tr>
80 <tr>
80 <tr>
81 <td colspan="6">
81 <td colspan="6">
82 <span id="add_perm" class="add_icon" style="cursor: pointer;">
82 <span id="add_perm" class="add_icon" style="cursor: pointer;">
83 ${_('Add another member')}
83 ${_('Add another member')}
84 </span>
84 </span>
85 </td>
85 </td>
86 </tr>
86 </tr>
87 </table>
87 </table>
88 <script type="text/javascript">
88 <script type="text/javascript">
89 function ajaxActionRevoke(obj_id, obj_type, field_id) {
89 function ajaxActionRevoke(obj_id, obj_type, field_id) {
90 var callback = {
90 url = "${h.url('delete_repo_perm_member',repo_name=c.repo_name)}";
91 success: function (o) {
91 ajaxActionRevokePermission(url, obj_id, obj_type, field_id);
92 var tr = YUD.get(String(field_id));
93 tr.parentNode.removeChild(tr);
94 },
95 failure: function (o) {
96 alert(_TM['Failed to remoke permission'] + ": " + o.status);
97 },
98 };
99 if (obj_type=='user'){
100 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
101 var postData = '_method=delete&user_id={0}&obj_type=user'.format(obj_id);
102 }
103 else if (obj_type=='user_group'){
104 var sUrl = "${h.url('delete_repo_users_group',repo_name=c.repo_name)}";
105 var postData = '_method=delete&users_group_id={0}&obj_type=user_group'.format(obj_id);
106
107 }
108
109 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
110 };
92 };
111
93
112
113 YUE.onDOMReady(function () {
94 YUE.onDOMReady(function () {
114 if (!YUD.hasClass('perm_new_member_name', 'error')) {
95 if (!YUD.hasClass('perm_new_member_name', 'error')) {
115 YUD.setStyle('add_perm_input', 'display', 'none');
96 YUD.setStyle('add_perm_input', 'display', 'none');
116 }
97 }
117 YAHOO.util.Event.addListener('add_perm', 'click', function () {
98 YAHOO.util.Event.addListener('add_perm', 'click', function () {
118 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
99 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
119 });
100 });
120 });
101 });
121
102
122 </script>
103 </script>
@@ -1,125 +1,106 b''
1 <table id="permissions_manage" class="noborder">
1 <table id="permissions_manage" class="noborder">
2 <tr>
2 <tr>
3 <td>${_('none')}</td>
3 <td>${_('none')}</td>
4 <td>${_('read')}</td>
4 <td>${_('read')}</td>
5 <td>${_('write')}</td>
5 <td>${_('write')}</td>
6 <td>${_('admin')}</td>
6 <td>${_('admin')}</td>
7 <td>${_('member')}</td>
7 <td>${_('member')}</td>
8 <td></td>
8 <td></td>
9 </tr>
9 </tr>
10 ## USERS
10 ## USERS
11 %for r2p in c.repos_group.repo_group_to_perm:
11 %for r2p in c.repos_group.repo_group_to_perm:
12 ##forbid revoking permission from yourself
12 ##forbid revoking permission from yourself
13 <tr id="id${id(r2p.user.username)}">
13 <tr id="id${id(r2p.user.username)}">
14 %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
14 %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td>
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read')}</td>
17 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
17 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write')}</td>
18 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
18 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin')}</td>
19 <td style="white-space: nowrap;">
19 <td style="white-space: nowrap;">
20 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
20 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
21 </td>
21 </td>
22 <td>
22 <td>
23 %if r2p.user.username !='default':
23 %if r2p.user.username !='default':
24 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
24 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
25 ${_('revoke')}
25 ${_('revoke')}
26 </span>
26 </span>
27 %endif
27 %endif
28 </td>
28 </td>
29 %else:
29 %else:
30 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none', disabled="disabled")}</td>
30 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.none', disabled="disabled")}</td>
31 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read', disabled="disabled")}</td>
31 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.read', disabled="disabled")}</td>
32 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write', disabled="disabled")}</td>
32 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.write', disabled="disabled")}</td>
33 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin', disabled="disabled")}</td>
33 <td>${h.radio('u_perm_%s' % r2p.user.username,'group.admin', disabled="disabled")}</td>
34 <td style="white-space: nowrap;">
34 <td style="white-space: nowrap;">
35 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
35 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
36 </td>
36 </td>
37 <td>
37 <td>
38 </td>
38 </td>
39 %endif
39 %endif
40 </tr>
40 </tr>
41 %endfor
41 %endfor
42
42
43 ## USER GROUPS
43 ## USER GROUPS
44 %for g2p in c.repos_group.users_group_to_perm:
44 %for g2p in c.repos_group.users_group_to_perm:
45 <tr id="id${id(g2p.users_group.users_group_name)}">
45 <tr id="id${id(g2p.users_group.users_group_name)}">
46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td>
46 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.none')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td>
47 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.read')}</td>
48 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
48 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.write')}</td>
49 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
49 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'group.admin')}</td>
50 <td style="white-space: nowrap;">
50 <td style="white-space: nowrap;">
51 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
51 <img class="perm-gravatar" src="${h.url('/images/icons/group.png')}"/>${g2p.users_group.users_group_name}
52 </td>
52 </td>
53 <td>
53 <td>
54 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${g2p.users_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.users_group.users_group_name)}')">
54 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${g2p.users_group.users_group_id}, 'user_group', '${'id%s'%id(g2p.users_group.users_group_name)}')">
55 ${_('revoke')}
55 ${_('revoke')}
56 </span>
56 </span>
57 </td>
57 </td>
58 </tr>
58 </tr>
59 %endfor
59 %endfor
60 <%
60 <%
61 _tmpl = h.literal("""' \
61 _tmpl = h.literal("""' \
62 <td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
62 <td><input type="radio" value="group.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
63 <td><input type="radio" value="group.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
63 <td><input type="radio" value="group.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
64 <td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
64 <td><input type="radio" value="group.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
65 <td><input type="radio" value="group.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
65 <td><input type="radio" value="group.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
66 <td class="ac"> \
66 <td class="ac"> \
67 <div class="perm_ac" id="perm_ac_{0}"> \
67 <div class="perm_ac" id="perm_ac_{0}"> \
68 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
68 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
69 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
69 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
70 <div id="perm_container_{0}"></div> \
70 <div id="perm_container_{0}"></div> \
71 </div> \
71 </div> \
72 </td> \
72 </td> \
73 <td></td>'""")
73 <td></td>'""")
74 %>
74 %>
75 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
75 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
76 <tr class="new_members last_new_member" id="add_perm_input"></tr>
76 <tr class="new_members last_new_member" id="add_perm_input"></tr>
77 <tr>
77 <tr>
78 <td colspan="6">
78 <td colspan="6">
79 <span id="add_perm" class="add_icon" style="cursor: pointer;">
79 <span id="add_perm" class="add_icon" style="cursor: pointer;">
80 ${_('Add another member')}
80 ${_('Add another member')}
81 </span>
81 </span>
82 </td>
82 </td>
83 </tr>
83 </tr>
84 <tr>
84 <tr>
85 <td colspan="6">
85 <td colspan="6">
86 ${h.checkbox('recursive',value="True", label=_('apply to children'))}
86 ${h.checkbox('recursive',value="True", label=_('apply to children'))}
87 <span class="help-block">${_('Set or revoke permission to all children of that group, including non-private repositories and other groups')}</span>
87 <span class="help-block">${_('Set or revoke permission to all children of that group, including non-private repositories and other groups')}</span>
88 </td>
88 </td>
89 </tr>
89 </tr>
90 </table>
90 </table>
91 <script type="text/javascript">
91 <script type="text/javascript">
92 function ajaxActionRevoke(obj_id, obj_type, field_id) {
92 function ajaxActionRevoke(obj_id, obj_type, field_id) {
93 var callback = {
93 url = "${h.url('delete_repo_group_perm_member', group_name=c.repos_group.group_name)}";
94 success: function (o) {
94 ajaxActionRevokePermission(url, obj_id, obj_type, field_id, {recursive:YUD.get('recursive').checked});
95 var tr = YUD.get(String(field_id));
96 tr.parentNode.removeChild(tr);
97 },
98 failure: function (o) {
99 alert(_TM['Failed to remoke permission'] + ": " + o.status);
100 },
101 };
102 var recursive = YUD.get('recursive').checked;
103
104 if (obj_type=='user'){
105 var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.group_name)}";
106 var postData = '_method=delete&recursive={0}&user_id={1}&obj_type=user'.format(recursive,obj_id);
107 }
108 else if (obj_type=='user_group'){
109 var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.group_name)}";
110 var postData = '_method=delete&recursive={0}&users_group_id={0}&obj_type=user_group'.format(recursive,obj_id);
111 }
112 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
113 };
95 };
114
96
115
116 YUE.onDOMReady(function () {
97 YUE.onDOMReady(function () {
117 if (!YUD.hasClass('perm_new_member_name', 'error')) {
98 if (!YUD.hasClass('perm_new_member_name', 'error')) {
118 YUD.setStyle('add_perm_input', 'display', 'none');
99 YUD.setStyle('add_perm_input', 'display', 'none');
119 }
100 }
120 YAHOO.util.Event.addListener('add_perm', 'click', function () {
101 YAHOO.util.Event.addListener('add_perm', 'click', function () {
121 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
102 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
122 });
103 });
123 });
104 });
124
105
125 </script>
106 </script>
@@ -1,100 +1,83 b''
1 <table id="permissions_manage" class="noborder">
1 <table id="permissions_manage" class="noborder">
2 <tr>
2 <tr>
3 <td>${_('none')}</td>
3 <td>${_('none')}</td>
4 <td>${_('read')}</td>
4 <td>${_('read')}</td>
5 <td>${_('write')}</td>
5 <td>${_('write')}</td>
6 <td>${_('admin')}</td>
6 <td>${_('admin')}</td>
7 <td>${_('member')}</td>
7 <td>${_('member')}</td>
8 <td></td>
8 <td></td>
9 </tr>
9 </tr>
10 ## USERS
10 ## USERS
11 %for r2p in c.users_group.user_user_group_to_perm:
11 %for r2p in c.users_group.user_user_group_to_perm:
12 ##forbid revoking permission from yourself
12 ##forbid revoking permission from yourself
13 <tr id="id${id(r2p.user.username)}">
13 <tr id="id${id(r2p.user.username)}">
14 %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
14 %if c.rhodecode_user.user_id != r2p.user.user_id or c.rhodecode_user.is_admin:
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none')}</td>
15 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read')}</td>
16 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read')}</td>
17 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write')}</td>
17 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write')}</td>
18 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin')}</td>
18 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin')}</td>
19 <td style="white-space: nowrap;">
19 <td style="white-space: nowrap;">
20 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
20 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
21 </td>
21 </td>
22 <td>
22 <td>
23 %if r2p.user.username !='default':
23 %if r2p.user.username !='default':
24 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
24 <span class="delete_icon action_button" onclick="ajaxActionRevoke(${r2p.user.user_id}, 'user', '${'id%s'%id(r2p.user.username)}')">
25 ${_('revoke')}
25 ${_('revoke')}
26 </span>
26 </span>
27 %endif
27 %endif
28 </td>
28 </td>
29 %else:
29 %else:
30 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none', disabled="disabled")}</td>
30 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.none', disabled="disabled")}</td>
31 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read', disabled="disabled")}</td>
31 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.read', disabled="disabled")}</td>
32 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write', disabled="disabled")}</td>
32 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.write', disabled="disabled")}</td>
33 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin', disabled="disabled")}</td>
33 <td>${h.radio('u_perm_%s' % r2p.user.username,'usergroup.admin', disabled="disabled")}</td>
34 <td style="white-space: nowrap;">
34 <td style="white-space: nowrap;">
35 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
35 <img class="perm-gravatar" src="${h.gravatar_url(r2p.user.email,14)}"/>${r2p.user.username if r2p.user.username != 'default' else _('default')}
36 </td>
36 </td>
37 <td>
37 <td>
38 </td>
38 </td>
39 %endif
39 %endif
40 </tr>
40 </tr>
41 %endfor
41 %endfor
42
42
43 <%
43 <%
44 _tmpl = h.literal("""' \
44 _tmpl = h.literal("""' \
45 <td><input type="radio" value="usergroup.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
45 <td><input type="radio" value="usergroup.none" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
46 <td><input type="radio" value="usergroup.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
46 <td><input type="radio" value="usergroup.read" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
47 <td><input type="radio" value="usergroup.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
47 <td><input type="radio" value="usergroup.write" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
48 <td><input type="radio" value="usergroup.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
48 <td><input type="radio" value="usergroup.admin" name="perm_new_member_{0}" id="perm_new_member_{0}"></td> \
49 <td class="ac"> \
49 <td class="ac"> \
50 <div class="perm_ac" id="perm_ac_{0}"> \
50 <div class="perm_ac" id="perm_ac_{0}"> \
51 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
51 <input class="yui-ac-input" id="perm_new_member_name_{0}" name="perm_new_member_name_{0}" value="" type="text"> \
52 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
52 <input id="perm_new_member_type_{0}" name="perm_new_member_type_{0}" value="" type="hidden"> \
53 <div id="perm_container_{0}"></div> \
53 <div id="perm_container_{0}"></div> \
54 </div> \
54 </div> \
55 </td> \
55 </td> \
56 <td></td>'""")
56 <td></td>'""")
57 %>
57 %>
58 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
58 ## ADD HERE DYNAMICALLY NEW INPUTS FROM THE '_tmpl'
59 <tr class="new_members last_new_member" id="add_perm_input"></tr>
59 <tr class="new_members last_new_member" id="add_perm_input"></tr>
60 <tr>
60 <tr>
61 <td colspan="6">
61 <td colspan="6">
62 <span id="add_perm" class="add_icon" style="cursor: pointer;">
62 <span id="add_perm" class="add_icon" style="cursor: pointer;">
63 ${_('Add another member')}
63 ${_('Add another member')}
64 </span>
64 </span>
65 </td>
65 </td>
66 </tr>
66 </tr>
67 </table>
67 </table>
68 <script type="text/javascript">
68 <script type="text/javascript">
69 function ajaxActionRevoke(obj_id, obj_type, field_id) {
69 function ajaxActionRevoke(obj_id, obj_type, field_id) {
70 var callback = {
70 url = "${h.url('delete_user_group_perm_member', id=c.users_group.users_group_id)}";
71 success: function (o) {
71 ajaxActionRevokePermission(url, obj_id, obj_type, field_id);
72 var tr = YUD.get(String(field_id));
73 tr.parentNode.removeChild(tr);
74 },
75 failure: function (o) {
76 alert(_TM['Failed to remoke permission'] + ": " + o.status);
77 },
78 };
79 var sUrl = "${h.url('delete_user_group_perm_member', id=c.users_group.users_group_id)}";
80 if (obj_type=='user'){
81 var postData = '_method=delete&user_id={0}&obj_type=user'.format(obj_id);
82 }
83 else if (obj_type=='user_group'){
84 var postData = '_method=delete&user_group_id={0}&obj_type=user_group'.format(obj_id);
85 }
86
87 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);
88 };
72 };
89
73
90
91 YUE.onDOMReady(function () {
74 YUE.onDOMReady(function () {
92 if (!YUD.hasClass('perm_new_member_name', 'error')) {
75 if (!YUD.hasClass('perm_new_member_name', 'error')) {
93 YUD.setStyle('add_perm_input', 'display', 'none');
76 YUD.setStyle('add_perm_input', 'display', 'none');
94 }
77 }
95 YAHOO.util.Event.addListener('add_perm', 'click', function () {
78 YAHOO.util.Event.addListener('add_perm', 'click', function () {
96 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
79 addPermAction(${_tmpl}, ${c.users_array|n}, ${c.users_groups_array|n});
97 });
80 });
98 });
81 });
99
82
100 </script>
83 </script>
General Comments 0
You need to be logged in to leave comments. Login now