##// END OF EJS Templates
routing: introduce 'notification_delete' url and use POST instead of DELETE
Mads Kiilerich -
r6039:d2ce61e4 default
parent child Browse files
Show More
@@ -1,803 +1,803 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 Routes configuration
15 Routes configuration
16
16
17 The more specific and detailed routes should be defined first so they
17 The more specific and detailed routes should be defined first so they
18 may take precedent over the more generic routes. For more information
18 may take precedent over the more generic routes. For more information
19 refer to the routes manual at http://routes.groovie.org/docs/
19 refer to the routes manual at http://routes.groovie.org/docs/
20 """
20 """
21
21
22 from routes import Mapper
22 from routes import Mapper
23
23
24 # prefix for non repository related links needs to be prefixed with `/`
24 # prefix for non repository related links needs to be prefixed with `/`
25 ADMIN_PREFIX = '/_admin'
25 ADMIN_PREFIX = '/_admin'
26
26
27
27
28 def make_map(config):
28 def make_map(config):
29 """Create, configure and return the routes Mapper"""
29 """Create, configure and return the routes Mapper"""
30 rmap = Mapper(directory=config['pylons.paths']['controllers'],
30 rmap = Mapper(directory=config['pylons.paths']['controllers'],
31 always_scan=config['debug'])
31 always_scan=config['debug'])
32 rmap.minimization = False
32 rmap.minimization = False
33 rmap.explicit = False
33 rmap.explicit = False
34
34
35 from kallithea.lib.utils import (is_valid_repo, is_valid_repo_group,
35 from kallithea.lib.utils import (is_valid_repo, is_valid_repo_group,
36 get_repo_by_id)
36 get_repo_by_id)
37
37
38 def check_repo(environ, match_dict):
38 def check_repo(environ, match_dict):
39 """
39 """
40 check for valid repository for proper 404 handling
40 check for valid repository for proper 404 handling
41
41
42 :param environ:
42 :param environ:
43 :param match_dict:
43 :param match_dict:
44 """
44 """
45 repo_name = match_dict.get('repo_name')
45 repo_name = match_dict.get('repo_name')
46
46
47 if match_dict.get('f_path'):
47 if match_dict.get('f_path'):
48 #fix for multiple initial slashes that causes errors
48 #fix for multiple initial slashes that causes errors
49 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
49 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
50
50
51 by_id_match = get_repo_by_id(repo_name)
51 by_id_match = get_repo_by_id(repo_name)
52 if by_id_match:
52 if by_id_match:
53 repo_name = by_id_match
53 repo_name = by_id_match
54 match_dict['repo_name'] = repo_name
54 match_dict['repo_name'] = repo_name
55
55
56 return is_valid_repo(repo_name, config['base_path'])
56 return is_valid_repo(repo_name, config['base_path'])
57
57
58 def check_group(environ, match_dict):
58 def check_group(environ, match_dict):
59 """
59 """
60 check for valid repository group for proper 404 handling
60 check for valid repository group for proper 404 handling
61
61
62 :param environ:
62 :param environ:
63 :param match_dict:
63 :param match_dict:
64 """
64 """
65 repo_group_name = match_dict.get('group_name')
65 repo_group_name = match_dict.get('group_name')
66 return is_valid_repo_group(repo_group_name, config['base_path'])
66 return is_valid_repo_group(repo_group_name, config['base_path'])
67
67
68 def check_group_skip_path(environ, match_dict):
68 def check_group_skip_path(environ, match_dict):
69 """
69 """
70 check for valid repository group for proper 404 handling, but skips
70 check for valid repository group for proper 404 handling, but skips
71 verification of existing path
71 verification of existing path
72
72
73 :param environ:
73 :param environ:
74 :param match_dict:
74 :param match_dict:
75 """
75 """
76 repo_group_name = match_dict.get('group_name')
76 repo_group_name = match_dict.get('group_name')
77 return is_valid_repo_group(repo_group_name, config['base_path'],
77 return is_valid_repo_group(repo_group_name, config['base_path'],
78 skip_path_check=True)
78 skip_path_check=True)
79
79
80 def check_user_group(environ, match_dict):
80 def check_user_group(environ, match_dict):
81 """
81 """
82 check for valid user group for proper 404 handling
82 check for valid user group for proper 404 handling
83
83
84 :param environ:
84 :param environ:
85 :param match_dict:
85 :param match_dict:
86 """
86 """
87 return True
87 return True
88
88
89 def check_int(environ, match_dict):
89 def check_int(environ, match_dict):
90 return match_dict.get('id').isdigit()
90 return match_dict.get('id').isdigit()
91
91
92 # The ErrorController route (handles 404/500 error pages); it should
92 # The ErrorController route (handles 404/500 error pages); it should
93 # likely stay at the top, ensuring it can always be resolved
93 # likely stay at the top, ensuring it can always be resolved
94 rmap.connect('/error/{action}', controller='error')
94 rmap.connect('/error/{action}', controller='error')
95 rmap.connect('/error/{action}/{id}', controller='error')
95 rmap.connect('/error/{action}/{id}', controller='error')
96
96
97 #==========================================================================
97 #==========================================================================
98 # CUSTOM ROUTES HERE
98 # CUSTOM ROUTES HERE
99 #==========================================================================
99 #==========================================================================
100
100
101 #MAIN PAGE
101 #MAIN PAGE
102 rmap.connect('home', '/', controller='home', action='index')
102 rmap.connect('home', '/', controller='home', action='index')
103 rmap.connect('about', '/about', controller='home', action='about')
103 rmap.connect('about', '/about', controller='home', action='about')
104 rmap.connect('repo_switcher_data', '/_repos', controller='home',
104 rmap.connect('repo_switcher_data', '/_repos', controller='home',
105 action='repo_switcher_data')
105 action='repo_switcher_data')
106
106
107 rmap.connect('rst_help',
107 rmap.connect('rst_help',
108 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
108 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
109 _static=True)
109 _static=True)
110 rmap.connect('kallithea_project_url', "https://kallithea-scm.org/", _static=True)
110 rmap.connect('kallithea_project_url', "https://kallithea-scm.org/", _static=True)
111 rmap.connect('issues_url', 'https://bitbucket.org/conservancy/kallithea/issues', _static=True)
111 rmap.connect('issues_url', 'https://bitbucket.org/conservancy/kallithea/issues', _static=True)
112
112
113 #ADMIN REPOSITORY ROUTES
113 #ADMIN REPOSITORY ROUTES
114 with rmap.submapper(path_prefix=ADMIN_PREFIX,
114 with rmap.submapper(path_prefix=ADMIN_PREFIX,
115 controller='admin/repos') as m:
115 controller='admin/repos') as m:
116 m.connect("repos", "/repos",
116 m.connect("repos", "/repos",
117 action="create", conditions=dict(method=["POST"]))
117 action="create", conditions=dict(method=["POST"]))
118 m.connect("repos", "/repos",
118 m.connect("repos", "/repos",
119 action="index", conditions=dict(method=["GET"]))
119 action="index", conditions=dict(method=["GET"]))
120 m.connect("new_repo", "/create_repository",
120 m.connect("new_repo", "/create_repository",
121 action="create_repository", conditions=dict(method=["GET"]))
121 action="create_repository", conditions=dict(method=["GET"]))
122 m.connect("put_repo", "/repos/{repo_name:.*?}",
122 m.connect("put_repo", "/repos/{repo_name:.*?}",
123 action="update", conditions=dict(method=["PUT"],
123 action="update", conditions=dict(method=["PUT"],
124 function=check_repo))
124 function=check_repo))
125 m.connect("delete_repo", "/repos/{repo_name:.*?}",
125 m.connect("delete_repo", "/repos/{repo_name:.*?}",
126 action="delete", conditions=dict(method=["DELETE"],
126 action="delete", conditions=dict(method=["DELETE"],
127 ))
127 ))
128
128
129 #ADMIN REPOSITORY GROUPS ROUTES
129 #ADMIN REPOSITORY GROUPS ROUTES
130 with rmap.submapper(path_prefix=ADMIN_PREFIX,
130 with rmap.submapper(path_prefix=ADMIN_PREFIX,
131 controller='admin/repo_groups') as m:
131 controller='admin/repo_groups') as m:
132 m.connect("repos_groups", "/repo_groups",
132 m.connect("repos_groups", "/repo_groups",
133 action="create", conditions=dict(method=["POST"]))
133 action="create", conditions=dict(method=["POST"]))
134 m.connect("repos_groups", "/repo_groups",
134 m.connect("repos_groups", "/repo_groups",
135 action="index", conditions=dict(method=["GET"]))
135 action="index", conditions=dict(method=["GET"]))
136 m.connect("new_repos_group", "/repo_groups/new",
136 m.connect("new_repos_group", "/repo_groups/new",
137 action="new", conditions=dict(method=["GET"]))
137 action="new", conditions=dict(method=["GET"]))
138 m.connect("update_repos_group", "/repo_groups/{group_name:.*?}",
138 m.connect("update_repos_group", "/repo_groups/{group_name:.*?}",
139 action="update", conditions=dict(method=["PUT"],
139 action="update", conditions=dict(method=["PUT"],
140 function=check_group))
140 function=check_group))
141
141
142 m.connect("repos_group", "/repo_groups/{group_name:.*?}",
142 m.connect("repos_group", "/repo_groups/{group_name:.*?}",
143 action="show", conditions=dict(method=["GET"],
143 action="show", conditions=dict(method=["GET"],
144 function=check_group))
144 function=check_group))
145
145
146 #EXTRAS REPO GROUP ROUTES
146 #EXTRAS REPO GROUP ROUTES
147 m.connect("edit_repo_group", "/repo_groups/{group_name:.*?}/edit",
147 m.connect("edit_repo_group", "/repo_groups/{group_name:.*?}/edit",
148 action="edit",
148 action="edit",
149 conditions=dict(method=["GET"], function=check_group))
149 conditions=dict(method=["GET"], function=check_group))
150
150
151 m.connect("edit_repo_group_advanced", "/repo_groups/{group_name:.*?}/edit/advanced",
151 m.connect("edit_repo_group_advanced", "/repo_groups/{group_name:.*?}/edit/advanced",
152 action="edit_repo_group_advanced",
152 action="edit_repo_group_advanced",
153 conditions=dict(method=["GET"], function=check_group))
153 conditions=dict(method=["GET"], function=check_group))
154
154
155 m.connect("edit_repo_group_perms", "/repo_groups/{group_name:.*?}/edit/permissions",
155 m.connect("edit_repo_group_perms", "/repo_groups/{group_name:.*?}/edit/permissions",
156 action="edit_repo_group_perms",
156 action="edit_repo_group_perms",
157 conditions=dict(method=["GET"], function=check_group))
157 conditions=dict(method=["GET"], function=check_group))
158 m.connect("edit_repo_group_perms", "/repo_groups/{group_name:.*?}/edit/permissions",
158 m.connect("edit_repo_group_perms", "/repo_groups/{group_name:.*?}/edit/permissions",
159 action="update_perms",
159 action="update_perms",
160 conditions=dict(method=["PUT"], function=check_group))
160 conditions=dict(method=["PUT"], function=check_group))
161 m.connect("edit_repo_group_perms", "/repo_groups/{group_name:.*?}/edit/permissions",
161 m.connect("edit_repo_group_perms", "/repo_groups/{group_name:.*?}/edit/permissions",
162 action="delete_perms",
162 action="delete_perms",
163 conditions=dict(method=["DELETE"], function=check_group))
163 conditions=dict(method=["DELETE"], function=check_group))
164
164
165 m.connect("delete_repo_group", "/repo_groups/{group_name:.*?}",
165 m.connect("delete_repo_group", "/repo_groups/{group_name:.*?}",
166 action="delete", conditions=dict(method=["DELETE"],
166 action="delete", conditions=dict(method=["DELETE"],
167 function=check_group_skip_path))
167 function=check_group_skip_path))
168
168
169
169
170 #ADMIN USER ROUTES
170 #ADMIN USER ROUTES
171 with rmap.submapper(path_prefix=ADMIN_PREFIX,
171 with rmap.submapper(path_prefix=ADMIN_PREFIX,
172 controller='admin/users') as m:
172 controller='admin/users') as m:
173 m.connect("users", "/users",
173 m.connect("users", "/users",
174 action="create", conditions=dict(method=["POST"]))
174 action="create", conditions=dict(method=["POST"]))
175 m.connect("users", "/users",
175 m.connect("users", "/users",
176 action="index", conditions=dict(method=["GET"]))
176 action="index", conditions=dict(method=["GET"]))
177 m.connect("formatted_users", "/users.{format}",
177 m.connect("formatted_users", "/users.{format}",
178 action="index", conditions=dict(method=["GET"]))
178 action="index", conditions=dict(method=["GET"]))
179 m.connect("new_user", "/users/new",
179 m.connect("new_user", "/users/new",
180 action="new", conditions=dict(method=["GET"]))
180 action="new", conditions=dict(method=["GET"]))
181 m.connect("update_user", "/users/{id}",
181 m.connect("update_user", "/users/{id}",
182 action="update", conditions=dict(method=["PUT"]))
182 action="update", conditions=dict(method=["PUT"]))
183 m.connect("delete_user", "/users/{id}",
183 m.connect("delete_user", "/users/{id}",
184 action="delete", conditions=dict(method=["DELETE"]))
184 action="delete", conditions=dict(method=["DELETE"]))
185 m.connect("edit_user", "/users/{id}/edit",
185 m.connect("edit_user", "/users/{id}/edit",
186 action="edit", conditions=dict(method=["GET"]))
186 action="edit", conditions=dict(method=["GET"]))
187
187
188 #EXTRAS USER ROUTES
188 #EXTRAS USER ROUTES
189 m.connect("edit_user_advanced", "/users/{id}/edit/advanced",
189 m.connect("edit_user_advanced", "/users/{id}/edit/advanced",
190 action="edit_advanced", conditions=dict(method=["GET"]))
190 action="edit_advanced", conditions=dict(method=["GET"]))
191
191
192 m.connect("edit_user_api_keys", "/users/{id}/edit/api_keys",
192 m.connect("edit_user_api_keys", "/users/{id}/edit/api_keys",
193 action="edit_api_keys", conditions=dict(method=["GET"]))
193 action="edit_api_keys", conditions=dict(method=["GET"]))
194 m.connect("edit_user_api_keys", "/users/{id}/edit/api_keys",
194 m.connect("edit_user_api_keys", "/users/{id}/edit/api_keys",
195 action="add_api_key", conditions=dict(method=["POST"]))
195 action="add_api_key", conditions=dict(method=["POST"]))
196 m.connect("edit_user_api_keys", "/users/{id}/edit/api_keys",
196 m.connect("edit_user_api_keys", "/users/{id}/edit/api_keys",
197 action="delete_api_key", conditions=dict(method=["DELETE"]))
197 action="delete_api_key", conditions=dict(method=["DELETE"]))
198
198
199 m.connect("edit_user_perms", "/users/{id}/edit/permissions",
199 m.connect("edit_user_perms", "/users/{id}/edit/permissions",
200 action="edit_perms", conditions=dict(method=["GET"]))
200 action="edit_perms", conditions=dict(method=["GET"]))
201 m.connect("edit_user_perms", "/users/{id}/edit/permissions",
201 m.connect("edit_user_perms", "/users/{id}/edit/permissions",
202 action="update_perms", conditions=dict(method=["PUT"]))
202 action="update_perms", conditions=dict(method=["PUT"]))
203
203
204 m.connect("edit_user_emails", "/users/{id}/edit/emails",
204 m.connect("edit_user_emails", "/users/{id}/edit/emails",
205 action="edit_emails", conditions=dict(method=["GET"]))
205 action="edit_emails", conditions=dict(method=["GET"]))
206 m.connect("edit_user_emails", "/users/{id}/edit/emails",
206 m.connect("edit_user_emails", "/users/{id}/edit/emails",
207 action="add_email", conditions=dict(method=["PUT"]))
207 action="add_email", conditions=dict(method=["PUT"]))
208 m.connect("edit_user_emails", "/users/{id}/edit/emails",
208 m.connect("edit_user_emails", "/users/{id}/edit/emails",
209 action="delete_email", conditions=dict(method=["DELETE"]))
209 action="delete_email", conditions=dict(method=["DELETE"]))
210
210
211 m.connect("edit_user_ips", "/users/{id}/edit/ips",
211 m.connect("edit_user_ips", "/users/{id}/edit/ips",
212 action="edit_ips", conditions=dict(method=["GET"]))
212 action="edit_ips", conditions=dict(method=["GET"]))
213 m.connect("edit_user_ips", "/users/{id}/edit/ips",
213 m.connect("edit_user_ips", "/users/{id}/edit/ips",
214 action="add_ip", conditions=dict(method=["PUT"]))
214 action="add_ip", conditions=dict(method=["PUT"]))
215 m.connect("edit_user_ips", "/users/{id}/edit/ips",
215 m.connect("edit_user_ips", "/users/{id}/edit/ips",
216 action="delete_ip", conditions=dict(method=["DELETE"]))
216 action="delete_ip", conditions=dict(method=["DELETE"]))
217
217
218 #ADMIN USER GROUPS REST ROUTES
218 #ADMIN USER GROUPS REST ROUTES
219 with rmap.submapper(path_prefix=ADMIN_PREFIX,
219 with rmap.submapper(path_prefix=ADMIN_PREFIX,
220 controller='admin/user_groups') as m:
220 controller='admin/user_groups') as m:
221 m.connect("users_groups", "/user_groups",
221 m.connect("users_groups", "/user_groups",
222 action="create", conditions=dict(method=["POST"]))
222 action="create", conditions=dict(method=["POST"]))
223 m.connect("users_groups", "/user_groups",
223 m.connect("users_groups", "/user_groups",
224 action="index", conditions=dict(method=["GET"]))
224 action="index", conditions=dict(method=["GET"]))
225 m.connect("new_users_group", "/user_groups/new",
225 m.connect("new_users_group", "/user_groups/new",
226 action="new", conditions=dict(method=["GET"]))
226 action="new", conditions=dict(method=["GET"]))
227 m.connect("update_users_group", "/user_groups/{id}",
227 m.connect("update_users_group", "/user_groups/{id}",
228 action="update", conditions=dict(method=["PUT"]))
228 action="update", conditions=dict(method=["PUT"]))
229 m.connect("delete_users_group", "/user_groups/{id}",
229 m.connect("delete_users_group", "/user_groups/{id}",
230 action="delete", conditions=dict(method=["DELETE"]))
230 action="delete", conditions=dict(method=["DELETE"]))
231 m.connect("edit_users_group", "/user_groups/{id}/edit",
231 m.connect("edit_users_group", "/user_groups/{id}/edit",
232 action="edit", conditions=dict(method=["GET"]),
232 action="edit", conditions=dict(method=["GET"]),
233 function=check_user_group)
233 function=check_user_group)
234
234
235 #EXTRAS USER GROUP ROUTES
235 #EXTRAS USER GROUP ROUTES
236 m.connect("edit_user_group_default_perms", "/user_groups/{id}/edit/default_perms",
236 m.connect("edit_user_group_default_perms", "/user_groups/{id}/edit/default_perms",
237 action="edit_default_perms", conditions=dict(method=["GET"]))
237 action="edit_default_perms", conditions=dict(method=["GET"]))
238 m.connect("edit_user_group_default_perms", "/user_groups/{id}/edit/default_perms",
238 m.connect("edit_user_group_default_perms", "/user_groups/{id}/edit/default_perms",
239 action="update_default_perms", conditions=dict(method=["PUT"]))
239 action="update_default_perms", conditions=dict(method=["PUT"]))
240
240
241
241
242 m.connect("edit_user_group_perms", "/user_groups/{id}/edit/perms",
242 m.connect("edit_user_group_perms", "/user_groups/{id}/edit/perms",
243 action="edit_perms", conditions=dict(method=["GET"]))
243 action="edit_perms", conditions=dict(method=["GET"]))
244 m.connect("edit_user_group_perms", "/user_groups/{id}/edit/perms",
244 m.connect("edit_user_group_perms", "/user_groups/{id}/edit/perms",
245 action="update_perms", conditions=dict(method=["PUT"]))
245 action="update_perms", conditions=dict(method=["PUT"]))
246 m.connect("edit_user_group_perms", "/user_groups/{id}/edit/perms",
246 m.connect("edit_user_group_perms", "/user_groups/{id}/edit/perms",
247 action="delete_perms", conditions=dict(method=["DELETE"]))
247 action="delete_perms", conditions=dict(method=["DELETE"]))
248
248
249 m.connect("edit_user_group_advanced", "/user_groups/{id}/edit/advanced",
249 m.connect("edit_user_group_advanced", "/user_groups/{id}/edit/advanced",
250 action="edit_advanced", conditions=dict(method=["GET"]))
250 action="edit_advanced", conditions=dict(method=["GET"]))
251
251
252 m.connect("edit_user_group_members", "/user_groups/{id}/edit/members",
252 m.connect("edit_user_group_members", "/user_groups/{id}/edit/members",
253 action="edit_members", conditions=dict(method=["GET"]))
253 action="edit_members", conditions=dict(method=["GET"]))
254
254
255
255
256
256
257 #ADMIN PERMISSIONS ROUTES
257 #ADMIN PERMISSIONS ROUTES
258 with rmap.submapper(path_prefix=ADMIN_PREFIX,
258 with rmap.submapper(path_prefix=ADMIN_PREFIX,
259 controller='admin/permissions') as m:
259 controller='admin/permissions') as m:
260 m.connect("admin_permissions", "/permissions",
260 m.connect("admin_permissions", "/permissions",
261 action="permission_globals", conditions=dict(method=["POST"]))
261 action="permission_globals", conditions=dict(method=["POST"]))
262 m.connect("admin_permissions", "/permissions",
262 m.connect("admin_permissions", "/permissions",
263 action="permission_globals", conditions=dict(method=["GET"]))
263 action="permission_globals", conditions=dict(method=["GET"]))
264
264
265 m.connect("admin_permissions_ips", "/permissions/ips",
265 m.connect("admin_permissions_ips", "/permissions/ips",
266 action="permission_ips", conditions=dict(method=["GET"]))
266 action="permission_ips", conditions=dict(method=["GET"]))
267
267
268 m.connect("admin_permissions_perms", "/permissions/perms",
268 m.connect("admin_permissions_perms", "/permissions/perms",
269 action="permission_perms", conditions=dict(method=["GET"]))
269 action="permission_perms", conditions=dict(method=["GET"]))
270
270
271
271
272 #ADMIN DEFAULTS ROUTES
272 #ADMIN DEFAULTS ROUTES
273 with rmap.submapper(path_prefix=ADMIN_PREFIX,
273 with rmap.submapper(path_prefix=ADMIN_PREFIX,
274 controller='admin/defaults') as m:
274 controller='admin/defaults') as m:
275 m.connect('defaults', 'defaults',
275 m.connect('defaults', 'defaults',
276 action="index")
276 action="index")
277 m.connect('defaults_update', 'defaults/{id}/update',
277 m.connect('defaults_update', 'defaults/{id}/update',
278 action="update", conditions=dict(method=["POST"]))
278 action="update", conditions=dict(method=["POST"]))
279
279
280 #ADMIN AUTH SETTINGS
280 #ADMIN AUTH SETTINGS
281 rmap.connect('auth_settings', '%s/auth' % ADMIN_PREFIX,
281 rmap.connect('auth_settings', '%s/auth' % ADMIN_PREFIX,
282 controller='admin/auth_settings', action='auth_settings',
282 controller='admin/auth_settings', action='auth_settings',
283 conditions=dict(method=["POST"]))
283 conditions=dict(method=["POST"]))
284 rmap.connect('auth_home', '%s/auth' % ADMIN_PREFIX,
284 rmap.connect('auth_home', '%s/auth' % ADMIN_PREFIX,
285 controller='admin/auth_settings')
285 controller='admin/auth_settings')
286
286
287 #ADMIN SETTINGS ROUTES
287 #ADMIN SETTINGS ROUTES
288 with rmap.submapper(path_prefix=ADMIN_PREFIX,
288 with rmap.submapper(path_prefix=ADMIN_PREFIX,
289 controller='admin/settings') as m:
289 controller='admin/settings') as m:
290 m.connect("admin_settings", "/settings",
290 m.connect("admin_settings", "/settings",
291 action="settings_vcs", conditions=dict(method=["POST"]))
291 action="settings_vcs", conditions=dict(method=["POST"]))
292 m.connect("admin_settings", "/settings",
292 m.connect("admin_settings", "/settings",
293 action="settings_vcs", conditions=dict(method=["GET"]))
293 action="settings_vcs", conditions=dict(method=["GET"]))
294
294
295 m.connect("admin_settings_mapping", "/settings/mapping",
295 m.connect("admin_settings_mapping", "/settings/mapping",
296 action="settings_mapping", conditions=dict(method=["POST"]))
296 action="settings_mapping", conditions=dict(method=["POST"]))
297 m.connect("admin_settings_mapping", "/settings/mapping",
297 m.connect("admin_settings_mapping", "/settings/mapping",
298 action="settings_mapping", conditions=dict(method=["GET"]))
298 action="settings_mapping", conditions=dict(method=["GET"]))
299
299
300 m.connect("admin_settings_global", "/settings/global",
300 m.connect("admin_settings_global", "/settings/global",
301 action="settings_global", conditions=dict(method=["POST"]))
301 action="settings_global", conditions=dict(method=["POST"]))
302 m.connect("admin_settings_global", "/settings/global",
302 m.connect("admin_settings_global", "/settings/global",
303 action="settings_global", conditions=dict(method=["GET"]))
303 action="settings_global", conditions=dict(method=["GET"]))
304
304
305 m.connect("admin_settings_visual", "/settings/visual",
305 m.connect("admin_settings_visual", "/settings/visual",
306 action="settings_visual", conditions=dict(method=["POST"]))
306 action="settings_visual", conditions=dict(method=["POST"]))
307 m.connect("admin_settings_visual", "/settings/visual",
307 m.connect("admin_settings_visual", "/settings/visual",
308 action="settings_visual", conditions=dict(method=["GET"]))
308 action="settings_visual", conditions=dict(method=["GET"]))
309
309
310 m.connect("admin_settings_email", "/settings/email",
310 m.connect("admin_settings_email", "/settings/email",
311 action="settings_email", conditions=dict(method=["POST"]))
311 action="settings_email", conditions=dict(method=["POST"]))
312 m.connect("admin_settings_email", "/settings/email",
312 m.connect("admin_settings_email", "/settings/email",
313 action="settings_email", conditions=dict(method=["GET"]))
313 action="settings_email", conditions=dict(method=["GET"]))
314
314
315 m.connect("admin_settings_hooks", "/settings/hooks",
315 m.connect("admin_settings_hooks", "/settings/hooks",
316 action="settings_hooks", conditions=dict(method=["POST"]))
316 action="settings_hooks", conditions=dict(method=["POST"]))
317 m.connect("admin_settings_hooks", "/settings/hooks",
317 m.connect("admin_settings_hooks", "/settings/hooks",
318 action="settings_hooks", conditions=dict(method=["DELETE"]))
318 action="settings_hooks", conditions=dict(method=["DELETE"]))
319 m.connect("admin_settings_hooks", "/settings/hooks",
319 m.connect("admin_settings_hooks", "/settings/hooks",
320 action="settings_hooks", conditions=dict(method=["GET"]))
320 action="settings_hooks", conditions=dict(method=["GET"]))
321
321
322 m.connect("admin_settings_search", "/settings/search",
322 m.connect("admin_settings_search", "/settings/search",
323 action="settings_search", conditions=dict(method=["POST"]))
323 action="settings_search", conditions=dict(method=["POST"]))
324 m.connect("admin_settings_search", "/settings/search",
324 m.connect("admin_settings_search", "/settings/search",
325 action="settings_search", conditions=dict(method=["GET"]))
325 action="settings_search", conditions=dict(method=["GET"]))
326
326
327 m.connect("admin_settings_system", "/settings/system",
327 m.connect("admin_settings_system", "/settings/system",
328 action="settings_system", conditions=dict(method=["POST"]))
328 action="settings_system", conditions=dict(method=["POST"]))
329 m.connect("admin_settings_system", "/settings/system",
329 m.connect("admin_settings_system", "/settings/system",
330 action="settings_system", conditions=dict(method=["GET"]))
330 action="settings_system", conditions=dict(method=["GET"]))
331 m.connect("admin_settings_system_update", "/settings/system/updates",
331 m.connect("admin_settings_system_update", "/settings/system/updates",
332 action="settings_system_update", conditions=dict(method=["GET"]))
332 action="settings_system_update", conditions=dict(method=["GET"]))
333
333
334 #ADMIN MY ACCOUNT
334 #ADMIN MY ACCOUNT
335 with rmap.submapper(path_prefix=ADMIN_PREFIX,
335 with rmap.submapper(path_prefix=ADMIN_PREFIX,
336 controller='admin/my_account') as m:
336 controller='admin/my_account') as m:
337
337
338 m.connect("my_account", "/my_account",
338 m.connect("my_account", "/my_account",
339 action="my_account", conditions=dict(method=["GET"]))
339 action="my_account", conditions=dict(method=["GET"]))
340 m.connect("my_account", "/my_account",
340 m.connect("my_account", "/my_account",
341 action="my_account", conditions=dict(method=["POST"]))
341 action="my_account", conditions=dict(method=["POST"]))
342
342
343 m.connect("my_account_password", "/my_account/password",
343 m.connect("my_account_password", "/my_account/password",
344 action="my_account_password", conditions=dict(method=["GET"]))
344 action="my_account_password", conditions=dict(method=["GET"]))
345 m.connect("my_account_password", "/my_account/password",
345 m.connect("my_account_password", "/my_account/password",
346 action="my_account_password", conditions=dict(method=["POST"]))
346 action="my_account_password", conditions=dict(method=["POST"]))
347
347
348 m.connect("my_account_repos", "/my_account/repos",
348 m.connect("my_account_repos", "/my_account/repos",
349 action="my_account_repos", conditions=dict(method=["GET"]))
349 action="my_account_repos", conditions=dict(method=["GET"]))
350
350
351 m.connect("my_account_watched", "/my_account/watched",
351 m.connect("my_account_watched", "/my_account/watched",
352 action="my_account_watched", conditions=dict(method=["GET"]))
352 action="my_account_watched", conditions=dict(method=["GET"]))
353
353
354 m.connect("my_account_perms", "/my_account/perms",
354 m.connect("my_account_perms", "/my_account/perms",
355 action="my_account_perms", conditions=dict(method=["GET"]))
355 action="my_account_perms", conditions=dict(method=["GET"]))
356
356
357 m.connect("my_account_emails", "/my_account/emails",
357 m.connect("my_account_emails", "/my_account/emails",
358 action="my_account_emails", conditions=dict(method=["GET"]))
358 action="my_account_emails", conditions=dict(method=["GET"]))
359 m.connect("my_account_emails", "/my_account/emails",
359 m.connect("my_account_emails", "/my_account/emails",
360 action="my_account_emails_add", conditions=dict(method=["POST"]))
360 action="my_account_emails_add", conditions=dict(method=["POST"]))
361 m.connect("my_account_emails", "/my_account/emails",
361 m.connect("my_account_emails", "/my_account/emails",
362 action="my_account_emails_delete", conditions=dict(method=["DELETE"]))
362 action="my_account_emails_delete", conditions=dict(method=["DELETE"]))
363
363
364 m.connect("my_account_api_keys", "/my_account/api_keys",
364 m.connect("my_account_api_keys", "/my_account/api_keys",
365 action="my_account_api_keys", conditions=dict(method=["GET"]))
365 action="my_account_api_keys", conditions=dict(method=["GET"]))
366 m.connect("my_account_api_keys", "/my_account/api_keys",
366 m.connect("my_account_api_keys", "/my_account/api_keys",
367 action="my_account_api_keys_add", conditions=dict(method=["POST"]))
367 action="my_account_api_keys_add", conditions=dict(method=["POST"]))
368 m.connect("my_account_api_keys", "/my_account/api_keys",
368 m.connect("my_account_api_keys", "/my_account/api_keys",
369 action="my_account_api_keys_delete", conditions=dict(method=["DELETE"]))
369 action="my_account_api_keys_delete", conditions=dict(method=["DELETE"]))
370
370
371 #NOTIFICATION REST ROUTES
371 #NOTIFICATION REST ROUTES
372 with rmap.submapper(path_prefix=ADMIN_PREFIX,
372 with rmap.submapper(path_prefix=ADMIN_PREFIX,
373 controller='admin/notifications') as m:
373 controller='admin/notifications') as m:
374 m.connect("notifications", "/notifications",
374 m.connect("notifications", "/notifications",
375 action="index", conditions=dict(method=["GET"]))
375 action="index", conditions=dict(method=["GET"]))
376 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
376 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
377 action="mark_all_read", conditions=dict(method=["GET"]))
377 action="mark_all_read", conditions=dict(method=["GET"]))
378 m.connect("formatted_notifications", "/notifications.{format}",
378 m.connect("formatted_notifications", "/notifications.{format}",
379 action="index", conditions=dict(method=["GET"]))
379 action="index", conditions=dict(method=["GET"]))
380 m.connect("/notifications/{notification_id}",
380 m.connect("/notifications/{notification_id}",
381 action="update", conditions=dict(method=["PUT"]))
381 action="update", conditions=dict(method=["PUT"]))
382 m.connect("/notifications/{notification_id}",
382 m.connect("notification_delete", "/notifications/{notification_id}/delete",
383 action="delete", conditions=dict(method=["DELETE"]))
383 action="delete", conditions=dict(method=["POST"]))
384 m.connect("notification", "/notifications/{notification_id}",
384 m.connect("notification", "/notifications/{notification_id}",
385 action="show", conditions=dict(method=["GET"]))
385 action="show", conditions=dict(method=["GET"]))
386 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
386 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
387 action="show", conditions=dict(method=["GET"]))
387 action="show", conditions=dict(method=["GET"]))
388
388
389 #ADMIN GIST
389 #ADMIN GIST
390 with rmap.submapper(path_prefix=ADMIN_PREFIX,
390 with rmap.submapper(path_prefix=ADMIN_PREFIX,
391 controller='admin/gists') as m:
391 controller='admin/gists') as m:
392 m.connect("gists", "/gists",
392 m.connect("gists", "/gists",
393 action="create", conditions=dict(method=["POST"]))
393 action="create", conditions=dict(method=["POST"]))
394 m.connect("gists", "/gists",
394 m.connect("gists", "/gists",
395 action="index", conditions=dict(method=["GET"]))
395 action="index", conditions=dict(method=["GET"]))
396 m.connect("new_gist", "/gists/new",
396 m.connect("new_gist", "/gists/new",
397 action="new", conditions=dict(method=["GET"]))
397 action="new", conditions=dict(method=["GET"]))
398
398
399
399
400 m.connect("/gists/{gist_id}",
400 m.connect("/gists/{gist_id}",
401 action="delete", conditions=dict(method=["DELETE"]))
401 action="delete", conditions=dict(method=["DELETE"]))
402 m.connect("edit_gist", "/gists/{gist_id}/edit",
402 m.connect("edit_gist", "/gists/{gist_id}/edit",
403 action="edit", conditions=dict(method=["GET", "POST"]))
403 action="edit", conditions=dict(method=["GET", "POST"]))
404 m.connect("edit_gist_check_revision", "/gists/{gist_id}/edit/check_revision",
404 m.connect("edit_gist_check_revision", "/gists/{gist_id}/edit/check_revision",
405 action="check_revision", conditions=dict(method=["POST"]))
405 action="check_revision", conditions=dict(method=["POST"]))
406
406
407
407
408 m.connect("gist", "/gists/{gist_id}",
408 m.connect("gist", "/gists/{gist_id}",
409 action="show", conditions=dict(method=["GET"]))
409 action="show", conditions=dict(method=["GET"]))
410 m.connect("gist_rev", "/gists/{gist_id}/{revision}",
410 m.connect("gist_rev", "/gists/{gist_id}/{revision}",
411 revision="tip",
411 revision="tip",
412 action="show", conditions=dict(method=["GET"]))
412 action="show", conditions=dict(method=["GET"]))
413 m.connect("formatted_gist", "/gists/{gist_id}/{revision}/{format}",
413 m.connect("formatted_gist", "/gists/{gist_id}/{revision}/{format}",
414 revision="tip",
414 revision="tip",
415 action="show", conditions=dict(method=["GET"]))
415 action="show", conditions=dict(method=["GET"]))
416 m.connect("formatted_gist_file", "/gists/{gist_id}/{revision}/{format}/{f_path:.*}",
416 m.connect("formatted_gist_file", "/gists/{gist_id}/{revision}/{format}/{f_path:.*}",
417 revision='tip',
417 revision='tip',
418 action="show", conditions=dict(method=["GET"]))
418 action="show", conditions=dict(method=["GET"]))
419
419
420 #ADMIN MAIN PAGES
420 #ADMIN MAIN PAGES
421 with rmap.submapper(path_prefix=ADMIN_PREFIX,
421 with rmap.submapper(path_prefix=ADMIN_PREFIX,
422 controller='admin/admin') as m:
422 controller='admin/admin') as m:
423 m.connect('admin_home', '', action='index')
423 m.connect('admin_home', '', action='index')
424 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
424 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
425 action='add_repo')
425 action='add_repo')
426 #==========================================================================
426 #==========================================================================
427 # API V2
427 # API V2
428 #==========================================================================
428 #==========================================================================
429 with rmap.submapper(path_prefix=ADMIN_PREFIX,
429 with rmap.submapper(path_prefix=ADMIN_PREFIX,
430 controller='api/api') as m:
430 controller='api/api') as m:
431 m.connect('api', '/api')
431 m.connect('api', '/api')
432
432
433 #USER JOURNAL
433 #USER JOURNAL
434 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
434 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
435 controller='journal', action='index')
435 controller='journal', action='index')
436 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
436 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
437 controller='journal', action='journal_rss')
437 controller='journal', action='journal_rss')
438 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
438 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
439 controller='journal', action='journal_atom')
439 controller='journal', action='journal_atom')
440
440
441 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
441 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
442 controller='journal', action="public_journal")
442 controller='journal', action="public_journal")
443
443
444 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
444 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
445 controller='journal', action="public_journal_rss")
445 controller='journal', action="public_journal_rss")
446
446
447 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
447 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
448 controller='journal', action="public_journal_rss")
448 controller='journal', action="public_journal_rss")
449
449
450 rmap.connect('public_journal_atom',
450 rmap.connect('public_journal_atom',
451 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
451 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
452 action="public_journal_atom")
452 action="public_journal_atom")
453
453
454 rmap.connect('public_journal_atom_old',
454 rmap.connect('public_journal_atom_old',
455 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
455 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
456 action="public_journal_atom")
456 action="public_journal_atom")
457
457
458 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
458 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
459 controller='journal', action='toggle_following',
459 controller='journal', action='toggle_following',
460 conditions=dict(method=["POST"]))
460 conditions=dict(method=["POST"]))
461
461
462 #SEARCH
462 #SEARCH
463 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
463 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
464 rmap.connect('search_repo_admin', '%s/search/{repo_name:.*}' % ADMIN_PREFIX,
464 rmap.connect('search_repo_admin', '%s/search/{repo_name:.*}' % ADMIN_PREFIX,
465 controller='search',
465 controller='search',
466 conditions=dict(function=check_repo))
466 conditions=dict(function=check_repo))
467 rmap.connect('search_repo', '/{repo_name:.*?}/search',
467 rmap.connect('search_repo', '/{repo_name:.*?}/search',
468 controller='search',
468 controller='search',
469 conditions=dict(function=check_repo),
469 conditions=dict(function=check_repo),
470 )
470 )
471
471
472 #LOGIN/LOGOUT/REGISTER/SIGN IN
472 #LOGIN/LOGOUT/REGISTER/SIGN IN
473 rmap.connect('authentication_token', '%s/authentication_token' % ADMIN_PREFIX, controller='login', action='authentication_token')
473 rmap.connect('authentication_token', '%s/authentication_token' % ADMIN_PREFIX, controller='login', action='authentication_token')
474 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
474 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
475 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
475 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
476 action='logout')
476 action='logout')
477
477
478 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
478 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
479 action='register')
479 action='register')
480
480
481 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
481 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
482 controller='login', action='password_reset')
482 controller='login', action='password_reset')
483
483
484 rmap.connect('reset_password_confirmation',
484 rmap.connect('reset_password_confirmation',
485 '%s/password_reset_confirmation' % ADMIN_PREFIX,
485 '%s/password_reset_confirmation' % ADMIN_PREFIX,
486 controller='login', action='password_reset_confirmation')
486 controller='login', action='password_reset_confirmation')
487
487
488 #FEEDS
488 #FEEDS
489 rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
489 rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
490 controller='feed', action='rss',
490 controller='feed', action='rss',
491 conditions=dict(function=check_repo))
491 conditions=dict(function=check_repo))
492
492
493 rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
493 rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
494 controller='feed', action='atom',
494 controller='feed', action='atom',
495 conditions=dict(function=check_repo))
495 conditions=dict(function=check_repo))
496
496
497 #==========================================================================
497 #==========================================================================
498 # REPOSITORY ROUTES
498 # REPOSITORY ROUTES
499 #==========================================================================
499 #==========================================================================
500 rmap.connect('repo_creating_home', '/{repo_name:.*?}/repo_creating',
500 rmap.connect('repo_creating_home', '/{repo_name:.*?}/repo_creating',
501 controller='admin/repos', action='repo_creating')
501 controller='admin/repos', action='repo_creating')
502 rmap.connect('repo_check_home', '/{repo_name:.*?}/crepo_check',
502 rmap.connect('repo_check_home', '/{repo_name:.*?}/crepo_check',
503 controller='admin/repos', action='repo_check')
503 controller='admin/repos', action='repo_check')
504
504
505 rmap.connect('summary_home', '/{repo_name:.*?}',
505 rmap.connect('summary_home', '/{repo_name:.*?}',
506 controller='summary',
506 controller='summary',
507 conditions=dict(function=check_repo))
507 conditions=dict(function=check_repo))
508
508
509 # must be here for proper group/repo catching
509 # must be here for proper group/repo catching
510 rmap.connect('repos_group_home', '/{group_name:.*}',
510 rmap.connect('repos_group_home', '/{group_name:.*}',
511 controller='admin/repo_groups', action="show_by_name",
511 controller='admin/repo_groups', action="show_by_name",
512 conditions=dict(function=check_group))
512 conditions=dict(function=check_group))
513 rmap.connect('repo_stats_home', '/{repo_name:.*?}/statistics',
513 rmap.connect('repo_stats_home', '/{repo_name:.*?}/statistics',
514 controller='summary', action='statistics',
514 controller='summary', action='statistics',
515 conditions=dict(function=check_repo))
515 conditions=dict(function=check_repo))
516
516
517 rmap.connect('repo_size', '/{repo_name:.*?}/repo_size',
517 rmap.connect('repo_size', '/{repo_name:.*?}/repo_size',
518 controller='summary', action='repo_size',
518 controller='summary', action='repo_size',
519 conditions=dict(function=check_repo))
519 conditions=dict(function=check_repo))
520
520
521 rmap.connect('repo_refs_data', '/{repo_name:.*?}/refs-data',
521 rmap.connect('repo_refs_data', '/{repo_name:.*?}/refs-data',
522 controller='home', action='repo_refs_data')
522 controller='home', action='repo_refs_data')
523
523
524 rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision:.*}',
524 rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision:.*}',
525 controller='changeset', revision='tip',
525 controller='changeset', revision='tip',
526 conditions=dict(function=check_repo))
526 conditions=dict(function=check_repo))
527 rmap.connect('changeset_children', '/{repo_name:.*?}/changeset_children/{revision}',
527 rmap.connect('changeset_children', '/{repo_name:.*?}/changeset_children/{revision}',
528 controller='changeset', revision='tip', action="changeset_children",
528 controller='changeset', revision='tip', action="changeset_children",
529 conditions=dict(function=check_repo))
529 conditions=dict(function=check_repo))
530 rmap.connect('changeset_parents', '/{repo_name:.*?}/changeset_parents/{revision}',
530 rmap.connect('changeset_parents', '/{repo_name:.*?}/changeset_parents/{revision}',
531 controller='changeset', revision='tip', action="changeset_parents",
531 controller='changeset', revision='tip', action="changeset_parents",
532 conditions=dict(function=check_repo))
532 conditions=dict(function=check_repo))
533
533
534 # repo edit options
534 # repo edit options
535 rmap.connect("edit_repo", "/{repo_name:.*?}/settings",
535 rmap.connect("edit_repo", "/{repo_name:.*?}/settings",
536 controller='admin/repos', action="edit",
536 controller='admin/repos', action="edit",
537 conditions=dict(method=["GET"], function=check_repo))
537 conditions=dict(method=["GET"], function=check_repo))
538
538
539 rmap.connect("edit_repo_perms", "/{repo_name:.*?}/settings/permissions",
539 rmap.connect("edit_repo_perms", "/{repo_name:.*?}/settings/permissions",
540 controller='admin/repos', action="edit_permissions",
540 controller='admin/repos', action="edit_permissions",
541 conditions=dict(method=["GET"], function=check_repo))
541 conditions=dict(method=["GET"], function=check_repo))
542 rmap.connect("edit_repo_perms_update", "/{repo_name:.*?}/settings/permissions",
542 rmap.connect("edit_repo_perms_update", "/{repo_name:.*?}/settings/permissions",
543 controller='admin/repos', action="edit_permissions_update",
543 controller='admin/repos', action="edit_permissions_update",
544 conditions=dict(method=["PUT"], function=check_repo))
544 conditions=dict(method=["PUT"], function=check_repo))
545 rmap.connect("edit_repo_perms_revoke", "/{repo_name:.*?}/settings/permissions",
545 rmap.connect("edit_repo_perms_revoke", "/{repo_name:.*?}/settings/permissions",
546 controller='admin/repos', action="edit_permissions_revoke",
546 controller='admin/repos', action="edit_permissions_revoke",
547 conditions=dict(method=["DELETE"], function=check_repo))
547 conditions=dict(method=["DELETE"], function=check_repo))
548
548
549 rmap.connect("edit_repo_fields", "/{repo_name:.*?}/settings/fields",
549 rmap.connect("edit_repo_fields", "/{repo_name:.*?}/settings/fields",
550 controller='admin/repos', action="edit_fields",
550 controller='admin/repos', action="edit_fields",
551 conditions=dict(method=["GET"], function=check_repo))
551 conditions=dict(method=["GET"], function=check_repo))
552 rmap.connect('create_repo_fields', "/{repo_name:.*?}/settings/fields/new",
552 rmap.connect('create_repo_fields', "/{repo_name:.*?}/settings/fields/new",
553 controller='admin/repos', action="create_repo_field",
553 controller='admin/repos', action="create_repo_field",
554 conditions=dict(method=["PUT"], function=check_repo))
554 conditions=dict(method=["PUT"], function=check_repo))
555 rmap.connect('delete_repo_fields', "/{repo_name:.*?}/settings/fields/{field_id}",
555 rmap.connect('delete_repo_fields', "/{repo_name:.*?}/settings/fields/{field_id}",
556 controller='admin/repos', action="delete_repo_field",
556 controller='admin/repos', action="delete_repo_field",
557 conditions=dict(method=["DELETE"], function=check_repo))
557 conditions=dict(method=["DELETE"], function=check_repo))
558
558
559
559
560 rmap.connect("edit_repo_advanced", "/{repo_name:.*?}/settings/advanced",
560 rmap.connect("edit_repo_advanced", "/{repo_name:.*?}/settings/advanced",
561 controller='admin/repos', action="edit_advanced",
561 controller='admin/repos', action="edit_advanced",
562 conditions=dict(method=["GET"], function=check_repo))
562 conditions=dict(method=["GET"], function=check_repo))
563
563
564 rmap.connect("edit_repo_advanced_locking", "/{repo_name:.*?}/settings/advanced/locking",
564 rmap.connect("edit_repo_advanced_locking", "/{repo_name:.*?}/settings/advanced/locking",
565 controller='admin/repos', action="edit_advanced_locking",
565 controller='admin/repos', action="edit_advanced_locking",
566 conditions=dict(method=["PUT"], function=check_repo))
566 conditions=dict(method=["PUT"], function=check_repo))
567 rmap.connect('toggle_locking', "/{repo_name:.*?}/settings/advanced/locking_toggle",
567 rmap.connect('toggle_locking', "/{repo_name:.*?}/settings/advanced/locking_toggle",
568 controller='admin/repos', action="toggle_locking",
568 controller='admin/repos', action="toggle_locking",
569 conditions=dict(method=["GET"], function=check_repo))
569 conditions=dict(method=["GET"], function=check_repo))
570
570
571 rmap.connect("edit_repo_advanced_journal", "/{repo_name:.*?}/settings/advanced/journal",
571 rmap.connect("edit_repo_advanced_journal", "/{repo_name:.*?}/settings/advanced/journal",
572 controller='admin/repos', action="edit_advanced_journal",
572 controller='admin/repos', action="edit_advanced_journal",
573 conditions=dict(method=["PUT"], function=check_repo))
573 conditions=dict(method=["PUT"], function=check_repo))
574
574
575 rmap.connect("edit_repo_advanced_fork", "/{repo_name:.*?}/settings/advanced/fork",
575 rmap.connect("edit_repo_advanced_fork", "/{repo_name:.*?}/settings/advanced/fork",
576 controller='admin/repos', action="edit_advanced_fork",
576 controller='admin/repos', action="edit_advanced_fork",
577 conditions=dict(method=["PUT"], function=check_repo))
577 conditions=dict(method=["PUT"], function=check_repo))
578
578
579
579
580 rmap.connect("edit_repo_caches", "/{repo_name:.*?}/settings/caches",
580 rmap.connect("edit_repo_caches", "/{repo_name:.*?}/settings/caches",
581 controller='admin/repos', action="edit_caches",
581 controller='admin/repos', action="edit_caches",
582 conditions=dict(method=["GET"], function=check_repo))
582 conditions=dict(method=["GET"], function=check_repo))
583 rmap.connect("edit_repo_caches", "/{repo_name:.*?}/settings/caches",
583 rmap.connect("edit_repo_caches", "/{repo_name:.*?}/settings/caches",
584 controller='admin/repos', action="edit_caches",
584 controller='admin/repos', action="edit_caches",
585 conditions=dict(method=["PUT"], function=check_repo))
585 conditions=dict(method=["PUT"], function=check_repo))
586
586
587
587
588 rmap.connect("edit_repo_remote", "/{repo_name:.*?}/settings/remote",
588 rmap.connect("edit_repo_remote", "/{repo_name:.*?}/settings/remote",
589 controller='admin/repos', action="edit_remote",
589 controller='admin/repos', action="edit_remote",
590 conditions=dict(method=["GET"], function=check_repo))
590 conditions=dict(method=["GET"], function=check_repo))
591 rmap.connect("edit_repo_remote", "/{repo_name:.*?}/settings/remote",
591 rmap.connect("edit_repo_remote", "/{repo_name:.*?}/settings/remote",
592 controller='admin/repos', action="edit_remote",
592 controller='admin/repos', action="edit_remote",
593 conditions=dict(method=["PUT"], function=check_repo))
593 conditions=dict(method=["PUT"], function=check_repo))
594
594
595 rmap.connect("edit_repo_statistics", "/{repo_name:.*?}/settings/statistics",
595 rmap.connect("edit_repo_statistics", "/{repo_name:.*?}/settings/statistics",
596 controller='admin/repos', action="edit_statistics",
596 controller='admin/repos', action="edit_statistics",
597 conditions=dict(method=["GET"], function=check_repo))
597 conditions=dict(method=["GET"], function=check_repo))
598 rmap.connect("edit_repo_statistics", "/{repo_name:.*?}/settings/statistics",
598 rmap.connect("edit_repo_statistics", "/{repo_name:.*?}/settings/statistics",
599 controller='admin/repos', action="edit_statistics",
599 controller='admin/repos', action="edit_statistics",
600 conditions=dict(method=["PUT"], function=check_repo))
600 conditions=dict(method=["PUT"], function=check_repo))
601
601
602 #still working url for backward compat.
602 #still working url for backward compat.
603 rmap.connect('raw_changeset_home_depraced',
603 rmap.connect('raw_changeset_home_depraced',
604 '/{repo_name:.*?}/raw-changeset/{revision}',
604 '/{repo_name:.*?}/raw-changeset/{revision}',
605 controller='changeset', action='changeset_raw',
605 controller='changeset', action='changeset_raw',
606 revision='tip', conditions=dict(function=check_repo))
606 revision='tip', conditions=dict(function=check_repo))
607
607
608 ## new URLs
608 ## new URLs
609 rmap.connect('changeset_raw_home',
609 rmap.connect('changeset_raw_home',
610 '/{repo_name:.*?}/changeset-diff/{revision}',
610 '/{repo_name:.*?}/changeset-diff/{revision}',
611 controller='changeset', action='changeset_raw',
611 controller='changeset', action='changeset_raw',
612 revision='tip', conditions=dict(function=check_repo))
612 revision='tip', conditions=dict(function=check_repo))
613
613
614 rmap.connect('changeset_patch_home',
614 rmap.connect('changeset_patch_home',
615 '/{repo_name:.*?}/changeset-patch/{revision}',
615 '/{repo_name:.*?}/changeset-patch/{revision}',
616 controller='changeset', action='changeset_patch',
616 controller='changeset', action='changeset_patch',
617 revision='tip', conditions=dict(function=check_repo))
617 revision='tip', conditions=dict(function=check_repo))
618
618
619 rmap.connect('changeset_download_home',
619 rmap.connect('changeset_download_home',
620 '/{repo_name:.*?}/changeset-download/{revision}',
620 '/{repo_name:.*?}/changeset-download/{revision}',
621 controller='changeset', action='changeset_download',
621 controller='changeset', action='changeset_download',
622 revision='tip', conditions=dict(function=check_repo))
622 revision='tip', conditions=dict(function=check_repo))
623
623
624 rmap.connect('changeset_comment',
624 rmap.connect('changeset_comment',
625 '/{repo_name:.*?}/changeset-comment/{revision}',
625 '/{repo_name:.*?}/changeset-comment/{revision}',
626 controller='changeset', revision='tip', action='comment',
626 controller='changeset', revision='tip', action='comment',
627 conditions=dict(function=check_repo))
627 conditions=dict(function=check_repo))
628
628
629 rmap.connect('changeset_comment_delete',
629 rmap.connect('changeset_comment_delete',
630 '/{repo_name:.*?}/changeset-comment/{comment_id}/delete',
630 '/{repo_name:.*?}/changeset-comment/{comment_id}/delete',
631 controller='changeset', action='delete_comment',
631 controller='changeset', action='delete_comment',
632 conditions=dict(function=check_repo, method=["POST"]))
632 conditions=dict(function=check_repo, method=["POST"]))
633
633
634 rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
634 rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
635 controller='changeset', action='changeset_info')
635 controller='changeset', action='changeset_info')
636
636
637 rmap.connect('compare_home',
637 rmap.connect('compare_home',
638 '/{repo_name:.*?}/compare',
638 '/{repo_name:.*?}/compare',
639 controller='compare', action='index',
639 controller='compare', action='index',
640 conditions=dict(function=check_repo))
640 conditions=dict(function=check_repo))
641
641
642 rmap.connect('compare_url',
642 rmap.connect('compare_url',
643 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref_name:.*?}...{other_ref_type}@{other_ref_name:.*?}',
643 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref_name:.*?}...{other_ref_type}@{other_ref_name:.*?}',
644 controller='compare', action='compare',
644 controller='compare', action='compare',
645 conditions=dict(function=check_repo),
645 conditions=dict(function=check_repo),
646 requirements=dict(
646 requirements=dict(
647 org_ref_type='(branch|book|tag|rev|__other_ref_type__)',
647 org_ref_type='(branch|book|tag|rev|__other_ref_type__)',
648 other_ref_type='(branch|book|tag|rev|__org_ref_type__)')
648 other_ref_type='(branch|book|tag|rev|__org_ref_type__)')
649 )
649 )
650
650
651 rmap.connect('pullrequest_home',
651 rmap.connect('pullrequest_home',
652 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
652 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
653 action='index', conditions=dict(function=check_repo,
653 action='index', conditions=dict(function=check_repo,
654 method=["GET"]))
654 method=["GET"]))
655
655
656 rmap.connect('pullrequest_repo_info',
656 rmap.connect('pullrequest_repo_info',
657 '/{repo_name:.*?}/pull-request-repo-info',
657 '/{repo_name:.*?}/pull-request-repo-info',
658 controller='pullrequests', action='repo_info',
658 controller='pullrequests', action='repo_info',
659 conditions=dict(function=check_repo, method=["GET"]))
659 conditions=dict(function=check_repo, method=["GET"]))
660
660
661 rmap.connect('pullrequest',
661 rmap.connect('pullrequest',
662 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
662 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
663 action='create', conditions=dict(function=check_repo,
663 action='create', conditions=dict(function=check_repo,
664 method=["POST"]))
664 method=["POST"]))
665
665
666 rmap.connect('pullrequest_show',
666 rmap.connect('pullrequest_show',
667 '/{repo_name:.*?}/pull-request/{pull_request_id:\\d+}{extra:(/.*)?}', extra='',
667 '/{repo_name:.*?}/pull-request/{pull_request_id:\\d+}{extra:(/.*)?}', extra='',
668 controller='pullrequests',
668 controller='pullrequests',
669 action='show', conditions=dict(function=check_repo,
669 action='show', conditions=dict(function=check_repo,
670 method=["GET"]))
670 method=["GET"]))
671 rmap.connect('pullrequest_post',
671 rmap.connect('pullrequest_post',
672 '/{repo_name:.*?}/pull-request/{pull_request_id}',
672 '/{repo_name:.*?}/pull-request/{pull_request_id}',
673 controller='pullrequests',
673 controller='pullrequests',
674 action='post', conditions=dict(function=check_repo,
674 action='post', conditions=dict(function=check_repo,
675 method=["POST"]))
675 method=["POST"]))
676 rmap.connect('pullrequest_delete',
676 rmap.connect('pullrequest_delete',
677 '/{repo_name:.*?}/pull-request/{pull_request_id}',
677 '/{repo_name:.*?}/pull-request/{pull_request_id}',
678 controller='pullrequests',
678 controller='pullrequests',
679 action='delete', conditions=dict(function=check_repo,
679 action='delete', conditions=dict(function=check_repo,
680 method=["DELETE"]))
680 method=["DELETE"]))
681
681
682 rmap.connect('pullrequest_show_all',
682 rmap.connect('pullrequest_show_all',
683 '/{repo_name:.*?}/pull-request',
683 '/{repo_name:.*?}/pull-request',
684 controller='pullrequests',
684 controller='pullrequests',
685 action='show_all', conditions=dict(function=check_repo,
685 action='show_all', conditions=dict(function=check_repo,
686 method=["GET"]))
686 method=["GET"]))
687
687
688 rmap.connect('my_pullrequests',
688 rmap.connect('my_pullrequests',
689 '/my_pullrequests',
689 '/my_pullrequests',
690 controller='pullrequests',
690 controller='pullrequests',
691 action='show_my', conditions=dict(method=["GET"]))
691 action='show_my', conditions=dict(method=["GET"]))
692
692
693 rmap.connect('pullrequest_comment',
693 rmap.connect('pullrequest_comment',
694 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
694 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
695 controller='pullrequests',
695 controller='pullrequests',
696 action='comment', conditions=dict(function=check_repo,
696 action='comment', conditions=dict(function=check_repo,
697 method=["POST"]))
697 method=["POST"]))
698
698
699 rmap.connect('pullrequest_comment_delete',
699 rmap.connect('pullrequest_comment_delete',
700 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
700 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
701 controller='pullrequests', action='delete_comment',
701 controller='pullrequests', action='delete_comment',
702 conditions=dict(function=check_repo, method=["POST"]))
702 conditions=dict(function=check_repo, method=["POST"]))
703
703
704 rmap.connect('summary_home_summary', '/{repo_name:.*?}/summary',
704 rmap.connect('summary_home_summary', '/{repo_name:.*?}/summary',
705 controller='summary', conditions=dict(function=check_repo))
705 controller='summary', conditions=dict(function=check_repo))
706
706
707 rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
707 rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
708 controller='changelog', conditions=dict(function=check_repo))
708 controller='changelog', conditions=dict(function=check_repo))
709
709
710 rmap.connect('changelog_summary_home', '/{repo_name:.*?}/changelog_summary',
710 rmap.connect('changelog_summary_home', '/{repo_name:.*?}/changelog_summary',
711 controller='changelog', action='changelog_summary',
711 controller='changelog', action='changelog_summary',
712 conditions=dict(function=check_repo))
712 conditions=dict(function=check_repo))
713
713
714 rmap.connect('changelog_file_home', '/{repo_name:.*?}/changelog/{revision}/{f_path:.*}',
714 rmap.connect('changelog_file_home', '/{repo_name:.*?}/changelog/{revision}/{f_path:.*}',
715 controller='changelog', f_path=None,
715 controller='changelog', f_path=None,
716 conditions=dict(function=check_repo))
716 conditions=dict(function=check_repo))
717
717
718 rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
718 rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
719 controller='changelog', action='changelog_details',
719 controller='changelog', action='changelog_details',
720 conditions=dict(function=check_repo))
720 conditions=dict(function=check_repo))
721
721
722 rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
722 rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
723 controller='files', revision='tip', f_path='',
723 controller='files', revision='tip', f_path='',
724 conditions=dict(function=check_repo))
724 conditions=dict(function=check_repo))
725
725
726 rmap.connect('files_home_nopath', '/{repo_name:.*?}/files/{revision}',
726 rmap.connect('files_home_nopath', '/{repo_name:.*?}/files/{revision}',
727 controller='files', revision='tip', f_path='',
727 controller='files', revision='tip', f_path='',
728 conditions=dict(function=check_repo))
728 conditions=dict(function=check_repo))
729
729
730 rmap.connect('files_history_home',
730 rmap.connect('files_history_home',
731 '/{repo_name:.*?}/history/{revision}/{f_path:.*}',
731 '/{repo_name:.*?}/history/{revision}/{f_path:.*}',
732 controller='files', action='history', revision='tip', f_path='',
732 controller='files', action='history', revision='tip', f_path='',
733 conditions=dict(function=check_repo))
733 conditions=dict(function=check_repo))
734
734
735 rmap.connect('files_authors_home',
735 rmap.connect('files_authors_home',
736 '/{repo_name:.*?}/authors/{revision}/{f_path:.*}',
736 '/{repo_name:.*?}/authors/{revision}/{f_path:.*}',
737 controller='files', action='authors', revision='tip', f_path='',
737 controller='files', action='authors', revision='tip', f_path='',
738 conditions=dict(function=check_repo))
738 conditions=dict(function=check_repo))
739
739
740 rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
740 rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
741 controller='files', action='diff', revision='tip', f_path='',
741 controller='files', action='diff', revision='tip', f_path='',
742 conditions=dict(function=check_repo))
742 conditions=dict(function=check_repo))
743
743
744 rmap.connect('files_diff_2way_home', '/{repo_name:.*?}/diff-2way/{f_path:.+}',
744 rmap.connect('files_diff_2way_home', '/{repo_name:.*?}/diff-2way/{f_path:.+}',
745 controller='files', action='diff_2way', revision='tip', f_path='',
745 controller='files', action='diff_2way', revision='tip', f_path='',
746 conditions=dict(function=check_repo))
746 conditions=dict(function=check_repo))
747
747
748 rmap.connect('files_rawfile_home',
748 rmap.connect('files_rawfile_home',
749 '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
749 '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
750 controller='files', action='rawfile', revision='tip',
750 controller='files', action='rawfile', revision='tip',
751 f_path='', conditions=dict(function=check_repo))
751 f_path='', conditions=dict(function=check_repo))
752
752
753 rmap.connect('files_raw_home',
753 rmap.connect('files_raw_home',
754 '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
754 '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
755 controller='files', action='raw', revision='tip', f_path='',
755 controller='files', action='raw', revision='tip', f_path='',
756 conditions=dict(function=check_repo))
756 conditions=dict(function=check_repo))
757
757
758 rmap.connect('files_annotate_home',
758 rmap.connect('files_annotate_home',
759 '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
759 '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
760 controller='files', action='index', revision='tip',
760 controller='files', action='index', revision='tip',
761 f_path='', annotate=True, conditions=dict(function=check_repo))
761 f_path='', annotate=True, conditions=dict(function=check_repo))
762
762
763 rmap.connect('files_edit_home',
763 rmap.connect('files_edit_home',
764 '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
764 '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
765 controller='files', action='edit', revision='tip',
765 controller='files', action='edit', revision='tip',
766 f_path='', conditions=dict(function=check_repo))
766 f_path='', conditions=dict(function=check_repo))
767
767
768 rmap.connect('files_add_home',
768 rmap.connect('files_add_home',
769 '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
769 '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
770 controller='files', action='add', revision='tip',
770 controller='files', action='add', revision='tip',
771 f_path='', conditions=dict(function=check_repo))
771 f_path='', conditions=dict(function=check_repo))
772
772
773 rmap.connect('files_delete_home',
773 rmap.connect('files_delete_home',
774 '/{repo_name:.*?}/delete/{revision}/{f_path:.*}',
774 '/{repo_name:.*?}/delete/{revision}/{f_path:.*}',
775 controller='files', action='delete', revision='tip',
775 controller='files', action='delete', revision='tip',
776 f_path='', conditions=dict(function=check_repo))
776 f_path='', conditions=dict(function=check_repo))
777
777
778 rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
778 rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
779 controller='files', action='archivefile',
779 controller='files', action='archivefile',
780 conditions=dict(function=check_repo))
780 conditions=dict(function=check_repo))
781
781
782 rmap.connect('files_nodelist_home',
782 rmap.connect('files_nodelist_home',
783 '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
783 '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
784 controller='files', action='nodelist',
784 controller='files', action='nodelist',
785 conditions=dict(function=check_repo))
785 conditions=dict(function=check_repo))
786
786
787 rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
787 rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
788 controller='forks', action='fork_create',
788 controller='forks', action='fork_create',
789 conditions=dict(function=check_repo, method=["POST"]))
789 conditions=dict(function=check_repo, method=["POST"]))
790
790
791 rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
791 rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
792 controller='forks', action='fork',
792 controller='forks', action='fork',
793 conditions=dict(function=check_repo))
793 conditions=dict(function=check_repo))
794
794
795 rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
795 rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
796 controller='forks', action='forks',
796 controller='forks', action='forks',
797 conditions=dict(function=check_repo))
797 conditions=dict(function=check_repo))
798
798
799 rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
799 rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
800 controller='followers', action='followers',
800 controller='followers', action='followers',
801 conditions=dict(function=check_repo))
801 conditions=dict(function=check_repo))
802
802
803 return rmap
803 return rmap
@@ -1,1539 +1,1539 b''
1 /**
1 /**
2 Kallithea JS Files
2 Kallithea JS Files
3 **/
3 **/
4 'use strict';
4 'use strict';
5
5
6 if (typeof console == "undefined" || typeof console.log == "undefined"){
6 if (typeof console == "undefined" || typeof console.log == "undefined"){
7 console = { log: function() {} }
7 console = { log: function() {} }
8 }
8 }
9
9
10 /**
10 /**
11 * INJECT .format function into String
11 * INJECT .format function into String
12 * Usage: "My name is {0} {1}".format("Johny","Bravo")
12 * Usage: "My name is {0} {1}".format("Johny","Bravo")
13 * Return "My name is Johny Bravo"
13 * Return "My name is Johny Bravo"
14 * Inspired by https://gist.github.com/1049426
14 * Inspired by https://gist.github.com/1049426
15 */
15 */
16 String.prototype.format = function() {
16 String.prototype.format = function() {
17 function format() {
17 function format() {
18 var str = this;
18 var str = this;
19 var len = arguments.length+1;
19 var len = arguments.length+1;
20 var safe = undefined;
20 var safe = undefined;
21 var arg = undefined;
21 var arg = undefined;
22
22
23 // For each {0} {1} {n...} replace with the argument in that position. If
23 // For each {0} {1} {n...} replace with the argument in that position. If
24 // the argument is an object or an array it will be stringified to JSON.
24 // the argument is an object or an array it will be stringified to JSON.
25 for (var i=0; i < len; arg = arguments[i++]) {
25 for (var i=0; i < len; arg = arguments[i++]) {
26 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
26 safe = typeof arg === 'object' ? JSON.stringify(arg) : arg;
27 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
27 str = str.replace(RegExp('\\{'+(i-1)+'\\}', 'g'), safe);
28 }
28 }
29 return str;
29 return str;
30 }
30 }
31
31
32 // Save a reference of what may already exist under the property native.
32 // Save a reference of what may already exist under the property native.
33 // Allows for doing something like: if("".format.native) { /* use native */ }
33 // Allows for doing something like: if("".format.native) { /* use native */ }
34 format.native = String.prototype.format;
34 format.native = String.prototype.format;
35
35
36 // Replace the prototype property
36 // Replace the prototype property
37 return format;
37 return format;
38
38
39 }();
39 }();
40
40
41 String.prototype.strip = function(char) {
41 String.prototype.strip = function(char) {
42 if(char === undefined){
42 if(char === undefined){
43 char = '\\s';
43 char = '\\s';
44 }
44 }
45 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
45 return this.replace(new RegExp('^'+char+'+|'+char+'+$','g'), '');
46 }
46 }
47
47
48 String.prototype.lstrip = function(char) {
48 String.prototype.lstrip = function(char) {
49 if(char === undefined){
49 if(char === undefined){
50 char = '\\s';
50 char = '\\s';
51 }
51 }
52 return this.replace(new RegExp('^'+char+'+'),'');
52 return this.replace(new RegExp('^'+char+'+'),'');
53 }
53 }
54
54
55 String.prototype.rstrip = function(char) {
55 String.prototype.rstrip = function(char) {
56 if(char === undefined){
56 if(char === undefined){
57 char = '\\s';
57 char = '\\s';
58 }
58 }
59 return this.replace(new RegExp(''+char+'+$'),'');
59 return this.replace(new RegExp(''+char+'+$'),'');
60 }
60 }
61
61
62 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
62 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#Polyfill
63 under MIT license / public domain, see
63 under MIT license / public domain, see
64 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
64 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
65 if(!Array.prototype.indexOf) {
65 if(!Array.prototype.indexOf) {
66 Array.prototype.indexOf = function (searchElement, fromIndex) {
66 Array.prototype.indexOf = function (searchElement, fromIndex) {
67 if ( this === undefined || this === null ) {
67 if ( this === undefined || this === null ) {
68 throw new TypeError( '"this" is null or not defined' );
68 throw new TypeError( '"this" is null or not defined' );
69 }
69 }
70
70
71 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
71 var length = this.length >>> 0; // Hack to convert object.length to a UInt32
72
72
73 fromIndex = +fromIndex || 0;
73 fromIndex = +fromIndex || 0;
74
74
75 if (Math.abs(fromIndex) === Infinity) {
75 if (Math.abs(fromIndex) === Infinity) {
76 fromIndex = 0;
76 fromIndex = 0;
77 }
77 }
78
78
79 if (fromIndex < 0) {
79 if (fromIndex < 0) {
80 fromIndex += length;
80 fromIndex += length;
81 if (fromIndex < 0) {
81 if (fromIndex < 0) {
82 fromIndex = 0;
82 fromIndex = 0;
83 }
83 }
84 }
84 }
85
85
86 for (;fromIndex < length; fromIndex++) {
86 for (;fromIndex < length; fromIndex++) {
87 if (this[fromIndex] === searchElement) {
87 if (this[fromIndex] === searchElement) {
88 return fromIndex;
88 return fromIndex;
89 }
89 }
90 }
90 }
91
91
92 return -1;
92 return -1;
93 };
93 };
94 }
94 }
95
95
96 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
96 /* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter#Compatibility
97 under MIT license / public domain, see
97 under MIT license / public domain, see
98 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
98 https://developer.mozilla.org/en-US/docs/MDN/About#Copyrights_and_licenses */
99 if (!Array.prototype.filter)
99 if (!Array.prototype.filter)
100 {
100 {
101 Array.prototype.filter = function(fun /*, thisArg */)
101 Array.prototype.filter = function(fun /*, thisArg */)
102 {
102 {
103 if (this === void 0 || this === null)
103 if (this === void 0 || this === null)
104 throw new TypeError();
104 throw new TypeError();
105
105
106 var t = Object(this);
106 var t = Object(this);
107 var len = t.length >>> 0;
107 var len = t.length >>> 0;
108 if (typeof fun !== "function")
108 if (typeof fun !== "function")
109 throw new TypeError();
109 throw new TypeError();
110
110
111 var res = [];
111 var res = [];
112 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
112 var thisArg = arguments.length >= 2 ? arguments[1] : void 0;
113 for (var i = 0; i < len; i++)
113 for (var i = 0; i < len; i++)
114 {
114 {
115 if (i in t)
115 if (i in t)
116 {
116 {
117 var val = t[i];
117 var val = t[i];
118
118
119 // NOTE: Technically this should Object.defineProperty at
119 // NOTE: Technically this should Object.defineProperty at
120 // the next index, as push can be affected by
120 // the next index, as push can be affected by
121 // properties on Object.prototype and Array.prototype.
121 // properties on Object.prototype and Array.prototype.
122 // But that method's new, and collisions should be
122 // But that method's new, and collisions should be
123 // rare, so use the more-compatible alternative.
123 // rare, so use the more-compatible alternative.
124 if (fun.call(thisArg, val, i, t))
124 if (fun.call(thisArg, val, i, t))
125 res.push(val);
125 res.push(val);
126 }
126 }
127 }
127 }
128
128
129 return res;
129 return res;
130 };
130 };
131 }
131 }
132
132
133 /**
133 /**
134 * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/
134 * A customized version of PyRoutes.JS from https://pypi.python.org/pypi/pyroutes.js/
135 * which is copyright Stephane Klein and was made available under the BSD License.
135 * which is copyright Stephane Klein and was made available under the BSD License.
136 *
136 *
137 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
137 * Usage pyroutes.url('mark_error_fixed',{"error_id":error_id}) // /mark_error_fixed/<error_id>
138 */
138 */
139 var pyroutes = (function() {
139 var pyroutes = (function() {
140 var matchlist = {};
140 var matchlist = {};
141 var sprintf = (function() {
141 var sprintf = (function() {
142 function get_type(variable) {
142 function get_type(variable) {
143 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
143 return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
144 }
144 }
145 function str_repeat(input, multiplier) {
145 function str_repeat(input, multiplier) {
146 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
146 for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
147 return output.join('');
147 return output.join('');
148 }
148 }
149
149
150 var str_format = function() {
150 var str_format = function() {
151 if (!str_format.cache.hasOwnProperty(arguments[0])) {
151 if (!str_format.cache.hasOwnProperty(arguments[0])) {
152 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
152 str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
153 }
153 }
154 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
154 return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
155 };
155 };
156
156
157 str_format.format = function(parse_tree, argv) {
157 str_format.format = function(parse_tree, argv) {
158 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
158 var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
159 for (i = 0; i < tree_length; i++) {
159 for (i = 0; i < tree_length; i++) {
160 node_type = get_type(parse_tree[i]);
160 node_type = get_type(parse_tree[i]);
161 if (node_type === 'string') {
161 if (node_type === 'string') {
162 output.push(parse_tree[i]);
162 output.push(parse_tree[i]);
163 }
163 }
164 else if (node_type === 'array') {
164 else if (node_type === 'array') {
165 match = parse_tree[i]; // convenience purposes only
165 match = parse_tree[i]; // convenience purposes only
166 if (match[2]) { // keyword argument
166 if (match[2]) { // keyword argument
167 arg = argv[cursor];
167 arg = argv[cursor];
168 for (k = 0; k < match[2].length; k++) {
168 for (k = 0; k < match[2].length; k++) {
169 if (!arg.hasOwnProperty(match[2][k])) {
169 if (!arg.hasOwnProperty(match[2][k])) {
170 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
170 throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
171 }
171 }
172 arg = arg[match[2][k]];
172 arg = arg[match[2][k]];
173 }
173 }
174 }
174 }
175 else if (match[1]) { // positional argument (explicit)
175 else if (match[1]) { // positional argument (explicit)
176 arg = argv[match[1]];
176 arg = argv[match[1]];
177 }
177 }
178 else { // positional argument (implicit)
178 else { // positional argument (implicit)
179 arg = argv[cursor++];
179 arg = argv[cursor++];
180 }
180 }
181
181
182 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
182 if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
183 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
183 throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
184 }
184 }
185 switch (match[8]) {
185 switch (match[8]) {
186 case 'b': arg = arg.toString(2); break;
186 case 'b': arg = arg.toString(2); break;
187 case 'c': arg = String.fromCharCode(arg); break;
187 case 'c': arg = String.fromCharCode(arg); break;
188 case 'd': arg = parseInt(arg, 10); break;
188 case 'd': arg = parseInt(arg, 10); break;
189 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
189 case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
190 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
190 case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
191 case 'o': arg = arg.toString(8); break;
191 case 'o': arg = arg.toString(8); break;
192 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
192 case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
193 case 'u': arg = Math.abs(arg); break;
193 case 'u': arg = Math.abs(arg); break;
194 case 'x': arg = arg.toString(16); break;
194 case 'x': arg = arg.toString(16); break;
195 case 'X': arg = arg.toString(16).toUpperCase(); break;
195 case 'X': arg = arg.toString(16).toUpperCase(); break;
196 }
196 }
197 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
197 arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
198 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
198 pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
199 pad_length = match[6] - String(arg).length;
199 pad_length = match[6] - String(arg).length;
200 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
200 pad = match[6] ? str_repeat(pad_character, pad_length) : '';
201 output.push(match[5] ? arg + pad : pad + arg);
201 output.push(match[5] ? arg + pad : pad + arg);
202 }
202 }
203 }
203 }
204 return output.join('');
204 return output.join('');
205 };
205 };
206
206
207 str_format.cache = {};
207 str_format.cache = {};
208
208
209 str_format.parse = function(fmt) {
209 str_format.parse = function(fmt) {
210 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
210 var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
211 while (_fmt) {
211 while (_fmt) {
212 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
212 if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
213 parse_tree.push(match[0]);
213 parse_tree.push(match[0]);
214 }
214 }
215 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
215 else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
216 parse_tree.push('%');
216 parse_tree.push('%');
217 }
217 }
218 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
218 else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
219 if (match[2]) {
219 if (match[2]) {
220 arg_names |= 1;
220 arg_names |= 1;
221 var field_list = [], replacement_field = match[2], field_match = [];
221 var field_list = [], replacement_field = match[2], field_match = [];
222 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
222 if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
223 field_list.push(field_match[1]);
223 field_list.push(field_match[1]);
224 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
224 while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
225 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
225 if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
226 field_list.push(field_match[1]);
226 field_list.push(field_match[1]);
227 }
227 }
228 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
228 else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
229 field_list.push(field_match[1]);
229 field_list.push(field_match[1]);
230 }
230 }
231 else {
231 else {
232 throw('[sprintf] huh?');
232 throw('[sprintf] huh?');
233 }
233 }
234 }
234 }
235 }
235 }
236 else {
236 else {
237 throw('[sprintf] huh?');
237 throw('[sprintf] huh?');
238 }
238 }
239 match[2] = field_list;
239 match[2] = field_list;
240 }
240 }
241 else {
241 else {
242 arg_names |= 2;
242 arg_names |= 2;
243 }
243 }
244 if (arg_names === 3) {
244 if (arg_names === 3) {
245 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
245 throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
246 }
246 }
247 parse_tree.push(match);
247 parse_tree.push(match);
248 }
248 }
249 else {
249 else {
250 throw('[sprintf] huh?');
250 throw('[sprintf] huh?');
251 }
251 }
252 _fmt = _fmt.substring(match[0].length);
252 _fmt = _fmt.substring(match[0].length);
253 }
253 }
254 return parse_tree;
254 return parse_tree;
255 };
255 };
256
256
257 return str_format;
257 return str_format;
258 })();
258 })();
259
259
260 var vsprintf = function(fmt, argv) {
260 var vsprintf = function(fmt, argv) {
261 argv.unshift(fmt);
261 argv.unshift(fmt);
262 return sprintf.apply(null, argv);
262 return sprintf.apply(null, argv);
263 };
263 };
264 return {
264 return {
265 'url': function(route_name, params) {
265 'url': function(route_name, params) {
266 var result = route_name;
266 var result = route_name;
267 if (typeof(params) != 'object'){
267 if (typeof(params) != 'object'){
268 params = {};
268 params = {};
269 }
269 }
270 if (matchlist.hasOwnProperty(route_name)) {
270 if (matchlist.hasOwnProperty(route_name)) {
271 var route = matchlist[route_name];
271 var route = matchlist[route_name];
272 // param substitution
272 // param substitution
273 for(var i=0; i < route[1].length; i++) {
273 for(var i=0; i < route[1].length; i++) {
274 if (!params.hasOwnProperty(route[1][i]))
274 if (!params.hasOwnProperty(route[1][i]))
275 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
275 throw new Error(route[1][i] + ' missing in "' + route_name + '" route generation');
276 }
276 }
277 result = sprintf(route[0], params);
277 result = sprintf(route[0], params);
278
278
279 var ret = [];
279 var ret = [];
280 //extra params => GET
280 //extra params => GET
281 for(var param in params){
281 for(var param in params){
282 if (route[1].indexOf(param) == -1){
282 if (route[1].indexOf(param) == -1){
283 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
283 ret.push(encodeURIComponent(param) + "=" + encodeURIComponent(params[param]));
284 }
284 }
285 }
285 }
286 var _parts = ret.join("&");
286 var _parts = ret.join("&");
287 if(_parts){
287 if(_parts){
288 result = result +'?'+ _parts
288 result = result +'?'+ _parts
289 }
289 }
290 }
290 }
291
291
292 return result;
292 return result;
293 },
293 },
294 'register': function(route_name, route_tmpl, req_params) {
294 'register': function(route_name, route_tmpl, req_params) {
295 if (typeof(req_params) != 'object') {
295 if (typeof(req_params) != 'object') {
296 req_params = [];
296 req_params = [];
297 }
297 }
298 var keys = [];
298 var keys = [];
299 for (var i=0; i < req_params.length; i++) {
299 for (var i=0; i < req_params.length; i++) {
300 keys.push(req_params[i]);
300 keys.push(req_params[i]);
301 }
301 }
302 matchlist[route_name] = [
302 matchlist[route_name] = [
303 unescape(route_tmpl),
303 unescape(route_tmpl),
304 keys
304 keys
305 ]
305 ]
306 },
306 },
307 '_routes': function(){
307 '_routes': function(){
308 return matchlist;
308 return matchlist;
309 }
309 }
310 }
310 }
311 })();
311 })();
312
312
313
313
314 /* Invoke all functions in callbacks */
314 /* Invoke all functions in callbacks */
315 var _run_callbacks = function(callbacks){
315 var _run_callbacks = function(callbacks){
316 if (callbacks !== undefined){
316 if (callbacks !== undefined){
317 var _l = callbacks.length;
317 var _l = callbacks.length;
318 for (var i=0;i<_l;i++){
318 for (var i=0;i<_l;i++){
319 var func = callbacks[i];
319 var func = callbacks[i];
320 if(typeof(func)=='function'){
320 if(typeof(func)=='function'){
321 try{
321 try{
322 func();
322 func();
323 }catch (err){};
323 }catch (err){};
324 }
324 }
325 }
325 }
326 }
326 }
327 }
327 }
328
328
329 /**
329 /**
330 * turns objects into GET query string
330 * turns objects into GET query string
331 */
331 */
332 var _toQueryString = function(o) {
332 var _toQueryString = function(o) {
333 if(typeof o !== 'object') {
333 if(typeof o !== 'object') {
334 return false;
334 return false;
335 }
335 }
336 var _p, _qs = [];
336 var _p, _qs = [];
337 for(_p in o) {
337 for(_p in o) {
338 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
338 _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p]));
339 }
339 }
340 return _qs.join('&');
340 return _qs.join('&');
341 };
341 };
342
342
343 /**
343 /**
344 * Load HTML into DOM using Ajax
344 * Load HTML into DOM using Ajax
345 *
345 *
346 * @param $target: load html async and place it (or an error message) here
346 * @param $target: load html async and place it (or an error message) here
347 * @param success: success callback function
347 * @param success: success callback function
348 * @param args: query parameters to pass to url
348 * @param args: query parameters to pass to url
349 */
349 */
350 function asynchtml(url, $target, success, args){
350 function asynchtml(url, $target, success, args){
351 if(args===undefined){
351 if(args===undefined){
352 args=null;
352 args=null;
353 }
353 }
354 $target.html(_TM['Loading ...']).css('opacity','0.3');
354 $target.html(_TM['Loading ...']).css('opacity','0.3');
355
355
356 return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
356 return $.ajax({url: url, data: args, headers: {'X-PARTIAL-XHR': '1'}, cache: false, dataType: 'html'})
357 .done(function(html) {
357 .done(function(html) {
358 $target.html(html);
358 $target.html(html);
359 $target.css('opacity','1.0');
359 $target.css('opacity','1.0');
360 //execute the given original callback
360 //execute the given original callback
361 if (success !== undefined && success) {
361 if (success !== undefined && success) {
362 success();
362 success();
363 }
363 }
364 })
364 })
365 .fail(function(jqXHR, textStatus, errorThrown) {
365 .fail(function(jqXHR, textStatus, errorThrown) {
366 if (textStatus == "abort")
366 if (textStatus == "abort")
367 return;
367 return;
368 $target.html('<span class="error_red">ERROR: {0}</span>'.format(textStatus));
368 $target.html('<span class="error_red">ERROR: {0}</span>'.format(textStatus));
369 $target.css('opacity','1.0');
369 $target.css('opacity','1.0');
370 })
370 })
371 ;
371 ;
372 };
372 };
373
373
374 var ajaxGET = function(url, success, failure) {
374 var ajaxGET = function(url, success, failure) {
375 if(failure === undefined) {
375 if(failure === undefined) {
376 failure = function(jqXHR, textStatus, errorThrown) {
376 failure = function(jqXHR, textStatus, errorThrown) {
377 if (textStatus != "abort")
377 if (textStatus != "abort")
378 alert("Ajax GET error: " + textStatus);
378 alert("Ajax GET error: " + textStatus);
379 };
379 };
380 }
380 }
381 return $.ajax({url: url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
381 return $.ajax({url: url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
382 .done(success)
382 .done(success)
383 .fail(failure);
383 .fail(failure);
384 };
384 };
385
385
386 var ajaxPOST = function(url, postData, success, failure) {
386 var ajaxPOST = function(url, postData, success, failure) {
387 postData['_authentication_token'] = _authentication_token;
387 postData['_authentication_token'] = _authentication_token;
388 var postData = _toQueryString(postData);
388 var postData = _toQueryString(postData);
389 if(failure === undefined) {
389 if(failure === undefined) {
390 failure = function(jqXHR, textStatus, errorThrown) {
390 failure = function(jqXHR, textStatus, errorThrown) {
391 if (textStatus != "abort")
391 if (textStatus != "abort")
392 alert("Error posting to server: " + textStatus);
392 alert("Error posting to server: " + textStatus);
393 };
393 };
394 }
394 }
395 return $.ajax({url: url, data: postData, type: 'POST', headers: {'X-PARTIAL-XHR': '1'}, cache: false})
395 return $.ajax({url: url, data: postData, type: 'POST', headers: {'X-PARTIAL-XHR': '1'}, cache: false})
396 .done(success)
396 .done(success)
397 .fail(failure);
397 .fail(failure);
398 };
398 };
399
399
400
400
401 /**
401 /**
402 * activate .show_more links
402 * activate .show_more links
403 * the .show_more must have an id that is the the id of an element to hide prefixed with _
403 * the .show_more must have an id that is the the id of an element to hide prefixed with _
404 * the parentnode will be displayed
404 * the parentnode will be displayed
405 */
405 */
406 var show_more_event = function(){
406 var show_more_event = function(){
407 $('.show_more').click(function(e){
407 $('.show_more').click(function(e){
408 var el = e.currentTarget;
408 var el = e.currentTarget;
409 $('#' + el.id.substring(1)).hide();
409 $('#' + el.id.substring(1)).hide();
410 $(el.parentNode).show();
410 $(el.parentNode).show();
411 });
411 });
412 };
412 };
413
413
414 /**
414 /**
415 * activate .lazy-cs mouseover for showing changeset tooltip
415 * activate .lazy-cs mouseover for showing changeset tooltip
416 */
416 */
417 var show_changeset_tooltip = function(){
417 var show_changeset_tooltip = function(){
418 $('.lazy-cs').mouseover(function(e){
418 $('.lazy-cs').mouseover(function(e){
419 var $target = $(e.currentTarget);
419 var $target = $(e.currentTarget);
420 var rid = $target.attr('raw_id');
420 var rid = $target.attr('raw_id');
421 var repo_name = $target.attr('repo_name');
421 var repo_name = $target.attr('repo_name');
422 if(rid && !$target.hasClass('tooltip')){
422 if(rid && !$target.hasClass('tooltip')){
423 _show_tooltip(e, _TM['loading ...']);
423 _show_tooltip(e, _TM['loading ...']);
424 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": rid});
424 var url = pyroutes.url('changeset_info', {"repo_name": repo_name, "revision": rid});
425 ajaxGET(url, function(json){
425 ajaxGET(url, function(json){
426 $target.addClass('tooltip');
426 $target.addClass('tooltip');
427 _show_tooltip(e, json['message']);
427 _show_tooltip(e, json['message']);
428 _activate_tooltip($target);
428 _activate_tooltip($target);
429 });
429 });
430 }
430 }
431 });
431 });
432 };
432 };
433
433
434 var _onSuccessFollow = function(target){
434 var _onSuccessFollow = function(target){
435 var $target = $(target);
435 var $target = $(target);
436 var $f_cnt = $('#current_followers_count');
436 var $f_cnt = $('#current_followers_count');
437 if ($target.hasClass('follow')) {
437 if ($target.hasClass('follow')) {
438 $target.removeClass('follow').addClass('following');
438 $target.removeClass('follow').addClass('following');
439 $target.prop('title', _TM['Stop following this repository']);
439 $target.prop('title', _TM['Stop following this repository']);
440 if ($f_cnt.html()) {
440 if ($f_cnt.html()) {
441 var cnt = Number($f_cnt.html())+1;
441 var cnt = Number($f_cnt.html())+1;
442 $f_cnt.html(cnt);
442 $f_cnt.html(cnt);
443 }
443 }
444 } else {
444 } else {
445 $target.removeClass('following').addClass('follow');
445 $target.removeClass('following').addClass('follow');
446 $target.prop('title', _TM['Start following this repository']);
446 $target.prop('title', _TM['Start following this repository']);
447 if ($f_cnt.html()) {
447 if ($f_cnt.html()) {
448 var cnt = Number($f_cnt.html())-1;
448 var cnt = Number($f_cnt.html())-1;
449 $f_cnt.html(cnt);
449 $f_cnt.html(cnt);
450 }
450 }
451 }
451 }
452 }
452 }
453
453
454 var toggleFollowingRepo = function(target, follows_repo_id){
454 var toggleFollowingRepo = function(target, follows_repo_id){
455 var args = 'follows_repo_id=' + follows_repo_id;
455 var args = 'follows_repo_id=' + follows_repo_id;
456 args += '&amp;_authentication_token=' + _authentication_token;
456 args += '&amp;_authentication_token=' + _authentication_token;
457 $.post(TOGGLE_FOLLOW_URL, args, function(data){
457 $.post(TOGGLE_FOLLOW_URL, args, function(data){
458 _onSuccessFollow(target);
458 _onSuccessFollow(target);
459 });
459 });
460 return false;
460 return false;
461 };
461 };
462
462
463 var showRepoSize = function(target, repo_name){
463 var showRepoSize = function(target, repo_name){
464 var args = '_authentication_token=' + _authentication_token;
464 var args = '_authentication_token=' + _authentication_token;
465
465
466 if(!$("#" + target).hasClass('loaded')){
466 if(!$("#" + target).hasClass('loaded')){
467 $("#" + target).html(_TM['Loading ...']);
467 $("#" + target).html(_TM['Loading ...']);
468 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
468 var url = pyroutes.url('repo_size', {"repo_name":repo_name});
469 $.post(url, args, function(data) {
469 $.post(url, args, function(data) {
470 $("#" + target).html(data);
470 $("#" + target).html(data);
471 $("#" + target).addClass('loaded');
471 $("#" + target).addClass('loaded');
472 });
472 });
473 }
473 }
474 return false;
474 return false;
475 };
475 };
476
476
477 /**
477 /**
478 * tooltips
478 * tooltips
479 */
479 */
480
480
481 var tooltip_activate = function(){
481 var tooltip_activate = function(){
482 $(document).ready(_init_tooltip);
482 $(document).ready(_init_tooltip);
483 };
483 };
484
484
485 var _activate_tooltip = function($tt){
485 var _activate_tooltip = function($tt){
486 $tt.mouseover(_show_tooltip);
486 $tt.mouseover(_show_tooltip);
487 $tt.mousemove(_move_tooltip);
487 $tt.mousemove(_move_tooltip);
488 $tt.mouseout(_close_tooltip);
488 $tt.mouseout(_close_tooltip);
489 };
489 };
490
490
491 var _init_tooltip = function(){
491 var _init_tooltip = function(){
492 var $tipBox = $('#tip-box');
492 var $tipBox = $('#tip-box');
493 if(!$tipBox.length){
493 if(!$tipBox.length){
494 $tipBox = $('<div id="tip-box"></div>');
494 $tipBox = $('<div id="tip-box"></div>');
495 $(document.body).append($tipBox);
495 $(document.body).append($tipBox);
496 }
496 }
497
497
498 $tipBox.hide();
498 $tipBox.hide();
499 $tipBox.css('position', 'absolute');
499 $tipBox.css('position', 'absolute');
500 $tipBox.css('max-width', '600px');
500 $tipBox.css('max-width', '600px');
501
501
502 _activate_tooltip($('.tooltip'));
502 _activate_tooltip($('.tooltip'));
503 };
503 };
504
504
505 var _show_tooltip = function(e, tipText, safe){
505 var _show_tooltip = function(e, tipText, safe){
506 e.stopImmediatePropagation();
506 e.stopImmediatePropagation();
507 var el = e.currentTarget;
507 var el = e.currentTarget;
508 var $el = $(el);
508 var $el = $(el);
509 if(tipText){
509 if(tipText){
510 // just use it
510 // just use it
511 } else if(el.tagName.toLowerCase() === 'img'){
511 } else if(el.tagName.toLowerCase() === 'img'){
512 tipText = el.alt ? el.alt : '';
512 tipText = el.alt ? el.alt : '';
513 } else {
513 } else {
514 tipText = el.title ? el.title : '';
514 tipText = el.title ? el.title : '';
515 safe = safe || $el.hasClass("safe-html-title");
515 safe = safe || $el.hasClass("safe-html-title");
516 }
516 }
517
517
518 if(tipText !== ''){
518 if(tipText !== ''){
519 // save org title
519 // save org title
520 $el.attr('tt_title', tipText);
520 $el.attr('tt_title', tipText);
521 // reset title to not show org tooltips
521 // reset title to not show org tooltips
522 $el.prop('title', '');
522 $el.prop('title', '');
523
523
524 var $tipBox = $('#tip-box');
524 var $tipBox = $('#tip-box');
525 if (safe) {
525 if (safe) {
526 $tipBox.html(tipText);
526 $tipBox.html(tipText);
527 } else {
527 } else {
528 $tipBox.text(tipText);
528 $tipBox.text(tipText);
529 }
529 }
530 $tipBox.css('display', 'block');
530 $tipBox.css('display', 'block');
531 }
531 }
532 };
532 };
533
533
534 var _move_tooltip = function(e){
534 var _move_tooltip = function(e){
535 e.stopImmediatePropagation();
535 e.stopImmediatePropagation();
536 var $tipBox = $('#tip-box');
536 var $tipBox = $('#tip-box');
537 $tipBox.css('top', (e.pageY + 15) + 'px');
537 $tipBox.css('top', (e.pageY + 15) + 'px');
538 $tipBox.css('left', (e.pageX + 15) + 'px');
538 $tipBox.css('left', (e.pageX + 15) + 'px');
539 };
539 };
540
540
541 var _close_tooltip = function(e){
541 var _close_tooltip = function(e){
542 e.stopImmediatePropagation();
542 e.stopImmediatePropagation();
543 var $tipBox = $('#tip-box');
543 var $tipBox = $('#tip-box');
544 $tipBox.hide();
544 $tipBox.hide();
545 var el = e.currentTarget;
545 var el = e.currentTarget;
546 $(el).prop('title', $(el).attr('tt_title'));
546 $(el).prop('title', $(el).attr('tt_title'));
547 };
547 };
548
548
549 /**
549 /**
550 * Quick filter widget
550 * Quick filter widget
551 *
551 *
552 * @param target: filter input target
552 * @param target: filter input target
553 * @param nodes: list of nodes in html we want to filter.
553 * @param nodes: list of nodes in html we want to filter.
554 * @param display_element function that takes current node from nodes and
554 * @param display_element function that takes current node from nodes and
555 * does hide or show based on the node
555 * does hide or show based on the node
556 */
556 */
557 var q_filter = (function() {
557 var q_filter = (function() {
558 var _namespace = {};
558 var _namespace = {};
559 var namespace = function (target) {
559 var namespace = function (target) {
560 if (!(target in _namespace)) {
560 if (!(target in _namespace)) {
561 _namespace[target] = {};
561 _namespace[target] = {};
562 }
562 }
563 return _namespace[target];
563 return _namespace[target];
564 };
564 };
565 return function (target, $nodes, display_element) {
565 return function (target, $nodes, display_element) {
566 var $nodes = $nodes;
566 var $nodes = $nodes;
567 var $q_filter_field = $('#' + target);
567 var $q_filter_field = $('#' + target);
568 var F = namespace(target);
568 var F = namespace(target);
569
569
570 $q_filter_field.keyup(function (e) {
570 $q_filter_field.keyup(function (e) {
571 clearTimeout(F.filterTimeout);
571 clearTimeout(F.filterTimeout);
572 F.filterTimeout = setTimeout(F.updateFilter, 600);
572 F.filterTimeout = setTimeout(F.updateFilter, 600);
573 });
573 });
574
574
575 F.filterTimeout = null;
575 F.filterTimeout = null;
576
576
577 F.updateFilter = function () {
577 F.updateFilter = function () {
578 // Reset timeout
578 // Reset timeout
579 F.filterTimeout = null;
579 F.filterTimeout = null;
580
580
581 var obsolete = [];
581 var obsolete = [];
582
582
583 var req = $q_filter_field.val().toLowerCase();
583 var req = $q_filter_field.val().toLowerCase();
584
584
585 var showing = 0;
585 var showing = 0;
586 $nodes.each(function () {
586 $nodes.each(function () {
587 var n = this;
587 var n = this;
588 var target_element = display_element(n);
588 var target_element = display_element(n);
589 if (req && n.innerHTML.toLowerCase().indexOf(req) == -1) {
589 if (req && n.innerHTML.toLowerCase().indexOf(req) == -1) {
590 $(target_element).hide();
590 $(target_element).hide();
591 }
591 }
592 else {
592 else {
593 $(target_element).show();
593 $(target_element).show();
594 showing += 1;
594 showing += 1;
595 }
595 }
596 });
596 });
597
597
598 $('#repo_count').html(showing);
598 $('#repo_count').html(showing);
599 /* FIXME: don't hardcode */
599 /* FIXME: don't hardcode */
600 }
600 }
601 }
601 }
602 })();
602 })();
603
603
604
604
605 /**
605 /**
606 * Comment handling
606 * Comment handling
607 */
607 */
608
608
609 // move comments to their right location, inside new trs
609 // move comments to their right location, inside new trs
610 function move_comments($anchorcomments) {
610 function move_comments($anchorcomments) {
611 $anchorcomments.each(function(i, anchorcomment) {
611 $anchorcomments.each(function(i, anchorcomment) {
612 var $anchorcomment = $(anchorcomment);
612 var $anchorcomment = $(anchorcomment);
613 var target_id = $anchorcomment.data('target-id');
613 var target_id = $anchorcomment.data('target-id');
614 var $comment_div = _get_add_comment_div(target_id);
614 var $comment_div = _get_add_comment_div(target_id);
615 var f_path = $anchorcomment.data('f_path');
615 var f_path = $anchorcomment.data('f_path');
616 var line_no = $anchorcomment.data('line_no');
616 var line_no = $anchorcomment.data('line_no');
617 if ($comment_div[0]) {
617 if ($comment_div[0]) {
618 $comment_div.append($anchorcomment.children());
618 $comment_div.append($anchorcomment.children());
619 if (f_path && line_no) {
619 if (f_path && line_no) {
620 _comment_div_append_add($comment_div, f_path, line_no);
620 _comment_div_append_add($comment_div, f_path, line_no);
621 } else {
621 } else {
622 _comment_div_append_form($comment_div, f_path, line_no);
622 _comment_div_append_form($comment_div, f_path, line_no);
623 }
623 }
624 } else {
624 } else {
625 $anchorcomment.before("Comment to {0} line {1} which is outside the diff context:".format(f_path || '?', line_no || '?'));
625 $anchorcomment.before("Comment to {0} line {1} which is outside the diff context:".format(f_path || '?', line_no || '?'));
626 }
626 }
627 });
627 });
628 linkInlineComments($('.firstlink'), $('.comment:first-child'));
628 linkInlineComments($('.firstlink'), $('.comment:first-child'));
629 }
629 }
630
630
631 // comment bubble was clicked - insert new tr and show form
631 // comment bubble was clicked - insert new tr and show form
632 function show_comment_form($bubble) {
632 function show_comment_form($bubble) {
633 var children = $bubble.closest('tr.line').children('[id]');
633 var children = $bubble.closest('tr.line').children('[id]');
634 var line_td_id = children[children.length - 1].id;
634 var line_td_id = children[children.length - 1].id;
635 var $comment_div = _get_add_comment_div(line_td_id);
635 var $comment_div = _get_add_comment_div(line_td_id);
636 var f_path = $bubble.closest('div.full_f_path').data('f_path');
636 var f_path = $bubble.closest('div.full_f_path').data('f_path');
637 var parts = line_td_id.split('_');
637 var parts = line_td_id.split('_');
638 var line_no = parts[parts.length-1];
638 var line_no = parts[parts.length-1];
639 comment_div_state($comment_div, f_path, line_no, true);
639 comment_div_state($comment_div, f_path, line_no, true);
640 }
640 }
641
641
642 // return comment div for target_id - add it if it doesn't exist yet
642 // return comment div for target_id - add it if it doesn't exist yet
643 function _get_add_comment_div(target_id) {
643 function _get_add_comment_div(target_id) {
644 var comments_box_id = 'comments-' + target_id;
644 var comments_box_id = 'comments-' + target_id;
645 var $comments_box = $('#' + comments_box_id);
645 var $comments_box = $('#' + comments_box_id);
646 if (!$comments_box.length) {
646 if (!$comments_box.length) {
647 var html = '<tr><td id="{0}" colspan="3" class="inline-comments"></td></tr>'.format(comments_box_id);
647 var html = '<tr><td id="{0}" colspan="3" class="inline-comments"></td></tr>'.format(comments_box_id);
648 $('#' + target_id).closest('tr').after(html);
648 $('#' + target_id).closest('tr').after(html);
649 $comments_box = $('#' + comments_box_id);
649 $comments_box = $('#' + comments_box_id);
650 }
650 }
651 return $comments_box;
651 return $comments_box;
652 }
652 }
653
653
654 // Set $comment_div state - showing or not showing form and Add button.
654 // Set $comment_div state - showing or not showing form and Add button.
655 // An Add button is shown on non-empty forms when no form is shown.
655 // An Add button is shown on non-empty forms when no form is shown.
656 // The form is controlled by show_form - if undefined, form is only shown for general comments.
656 // The form is controlled by show_form - if undefined, form is only shown for general comments.
657 function comment_div_state($comment_div, f_path, line_no, show_form_opt) {
657 function comment_div_state($comment_div, f_path, line_no, show_form_opt) {
658 var show_form = show_form_opt !== undefined ? show_form_opt : !f_path && !line_no;
658 var show_form = show_form_opt !== undefined ? show_form_opt : !f_path && !line_no;
659 var $forms = $comment_div.children('.comment-inline-form');
659 var $forms = $comment_div.children('.comment-inline-form');
660 var $buttonrow = $comment_div.children('.add-button-row');
660 var $buttonrow = $comment_div.children('.add-button-row');
661 var $comments = $comment_div.children('.comment');
661 var $comments = $comment_div.children('.comment');
662 $forms.remove();
662 $forms.remove();
663 $buttonrow.remove();
663 $buttonrow.remove();
664 if (show_form) {
664 if (show_form) {
665 _comment_div_append_form($comment_div, f_path, line_no);
665 _comment_div_append_form($comment_div, f_path, line_no);
666 } else if ($comments.length) {
666 } else if ($comments.length) {
667 _comment_div_append_add($comment_div, f_path, line_no);
667 _comment_div_append_add($comment_div, f_path, line_no);
668 }
668 }
669 }
669 }
670
670
671 // append an Add button to $comment_div and hook it up to show form
671 // append an Add button to $comment_div and hook it up to show form
672 function _comment_div_append_add($comment_div, f_path, line_no) {
672 function _comment_div_append_add($comment_div, f_path, line_no) {
673 var addlabel = TRANSLATION_MAP['Add Another Comment'];
673 var addlabel = TRANSLATION_MAP['Add Another Comment'];
674 var $add = $('<div class="add-button-row"><span class="btn btn-mini add-button">{0}</span></div>'.format(addlabel));
674 var $add = $('<div class="add-button-row"><span class="btn btn-mini add-button">{0}</span></div>'.format(addlabel));
675 $comment_div.append($add);
675 $comment_div.append($add);
676 $add.children('.add-button').click(function(e) {
676 $add.children('.add-button').click(function(e) {
677 comment_div_state($comment_div, f_path, line_no, true);
677 comment_div_state($comment_div, f_path, line_no, true);
678 });
678 });
679 }
679 }
680
680
681 // append a comment form to $comment_div
681 // append a comment form to $comment_div
682 function _comment_div_append_form($comment_div, f_path, line_no) {
682 function _comment_div_append_form($comment_div, f_path, line_no) {
683 var $form_div = $('#comment-inline-form-template').children()
683 var $form_div = $('#comment-inline-form-template').children()
684 .clone()
684 .clone()
685 .addClass('comment-inline-form');
685 .addClass('comment-inline-form');
686 $comment_div.append($form_div);
686 $comment_div.append($form_div);
687 var $form = $comment_div.find("form");
687 var $form = $comment_div.find("form");
688 var $textarea = $form.find('textarea');
688 var $textarea = $form.find('textarea');
689 var $mentions_container = $form.find('div.mentions-container');
689 var $mentions_container = $form.find('div.mentions-container');
690
690
691 $form.submit(function(e) {
691 $form.submit(function(e) {
692 e.preventDefault();
692 e.preventDefault();
693
693
694 var text = $textarea.val();
694 var text = $textarea.val();
695 var review_status = $form.find('input:radio[name=changeset_status]:checked').val();
695 var review_status = $form.find('input:radio[name=changeset_status]:checked').val();
696 var pr_close = $form.find('input:checkbox[name=save_close]:checked').length ? 'on' : '';
696 var pr_close = $form.find('input:checkbox[name=save_close]:checked').length ? 'on' : '';
697 var pr_delete = $form.find('input:checkbox[name=save_delete]:checked').length ? 'delete' : '';
697 var pr_delete = $form.find('input:checkbox[name=save_delete]:checked').length ? 'delete' : '';
698
698
699 if (!text && !review_status && !pr_close && !pr_delete) {
699 if (!text && !review_status && !pr_close && !pr_delete) {
700 alert("Please provide a comment");
700 alert("Please provide a comment");
701 return false;
701 return false;
702 }
702 }
703
703
704 if (pr_delete) {
704 if (pr_delete) {
705 if (text || review_status || pr_close) {
705 if (text || review_status || pr_close) {
706 alert('Cannot delete pull request while making other changes');
706 alert('Cannot delete pull request while making other changes');
707 return false;
707 return false;
708 }
708 }
709 if (!confirm('Confirm to delete this pull request')) {
709 if (!confirm('Confirm to delete this pull request')) {
710 return false;
710 return false;
711 }
711 }
712 var comments = $('.comment').size();
712 var comments = $('.comment').size();
713 if (comments > 0 &&
713 if (comments > 0 &&
714 !confirm('Confirm again to delete this pull request with {0} comments'.format(comments))) {
714 !confirm('Confirm again to delete this pull request with {0} comments'.format(comments))) {
715 return false;
715 return false;
716 }
716 }
717 }
717 }
718
718
719 $form.find('.submitting-overlay').show();
719 $form.find('.submitting-overlay').show();
720
720
721 var postData = {
721 var postData = {
722 'text': text,
722 'text': text,
723 'f_path': f_path,
723 'f_path': f_path,
724 'line': line_no,
724 'line': line_no,
725 'changeset_status': review_status,
725 'changeset_status': review_status,
726 'save_close': pr_close,
726 'save_close': pr_close,
727 'save_delete': pr_delete
727 'save_delete': pr_delete
728 };
728 };
729 var success = function(json_data) {
729 var success = function(json_data) {
730 if (pr_delete) {
730 if (pr_delete) {
731 location = json_data['location'];
731 location = json_data['location'];
732 } else {
732 } else {
733 $comment_div.append(json_data['rendered_text']);
733 $comment_div.append(json_data['rendered_text']);
734 comment_div_state($comment_div, f_path, line_no);
734 comment_div_state($comment_div, f_path, line_no);
735 linkInlineComments($('.firstlink'), $('.comment:first-child'));
735 linkInlineComments($('.firstlink'), $('.comment:first-child'));
736 if ((review_status || pr_close) && !f_path && !line_no) {
736 if ((review_status || pr_close) && !f_path && !line_no) {
737 // Page changed a lot - reload it after closing the submitted form
737 // Page changed a lot - reload it after closing the submitted form
738 comment_div_state($comment_div, f_path, line_no, false);
738 comment_div_state($comment_div, f_path, line_no, false);
739 location.reload(true);
739 location.reload(true);
740 }
740 }
741 }
741 }
742 };
742 };
743 ajaxPOST(AJAX_COMMENT_URL, postData, success);
743 ajaxPOST(AJAX_COMMENT_URL, postData, success);
744 });
744 });
745
745
746 // create event for hide button
746 // create event for hide button
747 $form.find('.hide-inline-form').click(function(e) {
747 $form.find('.hide-inline-form').click(function(e) {
748 comment_div_state($comment_div, f_path, line_no);
748 comment_div_state($comment_div, f_path, line_no);
749 });
749 });
750
750
751 tooltip_activate();
751 tooltip_activate();
752 MentionsAutoComplete($textarea, $mentions_container, _USERS_AC_DATA);
752 MentionsAutoComplete($textarea, $mentions_container, _USERS_AC_DATA);
753 if (f_path) {
753 if (f_path) {
754 $textarea.focus();
754 $textarea.focus();
755 }
755 }
756 }
756 }
757
757
758
758
759 function deleteComment(comment_id) {
759 function deleteComment(comment_id) {
760 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
760 var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__', comment_id);
761 var postData = {};
761 var postData = {};
762 var success = function(o) {
762 var success = function(o) {
763 $('#comment-'+comment_id).remove();
763 $('#comment-'+comment_id).remove();
764 // Ignore that this might leave a stray Add button (or have a pending form with another comment) ...
764 // Ignore that this might leave a stray Add button (or have a pending form with another comment) ...
765 }
765 }
766 ajaxPOST(url, postData, success);
766 ajaxPOST(url, postData, success);
767 }
767 }
768
768
769
769
770 /**
770 /**
771 * Double link comments
771 * Double link comments
772 */
772 */
773 var linkInlineComments = function($firstlinks, $comments){
773 var linkInlineComments = function($firstlinks, $comments){
774 if ($comments.length > 0) {
774 if ($comments.length > 0) {
775 $firstlinks.html('<a href="#{0}">First comment</a>'.format($comments.prop('id')));
775 $firstlinks.html('<a href="#{0}">First comment</a>'.format($comments.prop('id')));
776 }
776 }
777 if ($comments.length <= 1) {
777 if ($comments.length <= 1) {
778 return;
778 return;
779 }
779 }
780
780
781 $comments.each(function(i, e){
781 $comments.each(function(i, e){
782 var prev = '';
782 var prev = '';
783 if (i > 0){
783 if (i > 0){
784 var prev_anchor = $($comments.get(i-1)).prop('id');
784 var prev_anchor = $($comments.get(i-1)).prop('id');
785 prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
785 prev = '<a href="#{0}">Previous comment</a>'.format(prev_anchor);
786 }
786 }
787 var next = '';
787 var next = '';
788 if (i+1 < $comments.length){
788 if (i+1 < $comments.length){
789 var next_anchor = $($comments.get(i+1)).prop('id');
789 var next_anchor = $($comments.get(i+1)).prop('id');
790 next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
790 next = '<a href="#{0}">Next comment</a>'.format(next_anchor);
791 }
791 }
792 $(this).find('.comment-prev-next-links').html(
792 $(this).find('.comment-prev-next-links').html(
793 '<div class="prev-comment">{0}</div>'.format(prev) +
793 '<div class="prev-comment">{0}</div>'.format(prev) +
794 '<div class="next-comment">{0}</div>'.format(next));
794 '<div class="next-comment">{0}</div>'.format(next));
795 });
795 });
796 }
796 }
797
797
798 /* activate files.html stuff */
798 /* activate files.html stuff */
799 var fileBrowserListeners = function(current_url, node_list_url, url_base){
799 var fileBrowserListeners = function(current_url, node_list_url, url_base){
800 var current_url_branch = "?branch=__BRANCH__";
800 var current_url_branch = "?branch=__BRANCH__";
801
801
802 $('#stay_at_branch').on('click',function(e){
802 $('#stay_at_branch').on('click',function(e){
803 if(e.currentTarget.checked){
803 if(e.currentTarget.checked){
804 var uri = current_url_branch;
804 var uri = current_url_branch;
805 uri = uri.replace('__BRANCH__',e.currentTarget.value);
805 uri = uri.replace('__BRANCH__',e.currentTarget.value);
806 window.location = uri;
806 window.location = uri;
807 }
807 }
808 else{
808 else{
809 window.location = current_url;
809 window.location = current_url;
810 }
810 }
811 });
811 });
812
812
813 var $node_filter = $('#node_filter');
813 var $node_filter = $('#node_filter');
814
814
815 var filterTimeout = null;
815 var filterTimeout = null;
816 var nodes = null;
816 var nodes = null;
817
817
818 var initFilter = function(){
818 var initFilter = function(){
819 $('#node_filter_box_loading').show();
819 $('#node_filter_box_loading').show();
820 $('#search_activate_id').hide();
820 $('#search_activate_id').hide();
821 $('#add_node_id').hide();
821 $('#add_node_id').hide();
822 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
822 $.ajax({url: node_list_url, headers: {'X-PARTIAL-XHR': '1'}, cache: false})
823 .done(function(json) {
823 .done(function(json) {
824 nodes = json.nodes;
824 nodes = json.nodes;
825 $('#node_filter_box_loading').hide();
825 $('#node_filter_box_loading').hide();
826 $('#node_filter_box').show();
826 $('#node_filter_box').show();
827 $node_filter.focus();
827 $node_filter.focus();
828 if($node_filter.hasClass('init')){
828 if($node_filter.hasClass('init')){
829 $node_filter.val('');
829 $node_filter.val('');
830 $node_filter.removeClass('init');
830 $node_filter.removeClass('init');
831 }
831 }
832 })
832 })
833 .fail(function() {
833 .fail(function() {
834 console.log('fileBrowserListeners initFilter failed to load');
834 console.log('fileBrowserListeners initFilter failed to load');
835 })
835 })
836 ;
836 ;
837 }
837 }
838
838
839 var updateFilter = function(e) {
839 var updateFilter = function(e) {
840 return function(){
840 return function(){
841 // Reset timeout
841 // Reset timeout
842 filterTimeout = null;
842 filterTimeout = null;
843 var query = e.currentTarget.value.toLowerCase();
843 var query = e.currentTarget.value.toLowerCase();
844 var match = [];
844 var match = [];
845 var matches = 0;
845 var matches = 0;
846 var matches_max = 20;
846 var matches_max = 20;
847 if (query != ""){
847 if (query != ""){
848 for(var i=0;i<nodes.length;i++){
848 for(var i=0;i<nodes.length;i++){
849 var pos = nodes[i].name.toLowerCase().indexOf(query);
849 var pos = nodes[i].name.toLowerCase().indexOf(query);
850 if(query && pos != -1){
850 if(query && pos != -1){
851 matches++
851 matches++
852 //show only certain amount to not kill browser
852 //show only certain amount to not kill browser
853 if (matches > matches_max){
853 if (matches > matches_max){
854 break;
854 break;
855 }
855 }
856
856
857 var n = nodes[i].name;
857 var n = nodes[i].name;
858 var t = nodes[i].type;
858 var t = nodes[i].type;
859 var n_hl = n.substring(0,pos)
859 var n_hl = n.substring(0,pos)
860 + "<b>{0}</b>".format(n.substring(pos,pos+query.length))
860 + "<b>{0}</b>".format(n.substring(pos,pos+query.length))
861 + n.substring(pos+query.length);
861 + n.substring(pos+query.length);
862 var new_url = url_base.replace('__FPATH__',n);
862 var new_url = url_base.replace('__FPATH__',n);
863 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
863 match.push('<tr><td><a class="browser-{0}" href="{1}">{2}</a></td><td colspan="5"></td></tr>'.format(t,new_url,n_hl));
864 }
864 }
865 if(match.length >= matches_max){
865 if(match.length >= matches_max){
866 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
866 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['Search truncated']));
867 break;
867 break;
868 }
868 }
869 }
869 }
870 }
870 }
871 if(query != ""){
871 if(query != ""){
872 $('#tbody').hide();
872 $('#tbody').hide();
873 $('#tbody_filtered').show();
873 $('#tbody_filtered').show();
874
874
875 if (match.length==0){
875 if (match.length==0){
876 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
876 match.push('<tr><td>{0}</td><td colspan="5"></td></tr>'.format(_TM['No matching files']));
877 }
877 }
878
878
879 $('#tbody_filtered').html(match.join(""));
879 $('#tbody_filtered').html(match.join(""));
880 }
880 }
881 else{
881 else{
882 $('#tbody').show();
882 $('#tbody').show();
883 $('#tbody_filtered').hide();
883 $('#tbody_filtered').hide();
884 }
884 }
885 }
885 }
886 };
886 };
887
887
888 $('#filter_activate').click(function(){
888 $('#filter_activate').click(function(){
889 initFilter();
889 initFilter();
890 });
890 });
891 $node_filter.click(function(){
891 $node_filter.click(function(){
892 if($node_filter.hasClass('init')){
892 if($node_filter.hasClass('init')){
893 $node_filter.val('');
893 $node_filter.val('');
894 $node_filter.removeClass('init');
894 $node_filter.removeClass('init');
895 }
895 }
896 });
896 });
897 $node_filter.keyup(function(e){
897 $node_filter.keyup(function(e){
898 clearTimeout(filterTimeout);
898 clearTimeout(filterTimeout);
899 filterTimeout = setTimeout(updateFilter(e),600);
899 filterTimeout = setTimeout(updateFilter(e),600);
900 });
900 });
901 };
901 };
902
902
903
903
904 var initCodeMirror = function(textarea_id, baseUrl, resetUrl){
904 var initCodeMirror = function(textarea_id, baseUrl, resetUrl){
905 var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
905 var myCodeMirror = CodeMirror.fromTextArea($('#' + textarea_id)[0], {
906 mode: "null",
906 mode: "null",
907 lineNumbers: true,
907 lineNumbers: true,
908 indentUnit: 4,
908 indentUnit: 4,
909 autofocus: true
909 autofocus: true
910 });
910 });
911 CodeMirror.modeURL = baseUrl + "/codemirror/mode/%N/%N.js";
911 CodeMirror.modeURL = baseUrl + "/codemirror/mode/%N/%N.js";
912
912
913 $('#reset').click(function(e){
913 $('#reset').click(function(e){
914 window.location=resetUrl;
914 window.location=resetUrl;
915 });
915 });
916
916
917 $('#file_enable').click(function(){
917 $('#file_enable').click(function(){
918 $('#editor_container').show();
918 $('#editor_container').show();
919 $('#upload_file_container').hide();
919 $('#upload_file_container').hide();
920 $('#filename_container').show();
920 $('#filename_container').show();
921 $('#mimetype_header').show();
921 $('#mimetype_header').show();
922 });
922 });
923
923
924 $('#upload_file_enable').click(function(){
924 $('#upload_file_enable').click(function(){
925 $('#editor_container').hide();
925 $('#editor_container').hide();
926 $('#upload_file_container').show();
926 $('#upload_file_container').show();
927 $('#filename_container').hide();
927 $('#filename_container').hide();
928 $('#mimetype_header').hide();
928 $('#mimetype_header').hide();
929 });
929 });
930
930
931 return myCodeMirror
931 return myCodeMirror
932 };
932 };
933
933
934 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
934 var setCodeMirrorMode = function(codeMirrorInstance, mode) {
935 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
935 CodeMirror.autoLoadMode(codeMirrorInstance, mode);
936 }
936 }
937
937
938
938
939 var _getIdentNode = function(n){
939 var _getIdentNode = function(n){
940 //iterate thrugh nodes until matching interesting node
940 //iterate thrugh nodes until matching interesting node
941
941
942 if (typeof n == 'undefined'){
942 if (typeof n == 'undefined'){
943 return -1
943 return -1
944 }
944 }
945
945
946 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
946 if(typeof n.id != "undefined" && n.id.match('L[0-9]+')){
947 return n
947 return n
948 }
948 }
949 else{
949 else{
950 return _getIdentNode(n.parentNode);
950 return _getIdentNode(n.parentNode);
951 }
951 }
952 };
952 };
953
953
954 /* generate links for multi line selects that can be shown by files.html page_highlights.
954 /* generate links for multi line selects that can be shown by files.html page_highlights.
955 * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
955 * This is a mouseup handler for hlcode from CodeHtmlFormatter and pygmentize */
956 var getSelectionLink = function(e) {
956 var getSelectionLink = function(e) {
957 //get selection from start/to nodes
957 //get selection from start/to nodes
958 if (typeof window.getSelection != "undefined") {
958 if (typeof window.getSelection != "undefined") {
959 var s = window.getSelection();
959 var s = window.getSelection();
960
960
961 var from = _getIdentNode(s.anchorNode);
961 var from = _getIdentNode(s.anchorNode);
962 var till = _getIdentNode(s.focusNode);
962 var till = _getIdentNode(s.focusNode);
963
963
964 var f_int = parseInt(from.id.replace('L',''));
964 var f_int = parseInt(from.id.replace('L',''));
965 var t_int = parseInt(till.id.replace('L',''));
965 var t_int = parseInt(till.id.replace('L',''));
966
966
967 var yoffset = 35;
967 var yoffset = 35;
968 var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
968 var ranges = [parseInt(from.id.replace('L','')), parseInt(till.id.replace('L',''))];
969 if (ranges[0] > ranges[1]){
969 if (ranges[0] > ranges[1]){
970 //highlight from bottom
970 //highlight from bottom
971 yoffset = -yoffset;
971 yoffset = -yoffset;
972 ranges = [ranges[1], ranges[0]];
972 ranges = [ranges[1], ranges[0]];
973 }
973 }
974 var $hl_div = $('div#linktt');
974 var $hl_div = $('div#linktt');
975 // if we select more than 2 lines
975 // if we select more than 2 lines
976 if (ranges[0] != ranges[1]){
976 if (ranges[0] != ranges[1]){
977 if ($hl_div.length) {
977 if ($hl_div.length) {
978 $hl_div.html('');
978 $hl_div.html('');
979 } else {
979 } else {
980 $hl_div = $('<div id="linktt" class="hl-tip-box">');
980 $hl_div = $('<div id="linktt" class="hl-tip-box">');
981 $('body').prepend($hl_div);
981 $('body').prepend($hl_div);
982 }
982 }
983
983
984 $hl_div.append($('<a>').html(_TM['Selection Link']).prop('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
984 $hl_div.append($('<a>').html(_TM['Selection Link']).prop('href', location.href.substring(0, location.href.indexOf('#')) + '#L' + ranges[0] + '-'+ranges[1]));
985 var xy = $(till).offset();
985 var xy = $(till).offset();
986 $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
986 $hl_div.css('top', (xy.top + yoffset) + 'px').css('left', xy.left + 'px');
987 $hl_div.show();
987 $hl_div.show();
988 }
988 }
989 else{
989 else{
990 $hl_div.hide();
990 $hl_div.hide();
991 }
991 }
992 }
992 }
993 };
993 };
994
994
995 var deleteNotification = function(url, notification_id, callbacks){
995 var deleteNotification = function(url, notification_id, callbacks){
996 var success = function(o){
996 var success = function(o){
997 $("#notification_"+notification_id).remove();
997 $("#notification_"+notification_id).remove();
998 _run_callbacks(callbacks);
998 _run_callbacks(callbacks);
999 };
999 };
1000 var failure = function(o){
1000 var failure = function(o){
1001 alert("deleteNotification failure");
1001 alert("deleteNotification failure");
1002 };
1002 };
1003 var postData = {'_method': 'delete'};
1003 var postData = {};
1004 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1004 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1005 ajaxPOST(sUrl, postData, success, failure);
1005 ajaxPOST(sUrl, postData, success, failure);
1006 };
1006 };
1007
1007
1008 var readNotification = function(url, notification_id, callbacks){
1008 var readNotification = function(url, notification_id, callbacks){
1009 var success = function(o){
1009 var success = function(o){
1010 var $obj = $("#notification_"+notification_id);
1010 var $obj = $("#notification_"+notification_id);
1011 $obj.removeClass('unread');
1011 $obj.removeClass('unread');
1012 $obj.find('.read-notification').remove();
1012 $obj.find('.read-notification').remove();
1013 _run_callbacks(callbacks);
1013 _run_callbacks(callbacks);
1014 };
1014 };
1015 var failure = function(o){
1015 var failure = function(o){
1016 alert("readNotification failure");
1016 alert("readNotification failure");
1017 };
1017 };
1018 var postData = {'_method': 'put'};
1018 var postData = {'_method': 'put'};
1019 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1019 var sUrl = url.replace('__NOTIFICATION_ID__',notification_id);
1020 ajaxPOST(sUrl, postData, success, failure);
1020 ajaxPOST(sUrl, postData, success, failure);
1021 };
1021 };
1022
1022
1023 /**
1023 /**
1024 * Autocomplete functionality
1024 * Autocomplete functionality
1025 */
1025 */
1026
1026
1027 // Custom search function for the DataSource of users
1027 // Custom search function for the DataSource of users
1028 var autocompleteMatchUsers = function (sQuery, myUsers) {
1028 var autocompleteMatchUsers = function (sQuery, myUsers) {
1029 // Case insensitive matching
1029 // Case insensitive matching
1030 var query = sQuery.toLowerCase();
1030 var query = sQuery.toLowerCase();
1031 var i = 0;
1031 var i = 0;
1032 var l = myUsers.length;
1032 var l = myUsers.length;
1033 var matches = [];
1033 var matches = [];
1034
1034
1035 // Match against each name of each contact
1035 // Match against each name of each contact
1036 for (; i < l; i++) {
1036 for (; i < l; i++) {
1037 var contact = myUsers[i];
1037 var contact = myUsers[i];
1038 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1038 if (((contact.fname+"").toLowerCase().indexOf(query) > -1) ||
1039 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1039 ((contact.lname+"").toLowerCase().indexOf(query) > -1) ||
1040 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1040 ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) {
1041 matches[matches.length] = contact;
1041 matches[matches.length] = contact;
1042 }
1042 }
1043 }
1043 }
1044 return matches;
1044 return matches;
1045 };
1045 };
1046
1046
1047 // Custom search function for the DataSource of userGroups
1047 // Custom search function for the DataSource of userGroups
1048 var autocompleteMatchGroups = function (sQuery, myGroups) {
1048 var autocompleteMatchGroups = function (sQuery, myGroups) {
1049 // Case insensitive matching
1049 // Case insensitive matching
1050 var query = sQuery.toLowerCase();
1050 var query = sQuery.toLowerCase();
1051 var i = 0;
1051 var i = 0;
1052 var l = myGroups.length;
1052 var l = myGroups.length;
1053 var matches = [];
1053 var matches = [];
1054
1054
1055 // Match against each name of each group
1055 // Match against each name of each group
1056 for (; i < l; i++) {
1056 for (; i < l; i++) {
1057 var matched_group = myGroups[i];
1057 var matched_group = myGroups[i];
1058 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1058 if (matched_group.grname.toLowerCase().indexOf(query) > -1) {
1059 matches[matches.length] = matched_group;
1059 matches[matches.length] = matched_group;
1060 }
1060 }
1061 }
1061 }
1062 return matches;
1062 return matches;
1063 };
1063 };
1064
1064
1065 // Helper highlight function for the formatter
1065 // Helper highlight function for the formatter
1066 var autocompleteHighlightMatch = function (full, snippet, matchindex) {
1066 var autocompleteHighlightMatch = function (full, snippet, matchindex) {
1067 return full.substring(0, matchindex)
1067 return full.substring(0, matchindex)
1068 + "<span class='match'>"
1068 + "<span class='match'>"
1069 + full.substr(matchindex, snippet.length)
1069 + full.substr(matchindex, snippet.length)
1070 + "</span>" + full.substring(matchindex + snippet.length);
1070 + "</span>" + full.substring(matchindex + snippet.length);
1071 };
1071 };
1072
1072
1073 // Return html snippet for showing the provided gravatar url
1073 // Return html snippet for showing the provided gravatar url
1074 var gravatar = function(gravatar_lnk, size, cssclass) {
1074 var gravatar = function(gravatar_lnk, size, cssclass) {
1075 if (!gravatar_lnk) {
1075 if (!gravatar_lnk) {
1076 return '';
1076 return '';
1077 }
1077 }
1078 if (gravatar_lnk == 'default') {
1078 if (gravatar_lnk == 'default') {
1079 return '<i class="icon-user {1}" style="font-size: {0}px;"></i>'.format(size, cssclass);
1079 return '<i class="icon-user {1}" style="font-size: {0}px;"></i>'.format(size, cssclass);
1080 }
1080 }
1081 return '<img alt="" class="{2}" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, gravatar_lnk, cssclass);
1081 return '<img alt="" class="{2}" style="width: {0}px; height: {0}px" src="{1}"/>'.format(size, gravatar_lnk, cssclass);
1082 }
1082 }
1083
1083
1084 var autocompleteGravatar = function(res, gravatar_lnk, size, group) {
1084 var autocompleteGravatar = function(res, gravatar_lnk, size, group) {
1085 var elem;
1085 var elem;
1086 if (group !== undefined) {
1086 if (group !== undefined) {
1087 elem = '<i class="perm-gravatar-ac icon-users"></i>';
1087 elem = '<i class="perm-gravatar-ac icon-users"></i>';
1088 } else {
1088 } else {
1089 elem = gravatar(gravatar_lnk, size, "perm-gravatar-ac");
1089 elem = gravatar(gravatar_lnk, size, "perm-gravatar-ac");
1090 }
1090 }
1091 return '<div class="ac-container-wrap">{0}{1}</div>'.format(elem, res);
1091 return '<div class="ac-container-wrap">{0}{1}</div>'.format(elem, res);
1092 }
1092 }
1093
1093
1094 // Custom formatter to highlight the matching letters
1094 // Custom formatter to highlight the matching letters
1095 var autocompleteFormatter = function (oResultData, sQuery, sResultMatch) {
1095 var autocompleteFormatter = function (oResultData, sQuery, sResultMatch) {
1096 var query = sQuery.toLowerCase();
1096 var query = sQuery.toLowerCase();
1097
1097
1098 // group
1098 // group
1099 if (oResultData.grname != undefined) {
1099 if (oResultData.grname != undefined) {
1100 var grname = oResultData.grname;
1100 var grname = oResultData.grname;
1101 var grmembers = oResultData.grmembers;
1101 var grmembers = oResultData.grmembers;
1102 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1102 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
1103 var grprefix = "{0}: ".format(_TM['Group']);
1103 var grprefix = "{0}: ".format(_TM['Group']);
1104 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1104 var grsuffix = " ({0} {1})".format(grmembers, _TM['members']);
1105
1105
1106 if (grnameMatchIndex > -1) {
1106 if (grnameMatchIndex > -1) {
1107 return autocompleteGravatar(grprefix + autocompleteHighlightMatch(grname, query, grnameMatchIndex) + grsuffix, null, null, true);
1107 return autocompleteGravatar(grprefix + autocompleteHighlightMatch(grname, query, grnameMatchIndex) + grsuffix, null, null, true);
1108 }
1108 }
1109 return autocompleteGravatar(grprefix + oResultData.grname + grsuffix, null, null, true);
1109 return autocompleteGravatar(grprefix + oResultData.grname + grsuffix, null, null, true);
1110
1110
1111 // users
1111 // users
1112 } else if (oResultData.nname != undefined) {
1112 } else if (oResultData.nname != undefined) {
1113 var fname = oResultData.fname || "";
1113 var fname = oResultData.fname || "";
1114 var lname = oResultData.lname || "";
1114 var lname = oResultData.lname || "";
1115 var nname = oResultData.nname;
1115 var nname = oResultData.nname;
1116
1116
1117 // Guard against null value
1117 // Guard against null value
1118 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1118 var fnameMatchIndex = fname.toLowerCase().indexOf(query),
1119 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1119 lnameMatchIndex = lname.toLowerCase().indexOf(query),
1120 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1120 nnameMatchIndex = nname.toLowerCase().indexOf(query),
1121 displayfname, displaylname, displaynname, displayname;
1121 displayfname, displaylname, displaynname, displayname;
1122
1122
1123 if (fnameMatchIndex > -1) {
1123 if (fnameMatchIndex > -1) {
1124 displayfname = autocompleteHighlightMatch(fname, query, fnameMatchIndex);
1124 displayfname = autocompleteHighlightMatch(fname, query, fnameMatchIndex);
1125 } else {
1125 } else {
1126 displayfname = fname;
1126 displayfname = fname;
1127 }
1127 }
1128
1128
1129 if (lnameMatchIndex > -1) {
1129 if (lnameMatchIndex > -1) {
1130 displaylname = autocompleteHighlightMatch(lname, query, lnameMatchIndex);
1130 displaylname = autocompleteHighlightMatch(lname, query, lnameMatchIndex);
1131 } else {
1131 } else {
1132 displaylname = lname;
1132 displaylname = lname;
1133 }
1133 }
1134
1134
1135 if (nnameMatchIndex > -1) {
1135 if (nnameMatchIndex > -1) {
1136 displaynname = autocompleteHighlightMatch(nname, query, nnameMatchIndex);
1136 displaynname = autocompleteHighlightMatch(nname, query, nnameMatchIndex);
1137 } else {
1137 } else {
1138 displaynname = nname;
1138 displaynname = nname;
1139 }
1139 }
1140
1140
1141 displayname = displaynname;
1141 displayname = displaynname;
1142 if (displayfname && displaylname) {
1142 if (displayfname && displaylname) {
1143 displayname = "{0} {1} ({2})".format(displayfname, displaylname, displayname);
1143 displayname = "{0} {1} ({2})".format(displayfname, displaylname, displayname);
1144 }
1144 }
1145
1145
1146 return autocompleteGravatar(displayname, oResultData.gravatar_lnk, oResultData.gravatar_size);
1146 return autocompleteGravatar(displayname, oResultData.gravatar_lnk, oResultData.gravatar_size);
1147 } else {
1147 } else {
1148 return '';
1148 return '';
1149 }
1149 }
1150 };
1150 };
1151
1151
1152 // Generate a basic autocomplete instance that can be tweaked further by the caller
1152 // Generate a basic autocomplete instance that can be tweaked further by the caller
1153 var autocompleteCreate = function ($inputElement, $container, matchFunc) {
1153 var autocompleteCreate = function ($inputElement, $container, matchFunc) {
1154 var datasource = new YAHOO.util.FunctionDataSource(matchFunc);
1154 var datasource = new YAHOO.util.FunctionDataSource(matchFunc);
1155
1155
1156 var autocomplete = new YAHOO.widget.AutoComplete($inputElement[0], $container[0], datasource);
1156 var autocomplete = new YAHOO.widget.AutoComplete($inputElement[0], $container[0], datasource);
1157 autocomplete.useShadow = false;
1157 autocomplete.useShadow = false;
1158 autocomplete.resultTypeList = false;
1158 autocomplete.resultTypeList = false;
1159 autocomplete.animVert = false;
1159 autocomplete.animVert = false;
1160 autocomplete.animHoriz = false;
1160 autocomplete.animHoriz = false;
1161 autocomplete.animSpeed = 0.1;
1161 autocomplete.animSpeed = 0.1;
1162 autocomplete.formatResult = autocompleteFormatter;
1162 autocomplete.formatResult = autocompleteFormatter;
1163
1163
1164 return autocomplete;
1164 return autocomplete;
1165 }
1165 }
1166
1166
1167 var SimpleUserAutoComplete = function ($inputElement, $container, users_list) {
1167 var SimpleUserAutoComplete = function ($inputElement, $container, users_list) {
1168
1168
1169 var matchUsers = function (sQuery) {
1169 var matchUsers = function (sQuery) {
1170 return autocompleteMatchUsers(sQuery, users_list);
1170 return autocompleteMatchUsers(sQuery, users_list);
1171 }
1171 }
1172
1172
1173 var userAC = autocompleteCreate($inputElement, $container, matchUsers);
1173 var userAC = autocompleteCreate($inputElement, $container, matchUsers);
1174
1174
1175 // Handler for selection of an entry
1175 // Handler for selection of an entry
1176 var itemSelectHandler = function (sType, aArgs) {
1176 var itemSelectHandler = function (sType, aArgs) {
1177 var myAC = aArgs[0]; // reference back to the AC instance
1177 var myAC = aArgs[0]; // reference back to the AC instance
1178 var elLI = aArgs[1]; // reference to the selected LI element
1178 var elLI = aArgs[1]; // reference to the selected LI element
1179 var oData = aArgs[2]; // object literal of selected item's result data
1179 var oData = aArgs[2]; // object literal of selected item's result data
1180 myAC.getInputEl().value = oData.nname;
1180 myAC.getInputEl().value = oData.nname;
1181 };
1181 };
1182 userAC.itemSelectEvent.subscribe(itemSelectHandler);
1182 userAC.itemSelectEvent.subscribe(itemSelectHandler);
1183 }
1183 }
1184
1184
1185 var MembersAutoComplete = function ($inputElement, $container, users_list, groups_list) {
1185 var MembersAutoComplete = function ($inputElement, $container, users_list, groups_list) {
1186
1186
1187 var matchAll = function (sQuery) {
1187 var matchAll = function (sQuery) {
1188 var u = autocompleteMatchUsers(sQuery, users_list);
1188 var u = autocompleteMatchUsers(sQuery, users_list);
1189 var g = autocompleteMatchGroups(sQuery, groups_list);
1189 var g = autocompleteMatchGroups(sQuery, groups_list);
1190 return u.concat(g);
1190 return u.concat(g);
1191 };
1191 };
1192
1192
1193 var membersAC = autocompleteCreate($inputElement, $container, matchAll);
1193 var membersAC = autocompleteCreate($inputElement, $container, matchAll);
1194
1194
1195 // Handler for selection of an entry
1195 // Handler for selection of an entry
1196 var itemSelectHandler = function (sType, aArgs) {
1196 var itemSelectHandler = function (sType, aArgs) {
1197 var nextId = $inputElement.prop('id').split('perm_new_member_name_')[1];
1197 var nextId = $inputElement.prop('id').split('perm_new_member_name_')[1];
1198 var myAC = aArgs[0]; // reference back to the AC instance
1198 var myAC = aArgs[0]; // reference back to the AC instance
1199 var elLI = aArgs[1]; // reference to the selected LI element
1199 var elLI = aArgs[1]; // reference to the selected LI element
1200 var oData = aArgs[2]; // object literal of selected item's result data
1200 var oData = aArgs[2]; // object literal of selected item's result data
1201 //fill the autocomplete with value
1201 //fill the autocomplete with value
1202 if (oData.nname != undefined) {
1202 if (oData.nname != undefined) {
1203 //users
1203 //users
1204 myAC.getInputEl().value = oData.nname;
1204 myAC.getInputEl().value = oData.nname;
1205 $('#perm_new_member_type_'+nextId).val('user');
1205 $('#perm_new_member_type_'+nextId).val('user');
1206 } else {
1206 } else {
1207 //groups
1207 //groups
1208 myAC.getInputEl().value = oData.grname;
1208 myAC.getInputEl().value = oData.grname;
1209 $('#perm_new_member_type_'+nextId).val('users_group');
1209 $('#perm_new_member_type_'+nextId).val('users_group');
1210 }
1210 }
1211 };
1211 };
1212 membersAC.itemSelectEvent.subscribe(itemSelectHandler);
1212 membersAC.itemSelectEvent.subscribe(itemSelectHandler);
1213 }
1213 }
1214
1214
1215 var MentionsAutoComplete = function ($inputElement, $container, users_list) {
1215 var MentionsAutoComplete = function ($inputElement, $container, users_list) {
1216
1216
1217 var matchUsers = function (sQuery) {
1217 var matchUsers = function (sQuery) {
1218 var org_sQuery = sQuery;
1218 var org_sQuery = sQuery;
1219 if(this.mentionQuery == null){
1219 if(this.mentionQuery == null){
1220 return []
1220 return []
1221 }
1221 }
1222 sQuery = this.mentionQuery;
1222 sQuery = this.mentionQuery;
1223 return autocompleteMatchUsers(sQuery, users_list);
1223 return autocompleteMatchUsers(sQuery, users_list);
1224 }
1224 }
1225
1225
1226 var mentionsAC = autocompleteCreate($inputElement, $container, matchUsers);
1226 var mentionsAC = autocompleteCreate($inputElement, $container, matchUsers);
1227 mentionsAC.suppressInputUpdate = true;
1227 mentionsAC.suppressInputUpdate = true;
1228 // Overwrite formatResult to take into account mentionQuery
1228 // Overwrite formatResult to take into account mentionQuery
1229 mentionsAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1229 mentionsAC.formatResult = function (oResultData, sQuery, sResultMatch) {
1230 var org_sQuery = sQuery;
1230 var org_sQuery = sQuery;
1231 if (this.dataSource.mentionQuery != null) {
1231 if (this.dataSource.mentionQuery != null) {
1232 sQuery = this.dataSource.mentionQuery;
1232 sQuery = this.dataSource.mentionQuery;
1233 }
1233 }
1234 return autocompleteFormatter(oResultData, sQuery, sResultMatch);
1234 return autocompleteFormatter(oResultData, sQuery, sResultMatch);
1235 }
1235 }
1236
1236
1237 // Handler for selection of an entry
1237 // Handler for selection of an entry
1238 if(mentionsAC.itemSelectEvent){
1238 if(mentionsAC.itemSelectEvent){
1239 mentionsAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1239 mentionsAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1240 var myAC = aArgs[0]; // reference back to the AC instance
1240 var myAC = aArgs[0]; // reference back to the AC instance
1241 var elLI = aArgs[1]; // reference to the selected LI element
1241 var elLI = aArgs[1]; // reference to the selected LI element
1242 var oData = aArgs[2]; // object literal of selected item's result data
1242 var oData = aArgs[2]; // object literal of selected item's result data
1243 //Replace the mention name with replaced
1243 //Replace the mention name with replaced
1244 var re = new RegExp();
1244 var re = new RegExp();
1245 var org = myAC.getInputEl().value;
1245 var org = myAC.getInputEl().value;
1246 var chunks = myAC.dataSource.chunks
1246 var chunks = myAC.dataSource.chunks
1247 // replace middle chunk(the search term) with actuall match
1247 // replace middle chunk(the search term) with actuall match
1248 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1248 chunks[1] = chunks[1].replace('@'+myAC.dataSource.mentionQuery,
1249 '@'+oData.nname+' ');
1249 '@'+oData.nname+' ');
1250 myAC.getInputEl().value = chunks.join('');
1250 myAC.getInputEl().value = chunks.join('');
1251 myAC.getInputEl().focus(); // Y U NO WORK !?
1251 myAC.getInputEl().focus(); // Y U NO WORK !?
1252 });
1252 });
1253 }
1253 }
1254
1254
1255 // in this keybuffer we will gather current value of search !
1255 // in this keybuffer we will gather current value of search !
1256 // since we need to get this just when someone does `@` then we do the
1256 // since we need to get this just when someone does `@` then we do the
1257 // search
1257 // search
1258 mentionsAC.dataSource.chunks = [];
1258 mentionsAC.dataSource.chunks = [];
1259 mentionsAC.dataSource.mentionQuery = null;
1259 mentionsAC.dataSource.mentionQuery = null;
1260
1260
1261 mentionsAC.get_mention = function(msg, max_pos) {
1261 mentionsAC.get_mention = function(msg, max_pos) {
1262 var org = msg;
1262 var org = msg;
1263 // Must match utils2.py MENTIONS_REGEX.
1263 // Must match utils2.py MENTIONS_REGEX.
1264 // Only matching on string up to cursor, so it must end with $
1264 // Only matching on string up to cursor, so it must end with $
1265 var re = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$');
1265 var re = new RegExp('(?:^|[^a-zA-Z0-9])@([a-zA-Z0-9][-_.a-zA-Z0-9]*[a-zA-Z0-9])$');
1266 var chunks = [];
1266 var chunks = [];
1267
1267
1268 // cut first chunk until current pos
1268 // cut first chunk until current pos
1269 var to_max = msg.substr(0, max_pos);
1269 var to_max = msg.substr(0, max_pos);
1270 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1270 var at_pos = Math.max(0,to_max.lastIndexOf('@')-1);
1271 var msg2 = to_max.substr(at_pos);
1271 var msg2 = to_max.substr(at_pos);
1272
1272
1273 chunks.push(org.substr(0,at_pos)); // prefix chunk
1273 chunks.push(org.substr(0,at_pos)); // prefix chunk
1274 chunks.push(msg2); // search chunk
1274 chunks.push(msg2); // search chunk
1275 chunks.push(org.substr(max_pos)); // postfix chunk
1275 chunks.push(org.substr(max_pos)); // postfix chunk
1276
1276
1277 // clean up msg2 for filtering and regex match
1277 // clean up msg2 for filtering and regex match
1278 var msg2 = msg2.lstrip(' ').lstrip('\n');
1278 var msg2 = msg2.lstrip(' ').lstrip('\n');
1279
1279
1280 if(re.test(msg2)){
1280 if(re.test(msg2)){
1281 var unam = re.exec(msg2)[1];
1281 var unam = re.exec(msg2)[1];
1282 return [unam, chunks];
1282 return [unam, chunks];
1283 }
1283 }
1284 return [null, null];
1284 return [null, null];
1285 };
1285 };
1286
1286
1287 $inputElement.keyup(function(e){
1287 $inputElement.keyup(function(e){
1288 var currentMessage = $inputElement.val();
1288 var currentMessage = $inputElement.val();
1289 var currentCaretPosition = $inputElement[0].selectionStart;
1289 var currentCaretPosition = $inputElement[0].selectionStart;
1290
1290
1291 var unam = mentionsAC.get_mention(currentMessage, currentCaretPosition);
1291 var unam = mentionsAC.get_mention(currentMessage, currentCaretPosition);
1292 var curr_search = null;
1292 var curr_search = null;
1293 if(unam[0]){
1293 if(unam[0]){
1294 curr_search = unam[0];
1294 curr_search = unam[0];
1295 }
1295 }
1296
1296
1297 mentionsAC.dataSource.chunks = unam[1];
1297 mentionsAC.dataSource.chunks = unam[1];
1298 mentionsAC.dataSource.mentionQuery = curr_search;
1298 mentionsAC.dataSource.mentionQuery = curr_search;
1299 });
1299 });
1300 }
1300 }
1301
1301
1302 var addReviewMember = function(id,fname,lname,nname,gravatar_link,gravatar_size){
1302 var addReviewMember = function(id,fname,lname,nname,gravatar_link,gravatar_size){
1303 var displayname = nname;
1303 var displayname = nname;
1304 if ((fname != "") && (lname != "")) {
1304 if ((fname != "") && (lname != "")) {
1305 displayname = "{0} {1} ({2})".format(fname, lname, nname);
1305 displayname = "{0} {1} ({2})".format(fname, lname, nname);
1306 }
1306 }
1307 var gravatarelm = gravatar(gravatar_link, gravatar_size, "");
1307 var gravatarelm = gravatar(gravatar_link, gravatar_size, "");
1308 // WARNING: the HTML below is duplicate with
1308 // WARNING: the HTML below is duplicate with
1309 // kallithea/templates/pullrequests/pullrequest_show.html
1309 // kallithea/templates/pullrequests/pullrequest_show.html
1310 // If you change something here it should be reflected in the template too.
1310 // If you change something here it should be reflected in the template too.
1311 var element = (
1311 var element = (
1312 ' <li id="reviewer_{2}">\n'+
1312 ' <li id="reviewer_{2}">\n'+
1313 ' <div class="reviewers_member">\n'+
1313 ' <div class="reviewers_member">\n'+
1314 ' <div class="reviewer_status tooltip" title="not_reviewed">\n'+
1314 ' <div class="reviewer_status tooltip" title="not_reviewed">\n'+
1315 ' <i class="icon-circle changeset-status-not_reviewed"></i>\n'+
1315 ' <i class="icon-circle changeset-status-not_reviewed"></i>\n'+
1316 ' </div>\n'+
1316 ' </div>\n'+
1317 (gravatarelm ?
1317 (gravatarelm ?
1318 ' <div class="reviewer_gravatar gravatar">{0}</div>\n' :
1318 ' <div class="reviewer_gravatar gravatar">{0}</div>\n' :
1319 '')+
1319 '')+
1320 ' <div style="float:left;">{1}</div>\n'+
1320 ' <div style="float:left;">{1}</div>\n'+
1321 ' <input type="hidden" value="{2}" name="review_members" />\n'+
1321 ' <input type="hidden" value="{2}" name="review_members" />\n'+
1322 ' <div class="reviewer_member_remove action_button" onclick="removeReviewMember({2})">\n'+
1322 ' <div class="reviewer_member_remove action_button" onclick="removeReviewMember({2})">\n'+
1323 ' <i class="icon-minus-circled"></i>\n'+
1323 ' <i class="icon-minus-circled"></i>\n'+
1324 ' </div> (add not saved)\n'+
1324 ' </div> (add not saved)\n'+
1325 ' </div>\n'+
1325 ' </div>\n'+
1326 ' </li>\n'
1326 ' </li>\n'
1327 ).format(gravatarelm, displayname, id);
1327 ).format(gravatarelm, displayname, id);
1328 // check if we don't have this ID already in
1328 // check if we don't have this ID already in
1329 var ids = [];
1329 var ids = [];
1330 $('#review_members').find('li').each(function() {
1330 $('#review_members').find('li').each(function() {
1331 ids.push(this.id);
1331 ids.push(this.id);
1332 });
1332 });
1333 if(ids.indexOf('reviewer_'+id) == -1){
1333 if(ids.indexOf('reviewer_'+id) == -1){
1334 //only add if it's not there
1334 //only add if it's not there
1335 $('#review_members').append(element);
1335 $('#review_members').append(element);
1336 }
1336 }
1337 }
1337 }
1338
1338
1339 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1339 var removeReviewMember = function(reviewer_id, repo_name, pull_request_id){
1340 var $li = $('#reviewer_{0}'.format(reviewer_id));
1340 var $li = $('#reviewer_{0}'.format(reviewer_id));
1341 $li.find('div div').css("text-decoration", "line-through");
1341 $li.find('div div').css("text-decoration", "line-through");
1342 $li.find('input').prop('name', 'review_members_removed');
1342 $li.find('input').prop('name', 'review_members_removed');
1343 $li.find('.reviewer_member_remove').replaceWith('&nbsp;(remove not saved)');
1343 $li.find('.reviewer_member_remove').replaceWith('&nbsp;(remove not saved)');
1344 }
1344 }
1345
1345
1346 /* activate auto completion of users as PR reviewers */
1346 /* activate auto completion of users as PR reviewers */
1347 var PullRequestAutoComplete = function ($inputElement, $container, users_list) {
1347 var PullRequestAutoComplete = function ($inputElement, $container, users_list) {
1348
1348
1349 var matchUsers = function (sQuery) {
1349 var matchUsers = function (sQuery) {
1350 return autocompleteMatchUsers(sQuery, users_list);
1350 return autocompleteMatchUsers(sQuery, users_list);
1351 };
1351 };
1352
1352
1353 var reviewerAC = autocompleteCreate($inputElement, $container, matchUsers);
1353 var reviewerAC = autocompleteCreate($inputElement, $container, matchUsers);
1354 reviewerAC.suppressInputUpdate = true;
1354 reviewerAC.suppressInputUpdate = true;
1355
1355
1356 // Handler for selection of an entry
1356 // Handler for selection of an entry
1357 if(reviewerAC.itemSelectEvent){
1357 if(reviewerAC.itemSelectEvent){
1358 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1358 reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) {
1359 var myAC = aArgs[0]; // reference back to the AC instance
1359 var myAC = aArgs[0]; // reference back to the AC instance
1360 var elLI = aArgs[1]; // reference to the selected LI element
1360 var elLI = aArgs[1]; // reference to the selected LI element
1361 var oData = aArgs[2]; // object literal of selected item's result data
1361 var oData = aArgs[2]; // object literal of selected item's result data
1362
1362
1363 addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
1363 addReviewMember(oData.id, oData.fname, oData.lname, oData.nname,
1364 oData.gravatar_lnk, oData.gravatar_size);
1364 oData.gravatar_lnk, oData.gravatar_size);
1365 myAC.getInputEl().value = '';
1365 myAC.getInputEl().value = '';
1366 });
1366 });
1367 }
1367 }
1368 }
1368 }
1369
1369
1370 /**
1370 /**
1371 * Activate .quick_repo_menu
1371 * Activate .quick_repo_menu
1372 */
1372 */
1373 var quick_repo_menu = function(){
1373 var quick_repo_menu = function(){
1374 $(".quick_repo_menu").mouseenter(function(e) {
1374 $(".quick_repo_menu").mouseenter(function(e) {
1375 var $menu = $(e.currentTarget).children().first().children().first();
1375 var $menu = $(e.currentTarget).children().first().children().first();
1376 if($menu.hasClass('hidden')){
1376 if($menu.hasClass('hidden')){
1377 $menu.removeClass('hidden').addClass('active');
1377 $menu.removeClass('hidden').addClass('active');
1378 $(e.currentTarget).removeClass('hidden').addClass('active');
1378 $(e.currentTarget).removeClass('hidden').addClass('active');
1379 }
1379 }
1380 });
1380 });
1381 $(".quick_repo_menu").mouseleave(function(e) {
1381 $(".quick_repo_menu").mouseleave(function(e) {
1382 var $menu = $(e.currentTarget).children().first().children().first();
1382 var $menu = $(e.currentTarget).children().first().children().first();
1383 if($menu.hasClass('active')){
1383 if($menu.hasClass('active')){
1384 $menu.removeClass('active').addClass('hidden');
1384 $menu.removeClass('active').addClass('hidden');
1385 $(e.currentTarget).removeClass('active').addClass('hidden');
1385 $(e.currentTarget).removeClass('active').addClass('hidden');
1386 }
1386 }
1387 });
1387 });
1388 };
1388 };
1389
1389
1390
1390
1391 var addPermAction = function(_html, users_list, groups_list){
1391 var addPermAction = function(_html, users_list, groups_list){
1392 var $last_node = $('.last_new_member').last(); // empty tr between last and add
1392 var $last_node = $('.last_new_member').last(); // empty tr between last and add
1393 var next_id = $('.new_members').length;
1393 var next_id = $('.new_members').length;
1394 $last_node.before($('<tr class="new_members">').append(_html.format(next_id)));
1394 $last_node.before($('<tr class="new_members">').append(_html.format(next_id)));
1395 MembersAutoComplete($("#perm_new_member_name_"+next_id),
1395 MembersAutoComplete($("#perm_new_member_name_"+next_id),
1396 $("#perm_container_"+next_id), users_list, groups_list);
1396 $("#perm_container_"+next_id), users_list, groups_list);
1397 }
1397 }
1398
1398
1399 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
1399 function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) {
1400 var success = function (o) {
1400 var success = function (o) {
1401 $('#' + field_id).remove();
1401 $('#' + field_id).remove();
1402 };
1402 };
1403 var failure = function (o) {
1403 var failure = function (o) {
1404 alert(_TM['Failed to revoke permission'] + ": " + o.status);
1404 alert(_TM['Failed to revoke permission'] + ": " + o.status);
1405 };
1405 };
1406 var query_params = {
1406 var query_params = {
1407 '_method': 'delete'
1407 '_method': 'delete'
1408 }
1408 }
1409 // put extra data into POST
1409 // put extra data into POST
1410 if (extra_data !== undefined && (typeof extra_data === 'object')){
1410 if (extra_data !== undefined && (typeof extra_data === 'object')){
1411 for(var k in extra_data){
1411 for(var k in extra_data){
1412 query_params[k] = extra_data[k];
1412 query_params[k] = extra_data[k];
1413 }
1413 }
1414 }
1414 }
1415
1415
1416 if (obj_type=='user'){
1416 if (obj_type=='user'){
1417 query_params['user_id'] = obj_id;
1417 query_params['user_id'] = obj_id;
1418 query_params['obj_type'] = 'user';
1418 query_params['obj_type'] = 'user';
1419 }
1419 }
1420 else if (obj_type=='user_group'){
1420 else if (obj_type=='user_group'){
1421 query_params['user_group_id'] = obj_id;
1421 query_params['user_group_id'] = obj_id;
1422 query_params['obj_type'] = 'user_group';
1422 query_params['obj_type'] = 'user_group';
1423 }
1423 }
1424
1424
1425 ajaxPOST(url, query_params, success, failure);
1425 ajaxPOST(url, query_params, success, failure);
1426 };
1426 };
1427
1427
1428 /* Multi selectors */
1428 /* Multi selectors */
1429
1429
1430 var MultiSelectWidget = function(selected_id, available_id, form_id){
1430 var MultiSelectWidget = function(selected_id, available_id, form_id){
1431 var $availableselect = $('#' + available_id);
1431 var $availableselect = $('#' + available_id);
1432 var $selectedselect = $('#' + selected_id);
1432 var $selectedselect = $('#' + selected_id);
1433
1433
1434 //fill available only with those not in selected
1434 //fill available only with those not in selected
1435 var $selectedoptions = $selectedselect.children('option');
1435 var $selectedoptions = $selectedselect.children('option');
1436 $availableselect.children('option').filter(function(i, e){
1436 $availableselect.children('option').filter(function(i, e){
1437 for(var j = 0, node; node = $selectedoptions[j]; j++){
1437 for(var j = 0, node; node = $selectedoptions[j]; j++){
1438 if(node.value == e.value){
1438 if(node.value == e.value){
1439 return true;
1439 return true;
1440 }
1440 }
1441 }
1441 }
1442 return false;
1442 return false;
1443 }).remove();
1443 }).remove();
1444
1444
1445 $('#add_element').click(function(e){
1445 $('#add_element').click(function(e){
1446 $selectedselect.append($availableselect.children('option:selected'));
1446 $selectedselect.append($availableselect.children('option:selected'));
1447 });
1447 });
1448 $('#remove_element').click(function(e){
1448 $('#remove_element').click(function(e){
1449 $availableselect.append($selectedselect.children('option:selected'));
1449 $availableselect.append($selectedselect.children('option:selected'));
1450 });
1450 });
1451
1451
1452 $('#'+form_id).submit(function(){
1452 $('#'+form_id).submit(function(){
1453 $selectedselect.children('option').each(function(i, e){
1453 $selectedselect.children('option').each(function(i, e){
1454 e.selected = 'selected';
1454 e.selected = 'selected';
1455 });
1455 });
1456 });
1456 });
1457 }
1457 }
1458
1458
1459
1459
1460 /**
1460 /**
1461 Branch Sorting callback for select2, modifying the filtered result so prefix
1461 Branch Sorting callback for select2, modifying the filtered result so prefix
1462 matches come before matches in the line.
1462 matches come before matches in the line.
1463 **/
1463 **/
1464 var branchSort = function(results, container, query) {
1464 var branchSort = function(results, container, query) {
1465 if (query.term) {
1465 if (query.term) {
1466 return results.sort(function (a, b) {
1466 return results.sort(function (a, b) {
1467 // Put closed branches after open ones (a bit of a hack ...)
1467 // Put closed branches after open ones (a bit of a hack ...)
1468 var aClosed = a.text.indexOf("(closed)") > -1,
1468 var aClosed = a.text.indexOf("(closed)") > -1,
1469 bClosed = b.text.indexOf("(closed)") > -1;
1469 bClosed = b.text.indexOf("(closed)") > -1;
1470 if (aClosed && !bClosed) {
1470 if (aClosed && !bClosed) {
1471 return 1;
1471 return 1;
1472 }
1472 }
1473 if (bClosed && !aClosed) {
1473 if (bClosed && !aClosed) {
1474 return -1;
1474 return -1;
1475 }
1475 }
1476
1476
1477 // Put early (especially prefix) matches before later matches
1477 // Put early (especially prefix) matches before later matches
1478 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1478 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1479 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1479 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1480 if (aPos < bPos) {
1480 if (aPos < bPos) {
1481 return -1;
1481 return -1;
1482 }
1482 }
1483 if (bPos < aPos) {
1483 if (bPos < aPos) {
1484 return 1;
1484 return 1;
1485 }
1485 }
1486
1486
1487 // Default sorting
1487 // Default sorting
1488 if (a.text > b.text) {
1488 if (a.text > b.text) {
1489 return 1;
1489 return 1;
1490 }
1490 }
1491 if (a.text < b.text) {
1491 if (a.text < b.text) {
1492 return -1;
1492 return -1;
1493 }
1493 }
1494 return 0;
1494 return 0;
1495 });
1495 });
1496 }
1496 }
1497 return results;
1497 return results;
1498 };
1498 };
1499
1499
1500 var prefixFirstSort = function(results, container, query) {
1500 var prefixFirstSort = function(results, container, query) {
1501 if (query.term) {
1501 if (query.term) {
1502 return results.sort(function (a, b) {
1502 return results.sort(function (a, b) {
1503 // if parent node, no sorting
1503 // if parent node, no sorting
1504 if (a.children != undefined || b.children != undefined) {
1504 if (a.children != undefined || b.children != undefined) {
1505 return 0;
1505 return 0;
1506 }
1506 }
1507
1507
1508 // Put prefix matches before matches in the line
1508 // Put prefix matches before matches in the line
1509 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1509 var aPos = a.text.toLowerCase().indexOf(query.term.toLowerCase()),
1510 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1510 bPos = b.text.toLowerCase().indexOf(query.term.toLowerCase());
1511 if (aPos === 0 && bPos !== 0) {
1511 if (aPos === 0 && bPos !== 0) {
1512 return -1;
1512 return -1;
1513 }
1513 }
1514 if (bPos === 0 && aPos !== 0) {
1514 if (bPos === 0 && aPos !== 0) {
1515 return 1;
1515 return 1;
1516 }
1516 }
1517
1517
1518 // Default sorting
1518 // Default sorting
1519 if (a.text > b.text) {
1519 if (a.text > b.text) {
1520 return 1;
1520 return 1;
1521 }
1521 }
1522 if (a.text < b.text) {
1522 if (a.text < b.text) {
1523 return -1;
1523 return -1;
1524 }
1524 }
1525 return 0;
1525 return 0;
1526 });
1526 });
1527 }
1527 }
1528 return results;
1528 return results;
1529 };
1529 };
1530
1530
1531 /* Helper for jQuery DataTables */
1531 /* Helper for jQuery DataTables */
1532
1532
1533 var updateRowCountCallback = function updateRowCountCallback($elem, onlyDisplayed) {
1533 var updateRowCountCallback = function updateRowCountCallback($elem, onlyDisplayed) {
1534 return function drawCallback() {
1534 return function drawCallback() {
1535 var info = this.api().page.info(),
1535 var info = this.api().page.info(),
1536 count = onlyDisplayed === true ? info.recordsDisplay : info.recordsTotal;
1536 count = onlyDisplayed === true ? info.recordsDisplay : info.recordsTotal;
1537 $elem.html(count);
1537 $elem.html(count);
1538 }
1538 }
1539 };
1539 };
@@ -1,58 +1,59 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%block name="title">
4 <%block name="title">
5 ${_('My Notifications')} ${c.authuser.username}
5 ${_('My Notifications')} ${c.authuser.username}
6 </%block>
6 </%block>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${_('My Notifications')}
9 ${_('My Notifications')}
10 </%def>
10 </%def>
11
11
12 <%block name="header_menu">
12 <%block name="header_menu">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%block>
14 </%block>
15
15
16 <%def name="main()">
16 <%def name="main()">
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22
22
23 <div style="padding:14px 18px;text-align: right;float:left">
23 <div style="padding:14px 18px;text-align: right;float:left">
24 <span id='all' class="btn btn-small"><a href="${h.url.current()}">${_('All')}</a></span>
24 <span id='all' class="btn btn-small"><a href="${h.url.current()}">${_('All')}</a></span>
25 <span id='comment' class="btn btn-small"><a href="${h.url.current(type=c.comment_type)}">${_('Comments')}</a></span>
25 <span id='comment' class="btn btn-small"><a href="${h.url.current(type=c.comment_type)}">${_('Comments')}</a></span>
26 <span id='pull_request' class="btn btn-small"><a href="${h.url.current(type=c.pull_request_type)}">${_('Pull Requests')}</a></span>
26 <span id='pull_request' class="btn btn-small"><a href="${h.url.current(type=c.pull_request_type)}">${_('Pull Requests')}</a></span>
27 </div>
27 </div>
28 %if c.notifications:
28 %if c.notifications:
29 <div style="padding:14px 18px;text-align: right;float:right">
29 <div style="padding:14px 18px;text-align: right;float:right">
30 <span id='mark_all_read' class="btn btn-small">${_('Mark All Read')}</span>
30 <span id='mark_all_read' class="btn btn-small">${_('Mark All Read')}</span>
31 </div>
31 </div>
32 %endif
32 %endif
33 <div id='notification_data'>
33 <div id='notification_data'>
34 <%include file='notifications_data.html'/>
34 <%include file='notifications_data.html'/>
35 </div>
35 </div>
36 </div>
36 </div>
37 <script type="text/javascript">
37 <script type="text/javascript">
38 var url_delete = "${url('notification_delete', notification_id='__NOTIFICATION_ID__')}";
38 var url_action = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
39 var url_action = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
39 var run = function(){
40 var run = function(){
40 $('.delete-notification').click(function(e){
41 $('.delete-notification').click(function(e){
41 var notification_id = e.currentTarget.id;
42 var notification_id = e.currentTarget.id;
42 deleteNotification(url_action,notification_id);
43 deleteNotification(url_delete,notification_id);
43 });
44 });
44 $('.read-notification').click(function(e){
45 $('.read-notification').click(function(e){
45 var notification_id = e.currentTarget.id;
46 var notification_id = e.currentTarget.id;
46 readNotification(url_action,notification_id);
47 readNotification(url_action,notification_id);
47 });
48 });
48 }
49 }
49 run();
50 run();
50 $('#mark_all_read').click(function(){
51 $('#mark_all_read').click(function(){
51 var url = "${h.url('notifications_mark_all_read', **request.GET.mixed())}";
52 var url = "${h.url('notifications_mark_all_read', **request.GET.mixed())}";
52 asynchtml(url, $('#notification_data'), function(){run();});
53 asynchtml(url, $('#notification_data'), function(){run();});
53 });
54 });
54
55
55 var current_filter = "${c.current_filter}";
56 var current_filter = "${c.current_filter}";
56 $('#'+current_filter).addClass('active');
57 $('#'+current_filter).addClass('active');
57 </script>
58 </script>
58 </%def>
59 </%def>
@@ -1,52 +1,52 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%block name="title">
4 <%block name="title">
5 ${_('Show Notification')} ${c.authuser.username}
5 ${_('Show Notification')} ${c.authuser.username}
6 </%block>
6 </%block>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Notifications'),h.url('notifications'))}
9 ${h.link_to(_('Notifications'),h.url('notifications'))}
10 &raquo;
10 &raquo;
11 ${_('Show Notification')}
11 ${_('Show Notification')}
12 </%def>
12 </%def>
13
13
14 <%block name="header_menu">
14 <%block name="header_menu">
15 ${self.menu('admin')}
15 ${self.menu('admin')}
16 </%block>
16 </%block>
17
17
18 <%def name="main()">
18 <%def name="main()">
19 <div class="box">
19 <div class="box">
20 <!-- box / title -->
20 <!-- box / title -->
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 </div>
23 </div>
24 <div class="table">
24 <div class="table">
25 <div id="notification_${c.notification.notification_id}">
25 <div id="notification_${c.notification.notification_id}">
26 <div class="notification-header">
26 <div class="notification-header">
27 ${h.gravatar_div(c.notification.created_by_user.email, size=24)}
27 ${h.gravatar_div(c.notification.created_by_user.email, size=24)}
28 <div class="desc">
28 <div class="desc">
29 ${c.notification.description}
29 ${c.notification.description}
30 </div>
30 </div>
31 <div class="delete-notifications">
31 <div class="delete-notifications">
32 <span id="${c.notification.notification_id}" class="delete-notification action"><i class="icon-minus-circled" id="yui-gen24"></i></span>
32 <span id="${c.notification.notification_id}" class="delete-notification action"><i class="icon-minus-circled" id="yui-gen24"></i></span>
33 </div>
33 </div>
34 </div>
34 </div>
35 <div class="notification-body">
35 <div class="notification-body">
36 <div class="notification-subject">${h.literal(c.notification.subject)}</div>
36 <div class="notification-subject">${h.literal(c.notification.subject)}</div>
37 %if c.notification.body:
37 %if c.notification.body:
38 ${h.render_w_mentions(c.notification.body)}
38 ${h.render_w_mentions(c.notification.body)}
39 %endif
39 %endif
40 </div>
40 </div>
41 </div>
41 </div>
42 </div>
42 </div>
43 </div>
43 </div>
44 <script type="text/javascript">
44 <script type="text/javascript">
45 var url = "${url('notification', notification_id='__NOTIFICATION_ID__')}";
45 var url = "${url('notification_delete', notification_id='__NOTIFICATION_ID__')}";
46 var main = "${url('notifications')}";
46 var main = "${url('notifications')}";
47 $('.delete-notification').click(function(e){
47 $('.delete-notification').click(function(e){
48 var notification_id = e.currentTarget.id;
48 var notification_id = e.currentTarget.id;
49 deleteNotification(url,notification_id,[function(){window.location=main}]);
49 deleteNotification(url,notification_id,[function(){window.location=main}]);
50 });
50 });
51 </script>
51 </script>
52 </%def>
52 </%def>
@@ -1,162 +1,162 b''
1 from kallithea.tests import *
1 from kallithea.tests import *
2 from kallithea.model.db import User
2 from kallithea.model.db import User
3
3
4 from kallithea.model.user import UserModel
4 from kallithea.model.user import UserModel
5 from kallithea.model.notification import NotificationModel
5 from kallithea.model.notification import NotificationModel
6 from kallithea.model.meta import Session
6 from kallithea.model.meta import Session
7 from kallithea.lib import helpers as h
7 from kallithea.lib import helpers as h
8
8
9
9
10 class TestNotificationsController(TestController):
10 class TestNotificationsController(TestController):
11 def setup_method(self, method):
11 def setup_method(self, method):
12 self.remove_all_notifications()
12 self.remove_all_notifications()
13
13
14 def test_index(self, create_test_user):
14 def test_index(self, create_test_user):
15 self.log_user()
15 self.log_user()
16
16
17 u1 = create_test_user(dict(username='u1', password='qweqwe',
17 u1 = create_test_user(dict(username='u1', password='qweqwe',
18 email='u1@example.com',
18 email='u1@example.com',
19 firstname=u'u1', lastname=u'u1',
19 firstname=u'u1', lastname=u'u1',
20 active=True))
20 active=True))
21 u1 = u1.user_id
21 u1 = u1.user_id
22 Session().commit()
22 Session().commit()
23
23
24 response = self.app.get(url('notifications'))
24 response = self.app.get(url('notifications'))
25 response.mustcontain('<div class="table">No notifications here yet</div>')
25 response.mustcontain('<div class="table">No notifications here yet</div>')
26
26
27 cur_user = self._get_logged_user()
27 cur_user = self._get_logged_user()
28 notif = NotificationModel().create(created_by=u1, subject=u'test_notification_1',
28 notif = NotificationModel().create(created_by=u1, subject=u'test_notification_1',
29 body=u'notification_1', recipients=[cur_user])
29 body=u'notification_1', recipients=[cur_user])
30 Session().commit()
30 Session().commit()
31 response = self.app.get(url('notifications'))
31 response = self.app.get(url('notifications'))
32 response.mustcontain('id="notification_%s"' % notif.notification_id)
32 response.mustcontain('id="notification_%s"' % notif.notification_id)
33
33
34 def test_delete(self, create_test_user):
34 def test_delete(self, create_test_user):
35 self.log_user()
35 self.log_user()
36 cur_user = self._get_logged_user()
36 cur_user = self._get_logged_user()
37
37
38 u1 = create_test_user(dict(username='u1', password='qweqwe',
38 u1 = create_test_user(dict(username='u1', password='qweqwe',
39 email='u1@example.com',
39 email='u1@example.com',
40 firstname=u'u1', lastname=u'u1',
40 firstname=u'u1', lastname=u'u1',
41 active=True))
41 active=True))
42 u2 = create_test_user(dict(username='u2', password='qweqwe',
42 u2 = create_test_user(dict(username='u2', password='qweqwe',
43 email='u2@example.com',
43 email='u2@example.com',
44 firstname=u'u2', lastname=u'u2',
44 firstname=u'u2', lastname=u'u2',
45 active=True))
45 active=True))
46
46
47 # make notifications
47 # make notifications
48 notification = NotificationModel().create(created_by=cur_user,
48 notification = NotificationModel().create(created_by=cur_user,
49 subject=u'test',
49 subject=u'test',
50 body=u'hi there',
50 body=u'hi there',
51 recipients=[cur_user, u1, u2])
51 recipients=[cur_user, u1, u2])
52 Session().commit()
52 Session().commit()
53 u1 = User.get(u1.user_id)
53 u1 = User.get(u1.user_id)
54 u2 = User.get(u2.user_id)
54 u2 = User.get(u2.user_id)
55
55
56 # check DB
56 # check DB
57 get_notif = lambda un: [x.notification for x in un]
57 get_notif = lambda un: [x.notification for x in un]
58 assert get_notif(cur_user.notifications) == [notification]
58 assert get_notif(cur_user.notifications) == [notification]
59 assert get_notif(u1.notifications) == [notification]
59 assert get_notif(u1.notifications) == [notification]
60 assert get_notif(u2.notifications) == [notification]
60 assert get_notif(u2.notifications) == [notification]
61 cur_usr_id = cur_user.user_id
61 cur_usr_id = cur_user.user_id
62
62
63 response = self.app.post(
63 response = self.app.post(
64 url('notification', notification_id=notification.notification_id),
64 url('notification_delete', notification_id=notification.notification_id),
65 params={'_method': 'delete', '_authentication_token': self.authentication_token()})
65 params={'_authentication_token': self.authentication_token()})
66 assert response.body == 'ok'
66 assert response.body == 'ok'
67
67
68 cur_user = User.get(cur_usr_id)
68 cur_user = User.get(cur_usr_id)
69 assert cur_user.notifications == []
69 assert cur_user.notifications == []
70
70
71 def test_show(self, create_test_user):
71 def test_show(self, create_test_user):
72 self.log_user()
72 self.log_user()
73 cur_user = self._get_logged_user()
73 cur_user = self._get_logged_user()
74 u1 = create_test_user(dict(username='u1', password='qweqwe',
74 u1 = create_test_user(dict(username='u1', password='qweqwe',
75 email='u1@example.com',
75 email='u1@example.com',
76 firstname=u'u1', lastname=u'u1',
76 firstname=u'u1', lastname=u'u1',
77 active=True))
77 active=True))
78 u2 = create_test_user(dict(username='u2', password='qweqwe',
78 u2 = create_test_user(dict(username='u2', password='qweqwe',
79 email='u2@example.com',
79 email='u2@example.com',
80 firstname=u'u2', lastname=u'u2',
80 firstname=u'u2', lastname=u'u2',
81 active=True))
81 active=True))
82 Session().commit()
82 Session().commit()
83
83
84 subject = u'test'
84 subject = u'test'
85 notif_body = u'hi there'
85 notif_body = u'hi there'
86 notification = NotificationModel().create(created_by=cur_user,
86 notification = NotificationModel().create(created_by=cur_user,
87 subject=subject,
87 subject=subject,
88 body=notif_body,
88 body=notif_body,
89 recipients=[cur_user, u1, u2])
89 recipients=[cur_user, u1, u2])
90
90
91 response = self.app.get(url('notification',
91 response = self.app.get(url('notification',
92 notification_id=notification.notification_id))
92 notification_id=notification.notification_id))
93
93
94 response.mustcontain(subject)
94 response.mustcontain(subject)
95 response.mustcontain(notif_body)
95 response.mustcontain(notif_body)
96
96
97 def test_description_with_age(self):
97 def test_description_with_age(self):
98 self.log_user()
98 self.log_user()
99 cur_user = self._get_logged_user()
99 cur_user = self._get_logged_user()
100 subject = u'test'
100 subject = u'test'
101 notify_body = u'hi there'
101 notify_body = u'hi there'
102 notification = NotificationModel().create(created_by = cur_user,
102 notification = NotificationModel().create(created_by = cur_user,
103 subject = subject,
103 subject = subject,
104 body = notify_body)
104 body = notify_body)
105
105
106 description = NotificationModel().make_description(notification)
106 description = NotificationModel().make_description(notification)
107 assert description == "{0} sent message {1}".format(
107 assert description == "{0} sent message {1}".format(
108 cur_user.username,
108 cur_user.username,
109 h.age(notification.created_on)
109 h.age(notification.created_on)
110 )
110 )
111
111
112 def test_description_with_datetime(self):
112 def test_description_with_datetime(self):
113 self.log_user()
113 self.log_user()
114 cur_user = self._get_logged_user()
114 cur_user = self._get_logged_user()
115 subject = u'test'
115 subject = u'test'
116 notify_body = u'hi there'
116 notify_body = u'hi there'
117 notification = NotificationModel().create(created_by = cur_user,
117 notification = NotificationModel().create(created_by = cur_user,
118 subject = subject,
118 subject = subject,
119 body = notify_body)
119 body = notify_body)
120
120
121 description = NotificationModel().make_description(notification, False)
121 description = NotificationModel().make_description(notification, False)
122 assert description == "{0} sent message at {1}".format(
122 assert description == "{0} sent message at {1}".format(
123 cur_user.username,
123 cur_user.username,
124 h.fmt_date(notification.created_on)
124 h.fmt_date(notification.created_on)
125 )
125 )
126
126
127 def test_mark_all_read(self, create_test_user):
127 def test_mark_all_read(self, create_test_user):
128 self.log_user()
128 self.log_user()
129 u0 = self._get_logged_user()
129 u0 = self._get_logged_user()
130 u1 = create_test_user(dict(username='u1', password='qweqwe',
130 u1 = create_test_user(dict(username='u1', password='qweqwe',
131 email='u1@example.com',
131 email='u1@example.com',
132 firstname=u'u1', lastname=u'u1',
132 firstname=u'u1', lastname=u'u1',
133 active=True))
133 active=True))
134 u2 = create_test_user(dict(username='u2', password='qweqwe',
134 u2 = create_test_user(dict(username='u2', password='qweqwe',
135 email='u2@example.com',
135 email='u2@example.com',
136 firstname=u'u2', lastname=u'u2',
136 firstname=u'u2', lastname=u'u2',
137 active=True))
137 active=True))
138 notif = NotificationModel().create(created_by=u1,
138 notif = NotificationModel().create(created_by=u1,
139 subject=u'subject',
139 subject=u'subject',
140 body=u'body',
140 body=u'body',
141 recipients=[u0, u2])
141 recipients=[u0, u2])
142 u0_id, u1_id, u2_id = u0.user_id, u1.user_id, u2.user_id
142 u0_id, u1_id, u2_id = u0.user_id, u1.user_id, u2.user_id
143
143
144 assert [n.read for n in u0.notifications] == [False]
144 assert [n.read for n in u0.notifications] == [False]
145 assert u1.notifications == []
145 assert u1.notifications == []
146 assert [n.read for n in u2.notifications] == [False]
146 assert [n.read for n in u2.notifications] == [False]
147
147
148 # Mark all read for current user.
148 # Mark all read for current user.
149
149
150 response = self.app.get(url('notifications_mark_all_read'), # TODO: should be POST
150 response = self.app.get(url('notifications_mark_all_read'), # TODO: should be POST
151 extra_environ=dict(HTTP_X_PARTIAL_XHR='1'))
151 extra_environ=dict(HTTP_X_PARTIAL_XHR='1'))
152
152
153 assert response.status_int == 200
153 assert response.status_int == 200
154 response.mustcontain('id="notification_%s"' % notif.notification_id)
154 response.mustcontain('id="notification_%s"' % notif.notification_id)
155
155
156 u0 = User.get(u0_id)
156 u0 = User.get(u0_id)
157 u1 = User.get(u1_id)
157 u1 = User.get(u1_id)
158 u2 = User.get(u2_id)
158 u2 = User.get(u2_id)
159
159
160 assert [n.read for n in u0.notifications] == [True]
160 assert [n.read for n in u0.notifications] == [True]
161 assert u1.notifications == []
161 assert u1.notifications == []
162 assert [n.read for n in u2.notifications] == [False]
162 assert [n.read for n in u2.notifications] == [False]
General Comments 0
You need to be logged in to leave comments. Login now