##// END OF EJS Templates
refactored url.resource to full definition of routes...
marcink -
r3866:1fdec7e3 beta
parent child Browse files
Show More
@@ -1,683 +1,706 b''
1 """
1 """
2 Routes configuration
2 Routes configuration
3
3
4 The more specific and detailed routes should be defined first so they
4 The more specific and detailed routes should be defined first so they
5 may take precedent over the more generic routes. For more information
5 may take precedent over the more generic routes. For more information
6 refer to the routes manual at http://routes.groovie.org/docs/
6 refer to the routes manual at http://routes.groovie.org/docs/
7 """
7 """
8 from __future__ import with_statement
8 from __future__ import with_statement
9 from routes import Mapper
9 from routes import Mapper
10
10
11 # prefix for non repository related links needs to be prefixed with `/`
11 # prefix for non repository related links needs to be prefixed with `/`
12 ADMIN_PREFIX = '/_admin'
12 ADMIN_PREFIX = '/_admin'
13
13
14
14
15 def make_map(config):
15 def make_map(config):
16 """Create, configure and return the routes Mapper"""
16 """Create, configure and return the routes Mapper"""
17 rmap = Mapper(directory=config['pylons.paths']['controllers'],
17 rmap = Mapper(directory=config['pylons.paths']['controllers'],
18 always_scan=config['debug'])
18 always_scan=config['debug'])
19 rmap.minimization = False
19 rmap.minimization = False
20 rmap.explicit = False
20 rmap.explicit = False
21
21
22 from rhodecode.lib.utils import is_valid_repo
22 from rhodecode.lib.utils import is_valid_repo
23 from rhodecode.lib.utils import is_valid_repos_group
23 from rhodecode.lib.utils import is_valid_repos_group
24
24
25 def check_repo(environ, match_dict):
25 def check_repo(environ, match_dict):
26 """
26 """
27 check for valid repository for proper 404 handling
27 check for valid repository for proper 404 handling
28
28
29 :param environ:
29 :param environ:
30 :param match_dict:
30 :param match_dict:
31 """
31 """
32 from rhodecode.model.db import Repository
32 from rhodecode.model.db import Repository
33 repo_name = match_dict.get('repo_name')
33 repo_name = match_dict.get('repo_name')
34
34
35 if match_dict.get('f_path'):
35 if match_dict.get('f_path'):
36 #fix for multiple initial slashes that causes errors
36 #fix for multiple initial slashes that causes errors
37 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
37 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
38
38
39 try:
39 try:
40 by_id = repo_name.split('_')
40 by_id = repo_name.split('_')
41 if len(by_id) == 2 and by_id[1].isdigit() and by_id[0] == '':
41 if len(by_id) == 2 and by_id[1].isdigit() and by_id[0] == '':
42 repo_name = Repository.get(by_id[1]).repo_name
42 repo_name = Repository.get(by_id[1]).repo_name
43 match_dict['repo_name'] = repo_name
43 match_dict['repo_name'] = repo_name
44 except Exception:
44 except Exception:
45 pass
45 pass
46
46
47 return is_valid_repo(repo_name, config['base_path'])
47 return is_valid_repo(repo_name, config['base_path'])
48
48
49 def check_group(environ, match_dict):
49 def check_group(environ, match_dict):
50 """
50 """
51 check for valid repository group for proper 404 handling
51 check for valid repository group for proper 404 handling
52
52
53 :param environ:
53 :param environ:
54 :param match_dict:
54 :param match_dict:
55 """
55 """
56 repos_group_name = match_dict.get('group_name')
56 repos_group_name = match_dict.get('group_name')
57 return is_valid_repos_group(repos_group_name, config['base_path'])
57 return is_valid_repos_group(repos_group_name, config['base_path'])
58
58
59 def check_group_skip_path(environ, match_dict):
59 def check_group_skip_path(environ, match_dict):
60 """
60 """
61 check for valid repository group for proper 404 handling, but skips
61 check for valid repository group for proper 404 handling, but skips
62 verification of existing path
62 verification of existing path
63
63
64 :param environ:
64 :param environ:
65 :param match_dict:
65 :param match_dict:
66 """
66 """
67 repos_group_name = match_dict.get('group_name')
67 repos_group_name = match_dict.get('group_name')
68 return is_valid_repos_group(repos_group_name, config['base_path'],
68 return is_valid_repos_group(repos_group_name, config['base_path'],
69 skip_path_check=True)
69 skip_path_check=True)
70
70
71 def check_user_group(environ, match_dict):
71 def check_user_group(environ, match_dict):
72 """
72 """
73 check for valid user group for proper 404 handling
73 check for valid user group for proper 404 handling
74
74
75 :param environ:
75 :param environ:
76 :param match_dict:
76 :param match_dict:
77 """
77 """
78 return True
78 return True
79
79
80 def check_int(environ, match_dict):
80 def check_int(environ, match_dict):
81 return match_dict.get('id').isdigit()
81 return match_dict.get('id').isdigit()
82
82
83 # The ErrorController route (handles 404/500 error pages); it should
83 # The ErrorController route (handles 404/500 error pages); it should
84 # likely stay at the top, ensuring it can always be resolved
84 # likely stay at the top, ensuring it can always be resolved
85 rmap.connect('/error/{action}', controller='error')
85 rmap.connect('/error/{action}', controller='error')
86 rmap.connect('/error/{action}/{id}', controller='error')
86 rmap.connect('/error/{action}/{id}', controller='error')
87
87
88 #==========================================================================
88 #==========================================================================
89 # CUSTOM ROUTES HERE
89 # CUSTOM ROUTES HERE
90 #==========================================================================
90 #==========================================================================
91
91
92 #MAIN PAGE
92 #MAIN PAGE
93 rmap.connect('home', '/', controller='home', action='index')
93 rmap.connect('home', '/', controller='home', action='index')
94 rmap.connect('repo_switcher', '/repos', controller='home',
94 rmap.connect('repo_switcher', '/repos', controller='home',
95 action='repo_switcher')
95 action='repo_switcher')
96 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*?}',
96 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*?}',
97 controller='home', action='branch_tag_switcher')
97 controller='home', action='branch_tag_switcher')
98 rmap.connect('bugtracker',
98 rmap.connect('bugtracker',
99 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
99 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
100 _static=True)
100 _static=True)
101 rmap.connect('rst_help',
101 rmap.connect('rst_help',
102 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
102 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
103 _static=True)
103 _static=True)
104 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
104 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
105
105
106 #ADMIN REPOSITORY REST ROUTES
106 #ADMIN REPOSITORY REST ROUTES
107 with rmap.submapper(path_prefix=ADMIN_PREFIX,
107 with rmap.submapper(path_prefix=ADMIN_PREFIX,
108 controller='admin/repos') as m:
108 controller='admin/repos') as m:
109 m.connect("repos", "/repos",
109 m.connect("repos", "/repos",
110 action="create", conditions=dict(method=["POST"]))
110 action="create", conditions=dict(method=["POST"]))
111 m.connect("repos", "/repos",
111 m.connect("repos", "/repos",
112 action="index", conditions=dict(method=["GET"]))
112 action="index", conditions=dict(method=["GET"]))
113 m.connect("formatted_repos", "/repos.{format}",
113 m.connect("formatted_repos", "/repos.{format}",
114 action="index",
114 action="index",
115 conditions=dict(method=["GET"]))
115 conditions=dict(method=["GET"]))
116 m.connect("new_repo", "/create_repository",
116 m.connect("new_repo", "/create_repository",
117 action="create_repository", conditions=dict(method=["GET"]))
117 action="create_repository", conditions=dict(method=["GET"]))
118 m.connect("/repos/{repo_name:.*?}",
118 m.connect("/repos/{repo_name:.*?}",
119 action="update", conditions=dict(method=["PUT"],
119 action="update", conditions=dict(method=["PUT"],
120 function=check_repo))
120 function=check_repo))
121 m.connect("/repos/{repo_name:.*?}",
121 m.connect("/repos/{repo_name:.*?}",
122 action="delete", conditions=dict(method=["DELETE"],
122 action="delete", conditions=dict(method=["DELETE"],
123 function=check_repo))
123 function=check_repo))
124 m.connect("formatted_edit_repo", "/repos/{repo_name:.*?}.{format}/edit",
124 m.connect("formatted_edit_repo", "/repos/{repo_name:.*?}.{format}/edit",
125 action="edit", conditions=dict(method=["GET"],
125 action="edit", conditions=dict(method=["GET"],
126 function=check_repo))
126 function=check_repo))
127 m.connect("repo", "/repos/{repo_name:.*?}",
127 m.connect("repo", "/repos/{repo_name:.*?}",
128 action="show", conditions=dict(method=["GET"],
128 action="show", conditions=dict(method=["GET"],
129 function=check_repo))
129 function=check_repo))
130 m.connect("formatted_repo", "/repos/{repo_name:.*?}.{format}",
130 m.connect("formatted_repo", "/repos/{repo_name:.*?}.{format}",
131 action="show", conditions=dict(method=["GET"],
131 action="show", conditions=dict(method=["GET"],
132 function=check_repo))
132 function=check_repo))
133 #add repo perm member
133 #add repo perm member
134 m.connect('set_repo_perm_member',
134 m.connect('set_repo_perm_member',
135 "/repos/{repo_name:.*?}/grant_perm",
135 "/repos/{repo_name:.*?}/grant_perm",
136 action="set_repo_perm_member",
136 action="set_repo_perm_member",
137 conditions=dict(method=["POST"], function=check_repo))
137 conditions=dict(method=["POST"], function=check_repo))
138
138
139 #ajax delete repo perm user
139 #ajax delete repo perm user
140 m.connect('delete_repo_perm_member',
140 m.connect('delete_repo_perm_member',
141 "/repos/{repo_name:.*?}/revoke_perm",
141 "/repos/{repo_name:.*?}/revoke_perm",
142 action="delete_repo_perm_member",
142 action="delete_repo_perm_member",
143 conditions=dict(method=["DELETE"], function=check_repo))
143 conditions=dict(method=["DELETE"], function=check_repo))
144
144
145 #settings actions
145 #settings actions
146 m.connect('repo_stats', "/repos_stats/{repo_name:.*?}",
146 m.connect('repo_stats', "/repos_stats/{repo_name:.*?}",
147 action="repo_stats", conditions=dict(method=["DELETE"],
147 action="repo_stats", conditions=dict(method=["DELETE"],
148 function=check_repo))
148 function=check_repo))
149 m.connect('repo_cache', "/repos_cache/{repo_name:.*?}",
149 m.connect('repo_cache', "/repos_cache/{repo_name:.*?}",
150 action="repo_cache", conditions=dict(method=["DELETE"],
150 action="repo_cache", conditions=dict(method=["DELETE"],
151 function=check_repo))
151 function=check_repo))
152 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*?}",
152 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*?}",
153 action="repo_public_journal", conditions=dict(method=["PUT"],
153 action="repo_public_journal", conditions=dict(method=["PUT"],
154 function=check_repo))
154 function=check_repo))
155 m.connect('repo_pull', "/repo_pull/{repo_name:.*?}",
155 m.connect('repo_pull', "/repo_pull/{repo_name:.*?}",
156 action="repo_pull", conditions=dict(method=["PUT"],
156 action="repo_pull", conditions=dict(method=["PUT"],
157 function=check_repo))
157 function=check_repo))
158 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*?}",
158 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*?}",
159 action="repo_as_fork", conditions=dict(method=["PUT"],
159 action="repo_as_fork", conditions=dict(method=["PUT"],
160 function=check_repo))
160 function=check_repo))
161 m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
161 m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
162 action="repo_locking", conditions=dict(method=["PUT"],
162 action="repo_locking", conditions=dict(method=["PUT"],
163 function=check_repo))
163 function=check_repo))
164 m.connect('toggle_locking', "/locking_toggle/{repo_name:.*?}",
164 m.connect('toggle_locking', "/locking_toggle/{repo_name:.*?}",
165 action="toggle_locking", conditions=dict(method=["GET"],
165 action="toggle_locking", conditions=dict(method=["GET"],
166 function=check_repo))
166 function=check_repo))
167
167
168 #repo fields
168 #repo fields
169 m.connect('create_repo_fields', "/repo_fields/{repo_name:.*?}/new",
169 m.connect('create_repo_fields', "/repo_fields/{repo_name:.*?}/new",
170 action="create_repo_field", conditions=dict(method=["PUT"],
170 action="create_repo_field", conditions=dict(method=["PUT"],
171 function=check_repo))
171 function=check_repo))
172
172
173 m.connect('delete_repo_fields', "/repo_fields/{repo_name:.*?}/{field_id}",
173 m.connect('delete_repo_fields', "/repo_fields/{repo_name:.*?}/{field_id}",
174 action="delete_repo_field", conditions=dict(method=["DELETE"],
174 action="delete_repo_field", conditions=dict(method=["DELETE"],
175 function=check_repo))
175 function=check_repo))
176
176
177 with rmap.submapper(path_prefix=ADMIN_PREFIX,
177 with rmap.submapper(path_prefix=ADMIN_PREFIX,
178 controller='admin/repos_groups') as m:
178 controller='admin/repos_groups') as m:
179 m.connect("repos_groups", "/repos_groups",
179 m.connect("repos_groups", "/repos_groups",
180 action="create", conditions=dict(method=["POST"]))
180 action="create", conditions=dict(method=["POST"]))
181 m.connect("repos_groups", "/repos_groups",
181 m.connect("repos_groups", "/repos_groups",
182 action="index", conditions=dict(method=["GET"]))
182 action="index", conditions=dict(method=["GET"]))
183 m.connect("formatted_repos_groups", "/repos_groups.{format}",
183 m.connect("formatted_repos_groups", "/repos_groups.{format}",
184 action="index", conditions=dict(method=["GET"]))
184 action="index", conditions=dict(method=["GET"]))
185 m.connect("new_repos_group", "/repos_groups/new",
185 m.connect("new_repos_group", "/repos_groups/new",
186 action="new", conditions=dict(method=["GET"]))
186 action="new", conditions=dict(method=["GET"]))
187 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
187 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
188 action="new", conditions=dict(method=["GET"]))
188 action="new", conditions=dict(method=["GET"]))
189 m.connect("update_repos_group", "/repos_groups/{group_name:.*?}",
189 m.connect("update_repos_group", "/repos_groups/{group_name:.*?}",
190 action="update", conditions=dict(method=["PUT"],
190 action="update", conditions=dict(method=["PUT"],
191 function=check_group))
191 function=check_group))
192 #add repo group perm member
192 #add repo group perm member
193 m.connect('set_repo_group_perm_member',
193 m.connect('set_repo_group_perm_member',
194 "/repos_groups/{group_name:.*?}/grant_perm",
194 "/repos_groups/{group_name:.*?}/grant_perm",
195 action="set_repo_group_perm_member",
195 action="set_repo_group_perm_member",
196 conditions=dict(method=["POST"], function=check_group))
196 conditions=dict(method=["POST"], function=check_group))
197
197
198 #ajax delete repo group perm
198 #ajax delete repo group perm
199 m.connect('delete_repo_group_perm_member',
199 m.connect('delete_repo_group_perm_member',
200 "/repos_groups/{group_name:.*?}/revoke_perm",
200 "/repos_groups/{group_name:.*?}/revoke_perm",
201 action="delete_repo_group_perm_member",
201 action="delete_repo_group_perm_member",
202 conditions=dict(method=["DELETE"], function=check_group))
202 conditions=dict(method=["DELETE"], function=check_group))
203
203
204 m.connect("delete_repos_group", "/repos_groups/{group_name:.*?}",
204 m.connect("delete_repos_group", "/repos_groups/{group_name:.*?}",
205 action="delete", conditions=dict(method=["DELETE"],
205 action="delete", conditions=dict(method=["DELETE"],
206 function=check_group_skip_path))
206 function=check_group_skip_path))
207 m.connect("edit_repos_group", "/repos_groups/{group_name:.*?}/edit",
207 m.connect("edit_repos_group", "/repos_groups/{group_name:.*?}/edit",
208 action="edit", conditions=dict(method=["GET"],
208 action="edit", conditions=dict(method=["GET"],
209 function=check_group))
209 function=check_group))
210 m.connect("formatted_edit_repos_group",
210 m.connect("formatted_edit_repos_group",
211 "/repos_groups/{group_name:.*?}.{format}/edit",
211 "/repos_groups/{group_name:.*?}.{format}/edit",
212 action="edit", conditions=dict(method=["GET"],
212 action="edit", conditions=dict(method=["GET"],
213 function=check_group))
213 function=check_group))
214 m.connect("repos_group", "/repos_groups/{group_name:.*?}",
214 m.connect("repos_group", "/repos_groups/{group_name:.*?}",
215 action="show", conditions=dict(method=["GET"],
215 action="show", conditions=dict(method=["GET"],
216 function=check_group))
216 function=check_group))
217 m.connect("formatted_repos_group", "/repos_groups/{group_name:.*?}.{format}",
217 m.connect("formatted_repos_group", "/repos_groups/{group_name:.*?}.{format}",
218 action="show", conditions=dict(method=["GET"],
218 action="show", conditions=dict(method=["GET"],
219 function=check_group))
219 function=check_group))
220
220
221 #ADMIN USER REST ROUTES
221 #ADMIN USER REST ROUTES
222 with rmap.submapper(path_prefix=ADMIN_PREFIX,
222 with rmap.submapper(path_prefix=ADMIN_PREFIX,
223 controller='admin/users') as m:
223 controller='admin/users') as m:
224 m.connect("users", "/users",
224 m.connect("users", "/users",
225 action="create", conditions=dict(method=["POST"]))
225 action="create", conditions=dict(method=["POST"]))
226 m.connect("users", "/users",
226 m.connect("users", "/users",
227 action="index", conditions=dict(method=["GET"]))
227 action="index", conditions=dict(method=["GET"]))
228 m.connect("formatted_users", "/users.{format}",
228 m.connect("formatted_users", "/users.{format}",
229 action="index", conditions=dict(method=["GET"]))
229 action="index", conditions=dict(method=["GET"]))
230 m.connect("new_user", "/users/new",
230 m.connect("new_user", "/users/new",
231 action="new", conditions=dict(method=["GET"]))
231 action="new", conditions=dict(method=["GET"]))
232 m.connect("formatted_new_user", "/users/new.{format}",
232 m.connect("formatted_new_user", "/users/new.{format}",
233 action="new", conditions=dict(method=["GET"]))
233 action="new", conditions=dict(method=["GET"]))
234 m.connect("update_user", "/users/{id}",
234 m.connect("update_user", "/users/{id}",
235 action="update", conditions=dict(method=["PUT"]))
235 action="update", conditions=dict(method=["PUT"]))
236 m.connect("delete_user", "/users/{id}",
236 m.connect("delete_user", "/users/{id}",
237 action="delete", conditions=dict(method=["DELETE"]))
237 action="delete", conditions=dict(method=["DELETE"]))
238 m.connect("edit_user", "/users/{id}/edit",
238 m.connect("edit_user", "/users/{id}/edit",
239 action="edit", conditions=dict(method=["GET"]))
239 action="edit", conditions=dict(method=["GET"]))
240 m.connect("formatted_edit_user",
240 m.connect("formatted_edit_user",
241 "/users/{id}.{format}/edit",
241 "/users/{id}.{format}/edit",
242 action="edit", conditions=dict(method=["GET"]))
242 action="edit", conditions=dict(method=["GET"]))
243 m.connect("user", "/users/{id}",
243 m.connect("user", "/users/{id}",
244 action="show", conditions=dict(method=["GET"]))
244 action="show", conditions=dict(method=["GET"]))
245 m.connect("formatted_user", "/users/{id}.{format}",
245 m.connect("formatted_user", "/users/{id}.{format}",
246 action="show", conditions=dict(method=["GET"]))
246 action="show", conditions=dict(method=["GET"]))
247
247
248 #EXTRAS USER ROUTES
248 #EXTRAS USER ROUTES
249 m.connect("user_perm", "/users_perm/{id}",
249 m.connect("user_perm", "/users_perm/{id}",
250 action="update_perm", conditions=dict(method=["PUT"]))
250 action="update_perm", conditions=dict(method=["PUT"]))
251 m.connect("user_emails", "/users_emails/{id}",
251 m.connect("user_emails", "/users_emails/{id}",
252 action="add_email", conditions=dict(method=["PUT"]))
252 action="add_email", conditions=dict(method=["PUT"]))
253 m.connect("user_emails_delete", "/users_emails/{id}",
253 m.connect("user_emails_delete", "/users_emails/{id}",
254 action="delete_email", conditions=dict(method=["DELETE"]))
254 action="delete_email", conditions=dict(method=["DELETE"]))
255 m.connect("user_ips", "/users_ips/{id}",
255 m.connect("user_ips", "/users_ips/{id}",
256 action="add_ip", conditions=dict(method=["PUT"]))
256 action="add_ip", conditions=dict(method=["PUT"]))
257 m.connect("user_ips_delete", "/users_ips/{id}",
257 m.connect("user_ips_delete", "/users_ips/{id}",
258 action="delete_ip", conditions=dict(method=["DELETE"]))
258 action="delete_ip", conditions=dict(method=["DELETE"]))
259
259
260 #ADMIN USER GROUPS REST ROUTES
260 #ADMIN USER GROUPS REST ROUTES
261 with rmap.submapper(path_prefix=ADMIN_PREFIX,
261 with rmap.submapper(path_prefix=ADMIN_PREFIX,
262 controller='admin/users_groups') as m:
262 controller='admin/users_groups') as m:
263 m.connect("users_groups", "/users_groups",
263 m.connect("users_groups", "/users_groups",
264 action="create", conditions=dict(method=["POST"]))
264 action="create", conditions=dict(method=["POST"]))
265 m.connect("users_groups", "/users_groups",
265 m.connect("users_groups", "/users_groups",
266 action="index", conditions=dict(method=["GET"]))
266 action="index", conditions=dict(method=["GET"]))
267 m.connect("formatted_users_groups", "/users_groups.{format}",
267 m.connect("formatted_users_groups", "/users_groups.{format}",
268 action="index", conditions=dict(method=["GET"]))
268 action="index", conditions=dict(method=["GET"]))
269 m.connect("new_users_group", "/users_groups/new",
269 m.connect("new_users_group", "/users_groups/new",
270 action="new", conditions=dict(method=["GET"]))
270 action="new", conditions=dict(method=["GET"]))
271 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
271 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
272 action="new", conditions=dict(method=["GET"]))
272 action="new", conditions=dict(method=["GET"]))
273 m.connect("update_users_group", "/users_groups/{id}",
273 m.connect("update_users_group", "/users_groups/{id}",
274 action="update", conditions=dict(method=["PUT"]))
274 action="update", conditions=dict(method=["PUT"]))
275 m.connect("delete_users_group", "/users_groups/{id}",
275 m.connect("delete_users_group", "/users_groups/{id}",
276 action="delete", conditions=dict(method=["DELETE"]))
276 action="delete", conditions=dict(method=["DELETE"]))
277 m.connect("edit_users_group", "/users_groups/{id}/edit",
277 m.connect("edit_users_group", "/users_groups/{id}/edit",
278 action="edit", conditions=dict(method=["GET"]),
278 action="edit", conditions=dict(method=["GET"]),
279 function=check_user_group)
279 function=check_user_group)
280 m.connect("formatted_edit_users_group",
280 m.connect("formatted_edit_users_group",
281 "/users_groups/{id}.{format}/edit",
281 "/users_groups/{id}.{format}/edit",
282 action="edit", conditions=dict(method=["GET"]))
282 action="edit", conditions=dict(method=["GET"]))
283 m.connect("users_group", "/users_groups/{id}",
283 m.connect("users_group", "/users_groups/{id}",
284 action="show", conditions=dict(method=["GET"]))
284 action="show", conditions=dict(method=["GET"]))
285 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
285 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
286 action="show", conditions=dict(method=["GET"]))
286 action="show", conditions=dict(method=["GET"]))
287
287
288 #EXTRAS USER ROUTES
288 #EXTRAS USER ROUTES
289 # update
289 # update
290 m.connect("users_group_perm", "/users_groups/{id}/update_global_perm",
290 m.connect("users_group_perm", "/users_groups/{id}/update_global_perm",
291 action="update_perm", conditions=dict(method=["PUT"]))
291 action="update_perm", conditions=dict(method=["PUT"]))
292
292
293 #add user group perm member
293 #add user group perm member
294 m.connect('set_user_group_perm_member', "/users_groups/{id}/grant_perm",
294 m.connect('set_user_group_perm_member', "/users_groups/{id}/grant_perm",
295 action="set_user_group_perm_member",
295 action="set_user_group_perm_member",
296 conditions=dict(method=["POST"]))
296 conditions=dict(method=["POST"]))
297
297
298 #ajax delete user group perm
298 #ajax delete user group perm
299 m.connect('delete_user_group_perm_member', "/users_groups/{id}/revoke_perm",
299 m.connect('delete_user_group_perm_member', "/users_groups/{id}/revoke_perm",
300 action="delete_user_group_perm_member",
300 action="delete_user_group_perm_member",
301 conditions=dict(method=["DELETE"]))
301 conditions=dict(method=["DELETE"]))
302
302
303 #ADMIN GROUP REST ROUTES
303 #ADMIN GROUP REST ROUTES
304 rmap.resource('group', 'groups',
304 rmap.resource('group', 'groups',
305 controller='admin/groups', path_prefix=ADMIN_PREFIX)
305 controller='admin/groups', path_prefix=ADMIN_PREFIX)
306
306
307 #ADMIN PERMISSIONS REST ROUTES
307 #ADMIN PERMISSIONS REST ROUTES
308 rmap.resource('permission', 'permissions',
308 rmap.resource('permission', 'permissions',
309 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
309 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
310
310
311 #ADMIN DEFAULTS REST ROUTES
311 #ADMIN DEFAULTS REST ROUTES
312 rmap.resource('default', 'defaults',
312 rmap.resource('default', 'defaults',
313 controller='admin/defaults', path_prefix=ADMIN_PREFIX)
313 controller='admin/defaults', path_prefix=ADMIN_PREFIX)
314
314
315 ##ADMIN LDAP SETTINGS
315 ##ADMIN LDAP SETTINGS
316 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
316 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
317 controller='admin/ldap_settings', action='ldap_settings',
317 controller='admin/ldap_settings', action='ldap_settings',
318 conditions=dict(method=["POST"]))
318 conditions=dict(method=["POST"]))
319
319
320 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
320 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
321 controller='admin/ldap_settings')
321 controller='admin/ldap_settings')
322
322
323 #ADMIN SETTINGS REST ROUTES
323 #ADMIN SETTINGS REST ROUTES
324 with rmap.submapper(path_prefix=ADMIN_PREFIX,
324 with rmap.submapper(path_prefix=ADMIN_PREFIX,
325 controller='admin/settings') as m:
325 controller='admin/settings') as m:
326 m.connect("admin_settings", "/settings",
326 m.connect("admin_settings", "/settings",
327 action="create", conditions=dict(method=["POST"]))
327 action="create", conditions=dict(method=["POST"]))
328 m.connect("admin_settings", "/settings",
328 m.connect("admin_settings", "/settings",
329 action="index", conditions=dict(method=["GET"]))
329 action="index", conditions=dict(method=["GET"]))
330 m.connect("formatted_admin_settings", "/settings.{format}",
330 m.connect("formatted_admin_settings", "/settings.{format}",
331 action="index", conditions=dict(method=["GET"]))
331 action="index", conditions=dict(method=["GET"]))
332 m.connect("admin_new_setting", "/settings/new",
332 m.connect("admin_new_setting", "/settings/new",
333 action="new", conditions=dict(method=["GET"]))
333 action="new", conditions=dict(method=["GET"]))
334 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
334 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
335 action="new", conditions=dict(method=["GET"]))
335 action="new", conditions=dict(method=["GET"]))
336 m.connect("/settings/{setting_id}",
336 m.connect("/settings/{setting_id}",
337 action="update", conditions=dict(method=["PUT"]))
337 action="update", conditions=dict(method=["PUT"]))
338 m.connect("/settings/{setting_id}",
338 m.connect("/settings/{setting_id}",
339 action="delete", conditions=dict(method=["DELETE"]))
339 action="delete", conditions=dict(method=["DELETE"]))
340 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
340 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
341 action="edit", conditions=dict(method=["GET"]))
341 action="edit", conditions=dict(method=["GET"]))
342 m.connect("formatted_admin_edit_setting",
342 m.connect("formatted_admin_edit_setting",
343 "/settings/{setting_id}.{format}/edit",
343 "/settings/{setting_id}.{format}/edit",
344 action="edit", conditions=dict(method=["GET"]))
344 action="edit", conditions=dict(method=["GET"]))
345 m.connect("admin_setting", "/settings/{setting_id}",
345 m.connect("admin_setting", "/settings/{setting_id}",
346 action="show", conditions=dict(method=["GET"]))
346 action="show", conditions=dict(method=["GET"]))
347 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
347 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
348 action="show", conditions=dict(method=["GET"]))
348 action="show", conditions=dict(method=["GET"]))
349 m.connect("admin_settings_my_account", "/my_account",
349 m.connect("admin_settings_my_account", "/my_account",
350 action="my_account", conditions=dict(method=["GET"]))
350 action="my_account", conditions=dict(method=["GET"]))
351 m.connect("admin_settings_my_account_update", "/my_account_update",
351 m.connect("admin_settings_my_account_update", "/my_account_update",
352 action="my_account_update", conditions=dict(method=["PUT"]))
352 action="my_account_update", conditions=dict(method=["PUT"]))
353 m.connect("admin_settings_my_repos", "/my_account/repos",
353 m.connect("admin_settings_my_repos", "/my_account/repos",
354 action="my_account_my_repos", conditions=dict(method=["GET"]))
354 action="my_account_my_repos", conditions=dict(method=["GET"]))
355 m.connect("admin_settings_my_pullrequests", "/my_account/pull_requests",
355 m.connect("admin_settings_my_pullrequests", "/my_account/pull_requests",
356 action="my_account_my_pullrequests", conditions=dict(method=["GET"]))
356 action="my_account_my_pullrequests", conditions=dict(method=["GET"]))
357
357
358 #NOTIFICATION REST ROUTES
358 #NOTIFICATION REST ROUTES
359 with rmap.submapper(path_prefix=ADMIN_PREFIX,
359 with rmap.submapper(path_prefix=ADMIN_PREFIX,
360 controller='admin/notifications') as m:
360 controller='admin/notifications') as m:
361 m.connect("notifications", "/notifications",
361 m.connect("notifications", "/notifications",
362 action="create", conditions=dict(method=["POST"]))
362 action="create", conditions=dict(method=["POST"]))
363 m.connect("notifications", "/notifications",
363 m.connect("notifications", "/notifications",
364 action="index", conditions=dict(method=["GET"]))
364 action="index", conditions=dict(method=["GET"]))
365 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
365 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
366 action="mark_all_read", conditions=dict(method=["GET"]))
366 action="mark_all_read", conditions=dict(method=["GET"]))
367 m.connect("formatted_notifications", "/notifications.{format}",
367 m.connect("formatted_notifications", "/notifications.{format}",
368 action="index", conditions=dict(method=["GET"]))
368 action="index", conditions=dict(method=["GET"]))
369 m.connect("new_notification", "/notifications/new",
369 m.connect("new_notification", "/notifications/new",
370 action="new", conditions=dict(method=["GET"]))
370 action="new", conditions=dict(method=["GET"]))
371 m.connect("formatted_new_notification", "/notifications/new.{format}",
371 m.connect("formatted_new_notification", "/notifications/new.{format}",
372 action="new", conditions=dict(method=["GET"]))
372 action="new", conditions=dict(method=["GET"]))
373 m.connect("/notification/{notification_id}",
373 m.connect("/notification/{notification_id}",
374 action="update", conditions=dict(method=["PUT"]))
374 action="update", conditions=dict(method=["PUT"]))
375 m.connect("/notification/{notification_id}",
375 m.connect("/notification/{notification_id}",
376 action="delete", conditions=dict(method=["DELETE"]))
376 action="delete", conditions=dict(method=["DELETE"]))
377 m.connect("edit_notification", "/notification/{notification_id}/edit",
377 m.connect("edit_notification", "/notification/{notification_id}/edit",
378 action="edit", conditions=dict(method=["GET"]))
378 action="edit", conditions=dict(method=["GET"]))
379 m.connect("formatted_edit_notification",
379 m.connect("formatted_edit_notification",
380 "/notification/{notification_id}.{format}/edit",
380 "/notification/{notification_id}.{format}/edit",
381 action="edit", conditions=dict(method=["GET"]))
381 action="edit", conditions=dict(method=["GET"]))
382 m.connect("notification", "/notification/{notification_id}",
382 m.connect("notification", "/notification/{notification_id}",
383 action="show", conditions=dict(method=["GET"]))
383 action="show", conditions=dict(method=["GET"]))
384 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
384 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
385 action="show", conditions=dict(method=["GET"]))
385 action="show", conditions=dict(method=["GET"]))
386
386
387 #ADMIN GIST
388 with rmap.submapper(path_prefix=ADMIN_PREFIX,
389 controller='admin/gists') as m:
390 m.connect("gists", "/gists",
391 action="create", conditions=dict(method=["POST"]))
392 m.connect("gists", "/gists",
393 action="index", conditions=dict(method=["GET"]))
394 m.connect("formatted_gists", "/gists.{format}",
395 action="index", conditions=dict(method=["GET"]))
396 m.connect("new_gist", "/gists/new",
397 action="new", conditions=dict(method=["GET"]))
398 m.connect("formatted_new_gist", "/gists/new.{format}",
399 action="new", conditions=dict(method=["GET"]))
400 m.connect("/gist/{gist_id}",
401 action="update", conditions=dict(method=["PUT"]))
402 m.connect("/gist/{gist_id}",
403 action="delete", conditions=dict(method=["DELETE"]))
404 m.connect("edit_gist", "/gist/{gist_id}/edit",
405 action="edit", conditions=dict(method=["GET"]))
406 m.connect("formatted_edit_gist",
407 "/gist/{gist_id}.{format}/edit",
408 action="edit", conditions=dict(method=["GET"]))
409 m.connect("gist", "/gist/{gist_id}",
410 action="show", conditions=dict(method=["GET"]))
411 m.connect("formatted_gist", "/gists/{gist_id}.{format}",
412 action="show", conditions=dict(method=["GET"]))
413
387 #ADMIN MAIN PAGES
414 #ADMIN MAIN PAGES
388 with rmap.submapper(path_prefix=ADMIN_PREFIX,
415 with rmap.submapper(path_prefix=ADMIN_PREFIX,
389 controller='admin/admin') as m:
416 controller='admin/admin') as m:
390 m.connect('admin_home', '', action='index')
417 m.connect('admin_home', '', action='index')
391 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
418 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
392 action='add_repo')
419 action='add_repo')
393
394 #ADMIN GIST
395 rmap.resource('gist', 'gists', controller='admin/gists',
396 path_prefix=ADMIN_PREFIX)
397 #==========================================================================
420 #==========================================================================
398 # API V2
421 # API V2
399 #==========================================================================
422 #==========================================================================
400 with rmap.submapper(path_prefix=ADMIN_PREFIX,
423 with rmap.submapper(path_prefix=ADMIN_PREFIX,
401 controller='api/api') as m:
424 controller='api/api') as m:
402 m.connect('api', '/api')
425 m.connect('api', '/api')
403
426
404 #USER JOURNAL
427 #USER JOURNAL
405 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
428 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
406 controller='journal', action='index')
429 controller='journal', action='index')
407 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
430 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
408 controller='journal', action='journal_rss')
431 controller='journal', action='journal_rss')
409 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
432 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
410 controller='journal', action='journal_atom')
433 controller='journal', action='journal_atom')
411
434
412 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
435 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
413 controller='journal', action="public_journal")
436 controller='journal', action="public_journal")
414
437
415 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
438 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
416 controller='journal', action="public_journal_rss")
439 controller='journal', action="public_journal_rss")
417
440
418 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
441 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
419 controller='journal', action="public_journal_rss")
442 controller='journal', action="public_journal_rss")
420
443
421 rmap.connect('public_journal_atom',
444 rmap.connect('public_journal_atom',
422 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
445 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
423 action="public_journal_atom")
446 action="public_journal_atom")
424
447
425 rmap.connect('public_journal_atom_old',
448 rmap.connect('public_journal_atom_old',
426 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
449 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
427 action="public_journal_atom")
450 action="public_journal_atom")
428
451
429 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
452 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
430 controller='journal', action='toggle_following',
453 controller='journal', action='toggle_following',
431 conditions=dict(method=["POST"]))
454 conditions=dict(method=["POST"]))
432
455
433 #SEARCH
456 #SEARCH
434 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
457 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
435 rmap.connect('search_repo_admin', '%s/search/{repo_name:.*}' % ADMIN_PREFIX,
458 rmap.connect('search_repo_admin', '%s/search/{repo_name:.*}' % ADMIN_PREFIX,
436 controller='search',
459 controller='search',
437 conditions=dict(function=check_repo))
460 conditions=dict(function=check_repo))
438 rmap.connect('search_repo', '/{repo_name:.*?}/search',
461 rmap.connect('search_repo', '/{repo_name:.*?}/search',
439 controller='search',
462 controller='search',
440 conditions=dict(function=check_repo),
463 conditions=dict(function=check_repo),
441 )
464 )
442
465
443 #LOGIN/LOGOUT/REGISTER/SIGN IN
466 #LOGIN/LOGOUT/REGISTER/SIGN IN
444 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
467 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
445 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
468 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
446 action='logout')
469 action='logout')
447
470
448 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
471 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
449 action='register')
472 action='register')
450
473
451 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
474 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
452 controller='login', action='password_reset')
475 controller='login', action='password_reset')
453
476
454 rmap.connect('reset_password_confirmation',
477 rmap.connect('reset_password_confirmation',
455 '%s/password_reset_confirmation' % ADMIN_PREFIX,
478 '%s/password_reset_confirmation' % ADMIN_PREFIX,
456 controller='login', action='password_reset_confirmation')
479 controller='login', action='password_reset_confirmation')
457
480
458 #FEEDS
481 #FEEDS
459 rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
482 rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
460 controller='feed', action='rss',
483 controller='feed', action='rss',
461 conditions=dict(function=check_repo))
484 conditions=dict(function=check_repo))
462
485
463 rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
486 rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
464 controller='feed', action='atom',
487 controller='feed', action='atom',
465 conditions=dict(function=check_repo))
488 conditions=dict(function=check_repo))
466
489
467 #==========================================================================
490 #==========================================================================
468 # REPOSITORY ROUTES
491 # REPOSITORY ROUTES
469 #==========================================================================
492 #==========================================================================
470 rmap.connect('summary_home', '/{repo_name:.*?}',
493 rmap.connect('summary_home', '/{repo_name:.*?}',
471 controller='summary',
494 controller='summary',
472 conditions=dict(function=check_repo))
495 conditions=dict(function=check_repo))
473
496
474 rmap.connect('repo_size', '/{repo_name:.*?}/repo_size',
497 rmap.connect('repo_size', '/{repo_name:.*?}/repo_size',
475 controller='summary', action='repo_size',
498 controller='summary', action='repo_size',
476 conditions=dict(function=check_repo))
499 conditions=dict(function=check_repo))
477
500
478 rmap.connect('repos_group_home', '/{group_name:.*}',
501 rmap.connect('repos_group_home', '/{group_name:.*}',
479 controller='admin/repos_groups', action="show_by_name",
502 controller='admin/repos_groups', action="show_by_name",
480 conditions=dict(function=check_group))
503 conditions=dict(function=check_group))
481
504
482 rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision}',
505 rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision}',
483 controller='changeset', revision='tip',
506 controller='changeset', revision='tip',
484 conditions=dict(function=check_repo))
507 conditions=dict(function=check_repo))
485
508
486 # no longer user, but kept for routes to work
509 # no longer user, but kept for routes to work
487 rmap.connect("_edit_repo", "/{repo_name:.*?}/edit",
510 rmap.connect("_edit_repo", "/{repo_name:.*?}/edit",
488 controller='admin/repos', action="edit",
511 controller='admin/repos', action="edit",
489 conditions=dict(method=["GET"], function=check_repo)
512 conditions=dict(method=["GET"], function=check_repo)
490 )
513 )
491
514
492 rmap.connect("edit_repo", "/{repo_name:.*?}/settings",
515 rmap.connect("edit_repo", "/{repo_name:.*?}/settings",
493 controller='admin/repos', action="edit",
516 controller='admin/repos', action="edit",
494 conditions=dict(method=["GET"], function=check_repo)
517 conditions=dict(method=["GET"], function=check_repo)
495 )
518 )
496
519
497 #still working url for backward compat.
520 #still working url for backward compat.
498 rmap.connect('raw_changeset_home_depraced',
521 rmap.connect('raw_changeset_home_depraced',
499 '/{repo_name:.*?}/raw-changeset/{revision}',
522 '/{repo_name:.*?}/raw-changeset/{revision}',
500 controller='changeset', action='changeset_raw',
523 controller='changeset', action='changeset_raw',
501 revision='tip', conditions=dict(function=check_repo))
524 revision='tip', conditions=dict(function=check_repo))
502
525
503 ## new URLs
526 ## new URLs
504 rmap.connect('changeset_raw_home',
527 rmap.connect('changeset_raw_home',
505 '/{repo_name:.*?}/changeset-diff/{revision}',
528 '/{repo_name:.*?}/changeset-diff/{revision}',
506 controller='changeset', action='changeset_raw',
529 controller='changeset', action='changeset_raw',
507 revision='tip', conditions=dict(function=check_repo))
530 revision='tip', conditions=dict(function=check_repo))
508
531
509 rmap.connect('changeset_patch_home',
532 rmap.connect('changeset_patch_home',
510 '/{repo_name:.*?}/changeset-patch/{revision}',
533 '/{repo_name:.*?}/changeset-patch/{revision}',
511 controller='changeset', action='changeset_patch',
534 controller='changeset', action='changeset_patch',
512 revision='tip', conditions=dict(function=check_repo))
535 revision='tip', conditions=dict(function=check_repo))
513
536
514 rmap.connect('changeset_download_home',
537 rmap.connect('changeset_download_home',
515 '/{repo_name:.*?}/changeset-download/{revision}',
538 '/{repo_name:.*?}/changeset-download/{revision}',
516 controller='changeset', action='changeset_download',
539 controller='changeset', action='changeset_download',
517 revision='tip', conditions=dict(function=check_repo))
540 revision='tip', conditions=dict(function=check_repo))
518
541
519 rmap.connect('changeset_comment',
542 rmap.connect('changeset_comment',
520 '/{repo_name:.*?}/changeset/{revision}/comment',
543 '/{repo_name:.*?}/changeset/{revision}/comment',
521 controller='changeset', revision='tip', action='comment',
544 controller='changeset', revision='tip', action='comment',
522 conditions=dict(function=check_repo))
545 conditions=dict(function=check_repo))
523
546
524 rmap.connect('changeset_comment_preview',
547 rmap.connect('changeset_comment_preview',
525 '/{repo_name:.*?}/changeset/comment/preview',
548 '/{repo_name:.*?}/changeset/comment/preview',
526 controller='changeset', action='preview_comment',
549 controller='changeset', action='preview_comment',
527 conditions=dict(function=check_repo, method=["POST"]))
550 conditions=dict(function=check_repo, method=["POST"]))
528
551
529 rmap.connect('changeset_comment_delete',
552 rmap.connect('changeset_comment_delete',
530 '/{repo_name:.*?}/changeset/comment/{comment_id}/delete',
553 '/{repo_name:.*?}/changeset/comment/{comment_id}/delete',
531 controller='changeset', action='delete_comment',
554 controller='changeset', action='delete_comment',
532 conditions=dict(function=check_repo, method=["DELETE"]))
555 conditions=dict(function=check_repo, method=["DELETE"]))
533
556
534 rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
557 rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
535 controller='changeset', action='changeset_info')
558 controller='changeset', action='changeset_info')
536
559
537 rmap.connect('compare_url',
560 rmap.connect('compare_url',
538 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref:.*?}...{other_ref_type}@{other_ref:.*?}',
561 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref:.*?}...{other_ref_type}@{other_ref:.*?}',
539 controller='compare', action='index',
562 controller='compare', action='index',
540 conditions=dict(function=check_repo),
563 conditions=dict(function=check_repo),
541 requirements=dict(
564 requirements=dict(
542 org_ref_type='(branch|book|tag|rev|__other_ref_type__)',
565 org_ref_type='(branch|book|tag|rev|__other_ref_type__)',
543 other_ref_type='(branch|book|tag|rev|__org_ref_type__)')
566 other_ref_type='(branch|book|tag|rev|__org_ref_type__)')
544 )
567 )
545
568
546 rmap.connect('pullrequest_home',
569 rmap.connect('pullrequest_home',
547 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
570 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
548 action='index', conditions=dict(function=check_repo,
571 action='index', conditions=dict(function=check_repo,
549 method=["GET"]))
572 method=["GET"]))
550
573
551 rmap.connect('pullrequest',
574 rmap.connect('pullrequest',
552 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
575 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
553 action='create', conditions=dict(function=check_repo,
576 action='create', conditions=dict(function=check_repo,
554 method=["POST"]))
577 method=["POST"]))
555
578
556 rmap.connect('pullrequest_show',
579 rmap.connect('pullrequest_show',
557 '/{repo_name:.*?}/pull-request/{pull_request_id}',
580 '/{repo_name:.*?}/pull-request/{pull_request_id}',
558 controller='pullrequests',
581 controller='pullrequests',
559 action='show', conditions=dict(function=check_repo,
582 action='show', conditions=dict(function=check_repo,
560 method=["GET"]))
583 method=["GET"]))
561 rmap.connect('pullrequest_update',
584 rmap.connect('pullrequest_update',
562 '/{repo_name:.*?}/pull-request/{pull_request_id}',
585 '/{repo_name:.*?}/pull-request/{pull_request_id}',
563 controller='pullrequests',
586 controller='pullrequests',
564 action='update', conditions=dict(function=check_repo,
587 action='update', conditions=dict(function=check_repo,
565 method=["PUT"]))
588 method=["PUT"]))
566 rmap.connect('pullrequest_delete',
589 rmap.connect('pullrequest_delete',
567 '/{repo_name:.*?}/pull-request/{pull_request_id}',
590 '/{repo_name:.*?}/pull-request/{pull_request_id}',
568 controller='pullrequests',
591 controller='pullrequests',
569 action='delete', conditions=dict(function=check_repo,
592 action='delete', conditions=dict(function=check_repo,
570 method=["DELETE"]))
593 method=["DELETE"]))
571
594
572 rmap.connect('pullrequest_show_all',
595 rmap.connect('pullrequest_show_all',
573 '/{repo_name:.*?}/pull-request',
596 '/{repo_name:.*?}/pull-request',
574 controller='pullrequests',
597 controller='pullrequests',
575 action='show_all', conditions=dict(function=check_repo,
598 action='show_all', conditions=dict(function=check_repo,
576 method=["GET"]))
599 method=["GET"]))
577
600
578 rmap.connect('pullrequest_comment',
601 rmap.connect('pullrequest_comment',
579 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
602 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
580 controller='pullrequests',
603 controller='pullrequests',
581 action='comment', conditions=dict(function=check_repo,
604 action='comment', conditions=dict(function=check_repo,
582 method=["POST"]))
605 method=["POST"]))
583
606
584 rmap.connect('pullrequest_comment_delete',
607 rmap.connect('pullrequest_comment_delete',
585 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
608 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
586 controller='pullrequests', action='delete_comment',
609 controller='pullrequests', action='delete_comment',
587 conditions=dict(function=check_repo, method=["DELETE"]))
610 conditions=dict(function=check_repo, method=["DELETE"]))
588
611
589 rmap.connect('summary_home_summary', '/{repo_name:.*?}/summary',
612 rmap.connect('summary_home_summary', '/{repo_name:.*?}/summary',
590 controller='summary', conditions=dict(function=check_repo))
613 controller='summary', conditions=dict(function=check_repo))
591
614
592 rmap.connect('branches_home', '/{repo_name:.*?}/branches',
615 rmap.connect('branches_home', '/{repo_name:.*?}/branches',
593 controller='branches', conditions=dict(function=check_repo))
616 controller='branches', conditions=dict(function=check_repo))
594
617
595 rmap.connect('tags_home', '/{repo_name:.*?}/tags',
618 rmap.connect('tags_home', '/{repo_name:.*?}/tags',
596 controller='tags', conditions=dict(function=check_repo))
619 controller='tags', conditions=dict(function=check_repo))
597
620
598 rmap.connect('bookmarks_home', '/{repo_name:.*?}/bookmarks',
621 rmap.connect('bookmarks_home', '/{repo_name:.*?}/bookmarks',
599 controller='bookmarks', conditions=dict(function=check_repo))
622 controller='bookmarks', conditions=dict(function=check_repo))
600
623
601 rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
624 rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
602 controller='changelog', conditions=dict(function=check_repo))
625 controller='changelog', conditions=dict(function=check_repo))
603
626
604 rmap.connect('changelog_summary_home', '/{repo_name:.*?}/changelog_summary',
627 rmap.connect('changelog_summary_home', '/{repo_name:.*?}/changelog_summary',
605 controller='changelog', action='changelog_summary',
628 controller='changelog', action='changelog_summary',
606 conditions=dict(function=check_repo))
629 conditions=dict(function=check_repo))
607
630
608 rmap.connect('changelog_file_home', '/{repo_name:.*?}/changelog/{revision}/{f_path:.*}',
631 rmap.connect('changelog_file_home', '/{repo_name:.*?}/changelog/{revision}/{f_path:.*}',
609 controller='changelog', f_path=None,
632 controller='changelog', f_path=None,
610 conditions=dict(function=check_repo))
633 conditions=dict(function=check_repo))
611
634
612 rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
635 rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
613 controller='changelog', action='changelog_details',
636 controller='changelog', action='changelog_details',
614 conditions=dict(function=check_repo))
637 conditions=dict(function=check_repo))
615
638
616 rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
639 rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
617 controller='files', revision='tip', f_path='',
640 controller='files', revision='tip', f_path='',
618 conditions=dict(function=check_repo))
641 conditions=dict(function=check_repo))
619
642
620 rmap.connect('files_home_nopath', '/{repo_name:.*?}/files/{revision}',
643 rmap.connect('files_home_nopath', '/{repo_name:.*?}/files/{revision}',
621 controller='files', revision='tip', f_path='',
644 controller='files', revision='tip', f_path='',
622 conditions=dict(function=check_repo))
645 conditions=dict(function=check_repo))
623
646
624 rmap.connect('files_history_home',
647 rmap.connect('files_history_home',
625 '/{repo_name:.*?}/history/{revision}/{f_path:.*}',
648 '/{repo_name:.*?}/history/{revision}/{f_path:.*}',
626 controller='files', action='history', revision='tip', f_path='',
649 controller='files', action='history', revision='tip', f_path='',
627 conditions=dict(function=check_repo))
650 conditions=dict(function=check_repo))
628
651
629 rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
652 rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
630 controller='files', action='diff', revision='tip', f_path='',
653 controller='files', action='diff', revision='tip', f_path='',
631 conditions=dict(function=check_repo))
654 conditions=dict(function=check_repo))
632
655
633 rmap.connect('files_rawfile_home',
656 rmap.connect('files_rawfile_home',
634 '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
657 '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
635 controller='files', action='rawfile', revision='tip',
658 controller='files', action='rawfile', revision='tip',
636 f_path='', conditions=dict(function=check_repo))
659 f_path='', conditions=dict(function=check_repo))
637
660
638 rmap.connect('files_raw_home',
661 rmap.connect('files_raw_home',
639 '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
662 '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
640 controller='files', action='raw', revision='tip', f_path='',
663 controller='files', action='raw', revision='tip', f_path='',
641 conditions=dict(function=check_repo))
664 conditions=dict(function=check_repo))
642
665
643 rmap.connect('files_annotate_home',
666 rmap.connect('files_annotate_home',
644 '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
667 '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
645 controller='files', action='index', revision='tip',
668 controller='files', action='index', revision='tip',
646 f_path='', annotate=True, conditions=dict(function=check_repo))
669 f_path='', annotate=True, conditions=dict(function=check_repo))
647
670
648 rmap.connect('files_edit_home',
671 rmap.connect('files_edit_home',
649 '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
672 '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
650 controller='files', action='edit', revision='tip',
673 controller='files', action='edit', revision='tip',
651 f_path='', conditions=dict(function=check_repo))
674 f_path='', conditions=dict(function=check_repo))
652
675
653 rmap.connect('files_add_home',
676 rmap.connect('files_add_home',
654 '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
677 '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
655 controller='files', action='add', revision='tip',
678 controller='files', action='add', revision='tip',
656 f_path='', conditions=dict(function=check_repo))
679 f_path='', conditions=dict(function=check_repo))
657
680
658 rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
681 rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
659 controller='files', action='archivefile',
682 controller='files', action='archivefile',
660 conditions=dict(function=check_repo))
683 conditions=dict(function=check_repo))
661
684
662 rmap.connect('files_nodelist_home',
685 rmap.connect('files_nodelist_home',
663 '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
686 '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
664 controller='files', action='nodelist',
687 controller='files', action='nodelist',
665 conditions=dict(function=check_repo))
688 conditions=dict(function=check_repo))
666
689
667 rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
690 rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
668 controller='forks', action='fork_create',
691 controller='forks', action='fork_create',
669 conditions=dict(function=check_repo, method=["POST"]))
692 conditions=dict(function=check_repo, method=["POST"]))
670
693
671 rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
694 rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
672 controller='forks', action='fork',
695 controller='forks', action='fork',
673 conditions=dict(function=check_repo))
696 conditions=dict(function=check_repo))
674
697
675 rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
698 rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
676 controller='forks', action='forks',
699 controller='forks', action='forks',
677 conditions=dict(function=check_repo))
700 conditions=dict(function=check_repo))
678
701
679 rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
702 rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
680 controller='followers', action='followers',
703 controller='followers', action='followers',
681 conditions=dict(function=check_repo))
704 conditions=dict(function=check_repo))
682
705
683 return rmap
706 return rmap
@@ -1,196 +1,195 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.gist
3 rhodecode.controllers.admin.gist
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 gist controller for RhodeCode
6 gist controller for RhodeCode
7
7
8 :created_on: May 9, 2013
8 :created_on: May 9, 2013
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2013 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2013 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import time
25 import time
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from pylons import request, tmpl_context as c, url
31 from pylons import request, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.model.forms import GistForm
35 from rhodecode.model.forms import GistForm
36 from rhodecode.model.gist import GistModel
36 from rhodecode.model.gist import GistModel
37 from rhodecode.model.meta import Session
37 from rhodecode.model.meta import Session
38 from rhodecode.model.db import Gist
38 from rhodecode.model.db import Gist
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.auth import LoginRequired, NotAnonymous
41 from rhodecode.lib.auth import LoginRequired, NotAnonymous
42 from rhodecode.lib.utils2 import safe_str, safe_int, time_to_datetime
42 from rhodecode.lib.utils2 import safe_str, safe_int, time_to_datetime
43 from rhodecode.lib.helpers import Page
43 from rhodecode.lib.helpers import Page
44 from webob.exc import HTTPNotFound, HTTPForbidden
44 from webob.exc import HTTPNotFound, HTTPForbidden
45 from sqlalchemy.sql.expression import or_
45 from sqlalchemy.sql.expression import or_
46 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.exceptions import VCSError
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 class GistsController(BaseController):
51 class GistsController(BaseController):
52 """REST Controller styled on the Atom Publishing Protocol"""
52 """REST Controller styled on the Atom Publishing Protocol"""
53
53
54 def __load_defaults(self):
54 def __load_defaults(self):
55 c.lifetime_values = [
55 c.lifetime_values = [
56 (str(-1), _('forever')),
56 (str(-1), _('forever')),
57 (str(5), _('5 minutes')),
57 (str(5), _('5 minutes')),
58 (str(60), _('1 hour')),
58 (str(60), _('1 hour')),
59 (str(60 * 24), _('1 day')),
59 (str(60 * 24), _('1 day')),
60 (str(60 * 24 * 30), _('1 month')),
60 (str(60 * 24 * 30), _('1 month')),
61 ]
61 ]
62 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
62 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
63
63
64 @LoginRequired()
64 @LoginRequired()
65 def index(self, format='html'):
65 def index(self, format='html'):
66 """GET /admin/gists: All items in the collection"""
66 """GET /admin/gists: All items in the collection"""
67 # url('gists')
67 # url('gists')
68 c.show_private = request.GET.get('private') and c.rhodecode_user.username != 'default'
68 c.show_private = request.GET.get('private') and c.rhodecode_user.username != 'default'
69 c.show_public = request.GET.get('public') and c.rhodecode_user.username != 'default'
69 c.show_public = request.GET.get('public') and c.rhodecode_user.username != 'default'
70
70
71 gists = Gist().query()\
71 gists = Gist().query()\
72 .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
72 .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\
73 .order_by(Gist.created_on.desc())
73 .order_by(Gist.created_on.desc())
74 if c.show_private:
74 if c.show_private:
75 c.gists = gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\
75 c.gists = gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\
76 .filter(Gist.gist_owner == c.rhodecode_user.user_id)
76 .filter(Gist.gist_owner == c.rhodecode_user.user_id)
77 elif c.show_public:
77 elif c.show_public:
78 c.gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\
78 c.gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\
79 .filter(Gist.gist_owner == c.rhodecode_user.user_id)
79 .filter(Gist.gist_owner == c.rhodecode_user.user_id)
80
80
81 else:
81 else:
82 c.gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)
82 c.gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)
83 p = safe_int(request.GET.get('page', 1), 1)
83 p = safe_int(request.GET.get('page', 1), 1)
84 c.gists_pager = Page(c.gists, page=p, items_per_page=10)
84 c.gists_pager = Page(c.gists, page=p, items_per_page=10)
85 return render('admin/gists/index.html')
85 return render('admin/gists/index.html')
86
86
87 @LoginRequired()
87 @LoginRequired()
88 @NotAnonymous()
88 @NotAnonymous()
89 def create(self):
89 def create(self):
90 """POST /admin/gists: Create a new item"""
90 """POST /admin/gists: Create a new item"""
91 # url('gists')
91 # url('gists')
92 self.__load_defaults()
92 self.__load_defaults()
93 gist_form = GistForm([x[0] for x in c.lifetime_values])()
93 gist_form = GistForm([x[0] for x in c.lifetime_values])()
94 try:
94 try:
95 form_result = gist_form.to_python(dict(request.POST))
95 form_result = gist_form.to_python(dict(request.POST))
96 #TODO: multiple files support, from the form
96 #TODO: multiple files support, from the form
97 nodes = {
97 nodes = {
98 form_result['filename'] or 'gistfile1.txt': {
98 form_result['filename'] or 'gistfile1.txt': {
99 'content': form_result['content'],
99 'content': form_result['content'],
100 'lexer': None # autodetect
100 'lexer': None # autodetect
101 }
101 }
102 }
102 }
103 _public = form_result['public']
103 _public = form_result['public']
104 gist_type = Gist.GIST_PUBLIC if _public else Gist.GIST_PRIVATE
104 gist_type = Gist.GIST_PUBLIC if _public else Gist.GIST_PRIVATE
105 gist = GistModel().create(
105 gist = GistModel().create(
106 description=form_result['description'],
106 description=form_result['description'],
107 owner=c.rhodecode_user,
107 owner=c.rhodecode_user,
108 gist_mapping=nodes,
108 gist_mapping=nodes,
109 gist_type=gist_type,
109 gist_type=gist_type,
110 lifetime=form_result['lifetime']
110 lifetime=form_result['lifetime']
111 )
111 )
112 Session().commit()
112 Session().commit()
113 new_gist_id = gist.gist_access_id
113 new_gist_id = gist.gist_access_id
114 except formencode.Invalid, errors:
114 except formencode.Invalid, errors:
115 defaults = errors.value
115 defaults = errors.value
116
116
117 return formencode.htmlfill.render(
117 return formencode.htmlfill.render(
118 render('admin/gists/new.html'),
118 render('admin/gists/new.html'),
119 defaults=defaults,
119 defaults=defaults,
120 errors=errors.error_dict or {},
120 errors=errors.error_dict or {},
121 prefix_error=False,
121 prefix_error=False,
122 encoding="UTF-8"
122 encoding="UTF-8"
123 )
123 )
124
124
125 except Exception, e:
125 except Exception, e:
126 log.error(traceback.format_exc())
126 log.error(traceback.format_exc())
127 h.flash(_('Error occurred during gist creation'), category='error')
127 h.flash(_('Error occurred during gist creation'), category='error')
128 return redirect(url('new_gist'))
128 return redirect(url('new_gist'))
129 return redirect(url('gist', id=new_gist_id))
129 return redirect(url('gist', gist_id=new_gist_id))
130
130
131 @LoginRequired()
131 @LoginRequired()
132 @NotAnonymous()
132 @NotAnonymous()
133 def new(self, format='html'):
133 def new(self, format='html'):
134 """GET /admin/gists/new: Form to create a new item"""
134 """GET /admin/gists/new: Form to create a new item"""
135 # url('new_gist')
135 # url('new_gist')
136 self.__load_defaults()
136 self.__load_defaults()
137 return render('admin/gists/new.html')
137 return render('admin/gists/new.html')
138
138
139 @LoginRequired()
139 @LoginRequired()
140 @NotAnonymous()
140 @NotAnonymous()
141 def update(self, id):
141 def update(self, gist_id):
142 """PUT /admin/gists/id: Update an existing item"""
142 """PUT /admin/gists/gist_id: Update an existing item"""
143 # Forms posted to this method should contain a hidden field:
143 # Forms posted to this method should contain a hidden field:
144 # <input type="hidden" name="_method" value="PUT" />
144 # <input type="hidden" name="_method" value="PUT" />
145 # Or using helpers:
145 # Or using helpers:
146 # h.form(url('gist', id=ID),
146 # h.form(url('gist', gist_id=ID),
147 # method='put')
147 # method='put')
148 # url('gist', id=ID)
148 # url('gist', gist_id=ID)
149
149
150 @LoginRequired()
150 @LoginRequired()
151 @NotAnonymous()
151 @NotAnonymous()
152 def delete(self, id):
152 def delete(self, gist_id):
153 """DELETE /admin/gists/id: Delete an existing item"""
153 """DELETE /admin/gists/gist_id: Delete an existing item"""
154 # Forms posted to this method should contain a hidden field:
154 # Forms posted to this method should contain a hidden field:
155 # <input type="hidden" name="_method" value="DELETE" />
155 # <input type="hidden" name="_method" value="DELETE" />
156 # Or using helpers:
156 # Or using helpers:
157 # h.form(url('gist', id=ID),
157 # h.form(url('gist', gist_id=ID),
158 # method='delete')
158 # method='delete')
159 # url('gist', id=ID)
159 # url('gist', gist_id=ID)
160 gist = GistModel().get_gist(id)
160 gist = GistModel().get_gist(gist_id)
161 owner = gist.gist_owner == c.rhodecode_user.user_id
161 owner = gist.gist_owner == c.rhodecode_user.user_id
162 if h.HasPermissionAny('hg.admin')() or owner:
162 if h.HasPermissionAny('hg.admin')() or owner:
163 GistModel().delete(gist)
163 GistModel().delete(gist)
164 Session().commit()
164 Session().commit()
165 h.flash(_('Deleted gist %s') % gist.gist_access_id, category='success')
165 h.flash(_('Deleted gist %s') % gist.gist_access_id, category='success')
166 else:
166 else:
167 raise HTTPForbidden()
167 raise HTTPForbidden()
168
168
169 return redirect(url('gists'))
169 return redirect(url('gists'))
170
170
171 @LoginRequired()
171 @LoginRequired()
172 def show(self, id, format='html'):
172 def show(self, gist_id, format='html'):
173 """GET /admin/gists/id: Show a specific item"""
173 """GET /admin/gists/gist_id: Show a specific item"""
174 # url('gist', id=ID)
174 # url('gist', gist_id=ID)
175 gist_id = id
176 c.gist = Gist.get_or_404(gist_id)
175 c.gist = Gist.get_or_404(gist_id)
177
176
178 #check if this gist is not expired
177 #check if this gist is not expired
179 if c.gist.gist_expires != -1:
178 if c.gist.gist_expires != -1:
180 if time.time() > c.gist.gist_expires:
179 if time.time() > c.gist.gist_expires:
181 log.error('Gist expired at %s' %
180 log.error('Gist expired at %s' %
182 (time_to_datetime(c.gist.gist_expires)))
181 (time_to_datetime(c.gist.gist_expires)))
183 raise HTTPNotFound()
182 raise HTTPNotFound()
184 try:
183 try:
185 c.file_changeset, c.files = GistModel().get_gist_files(gist_id)
184 c.file_changeset, c.files = GistModel().get_gist_files(gist_id)
186 except VCSError:
185 except VCSError:
187 log.error(traceback.format_exc())
186 log.error(traceback.format_exc())
188 raise HTTPNotFound()
187 raise HTTPNotFound()
189
188
190 return render('admin/gists/show.html')
189 return render('admin/gists/show.html')
191
190
192 @LoginRequired()
191 @LoginRequired()
193 @NotAnonymous()
192 @NotAnonymous()
194 def edit(self, id, format='html'):
193 def edit(self, gist_id, format='html'):
195 """GET /admin/gists/id/edit: Form to edit an existing item"""
194 """GET /admin/gists/gist_id/edit: Form to edit an existing item"""
196 # url('edit_gist', id=ID)
195 # url('edit_gist', gist_id=ID)
@@ -1,2218 +1,2218 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import time
27 import time
28 import logging
28 import logging
29 import datetime
29 import datetime
30 import traceback
30 import traceback
31 import hashlib
31 import hashlib
32 import collections
32 import collections
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48
48
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model.meta import Base, Session
54 from rhodecode.model.meta import Base, Session
55
55
56 URL_SEP = '/'
56 URL_SEP = '/'
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59 #==============================================================================
59 #==============================================================================
60 # BASE CLASSES
60 # BASE CLASSES
61 #==============================================================================
61 #==============================================================================
62
62
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
64
64
65
65
66 class BaseModel(object):
66 class BaseModel(object):
67 """
67 """
68 Base Model for all classess
68 Base Model for all classess
69 """
69 """
70
70
71 @classmethod
71 @classmethod
72 def _get_keys(cls):
72 def _get_keys(cls):
73 """return column names for this model """
73 """return column names for this model """
74 return class_mapper(cls).c.keys()
74 return class_mapper(cls).c.keys()
75
75
76 def get_dict(self):
76 def get_dict(self):
77 """
77 """
78 return dict with keys and values corresponding
78 return dict with keys and values corresponding
79 to this model data """
79 to this model data """
80
80
81 d = {}
81 d = {}
82 for k in self._get_keys():
82 for k in self._get_keys():
83 d[k] = getattr(self, k)
83 d[k] = getattr(self, k)
84
84
85 # also use __json__() if present to get additional fields
85 # also use __json__() if present to get additional fields
86 _json_attr = getattr(self, '__json__', None)
86 _json_attr = getattr(self, '__json__', None)
87 if _json_attr:
87 if _json_attr:
88 # update with attributes from __json__
88 # update with attributes from __json__
89 if callable(_json_attr):
89 if callable(_json_attr):
90 _json_attr = _json_attr()
90 _json_attr = _json_attr()
91 for k, val in _json_attr.iteritems():
91 for k, val in _json_attr.iteritems():
92 d[k] = val
92 d[k] = val
93 return d
93 return d
94
94
95 def get_appstruct(self):
95 def get_appstruct(self):
96 """return list with keys and values tupples corresponding
96 """return list with keys and values tupples corresponding
97 to this model data """
97 to this model data """
98
98
99 l = []
99 l = []
100 for k in self._get_keys():
100 for k in self._get_keys():
101 l.append((k, getattr(self, k),))
101 l.append((k, getattr(self, k),))
102 return l
102 return l
103
103
104 def populate_obj(self, populate_dict):
104 def populate_obj(self, populate_dict):
105 """populate model with data from given populate_dict"""
105 """populate model with data from given populate_dict"""
106
106
107 for k in self._get_keys():
107 for k in self._get_keys():
108 if k in populate_dict:
108 if k in populate_dict:
109 setattr(self, k, populate_dict[k])
109 setattr(self, k, populate_dict[k])
110
110
111 @classmethod
111 @classmethod
112 def query(cls):
112 def query(cls):
113 return Session().query(cls)
113 return Session().query(cls)
114
114
115 @classmethod
115 @classmethod
116 def get(cls, id_):
116 def get(cls, id_):
117 if id_:
117 if id_:
118 return cls.query().get(id_)
118 return cls.query().get(id_)
119
119
120 @classmethod
120 @classmethod
121 def get_or_404(cls, id_):
121 def get_or_404(cls, id_):
122 try:
122 try:
123 id_ = int(id_)
123 id_ = int(id_)
124 except (TypeError, ValueError):
124 except (TypeError, ValueError):
125 raise HTTPNotFound
125 raise HTTPNotFound
126
126
127 res = cls.query().get(id_)
127 res = cls.query().get(id_)
128 if not res:
128 if not res:
129 raise HTTPNotFound
129 raise HTTPNotFound
130 return res
130 return res
131
131
132 @classmethod
132 @classmethod
133 def getAll(cls):
133 def getAll(cls):
134 # deprecated and left for backward compatibility
134 # deprecated and left for backward compatibility
135 return cls.get_all()
135 return cls.get_all()
136
136
137 @classmethod
137 @classmethod
138 def get_all(cls):
138 def get_all(cls):
139 return cls.query().all()
139 return cls.query().all()
140
140
141 @classmethod
141 @classmethod
142 def delete(cls, id_):
142 def delete(cls, id_):
143 obj = cls.query().get(id_)
143 obj = cls.query().get(id_)
144 Session().delete(obj)
144 Session().delete(obj)
145
145
146 def __repr__(self):
146 def __repr__(self):
147 if hasattr(self, '__unicode__'):
147 if hasattr(self, '__unicode__'):
148 # python repr needs to return str
148 # python repr needs to return str
149 return safe_str(self.__unicode__())
149 return safe_str(self.__unicode__())
150 return '<DB:%s>' % (self.__class__.__name__)
150 return '<DB:%s>' % (self.__class__.__name__)
151
151
152
152
153 class RhodeCodeSetting(Base, BaseModel):
153 class RhodeCodeSetting(Base, BaseModel):
154 __tablename__ = 'rhodecode_settings'
154 __tablename__ = 'rhodecode_settings'
155 __table_args__ = (
155 __table_args__ = (
156 UniqueConstraint('app_settings_name'),
156 UniqueConstraint('app_settings_name'),
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
158 'mysql_charset': 'utf8'}
158 'mysql_charset': 'utf8'}
159 )
159 )
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
163
163
164 def __init__(self, k='', v=''):
164 def __init__(self, k='', v=''):
165 self.app_settings_name = k
165 self.app_settings_name = k
166 self.app_settings_value = v
166 self.app_settings_value = v
167
167
168 @validates('_app_settings_value')
168 @validates('_app_settings_value')
169 def validate_settings_value(self, key, val):
169 def validate_settings_value(self, key, val):
170 assert type(val) == unicode
170 assert type(val) == unicode
171 return val
171 return val
172
172
173 @hybrid_property
173 @hybrid_property
174 def app_settings_value(self):
174 def app_settings_value(self):
175 v = self._app_settings_value
175 v = self._app_settings_value
176 if self.app_settings_name in ["ldap_active",
176 if self.app_settings_name in ["ldap_active",
177 "default_repo_enable_statistics",
177 "default_repo_enable_statistics",
178 "default_repo_enable_locking",
178 "default_repo_enable_locking",
179 "default_repo_private",
179 "default_repo_private",
180 "default_repo_enable_downloads"]:
180 "default_repo_enable_downloads"]:
181 v = str2bool(v)
181 v = str2bool(v)
182 return v
182 return v
183
183
184 @app_settings_value.setter
184 @app_settings_value.setter
185 def app_settings_value(self, val):
185 def app_settings_value(self, val):
186 """
186 """
187 Setter that will always make sure we use unicode in app_settings_value
187 Setter that will always make sure we use unicode in app_settings_value
188
188
189 :param val:
189 :param val:
190 """
190 """
191 self._app_settings_value = safe_unicode(val)
191 self._app_settings_value = safe_unicode(val)
192
192
193 def __unicode__(self):
193 def __unicode__(self):
194 return u"<%s('%s:%s')>" % (
194 return u"<%s('%s:%s')>" % (
195 self.__class__.__name__,
195 self.__class__.__name__,
196 self.app_settings_name, self.app_settings_value
196 self.app_settings_name, self.app_settings_value
197 )
197 )
198
198
199 @classmethod
199 @classmethod
200 def get_by_name(cls, key):
200 def get_by_name(cls, key):
201 return cls.query()\
201 return cls.query()\
202 .filter(cls.app_settings_name == key).scalar()
202 .filter(cls.app_settings_name == key).scalar()
203
203
204 @classmethod
204 @classmethod
205 def get_by_name_or_create(cls, key):
205 def get_by_name_or_create(cls, key):
206 res = cls.get_by_name(key)
206 res = cls.get_by_name(key)
207 if not res:
207 if not res:
208 res = cls(key)
208 res = cls(key)
209 return res
209 return res
210
210
211 @classmethod
211 @classmethod
212 def get_app_settings(cls, cache=False):
212 def get_app_settings(cls, cache=False):
213
213
214 ret = cls.query()
214 ret = cls.query()
215
215
216 if cache:
216 if cache:
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
218
218
219 if not ret:
219 if not ret:
220 raise Exception('Could not get application settings !')
220 raise Exception('Could not get application settings !')
221 settings = {}
221 settings = {}
222 for each in ret:
222 for each in ret:
223 settings['rhodecode_' + each.app_settings_name] = \
223 settings['rhodecode_' + each.app_settings_name] = \
224 each.app_settings_value
224 each.app_settings_value
225
225
226 return settings
226 return settings
227
227
228 @classmethod
228 @classmethod
229 def get_ldap_settings(cls, cache=False):
229 def get_ldap_settings(cls, cache=False):
230 ret = cls.query()\
230 ret = cls.query()\
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
232 fd = {}
232 fd = {}
233 for row in ret:
233 for row in ret:
234 fd.update({row.app_settings_name: row.app_settings_value})
234 fd.update({row.app_settings_name: row.app_settings_value})
235
235
236 return fd
236 return fd
237
237
238 @classmethod
238 @classmethod
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
240 ret = cls.query()\
240 ret = cls.query()\
241 .filter(cls.app_settings_name.startswith('default_')).all()
241 .filter(cls.app_settings_name.startswith('default_')).all()
242 fd = {}
242 fd = {}
243 for row in ret:
243 for row in ret:
244 key = row.app_settings_name
244 key = row.app_settings_name
245 if strip_prefix:
245 if strip_prefix:
246 key = remove_prefix(key, prefix='default_')
246 key = remove_prefix(key, prefix='default_')
247 fd.update({key: row.app_settings_value})
247 fd.update({key: row.app_settings_value})
248
248
249 return fd
249 return fd
250
250
251
251
252 class RhodeCodeUi(Base, BaseModel):
252 class RhodeCodeUi(Base, BaseModel):
253 __tablename__ = 'rhodecode_ui'
253 __tablename__ = 'rhodecode_ui'
254 __table_args__ = (
254 __table_args__ = (
255 UniqueConstraint('ui_key'),
255 UniqueConstraint('ui_key'),
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
257 'mysql_charset': 'utf8'}
257 'mysql_charset': 'utf8'}
258 )
258 )
259
259
260 HOOK_UPDATE = 'changegroup.update'
260 HOOK_UPDATE = 'changegroup.update'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
262 HOOK_PUSH = 'changegroup.push_logger'
262 HOOK_PUSH = 'changegroup.push_logger'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
264 HOOK_PULL = 'outgoing.pull_logger'
264 HOOK_PULL = 'outgoing.pull_logger'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
266
266
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
272
272
273 @classmethod
273 @classmethod
274 def get_by_key(cls, key):
274 def get_by_key(cls, key):
275 return cls.query().filter(cls.ui_key == key).scalar()
275 return cls.query().filter(cls.ui_key == key).scalar()
276
276
277 @classmethod
277 @classmethod
278 def get_builtin_hooks(cls):
278 def get_builtin_hooks(cls):
279 q = cls.query()
279 q = cls.query()
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
283 return q.all()
283 return q.all()
284
284
285 @classmethod
285 @classmethod
286 def get_custom_hooks(cls):
286 def get_custom_hooks(cls):
287 q = cls.query()
287 q = cls.query()
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
291 q = q.filter(cls.ui_section == 'hooks')
291 q = q.filter(cls.ui_section == 'hooks')
292 return q.all()
292 return q.all()
293
293
294 @classmethod
294 @classmethod
295 def get_repos_location(cls):
295 def get_repos_location(cls):
296 return cls.get_by_key('/').ui_value
296 return cls.get_by_key('/').ui_value
297
297
298 @classmethod
298 @classmethod
299 def create_or_update_hook(cls, key, val):
299 def create_or_update_hook(cls, key, val):
300 new_ui = cls.get_by_key(key) or cls()
300 new_ui = cls.get_by_key(key) or cls()
301 new_ui.ui_section = 'hooks'
301 new_ui.ui_section = 'hooks'
302 new_ui.ui_active = True
302 new_ui.ui_active = True
303 new_ui.ui_key = key
303 new_ui.ui_key = key
304 new_ui.ui_value = val
304 new_ui.ui_value = val
305
305
306 Session().add(new_ui)
306 Session().add(new_ui)
307
307
308 def __repr__(self):
308 def __repr__(self):
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
310 self.ui_value)
310 self.ui_value)
311
311
312
312
313 class User(Base, BaseModel):
313 class User(Base, BaseModel):
314 __tablename__ = 'users'
314 __tablename__ = 'users'
315 __table_args__ = (
315 __table_args__ = (
316 UniqueConstraint('username'), UniqueConstraint('email'),
316 UniqueConstraint('username'), UniqueConstraint('email'),
317 Index('u_username_idx', 'username'),
317 Index('u_username_idx', 'username'),
318 Index('u_email_idx', 'email'),
318 Index('u_email_idx', 'email'),
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
320 'mysql_charset': 'utf8'}
320 'mysql_charset': 'utf8'}
321 )
321 )
322 DEFAULT_USER = 'default'
322 DEFAULT_USER = 'default'
323
323
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
336
336
337 user_log = relationship('UserLog')
337 user_log = relationship('UserLog')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
339
339
340 repositories = relationship('Repository')
340 repositories = relationship('Repository')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
343
343
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
346
346
347 group_member = relationship('UserGroupMember', cascade='all')
347 group_member = relationship('UserGroupMember', cascade='all')
348
348
349 notifications = relationship('UserNotification', cascade='all')
349 notifications = relationship('UserNotification', cascade='all')
350 # notifications assigned to this user
350 # notifications assigned to this user
351 user_created_notifications = relationship('Notification', cascade='all')
351 user_created_notifications = relationship('Notification', cascade='all')
352 # comments created by this user
352 # comments created by this user
353 user_comments = relationship('ChangesetComment', cascade='all')
353 user_comments = relationship('ChangesetComment', cascade='all')
354 #extra emails for this user
354 #extra emails for this user
355 user_emails = relationship('UserEmailMap', cascade='all')
355 user_emails = relationship('UserEmailMap', cascade='all')
356
356
357 @hybrid_property
357 @hybrid_property
358 def email(self):
358 def email(self):
359 return self._email
359 return self._email
360
360
361 @email.setter
361 @email.setter
362 def email(self, val):
362 def email(self, val):
363 self._email = val.lower() if val else None
363 self._email = val.lower() if val else None
364
364
365 @property
365 @property
366 def firstname(self):
366 def firstname(self):
367 # alias for future
367 # alias for future
368 return self.name
368 return self.name
369
369
370 @property
370 @property
371 def emails(self):
371 def emails(self):
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
373 return [self.email] + [x.email for x in other]
373 return [self.email] + [x.email for x in other]
374
374
375 @property
375 @property
376 def ip_addresses(self):
376 def ip_addresses(self):
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
378 return [x.ip_addr for x in ret]
378 return [x.ip_addr for x in ret]
379
379
380 @property
380 @property
381 def username_and_name(self):
381 def username_and_name(self):
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
383
383
384 @property
384 @property
385 def full_name(self):
385 def full_name(self):
386 return '%s %s' % (self.firstname, self.lastname)
386 return '%s %s' % (self.firstname, self.lastname)
387
387
388 @property
388 @property
389 def full_name_or_username(self):
389 def full_name_or_username(self):
390 return ('%s %s' % (self.firstname, self.lastname)
390 return ('%s %s' % (self.firstname, self.lastname)
391 if (self.firstname and self.lastname) else self.username)
391 if (self.firstname and self.lastname) else self.username)
392
392
393 @property
393 @property
394 def full_contact(self):
394 def full_contact(self):
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
396
396
397 @property
397 @property
398 def short_contact(self):
398 def short_contact(self):
399 return '%s %s' % (self.firstname, self.lastname)
399 return '%s %s' % (self.firstname, self.lastname)
400
400
401 @property
401 @property
402 def is_admin(self):
402 def is_admin(self):
403 return self.admin
403 return self.admin
404
404
405 @property
405 @property
406 def AuthUser(self):
406 def AuthUser(self):
407 """
407 """
408 Returns instance of AuthUser for this user
408 Returns instance of AuthUser for this user
409 """
409 """
410 from rhodecode.lib.auth import AuthUser
410 from rhodecode.lib.auth import AuthUser
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
412 username=self.username)
412 username=self.username)
413
413
414 def __unicode__(self):
414 def __unicode__(self):
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
416 self.user_id, self.username)
416 self.user_id, self.username)
417
417
418 @classmethod
418 @classmethod
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
420 if case_insensitive:
420 if case_insensitive:
421 q = cls.query().filter(cls.username.ilike(username))
421 q = cls.query().filter(cls.username.ilike(username))
422 else:
422 else:
423 q = cls.query().filter(cls.username == username)
423 q = cls.query().filter(cls.username == username)
424
424
425 if cache:
425 if cache:
426 q = q.options(FromCache(
426 q = q.options(FromCache(
427 "sql_cache_short",
427 "sql_cache_short",
428 "get_user_%s" % _hash_key(username)
428 "get_user_%s" % _hash_key(username)
429 )
429 )
430 )
430 )
431 return q.scalar()
431 return q.scalar()
432
432
433 @classmethod
433 @classmethod
434 def get_by_api_key(cls, api_key, cache=False):
434 def get_by_api_key(cls, api_key, cache=False):
435 q = cls.query().filter(cls.api_key == api_key)
435 q = cls.query().filter(cls.api_key == api_key)
436
436
437 if cache:
437 if cache:
438 q = q.options(FromCache("sql_cache_short",
438 q = q.options(FromCache("sql_cache_short",
439 "get_api_key_%s" % api_key))
439 "get_api_key_%s" % api_key))
440 return q.scalar()
440 return q.scalar()
441
441
442 @classmethod
442 @classmethod
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
444 if case_insensitive:
444 if case_insensitive:
445 q = cls.query().filter(cls.email.ilike(email))
445 q = cls.query().filter(cls.email.ilike(email))
446 else:
446 else:
447 q = cls.query().filter(cls.email == email)
447 q = cls.query().filter(cls.email == email)
448
448
449 if cache:
449 if cache:
450 q = q.options(FromCache("sql_cache_short",
450 q = q.options(FromCache("sql_cache_short",
451 "get_email_key_%s" % email))
451 "get_email_key_%s" % email))
452
452
453 ret = q.scalar()
453 ret = q.scalar()
454 if ret is None:
454 if ret is None:
455 q = UserEmailMap.query()
455 q = UserEmailMap.query()
456 # try fetching in alternate email map
456 # try fetching in alternate email map
457 if case_insensitive:
457 if case_insensitive:
458 q = q.filter(UserEmailMap.email.ilike(email))
458 q = q.filter(UserEmailMap.email.ilike(email))
459 else:
459 else:
460 q = q.filter(UserEmailMap.email == email)
460 q = q.filter(UserEmailMap.email == email)
461 q = q.options(joinedload(UserEmailMap.user))
461 q = q.options(joinedload(UserEmailMap.user))
462 if cache:
462 if cache:
463 q = q.options(FromCache("sql_cache_short",
463 q = q.options(FromCache("sql_cache_short",
464 "get_email_map_key_%s" % email))
464 "get_email_map_key_%s" % email))
465 ret = getattr(q.scalar(), 'user', None)
465 ret = getattr(q.scalar(), 'user', None)
466
466
467 return ret
467 return ret
468
468
469 @classmethod
469 @classmethod
470 def get_from_cs_author(cls, author):
470 def get_from_cs_author(cls, author):
471 """
471 """
472 Tries to get User objects out of commit author string
472 Tries to get User objects out of commit author string
473
473
474 :param author:
474 :param author:
475 """
475 """
476 from rhodecode.lib.helpers import email, author_name
476 from rhodecode.lib.helpers import email, author_name
477 # Valid email in the attribute passed, see if they're in the system
477 # Valid email in the attribute passed, see if they're in the system
478 _email = email(author)
478 _email = email(author)
479 if _email:
479 if _email:
480 user = cls.get_by_email(_email, case_insensitive=True)
480 user = cls.get_by_email(_email, case_insensitive=True)
481 if user:
481 if user:
482 return user
482 return user
483 # Maybe we can match by username?
483 # Maybe we can match by username?
484 _author = author_name(author)
484 _author = author_name(author)
485 user = cls.get_by_username(_author, case_insensitive=True)
485 user = cls.get_by_username(_author, case_insensitive=True)
486 if user:
486 if user:
487 return user
487 return user
488
488
489 def update_lastlogin(self):
489 def update_lastlogin(self):
490 """Update user lastlogin"""
490 """Update user lastlogin"""
491 self.last_login = datetime.datetime.now()
491 self.last_login = datetime.datetime.now()
492 Session().add(self)
492 Session().add(self)
493 log.debug('updated user %s lastlogin' % self.username)
493 log.debug('updated user %s lastlogin' % self.username)
494
494
495 @classmethod
495 @classmethod
496 def get_first_admin(cls):
496 def get_first_admin(cls):
497 user = User.query().filter(User.admin == True).first()
497 user = User.query().filter(User.admin == True).first()
498 if user is None:
498 if user is None:
499 raise Exception('Missing administrative account!')
499 raise Exception('Missing administrative account!')
500 return user
500 return user
501
501
502 @classmethod
502 @classmethod
503 def get_default_user(cls, cache=False):
503 def get_default_user(cls, cache=False):
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
505 if user is None:
505 if user is None:
506 raise Exception('Missing default account!')
506 raise Exception('Missing default account!')
507 return user
507 return user
508
508
509 def get_api_data(self):
509 def get_api_data(self):
510 """
510 """
511 Common function for generating user related data for API
511 Common function for generating user related data for API
512 """
512 """
513 user = self
513 user = self
514 data = dict(
514 data = dict(
515 user_id=user.user_id,
515 user_id=user.user_id,
516 username=user.username,
516 username=user.username,
517 firstname=user.name,
517 firstname=user.name,
518 lastname=user.lastname,
518 lastname=user.lastname,
519 email=user.email,
519 email=user.email,
520 emails=user.emails,
520 emails=user.emails,
521 api_key=user.api_key,
521 api_key=user.api_key,
522 active=user.active,
522 active=user.active,
523 admin=user.admin,
523 admin=user.admin,
524 ldap_dn=user.ldap_dn,
524 ldap_dn=user.ldap_dn,
525 last_login=user.last_login,
525 last_login=user.last_login,
526 ip_addresses=user.ip_addresses
526 ip_addresses=user.ip_addresses
527 )
527 )
528 return data
528 return data
529
529
530 def __json__(self):
530 def __json__(self):
531 data = dict(
531 data = dict(
532 full_name=self.full_name,
532 full_name=self.full_name,
533 full_name_or_username=self.full_name_or_username,
533 full_name_or_username=self.full_name_or_username,
534 short_contact=self.short_contact,
534 short_contact=self.short_contact,
535 full_contact=self.full_contact
535 full_contact=self.full_contact
536 )
536 )
537 data.update(self.get_api_data())
537 data.update(self.get_api_data())
538 return data
538 return data
539
539
540
540
541 class UserEmailMap(Base, BaseModel):
541 class UserEmailMap(Base, BaseModel):
542 __tablename__ = 'user_email_map'
542 __tablename__ = 'user_email_map'
543 __table_args__ = (
543 __table_args__ = (
544 Index('uem_email_idx', 'email'),
544 Index('uem_email_idx', 'email'),
545 UniqueConstraint('email'),
545 UniqueConstraint('email'),
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
547 'mysql_charset': 'utf8'}
547 'mysql_charset': 'utf8'}
548 )
548 )
549 __mapper_args__ = {}
549 __mapper_args__ = {}
550
550
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
554 user = relationship('User', lazy='joined')
554 user = relationship('User', lazy='joined')
555
555
556 @validates('_email')
556 @validates('_email')
557 def validate_email(self, key, email):
557 def validate_email(self, key, email):
558 # check if this email is not main one
558 # check if this email is not main one
559 main_email = Session().query(User).filter(User.email == email).scalar()
559 main_email = Session().query(User).filter(User.email == email).scalar()
560 if main_email is not None:
560 if main_email is not None:
561 raise AttributeError('email %s is present is user table' % email)
561 raise AttributeError('email %s is present is user table' % email)
562 return email
562 return email
563
563
564 @hybrid_property
564 @hybrid_property
565 def email(self):
565 def email(self):
566 return self._email
566 return self._email
567
567
568 @email.setter
568 @email.setter
569 def email(self, val):
569 def email(self, val):
570 self._email = val.lower() if val else None
570 self._email = val.lower() if val else None
571
571
572
572
573 class UserIpMap(Base, BaseModel):
573 class UserIpMap(Base, BaseModel):
574 __tablename__ = 'user_ip_map'
574 __tablename__ = 'user_ip_map'
575 __table_args__ = (
575 __table_args__ = (
576 UniqueConstraint('user_id', 'ip_addr'),
576 UniqueConstraint('user_id', 'ip_addr'),
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
578 'mysql_charset': 'utf8'}
578 'mysql_charset': 'utf8'}
579 )
579 )
580 __mapper_args__ = {}
580 __mapper_args__ = {}
581
581
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
586 user = relationship('User', lazy='joined')
586 user = relationship('User', lazy='joined')
587
587
588 @classmethod
588 @classmethod
589 def _get_ip_range(cls, ip_addr):
589 def _get_ip_range(cls, ip_addr):
590 from rhodecode.lib import ipaddr
590 from rhodecode.lib import ipaddr
591 net = ipaddr.IPNetwork(address=ip_addr)
591 net = ipaddr.IPNetwork(address=ip_addr)
592 return [str(net.network), str(net.broadcast)]
592 return [str(net.network), str(net.broadcast)]
593
593
594 def __json__(self):
594 def __json__(self):
595 return dict(
595 return dict(
596 ip_addr=self.ip_addr,
596 ip_addr=self.ip_addr,
597 ip_range=self._get_ip_range(self.ip_addr)
597 ip_range=self._get_ip_range(self.ip_addr)
598 )
598 )
599
599
600
600
601 class UserLog(Base, BaseModel):
601 class UserLog(Base, BaseModel):
602 __tablename__ = 'user_logs'
602 __tablename__ = 'user_logs'
603 __table_args__ = (
603 __table_args__ = (
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
605 'mysql_charset': 'utf8'},
605 'mysql_charset': 'utf8'},
606 )
606 )
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
615
615
616 @property
616 @property
617 def action_as_day(self):
617 def action_as_day(self):
618 return datetime.date(*self.action_date.timetuple()[:3])
618 return datetime.date(*self.action_date.timetuple()[:3])
619
619
620 user = relationship('User')
620 user = relationship('User')
621 repository = relationship('Repository', cascade='')
621 repository = relationship('Repository', cascade='')
622
622
623
623
624 class UserGroup(Base, BaseModel):
624 class UserGroup(Base, BaseModel):
625 __tablename__ = 'users_groups'
625 __tablename__ = 'users_groups'
626 __table_args__ = (
626 __table_args__ = (
627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
628 'mysql_charset': 'utf8'},
628 'mysql_charset': 'utf8'},
629 )
629 )
630
630
631 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
631 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
632 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
632 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
633 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
633 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
634 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
634 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
636
636
637 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
637 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
638 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
638 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
639 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
639 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
640 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
640 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
641 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
641 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
642 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
642 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
643
643
644 user = relationship('User')
644 user = relationship('User')
645
645
646 def __unicode__(self):
646 def __unicode__(self):
647 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
647 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
648 self.users_group_id,
648 self.users_group_id,
649 self.users_group_name)
649 self.users_group_name)
650
650
651 @classmethod
651 @classmethod
652 def get_by_group_name(cls, group_name, cache=False,
652 def get_by_group_name(cls, group_name, cache=False,
653 case_insensitive=False):
653 case_insensitive=False):
654 if case_insensitive:
654 if case_insensitive:
655 q = cls.query().filter(cls.users_group_name.ilike(group_name))
655 q = cls.query().filter(cls.users_group_name.ilike(group_name))
656 else:
656 else:
657 q = cls.query().filter(cls.users_group_name == group_name)
657 q = cls.query().filter(cls.users_group_name == group_name)
658 if cache:
658 if cache:
659 q = q.options(FromCache(
659 q = q.options(FromCache(
660 "sql_cache_short",
660 "sql_cache_short",
661 "get_user_%s" % _hash_key(group_name)
661 "get_user_%s" % _hash_key(group_name)
662 )
662 )
663 )
663 )
664 return q.scalar()
664 return q.scalar()
665
665
666 @classmethod
666 @classmethod
667 def get(cls, users_group_id, cache=False):
667 def get(cls, users_group_id, cache=False):
668 users_group = cls.query()
668 users_group = cls.query()
669 if cache:
669 if cache:
670 users_group = users_group.options(FromCache("sql_cache_short",
670 users_group = users_group.options(FromCache("sql_cache_short",
671 "get_users_group_%s" % users_group_id))
671 "get_users_group_%s" % users_group_id))
672 return users_group.get(users_group_id)
672 return users_group.get(users_group_id)
673
673
674 def get_api_data(self):
674 def get_api_data(self):
675 users_group = self
675 users_group = self
676
676
677 data = dict(
677 data = dict(
678 users_group_id=users_group.users_group_id,
678 users_group_id=users_group.users_group_id,
679 group_name=users_group.users_group_name,
679 group_name=users_group.users_group_name,
680 active=users_group.users_group_active,
680 active=users_group.users_group_active,
681 )
681 )
682
682
683 return data
683 return data
684
684
685
685
686 class UserGroupMember(Base, BaseModel):
686 class UserGroupMember(Base, BaseModel):
687 __tablename__ = 'users_groups_members'
687 __tablename__ = 'users_groups_members'
688 __table_args__ = (
688 __table_args__ = (
689 {'extend_existing': True, 'mysql_engine': 'InnoDB',
689 {'extend_existing': True, 'mysql_engine': 'InnoDB',
690 'mysql_charset': 'utf8'},
690 'mysql_charset': 'utf8'},
691 )
691 )
692
692
693 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
693 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
694 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
694 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
695 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
695 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
696
696
697 user = relationship('User', lazy='joined')
697 user = relationship('User', lazy='joined')
698 users_group = relationship('UserGroup')
698 users_group = relationship('UserGroup')
699
699
700 def __init__(self, gr_id='', u_id=''):
700 def __init__(self, gr_id='', u_id=''):
701 self.users_group_id = gr_id
701 self.users_group_id = gr_id
702 self.user_id = u_id
702 self.user_id = u_id
703
703
704
704
705 class RepositoryField(Base, BaseModel):
705 class RepositoryField(Base, BaseModel):
706 __tablename__ = 'repositories_fields'
706 __tablename__ = 'repositories_fields'
707 __table_args__ = (
707 __table_args__ = (
708 UniqueConstraint('repository_id', 'field_key'), # no-multi field
708 UniqueConstraint('repository_id', 'field_key'), # no-multi field
709 {'extend_existing': True, 'mysql_engine': 'InnoDB',
709 {'extend_existing': True, 'mysql_engine': 'InnoDB',
710 'mysql_charset': 'utf8'},
710 'mysql_charset': 'utf8'},
711 )
711 )
712 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
712 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
713
713
714 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
714 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
715 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
715 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
716 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
716 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
717 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
717 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
718 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
718 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
719 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
719 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
720 field_type = Column("field_type", String(256), nullable=False, unique=None)
720 field_type = Column("field_type", String(256), nullable=False, unique=None)
721 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
721 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
722
722
723 repository = relationship('Repository')
723 repository = relationship('Repository')
724
724
725 @property
725 @property
726 def field_key_prefixed(self):
726 def field_key_prefixed(self):
727 return 'ex_%s' % self.field_key
727 return 'ex_%s' % self.field_key
728
728
729 @classmethod
729 @classmethod
730 def un_prefix_key(cls, key):
730 def un_prefix_key(cls, key):
731 if key.startswith(cls.PREFIX):
731 if key.startswith(cls.PREFIX):
732 return key[len(cls.PREFIX):]
732 return key[len(cls.PREFIX):]
733 return key
733 return key
734
734
735 @classmethod
735 @classmethod
736 def get_by_key_name(cls, key, repo):
736 def get_by_key_name(cls, key, repo):
737 row = cls.query()\
737 row = cls.query()\
738 .filter(cls.repository == repo)\
738 .filter(cls.repository == repo)\
739 .filter(cls.field_key == key).scalar()
739 .filter(cls.field_key == key).scalar()
740 return row
740 return row
741
741
742
742
743 class Repository(Base, BaseModel):
743 class Repository(Base, BaseModel):
744 __tablename__ = 'repositories'
744 __tablename__ = 'repositories'
745 __table_args__ = (
745 __table_args__ = (
746 UniqueConstraint('repo_name'),
746 UniqueConstraint('repo_name'),
747 Index('r_repo_name_idx', 'repo_name'),
747 Index('r_repo_name_idx', 'repo_name'),
748 {'extend_existing': True, 'mysql_engine': 'InnoDB',
748 {'extend_existing': True, 'mysql_engine': 'InnoDB',
749 'mysql_charset': 'utf8'},
749 'mysql_charset': 'utf8'},
750 )
750 )
751
751
752 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
752 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
753 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
753 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
754 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
754 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
755 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
755 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
756 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
756 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
757 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
757 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
758 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
758 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
759 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
759 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
760 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
760 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
761 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
761 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
762 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
762 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
763 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
763 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
764 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
764 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
765 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
765 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
766 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
766 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
767
767
768 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
768 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
769 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
769 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
770
770
771 user = relationship('User')
771 user = relationship('User')
772 fork = relationship('Repository', remote_side=repo_id)
772 fork = relationship('Repository', remote_side=repo_id)
773 group = relationship('RepoGroup')
773 group = relationship('RepoGroup')
774 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
774 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
775 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
775 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
776 stats = relationship('Statistics', cascade='all', uselist=False)
776 stats = relationship('Statistics', cascade='all', uselist=False)
777
777
778 followers = relationship('UserFollowing',
778 followers = relationship('UserFollowing',
779 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
779 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
780 cascade='all')
780 cascade='all')
781 extra_fields = relationship('RepositoryField',
781 extra_fields = relationship('RepositoryField',
782 cascade="all, delete, delete-orphan")
782 cascade="all, delete, delete-orphan")
783
783
784 logs = relationship('UserLog')
784 logs = relationship('UserLog')
785 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
785 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
786
786
787 pull_requests_org = relationship('PullRequest',
787 pull_requests_org = relationship('PullRequest',
788 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
788 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
789 cascade="all, delete, delete-orphan")
789 cascade="all, delete, delete-orphan")
790
790
791 pull_requests_other = relationship('PullRequest',
791 pull_requests_other = relationship('PullRequest',
792 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
792 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
793 cascade="all, delete, delete-orphan")
793 cascade="all, delete, delete-orphan")
794
794
795 def __unicode__(self):
795 def __unicode__(self):
796 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
796 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
797 self.repo_name)
797 self.repo_name)
798
798
799 @hybrid_property
799 @hybrid_property
800 def locked(self):
800 def locked(self):
801 # always should return [user_id, timelocked]
801 # always should return [user_id, timelocked]
802 if self._locked:
802 if self._locked:
803 _lock_info = self._locked.split(':')
803 _lock_info = self._locked.split(':')
804 return int(_lock_info[0]), _lock_info[1]
804 return int(_lock_info[0]), _lock_info[1]
805 return [None, None]
805 return [None, None]
806
806
807 @locked.setter
807 @locked.setter
808 def locked(self, val):
808 def locked(self, val):
809 if val and isinstance(val, (list, tuple)):
809 if val and isinstance(val, (list, tuple)):
810 self._locked = ':'.join(map(str, val))
810 self._locked = ':'.join(map(str, val))
811 else:
811 else:
812 self._locked = None
812 self._locked = None
813
813
814 @hybrid_property
814 @hybrid_property
815 def changeset_cache(self):
815 def changeset_cache(self):
816 from rhodecode.lib.vcs.backends.base import EmptyChangeset
816 from rhodecode.lib.vcs.backends.base import EmptyChangeset
817 dummy = EmptyChangeset().__json__()
817 dummy = EmptyChangeset().__json__()
818 if not self._changeset_cache:
818 if not self._changeset_cache:
819 return dummy
819 return dummy
820 try:
820 try:
821 return json.loads(self._changeset_cache)
821 return json.loads(self._changeset_cache)
822 except TypeError:
822 except TypeError:
823 return dummy
823 return dummy
824
824
825 @changeset_cache.setter
825 @changeset_cache.setter
826 def changeset_cache(self, val):
826 def changeset_cache(self, val):
827 try:
827 try:
828 self._changeset_cache = json.dumps(val)
828 self._changeset_cache = json.dumps(val)
829 except Exception:
829 except Exception:
830 log.error(traceback.format_exc())
830 log.error(traceback.format_exc())
831
831
832 @classmethod
832 @classmethod
833 def url_sep(cls):
833 def url_sep(cls):
834 return URL_SEP
834 return URL_SEP
835
835
836 @classmethod
836 @classmethod
837 def normalize_repo_name(cls, repo_name):
837 def normalize_repo_name(cls, repo_name):
838 """
838 """
839 Normalizes os specific repo_name to the format internally stored inside
839 Normalizes os specific repo_name to the format internally stored inside
840 dabatabase using URL_SEP
840 dabatabase using URL_SEP
841
841
842 :param cls:
842 :param cls:
843 :param repo_name:
843 :param repo_name:
844 """
844 """
845 return cls.url_sep().join(repo_name.split(os.sep))
845 return cls.url_sep().join(repo_name.split(os.sep))
846
846
847 @classmethod
847 @classmethod
848 def get_by_repo_name(cls, repo_name):
848 def get_by_repo_name(cls, repo_name):
849 q = Session().query(cls).filter(cls.repo_name == repo_name)
849 q = Session().query(cls).filter(cls.repo_name == repo_name)
850 q = q.options(joinedload(Repository.fork))\
850 q = q.options(joinedload(Repository.fork))\
851 .options(joinedload(Repository.user))\
851 .options(joinedload(Repository.user))\
852 .options(joinedload(Repository.group))
852 .options(joinedload(Repository.group))
853 return q.scalar()
853 return q.scalar()
854
854
855 @classmethod
855 @classmethod
856 def get_by_full_path(cls, repo_full_path):
856 def get_by_full_path(cls, repo_full_path):
857 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
857 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
858 repo_name = cls.normalize_repo_name(repo_name)
858 repo_name = cls.normalize_repo_name(repo_name)
859 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
859 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
860
860
861 @classmethod
861 @classmethod
862 def get_repo_forks(cls, repo_id):
862 def get_repo_forks(cls, repo_id):
863 return cls.query().filter(Repository.fork_id == repo_id)
863 return cls.query().filter(Repository.fork_id == repo_id)
864
864
865 @classmethod
865 @classmethod
866 def base_path(cls):
866 def base_path(cls):
867 """
867 """
868 Returns base path when all repos are stored
868 Returns base path when all repos are stored
869
869
870 :param cls:
870 :param cls:
871 """
871 """
872 q = Session().query(RhodeCodeUi)\
872 q = Session().query(RhodeCodeUi)\
873 .filter(RhodeCodeUi.ui_key == cls.url_sep())
873 .filter(RhodeCodeUi.ui_key == cls.url_sep())
874 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
874 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
875 return q.one().ui_value
875 return q.one().ui_value
876
876
877 @property
877 @property
878 def forks(self):
878 def forks(self):
879 """
879 """
880 Return forks of this repo
880 Return forks of this repo
881 """
881 """
882 return Repository.get_repo_forks(self.repo_id)
882 return Repository.get_repo_forks(self.repo_id)
883
883
884 @property
884 @property
885 def parent(self):
885 def parent(self):
886 """
886 """
887 Returns fork parent
887 Returns fork parent
888 """
888 """
889 return self.fork
889 return self.fork
890
890
891 @property
891 @property
892 def just_name(self):
892 def just_name(self):
893 return self.repo_name.split(Repository.url_sep())[-1]
893 return self.repo_name.split(Repository.url_sep())[-1]
894
894
895 @property
895 @property
896 def groups_with_parents(self):
896 def groups_with_parents(self):
897 groups = []
897 groups = []
898 if self.group is None:
898 if self.group is None:
899 return groups
899 return groups
900
900
901 cur_gr = self.group
901 cur_gr = self.group
902 groups.insert(0, cur_gr)
902 groups.insert(0, cur_gr)
903 while 1:
903 while 1:
904 gr = getattr(cur_gr, 'parent_group', None)
904 gr = getattr(cur_gr, 'parent_group', None)
905 cur_gr = cur_gr.parent_group
905 cur_gr = cur_gr.parent_group
906 if gr is None:
906 if gr is None:
907 break
907 break
908 groups.insert(0, gr)
908 groups.insert(0, gr)
909
909
910 return groups
910 return groups
911
911
912 @property
912 @property
913 def groups_and_repo(self):
913 def groups_and_repo(self):
914 return self.groups_with_parents, self.just_name, self.repo_name
914 return self.groups_with_parents, self.just_name, self.repo_name
915
915
916 @LazyProperty
916 @LazyProperty
917 def repo_path(self):
917 def repo_path(self):
918 """
918 """
919 Returns base full path for that repository means where it actually
919 Returns base full path for that repository means where it actually
920 exists on a filesystem
920 exists on a filesystem
921 """
921 """
922 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
922 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
923 Repository.url_sep())
923 Repository.url_sep())
924 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
924 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
925 return q.one().ui_value
925 return q.one().ui_value
926
926
927 @property
927 @property
928 def repo_full_path(self):
928 def repo_full_path(self):
929 p = [self.repo_path]
929 p = [self.repo_path]
930 # we need to split the name by / since this is how we store the
930 # we need to split the name by / since this is how we store the
931 # names in the database, but that eventually needs to be converted
931 # names in the database, but that eventually needs to be converted
932 # into a valid system path
932 # into a valid system path
933 p += self.repo_name.split(Repository.url_sep())
933 p += self.repo_name.split(Repository.url_sep())
934 return os.path.join(*map(safe_unicode, p))
934 return os.path.join(*map(safe_unicode, p))
935
935
936 @property
936 @property
937 def cache_keys(self):
937 def cache_keys(self):
938 """
938 """
939 Returns associated cache keys for that repo
939 Returns associated cache keys for that repo
940 """
940 """
941 return CacheInvalidation.query()\
941 return CacheInvalidation.query()\
942 .filter(CacheInvalidation.cache_args == self.repo_name)\
942 .filter(CacheInvalidation.cache_args == self.repo_name)\
943 .order_by(CacheInvalidation.cache_key)\
943 .order_by(CacheInvalidation.cache_key)\
944 .all()
944 .all()
945
945
946 def get_new_name(self, repo_name):
946 def get_new_name(self, repo_name):
947 """
947 """
948 returns new full repository name based on assigned group and new new
948 returns new full repository name based on assigned group and new new
949
949
950 :param group_name:
950 :param group_name:
951 """
951 """
952 path_prefix = self.group.full_path_splitted if self.group else []
952 path_prefix = self.group.full_path_splitted if self.group else []
953 return Repository.url_sep().join(path_prefix + [repo_name])
953 return Repository.url_sep().join(path_prefix + [repo_name])
954
954
955 @property
955 @property
956 def _ui(self):
956 def _ui(self):
957 """
957 """
958 Creates an db based ui object for this repository
958 Creates an db based ui object for this repository
959 """
959 """
960 from rhodecode.lib.utils import make_ui
960 from rhodecode.lib.utils import make_ui
961 return make_ui('db', clear_session=False)
961 return make_ui('db', clear_session=False)
962
962
963 @classmethod
963 @classmethod
964 def is_valid(cls, repo_name):
964 def is_valid(cls, repo_name):
965 """
965 """
966 returns True if given repo name is a valid filesystem repository
966 returns True if given repo name is a valid filesystem repository
967
967
968 :param cls:
968 :param cls:
969 :param repo_name:
969 :param repo_name:
970 """
970 """
971 from rhodecode.lib.utils import is_valid_repo
971 from rhodecode.lib.utils import is_valid_repo
972
972
973 return is_valid_repo(repo_name, cls.base_path())
973 return is_valid_repo(repo_name, cls.base_path())
974
974
975 def get_api_data(self):
975 def get_api_data(self):
976 """
976 """
977 Common function for generating repo api data
977 Common function for generating repo api data
978
978
979 """
979 """
980 repo = self
980 repo = self
981 data = dict(
981 data = dict(
982 repo_id=repo.repo_id,
982 repo_id=repo.repo_id,
983 repo_name=repo.repo_name,
983 repo_name=repo.repo_name,
984 repo_type=repo.repo_type,
984 repo_type=repo.repo_type,
985 clone_uri=repo.clone_uri,
985 clone_uri=repo.clone_uri,
986 private=repo.private,
986 private=repo.private,
987 created_on=repo.created_on,
987 created_on=repo.created_on,
988 description=repo.description,
988 description=repo.description,
989 landing_rev=repo.landing_rev,
989 landing_rev=repo.landing_rev,
990 owner=repo.user.username,
990 owner=repo.user.username,
991 fork_of=repo.fork.repo_name if repo.fork else None,
991 fork_of=repo.fork.repo_name if repo.fork else None,
992 enable_statistics=repo.enable_statistics,
992 enable_statistics=repo.enable_statistics,
993 enable_locking=repo.enable_locking,
993 enable_locking=repo.enable_locking,
994 enable_downloads=repo.enable_downloads,
994 enable_downloads=repo.enable_downloads,
995 last_changeset=repo.changeset_cache,
995 last_changeset=repo.changeset_cache,
996 locked_by=User.get(self.locked[0]).get_api_data() \
996 locked_by=User.get(self.locked[0]).get_api_data() \
997 if self.locked[0] else None,
997 if self.locked[0] else None,
998 locked_date=time_to_datetime(self.locked[1]) \
998 locked_date=time_to_datetime(self.locked[1]) \
999 if self.locked[1] else None
999 if self.locked[1] else None
1000 )
1000 )
1001 rc_config = RhodeCodeSetting.get_app_settings()
1001 rc_config = RhodeCodeSetting.get_app_settings()
1002 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1002 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1003 if repository_fields:
1003 if repository_fields:
1004 for f in self.extra_fields:
1004 for f in self.extra_fields:
1005 data[f.field_key_prefixed] = f.field_value
1005 data[f.field_key_prefixed] = f.field_value
1006
1006
1007 return data
1007 return data
1008
1008
1009 @classmethod
1009 @classmethod
1010 def lock(cls, repo, user_id, lock_time=None):
1010 def lock(cls, repo, user_id, lock_time=None):
1011 if not lock_time:
1011 if not lock_time:
1012 lock_time = time.time()
1012 lock_time = time.time()
1013 repo.locked = [user_id, lock_time]
1013 repo.locked = [user_id, lock_time]
1014 Session().add(repo)
1014 Session().add(repo)
1015 Session().commit()
1015 Session().commit()
1016
1016
1017 @classmethod
1017 @classmethod
1018 def unlock(cls, repo):
1018 def unlock(cls, repo):
1019 repo.locked = None
1019 repo.locked = None
1020 Session().add(repo)
1020 Session().add(repo)
1021 Session().commit()
1021 Session().commit()
1022
1022
1023 @classmethod
1023 @classmethod
1024 def getlock(cls, repo):
1024 def getlock(cls, repo):
1025 return repo.locked
1025 return repo.locked
1026
1026
1027 @property
1027 @property
1028 def last_db_change(self):
1028 def last_db_change(self):
1029 return self.updated_on
1029 return self.updated_on
1030
1030
1031 def clone_url(self, **override):
1031 def clone_url(self, **override):
1032 from pylons import url
1032 from pylons import url
1033 from urlparse import urlparse
1033 from urlparse import urlparse
1034 import urllib
1034 import urllib
1035 parsed_url = urlparse(url('home', qualified=True))
1035 parsed_url = urlparse(url('home', qualified=True))
1036 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1036 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1037 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1037 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1038 args = {
1038 args = {
1039 'user': '',
1039 'user': '',
1040 'pass': '',
1040 'pass': '',
1041 'scheme': parsed_url.scheme,
1041 'scheme': parsed_url.scheme,
1042 'netloc': parsed_url.netloc,
1042 'netloc': parsed_url.netloc,
1043 'prefix': decoded_path,
1043 'prefix': decoded_path,
1044 'path': self.repo_name
1044 'path': self.repo_name
1045 }
1045 }
1046
1046
1047 args.update(override)
1047 args.update(override)
1048 return default_clone_uri % args
1048 return default_clone_uri % args
1049
1049
1050 #==========================================================================
1050 #==========================================================================
1051 # SCM PROPERTIES
1051 # SCM PROPERTIES
1052 #==========================================================================
1052 #==========================================================================
1053
1053
1054 def get_changeset(self, rev=None):
1054 def get_changeset(self, rev=None):
1055 return get_changeset_safe(self.scm_instance, rev)
1055 return get_changeset_safe(self.scm_instance, rev)
1056
1056
1057 def get_landing_changeset(self):
1057 def get_landing_changeset(self):
1058 """
1058 """
1059 Returns landing changeset, or if that doesn't exist returns the tip
1059 Returns landing changeset, or if that doesn't exist returns the tip
1060 """
1060 """
1061 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1061 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1062 return cs
1062 return cs
1063
1063
1064 def update_changeset_cache(self, cs_cache=None):
1064 def update_changeset_cache(self, cs_cache=None):
1065 """
1065 """
1066 Update cache of last changeset for repository, keys should be::
1066 Update cache of last changeset for repository, keys should be::
1067
1067
1068 short_id
1068 short_id
1069 raw_id
1069 raw_id
1070 revision
1070 revision
1071 message
1071 message
1072 date
1072 date
1073 author
1073 author
1074
1074
1075 :param cs_cache:
1075 :param cs_cache:
1076 """
1076 """
1077 from rhodecode.lib.vcs.backends.base import BaseChangeset
1077 from rhodecode.lib.vcs.backends.base import BaseChangeset
1078 if cs_cache is None:
1078 if cs_cache is None:
1079 cs_cache = EmptyChangeset()
1079 cs_cache = EmptyChangeset()
1080 # use no-cache version here
1080 # use no-cache version here
1081 scm_repo = self.scm_instance_no_cache()
1081 scm_repo = self.scm_instance_no_cache()
1082 if scm_repo:
1082 if scm_repo:
1083 cs_cache = scm_repo.get_changeset()
1083 cs_cache = scm_repo.get_changeset()
1084
1084
1085 if isinstance(cs_cache, BaseChangeset):
1085 if isinstance(cs_cache, BaseChangeset):
1086 cs_cache = cs_cache.__json__()
1086 cs_cache = cs_cache.__json__()
1087
1087
1088 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1088 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1089 _default = datetime.datetime.fromtimestamp(0)
1089 _default = datetime.datetime.fromtimestamp(0)
1090 last_change = cs_cache.get('date') or _default
1090 last_change = cs_cache.get('date') or _default
1091 log.debug('updated repo %s with new cs cache %s'
1091 log.debug('updated repo %s with new cs cache %s'
1092 % (self.repo_name, cs_cache))
1092 % (self.repo_name, cs_cache))
1093 self.updated_on = last_change
1093 self.updated_on = last_change
1094 self.changeset_cache = cs_cache
1094 self.changeset_cache = cs_cache
1095 Session().add(self)
1095 Session().add(self)
1096 Session().commit()
1096 Session().commit()
1097 else:
1097 else:
1098 log.debug('Skipping repo:%s already with latest changes'
1098 log.debug('Skipping repo:%s already with latest changes'
1099 % self.repo_name)
1099 % self.repo_name)
1100
1100
1101 @property
1101 @property
1102 def tip(self):
1102 def tip(self):
1103 return self.get_changeset('tip')
1103 return self.get_changeset('tip')
1104
1104
1105 @property
1105 @property
1106 def author(self):
1106 def author(self):
1107 return self.tip.author
1107 return self.tip.author
1108
1108
1109 @property
1109 @property
1110 def last_change(self):
1110 def last_change(self):
1111 return self.scm_instance.last_change
1111 return self.scm_instance.last_change
1112
1112
1113 def get_comments(self, revisions=None):
1113 def get_comments(self, revisions=None):
1114 """
1114 """
1115 Returns comments for this repository grouped by revisions
1115 Returns comments for this repository grouped by revisions
1116
1116
1117 :param revisions: filter query by revisions only
1117 :param revisions: filter query by revisions only
1118 """
1118 """
1119 cmts = ChangesetComment.query()\
1119 cmts = ChangesetComment.query()\
1120 .filter(ChangesetComment.repo == self)
1120 .filter(ChangesetComment.repo == self)
1121 if revisions:
1121 if revisions:
1122 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1122 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1123 grouped = collections.defaultdict(list)
1123 grouped = collections.defaultdict(list)
1124 for cmt in cmts.all():
1124 for cmt in cmts.all():
1125 grouped[cmt.revision].append(cmt)
1125 grouped[cmt.revision].append(cmt)
1126 return grouped
1126 return grouped
1127
1127
1128 def statuses(self, revisions=None):
1128 def statuses(self, revisions=None):
1129 """
1129 """
1130 Returns statuses for this repository
1130 Returns statuses for this repository
1131
1131
1132 :param revisions: list of revisions to get statuses for
1132 :param revisions: list of revisions to get statuses for
1133 """
1133 """
1134
1134
1135 statuses = ChangesetStatus.query()\
1135 statuses = ChangesetStatus.query()\
1136 .filter(ChangesetStatus.repo == self)\
1136 .filter(ChangesetStatus.repo == self)\
1137 .filter(ChangesetStatus.version == 0)
1137 .filter(ChangesetStatus.version == 0)
1138 if revisions:
1138 if revisions:
1139 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1139 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1140 grouped = {}
1140 grouped = {}
1141
1141
1142 #maybe we have open new pullrequest without a status ?
1142 #maybe we have open new pullrequest without a status ?
1143 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1143 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1144 status_lbl = ChangesetStatus.get_status_lbl(stat)
1144 status_lbl = ChangesetStatus.get_status_lbl(stat)
1145 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1145 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1146 for rev in pr.revisions:
1146 for rev in pr.revisions:
1147 pr_id = pr.pull_request_id
1147 pr_id = pr.pull_request_id
1148 pr_repo = pr.other_repo.repo_name
1148 pr_repo = pr.other_repo.repo_name
1149 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1149 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1150
1150
1151 for stat in statuses.all():
1151 for stat in statuses.all():
1152 pr_id = pr_repo = None
1152 pr_id = pr_repo = None
1153 if stat.pull_request:
1153 if stat.pull_request:
1154 pr_id = stat.pull_request.pull_request_id
1154 pr_id = stat.pull_request.pull_request_id
1155 pr_repo = stat.pull_request.other_repo.repo_name
1155 pr_repo = stat.pull_request.other_repo.repo_name
1156 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1156 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1157 pr_id, pr_repo]
1157 pr_id, pr_repo]
1158 return grouped
1158 return grouped
1159
1159
1160 def _repo_size(self):
1160 def _repo_size(self):
1161 from rhodecode.lib import helpers as h
1161 from rhodecode.lib import helpers as h
1162 log.debug('calculating repository size...')
1162 log.debug('calculating repository size...')
1163 return h.format_byte_size(self.scm_instance.size)
1163 return h.format_byte_size(self.scm_instance.size)
1164
1164
1165 #==========================================================================
1165 #==========================================================================
1166 # SCM CACHE INSTANCE
1166 # SCM CACHE INSTANCE
1167 #==========================================================================
1167 #==========================================================================
1168
1168
1169 def set_invalidate(self):
1169 def set_invalidate(self):
1170 """
1170 """
1171 Mark caches of this repo as invalid.
1171 Mark caches of this repo as invalid.
1172 """
1172 """
1173 CacheInvalidation.set_invalidate(self.repo_name)
1173 CacheInvalidation.set_invalidate(self.repo_name)
1174
1174
1175 def scm_instance_no_cache(self):
1175 def scm_instance_no_cache(self):
1176 return self.__get_instance()
1176 return self.__get_instance()
1177
1177
1178 @property
1178 @property
1179 def scm_instance(self):
1179 def scm_instance(self):
1180 import rhodecode
1180 import rhodecode
1181 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1181 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1182 if full_cache:
1182 if full_cache:
1183 return self.scm_instance_cached()
1183 return self.scm_instance_cached()
1184 return self.__get_instance()
1184 return self.__get_instance()
1185
1185
1186 def scm_instance_cached(self, valid_cache_keys=None):
1186 def scm_instance_cached(self, valid_cache_keys=None):
1187 @cache_region('long_term')
1187 @cache_region('long_term')
1188 def _c(repo_name):
1188 def _c(repo_name):
1189 return self.__get_instance()
1189 return self.__get_instance()
1190 rn = self.repo_name
1190 rn = self.repo_name
1191
1191
1192 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1192 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1193 if not valid:
1193 if not valid:
1194 log.debug('Cache for %s invalidated, getting new object' % (rn))
1194 log.debug('Cache for %s invalidated, getting new object' % (rn))
1195 region_invalidate(_c, None, rn)
1195 region_invalidate(_c, None, rn)
1196 else:
1196 else:
1197 log.debug('Getting obj for %s from cache' % (rn))
1197 log.debug('Getting obj for %s from cache' % (rn))
1198 return _c(rn)
1198 return _c(rn)
1199
1199
1200 def __get_instance(self):
1200 def __get_instance(self):
1201 repo_full_path = self.repo_full_path
1201 repo_full_path = self.repo_full_path
1202 try:
1202 try:
1203 alias = get_scm(repo_full_path)[0]
1203 alias = get_scm(repo_full_path)[0]
1204 log.debug('Creating instance of %s repository from %s'
1204 log.debug('Creating instance of %s repository from %s'
1205 % (alias, repo_full_path))
1205 % (alias, repo_full_path))
1206 backend = get_backend(alias)
1206 backend = get_backend(alias)
1207 except VCSError:
1207 except VCSError:
1208 log.error(traceback.format_exc())
1208 log.error(traceback.format_exc())
1209 log.error('Perhaps this repository is in db and not in '
1209 log.error('Perhaps this repository is in db and not in '
1210 'filesystem run rescan repositories with '
1210 'filesystem run rescan repositories with '
1211 '"destroy old data " option from admin panel')
1211 '"destroy old data " option from admin panel')
1212 return
1212 return
1213
1213
1214 if alias == 'hg':
1214 if alias == 'hg':
1215
1215
1216 repo = backend(safe_str(repo_full_path), create=False,
1216 repo = backend(safe_str(repo_full_path), create=False,
1217 baseui=self._ui)
1217 baseui=self._ui)
1218 # skip hidden web repository
1218 # skip hidden web repository
1219 if repo._get_hidden():
1219 if repo._get_hidden():
1220 return
1220 return
1221 else:
1221 else:
1222 repo = backend(repo_full_path, create=False)
1222 repo = backend(repo_full_path, create=False)
1223
1223
1224 return repo
1224 return repo
1225
1225
1226
1226
1227 class RepoGroup(Base, BaseModel):
1227 class RepoGroup(Base, BaseModel):
1228 __tablename__ = 'groups'
1228 __tablename__ = 'groups'
1229 __table_args__ = (
1229 __table_args__ = (
1230 UniqueConstraint('group_name', 'group_parent_id'),
1230 UniqueConstraint('group_name', 'group_parent_id'),
1231 CheckConstraint('group_id != group_parent_id'),
1231 CheckConstraint('group_id != group_parent_id'),
1232 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1232 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1233 'mysql_charset': 'utf8'},
1233 'mysql_charset': 'utf8'},
1234 )
1234 )
1235 __mapper_args__ = {'order_by': 'group_name'}
1235 __mapper_args__ = {'order_by': 'group_name'}
1236
1236
1237 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1237 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1238 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1238 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1239 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1239 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1240 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1240 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1241 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1241 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1242 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1242 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1243
1243
1244 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1244 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1245 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1245 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1246 parent_group = relationship('RepoGroup', remote_side=group_id)
1246 parent_group = relationship('RepoGroup', remote_side=group_id)
1247 user = relationship('User')
1247 user = relationship('User')
1248
1248
1249 def __init__(self, group_name='', parent_group=None):
1249 def __init__(self, group_name='', parent_group=None):
1250 self.group_name = group_name
1250 self.group_name = group_name
1251 self.parent_group = parent_group
1251 self.parent_group = parent_group
1252
1252
1253 def __unicode__(self):
1253 def __unicode__(self):
1254 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1254 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1255 self.group_name)
1255 self.group_name)
1256
1256
1257 @classmethod
1257 @classmethod
1258 def groups_choices(cls, groups=None, show_empty_group=True):
1258 def groups_choices(cls, groups=None, show_empty_group=True):
1259 from webhelpers.html import literal as _literal
1259 from webhelpers.html import literal as _literal
1260 if not groups:
1260 if not groups:
1261 groups = cls.query().all()
1261 groups = cls.query().all()
1262
1262
1263 repo_groups = []
1263 repo_groups = []
1264 if show_empty_group:
1264 if show_empty_group:
1265 repo_groups = [('-1', '-- %s --' % _('top level'))]
1265 repo_groups = [('-1', '-- %s --' % _('top level'))]
1266 sep = ' &raquo; '
1266 sep = ' &raquo; '
1267 _name = lambda k: _literal(sep.join(k))
1267 _name = lambda k: _literal(sep.join(k))
1268
1268
1269 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1269 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1270 for x in groups])
1270 for x in groups])
1271
1271
1272 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1272 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1273 return repo_groups
1273 return repo_groups
1274
1274
1275 @classmethod
1275 @classmethod
1276 def url_sep(cls):
1276 def url_sep(cls):
1277 return URL_SEP
1277 return URL_SEP
1278
1278
1279 @classmethod
1279 @classmethod
1280 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1280 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1281 if case_insensitive:
1281 if case_insensitive:
1282 gr = cls.query()\
1282 gr = cls.query()\
1283 .filter(cls.group_name.ilike(group_name))
1283 .filter(cls.group_name.ilike(group_name))
1284 else:
1284 else:
1285 gr = cls.query()\
1285 gr = cls.query()\
1286 .filter(cls.group_name == group_name)
1286 .filter(cls.group_name == group_name)
1287 if cache:
1287 if cache:
1288 gr = gr.options(FromCache(
1288 gr = gr.options(FromCache(
1289 "sql_cache_short",
1289 "sql_cache_short",
1290 "get_group_%s" % _hash_key(group_name)
1290 "get_group_%s" % _hash_key(group_name)
1291 )
1291 )
1292 )
1292 )
1293 return gr.scalar()
1293 return gr.scalar()
1294
1294
1295 @property
1295 @property
1296 def parents(self):
1296 def parents(self):
1297 parents_recursion_limit = 5
1297 parents_recursion_limit = 5
1298 groups = []
1298 groups = []
1299 if self.parent_group is None:
1299 if self.parent_group is None:
1300 return groups
1300 return groups
1301 cur_gr = self.parent_group
1301 cur_gr = self.parent_group
1302 groups.insert(0, cur_gr)
1302 groups.insert(0, cur_gr)
1303 cnt = 0
1303 cnt = 0
1304 while 1:
1304 while 1:
1305 cnt += 1
1305 cnt += 1
1306 gr = getattr(cur_gr, 'parent_group', None)
1306 gr = getattr(cur_gr, 'parent_group', None)
1307 cur_gr = cur_gr.parent_group
1307 cur_gr = cur_gr.parent_group
1308 if gr is None:
1308 if gr is None:
1309 break
1309 break
1310 if cnt == parents_recursion_limit:
1310 if cnt == parents_recursion_limit:
1311 # this will prevent accidental infinit loops
1311 # this will prevent accidental infinit loops
1312 log.error('group nested more than %s' %
1312 log.error('group nested more than %s' %
1313 parents_recursion_limit)
1313 parents_recursion_limit)
1314 break
1314 break
1315
1315
1316 groups.insert(0, gr)
1316 groups.insert(0, gr)
1317 return groups
1317 return groups
1318
1318
1319 @property
1319 @property
1320 def children(self):
1320 def children(self):
1321 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1321 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1322
1322
1323 @property
1323 @property
1324 def name(self):
1324 def name(self):
1325 return self.group_name.split(RepoGroup.url_sep())[-1]
1325 return self.group_name.split(RepoGroup.url_sep())[-1]
1326
1326
1327 @property
1327 @property
1328 def full_path(self):
1328 def full_path(self):
1329 return self.group_name
1329 return self.group_name
1330
1330
1331 @property
1331 @property
1332 def full_path_splitted(self):
1332 def full_path_splitted(self):
1333 return self.group_name.split(RepoGroup.url_sep())
1333 return self.group_name.split(RepoGroup.url_sep())
1334
1334
1335 @property
1335 @property
1336 def repositories(self):
1336 def repositories(self):
1337 return Repository.query()\
1337 return Repository.query()\
1338 .filter(Repository.group == self)\
1338 .filter(Repository.group == self)\
1339 .order_by(Repository.repo_name)
1339 .order_by(Repository.repo_name)
1340
1340
1341 @property
1341 @property
1342 def repositories_recursive_count(self):
1342 def repositories_recursive_count(self):
1343 cnt = self.repositories.count()
1343 cnt = self.repositories.count()
1344
1344
1345 def children_count(group):
1345 def children_count(group):
1346 cnt = 0
1346 cnt = 0
1347 for child in group.children:
1347 for child in group.children:
1348 cnt += child.repositories.count()
1348 cnt += child.repositories.count()
1349 cnt += children_count(child)
1349 cnt += children_count(child)
1350 return cnt
1350 return cnt
1351
1351
1352 return cnt + children_count(self)
1352 return cnt + children_count(self)
1353
1353
1354 def _recursive_objects(self, include_repos=True):
1354 def _recursive_objects(self, include_repos=True):
1355 all_ = []
1355 all_ = []
1356
1356
1357 def _get_members(root_gr):
1357 def _get_members(root_gr):
1358 if include_repos:
1358 if include_repos:
1359 for r in root_gr.repositories:
1359 for r in root_gr.repositories:
1360 all_.append(r)
1360 all_.append(r)
1361 childs = root_gr.children.all()
1361 childs = root_gr.children.all()
1362 if childs:
1362 if childs:
1363 for gr in childs:
1363 for gr in childs:
1364 all_.append(gr)
1364 all_.append(gr)
1365 _get_members(gr)
1365 _get_members(gr)
1366
1366
1367 _get_members(self)
1367 _get_members(self)
1368 return [self] + all_
1368 return [self] + all_
1369
1369
1370 def recursive_groups_and_repos(self):
1370 def recursive_groups_and_repos(self):
1371 """
1371 """
1372 Recursive return all groups, with repositories in those groups
1372 Recursive return all groups, with repositories in those groups
1373 """
1373 """
1374 return self._recursive_objects()
1374 return self._recursive_objects()
1375
1375
1376 def recursive_groups(self):
1376 def recursive_groups(self):
1377 """
1377 """
1378 Returns all children groups for this group including children of children
1378 Returns all children groups for this group including children of children
1379 """
1379 """
1380 return self._recursive_objects(include_repos=False)
1380 return self._recursive_objects(include_repos=False)
1381
1381
1382 def get_new_name(self, group_name):
1382 def get_new_name(self, group_name):
1383 """
1383 """
1384 returns new full group name based on parent and new name
1384 returns new full group name based on parent and new name
1385
1385
1386 :param group_name:
1386 :param group_name:
1387 """
1387 """
1388 path_prefix = (self.parent_group.full_path_splitted if
1388 path_prefix = (self.parent_group.full_path_splitted if
1389 self.parent_group else [])
1389 self.parent_group else [])
1390 return RepoGroup.url_sep().join(path_prefix + [group_name])
1390 return RepoGroup.url_sep().join(path_prefix + [group_name])
1391
1391
1392
1392
1393 class Permission(Base, BaseModel):
1393 class Permission(Base, BaseModel):
1394 __tablename__ = 'permissions'
1394 __tablename__ = 'permissions'
1395 __table_args__ = (
1395 __table_args__ = (
1396 Index('p_perm_name_idx', 'permission_name'),
1396 Index('p_perm_name_idx', 'permission_name'),
1397 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1397 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1398 'mysql_charset': 'utf8'},
1398 'mysql_charset': 'utf8'},
1399 )
1399 )
1400 PERMS = [
1400 PERMS = [
1401 ('hg.admin', _('RhodeCode Administrator')),
1401 ('hg.admin', _('RhodeCode Administrator')),
1402
1402
1403 ('repository.none', _('Repository no access')),
1403 ('repository.none', _('Repository no access')),
1404 ('repository.read', _('Repository read access')),
1404 ('repository.read', _('Repository read access')),
1405 ('repository.write', _('Repository write access')),
1405 ('repository.write', _('Repository write access')),
1406 ('repository.admin', _('Repository admin access')),
1406 ('repository.admin', _('Repository admin access')),
1407
1407
1408 ('group.none', _('Repository group no access')),
1408 ('group.none', _('Repository group no access')),
1409 ('group.read', _('Repository group read access')),
1409 ('group.read', _('Repository group read access')),
1410 ('group.write', _('Repository group write access')),
1410 ('group.write', _('Repository group write access')),
1411 ('group.admin', _('Repository group admin access')),
1411 ('group.admin', _('Repository group admin access')),
1412
1412
1413 ('usergroup.none', _('User group no access')),
1413 ('usergroup.none', _('User group no access')),
1414 ('usergroup.read', _('User group read access')),
1414 ('usergroup.read', _('User group read access')),
1415 ('usergroup.write', _('User group write access')),
1415 ('usergroup.write', _('User group write access')),
1416 ('usergroup.admin', _('User group admin access')),
1416 ('usergroup.admin', _('User group admin access')),
1417
1417
1418 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1418 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1419 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1419 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1420
1420
1421 ('hg.usergroup.create.false', _('User Group creation disabled')),
1421 ('hg.usergroup.create.false', _('User Group creation disabled')),
1422 ('hg.usergroup.create.true', _('User Group creation enabled')),
1422 ('hg.usergroup.create.true', _('User Group creation enabled')),
1423
1423
1424 ('hg.create.none', _('Repository creation disabled')),
1424 ('hg.create.none', _('Repository creation disabled')),
1425 ('hg.create.repository', _('Repository creation enabled')),
1425 ('hg.create.repository', _('Repository creation enabled')),
1426
1426
1427 ('hg.fork.none', _('Repository forking disabled')),
1427 ('hg.fork.none', _('Repository forking disabled')),
1428 ('hg.fork.repository', _('Repository forking enabled')),
1428 ('hg.fork.repository', _('Repository forking enabled')),
1429
1429
1430 ('hg.register.none', _('Registration disabled')),
1430 ('hg.register.none', _('Registration disabled')),
1431 ('hg.register.manual_activate', _('User Registration with manual account activation')),
1431 ('hg.register.manual_activate', _('User Registration with manual account activation')),
1432 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
1432 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
1433
1433
1434 ('hg.extern_activate.manual', _('Manual activation of external account')),
1434 ('hg.extern_activate.manual', _('Manual activation of external account')),
1435 ('hg.extern_activate.auto', _('Automatic activation of external account')),
1435 ('hg.extern_activate.auto', _('Automatic activation of external account')),
1436
1436
1437 ]
1437 ]
1438
1438
1439 #definition of system default permissions for DEFAULT user
1439 #definition of system default permissions for DEFAULT user
1440 DEFAULT_USER_PERMISSIONS = [
1440 DEFAULT_USER_PERMISSIONS = [
1441 'repository.read',
1441 'repository.read',
1442 'group.read',
1442 'group.read',
1443 'usergroup.read',
1443 'usergroup.read',
1444 'hg.create.repository',
1444 'hg.create.repository',
1445 'hg.fork.repository',
1445 'hg.fork.repository',
1446 'hg.register.manual_activate',
1446 'hg.register.manual_activate',
1447 'hg.extern_activate.auto',
1447 'hg.extern_activate.auto',
1448 ]
1448 ]
1449
1449
1450 # defines which permissions are more important higher the more important
1450 # defines which permissions are more important higher the more important
1451 # Weight defines which permissions are more important.
1451 # Weight defines which permissions are more important.
1452 # The higher number the more important.
1452 # The higher number the more important.
1453 PERM_WEIGHTS = {
1453 PERM_WEIGHTS = {
1454 'repository.none': 0,
1454 'repository.none': 0,
1455 'repository.read': 1,
1455 'repository.read': 1,
1456 'repository.write': 3,
1456 'repository.write': 3,
1457 'repository.admin': 4,
1457 'repository.admin': 4,
1458
1458
1459 'group.none': 0,
1459 'group.none': 0,
1460 'group.read': 1,
1460 'group.read': 1,
1461 'group.write': 3,
1461 'group.write': 3,
1462 'group.admin': 4,
1462 'group.admin': 4,
1463
1463
1464 'usergroup.none': 0,
1464 'usergroup.none': 0,
1465 'usergroup.read': 1,
1465 'usergroup.read': 1,
1466 'usergroup.write': 3,
1466 'usergroup.write': 3,
1467 'usergroup.admin': 4,
1467 'usergroup.admin': 4,
1468 'hg.repogroup.create.false': 0,
1468 'hg.repogroup.create.false': 0,
1469 'hg.repogroup.create.true': 1,
1469 'hg.repogroup.create.true': 1,
1470
1470
1471 'hg.usergroup.create.false': 0,
1471 'hg.usergroup.create.false': 0,
1472 'hg.usergroup.create.true': 1,
1472 'hg.usergroup.create.true': 1,
1473
1473
1474 'hg.fork.none': 0,
1474 'hg.fork.none': 0,
1475 'hg.fork.repository': 1,
1475 'hg.fork.repository': 1,
1476 'hg.create.none': 0,
1476 'hg.create.none': 0,
1477 'hg.create.repository': 1
1477 'hg.create.repository': 1
1478 }
1478 }
1479
1479
1480 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1480 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1481 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1481 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1482 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1482 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1483
1483
1484 def __unicode__(self):
1484 def __unicode__(self):
1485 return u"<%s('%s:%s')>" % (
1485 return u"<%s('%s:%s')>" % (
1486 self.__class__.__name__, self.permission_id, self.permission_name
1486 self.__class__.__name__, self.permission_id, self.permission_name
1487 )
1487 )
1488
1488
1489 @classmethod
1489 @classmethod
1490 def get_by_key(cls, key):
1490 def get_by_key(cls, key):
1491 return cls.query().filter(cls.permission_name == key).scalar()
1491 return cls.query().filter(cls.permission_name == key).scalar()
1492
1492
1493 @classmethod
1493 @classmethod
1494 def get_default_perms(cls, default_user_id):
1494 def get_default_perms(cls, default_user_id):
1495 q = Session().query(UserRepoToPerm, Repository, cls)\
1495 q = Session().query(UserRepoToPerm, Repository, cls)\
1496 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1496 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1497 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1497 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1498 .filter(UserRepoToPerm.user_id == default_user_id)
1498 .filter(UserRepoToPerm.user_id == default_user_id)
1499
1499
1500 return q.all()
1500 return q.all()
1501
1501
1502 @classmethod
1502 @classmethod
1503 def get_default_group_perms(cls, default_user_id):
1503 def get_default_group_perms(cls, default_user_id):
1504 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1504 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1505 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1505 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1506 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1506 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1507 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1507 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1508
1508
1509 return q.all()
1509 return q.all()
1510
1510
1511 @classmethod
1511 @classmethod
1512 def get_default_user_group_perms(cls, default_user_id):
1512 def get_default_user_group_perms(cls, default_user_id):
1513 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1513 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1514 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1514 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1515 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1515 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1516 .filter(UserUserGroupToPerm.user_id == default_user_id)
1516 .filter(UserUserGroupToPerm.user_id == default_user_id)
1517
1517
1518 return q.all()
1518 return q.all()
1519
1519
1520
1520
1521 class UserRepoToPerm(Base, BaseModel):
1521 class UserRepoToPerm(Base, BaseModel):
1522 __tablename__ = 'repo_to_perm'
1522 __tablename__ = 'repo_to_perm'
1523 __table_args__ = (
1523 __table_args__ = (
1524 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1524 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1525 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1525 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1526 'mysql_charset': 'utf8'}
1526 'mysql_charset': 'utf8'}
1527 )
1527 )
1528 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1528 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1529 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1529 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1530 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1530 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1531 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1531 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1532
1532
1533 user = relationship('User')
1533 user = relationship('User')
1534 repository = relationship('Repository')
1534 repository = relationship('Repository')
1535 permission = relationship('Permission')
1535 permission = relationship('Permission')
1536
1536
1537 @classmethod
1537 @classmethod
1538 def create(cls, user, repository, permission):
1538 def create(cls, user, repository, permission):
1539 n = cls()
1539 n = cls()
1540 n.user = user
1540 n.user = user
1541 n.repository = repository
1541 n.repository = repository
1542 n.permission = permission
1542 n.permission = permission
1543 Session().add(n)
1543 Session().add(n)
1544 return n
1544 return n
1545
1545
1546 def __unicode__(self):
1546 def __unicode__(self):
1547 return u'<%s => %s >' % (self.user, self.repository)
1547 return u'<%s => %s >' % (self.user, self.repository)
1548
1548
1549
1549
1550 class UserUserGroupToPerm(Base, BaseModel):
1550 class UserUserGroupToPerm(Base, BaseModel):
1551 __tablename__ = 'user_user_group_to_perm'
1551 __tablename__ = 'user_user_group_to_perm'
1552 __table_args__ = (
1552 __table_args__ = (
1553 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1553 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1554 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1554 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1555 'mysql_charset': 'utf8'}
1555 'mysql_charset': 'utf8'}
1556 )
1556 )
1557 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1557 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1558 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1558 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1559 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1559 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1560 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1560 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1561
1561
1562 user = relationship('User')
1562 user = relationship('User')
1563 user_group = relationship('UserGroup')
1563 user_group = relationship('UserGroup')
1564 permission = relationship('Permission')
1564 permission = relationship('Permission')
1565
1565
1566 @classmethod
1566 @classmethod
1567 def create(cls, user, user_group, permission):
1567 def create(cls, user, user_group, permission):
1568 n = cls()
1568 n = cls()
1569 n.user = user
1569 n.user = user
1570 n.user_group = user_group
1570 n.user_group = user_group
1571 n.permission = permission
1571 n.permission = permission
1572 Session().add(n)
1572 Session().add(n)
1573 return n
1573 return n
1574
1574
1575 def __unicode__(self):
1575 def __unicode__(self):
1576 return u'<%s => %s >' % (self.user, self.user_group)
1576 return u'<%s => %s >' % (self.user, self.user_group)
1577
1577
1578
1578
1579 class UserToPerm(Base, BaseModel):
1579 class UserToPerm(Base, BaseModel):
1580 __tablename__ = 'user_to_perm'
1580 __tablename__ = 'user_to_perm'
1581 __table_args__ = (
1581 __table_args__ = (
1582 UniqueConstraint('user_id', 'permission_id'),
1582 UniqueConstraint('user_id', 'permission_id'),
1583 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1583 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1584 'mysql_charset': 'utf8'}
1584 'mysql_charset': 'utf8'}
1585 )
1585 )
1586 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1586 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1587 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1588 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1588 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1589
1589
1590 user = relationship('User')
1590 user = relationship('User')
1591 permission = relationship('Permission', lazy='joined')
1591 permission = relationship('Permission', lazy='joined')
1592
1592
1593 def __unicode__(self):
1593 def __unicode__(self):
1594 return u'<%s => %s >' % (self.user, self.permission)
1594 return u'<%s => %s >' % (self.user, self.permission)
1595
1595
1596
1596
1597 class UserGroupRepoToPerm(Base, BaseModel):
1597 class UserGroupRepoToPerm(Base, BaseModel):
1598 __tablename__ = 'users_group_repo_to_perm'
1598 __tablename__ = 'users_group_repo_to_perm'
1599 __table_args__ = (
1599 __table_args__ = (
1600 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1600 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1601 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1602 'mysql_charset': 'utf8'}
1602 'mysql_charset': 'utf8'}
1603 )
1603 )
1604 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1604 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1605 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1605 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1606 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1606 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1607 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1607 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1608
1608
1609 users_group = relationship('UserGroup')
1609 users_group = relationship('UserGroup')
1610 permission = relationship('Permission')
1610 permission = relationship('Permission')
1611 repository = relationship('Repository')
1611 repository = relationship('Repository')
1612
1612
1613 @classmethod
1613 @classmethod
1614 def create(cls, users_group, repository, permission):
1614 def create(cls, users_group, repository, permission):
1615 n = cls()
1615 n = cls()
1616 n.users_group = users_group
1616 n.users_group = users_group
1617 n.repository = repository
1617 n.repository = repository
1618 n.permission = permission
1618 n.permission = permission
1619 Session().add(n)
1619 Session().add(n)
1620 return n
1620 return n
1621
1621
1622 def __unicode__(self):
1622 def __unicode__(self):
1623 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
1623 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
1624
1624
1625
1625
1626 class UserGroupUserGroupToPerm(Base, BaseModel):
1626 class UserGroupUserGroupToPerm(Base, BaseModel):
1627 __tablename__ = 'user_group_user_group_to_perm'
1627 __tablename__ = 'user_group_user_group_to_perm'
1628 __table_args__ = (
1628 __table_args__ = (
1629 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
1629 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
1630 CheckConstraint('target_user_group_id != user_group_id'),
1630 CheckConstraint('target_user_group_id != user_group_id'),
1631 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1631 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1632 'mysql_charset': 'utf8'}
1632 'mysql_charset': 'utf8'}
1633 )
1633 )
1634 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1634 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1635 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1635 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1636 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1636 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1637 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1637 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1638
1638
1639 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1639 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1640 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1640 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1641 permission = relationship('Permission')
1641 permission = relationship('Permission')
1642
1642
1643 @classmethod
1643 @classmethod
1644 def create(cls, target_user_group, user_group, permission):
1644 def create(cls, target_user_group, user_group, permission):
1645 n = cls()
1645 n = cls()
1646 n.target_user_group = target_user_group
1646 n.target_user_group = target_user_group
1647 n.user_group = user_group
1647 n.user_group = user_group
1648 n.permission = permission
1648 n.permission = permission
1649 Session().add(n)
1649 Session().add(n)
1650 return n
1650 return n
1651
1651
1652 def __unicode__(self):
1652 def __unicode__(self):
1653 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1653 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1654
1654
1655
1655
1656 class UserGroupToPerm(Base, BaseModel):
1656 class UserGroupToPerm(Base, BaseModel):
1657 __tablename__ = 'users_group_to_perm'
1657 __tablename__ = 'users_group_to_perm'
1658 __table_args__ = (
1658 __table_args__ = (
1659 UniqueConstraint('users_group_id', 'permission_id',),
1659 UniqueConstraint('users_group_id', 'permission_id',),
1660 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1660 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1661 'mysql_charset': 'utf8'}
1661 'mysql_charset': 'utf8'}
1662 )
1662 )
1663 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1663 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1664 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1664 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1665 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1665 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1666
1666
1667 users_group = relationship('UserGroup')
1667 users_group = relationship('UserGroup')
1668 permission = relationship('Permission')
1668 permission = relationship('Permission')
1669
1669
1670
1670
1671 class UserRepoGroupToPerm(Base, BaseModel):
1671 class UserRepoGroupToPerm(Base, BaseModel):
1672 __tablename__ = 'user_repo_group_to_perm'
1672 __tablename__ = 'user_repo_group_to_perm'
1673 __table_args__ = (
1673 __table_args__ = (
1674 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1674 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1675 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1675 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1676 'mysql_charset': 'utf8'}
1676 'mysql_charset': 'utf8'}
1677 )
1677 )
1678
1678
1679 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1679 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1680 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1680 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1681 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1681 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1682 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1682 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1683
1683
1684 user = relationship('User')
1684 user = relationship('User')
1685 group = relationship('RepoGroup')
1685 group = relationship('RepoGroup')
1686 permission = relationship('Permission')
1686 permission = relationship('Permission')
1687
1687
1688
1688
1689 class UserGroupRepoGroupToPerm(Base, BaseModel):
1689 class UserGroupRepoGroupToPerm(Base, BaseModel):
1690 __tablename__ = 'users_group_repo_group_to_perm'
1690 __tablename__ = 'users_group_repo_group_to_perm'
1691 __table_args__ = (
1691 __table_args__ = (
1692 UniqueConstraint('users_group_id', 'group_id'),
1692 UniqueConstraint('users_group_id', 'group_id'),
1693 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1693 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1694 'mysql_charset': 'utf8'}
1694 'mysql_charset': 'utf8'}
1695 )
1695 )
1696
1696
1697 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1697 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1698 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1698 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1699 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1699 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1700 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1700 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1701
1701
1702 users_group = relationship('UserGroup')
1702 users_group = relationship('UserGroup')
1703 permission = relationship('Permission')
1703 permission = relationship('Permission')
1704 group = relationship('RepoGroup')
1704 group = relationship('RepoGroup')
1705
1705
1706
1706
1707 class Statistics(Base, BaseModel):
1707 class Statistics(Base, BaseModel):
1708 __tablename__ = 'statistics'
1708 __tablename__ = 'statistics'
1709 __table_args__ = (
1709 __table_args__ = (
1710 UniqueConstraint('repository_id'),
1710 UniqueConstraint('repository_id'),
1711 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1711 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1712 'mysql_charset': 'utf8'}
1712 'mysql_charset': 'utf8'}
1713 )
1713 )
1714 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1714 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1715 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1715 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1716 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1716 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1717 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1717 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1718 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1718 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1719 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1719 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1720
1720
1721 repository = relationship('Repository', single_parent=True)
1721 repository = relationship('Repository', single_parent=True)
1722
1722
1723
1723
1724 class UserFollowing(Base, BaseModel):
1724 class UserFollowing(Base, BaseModel):
1725 __tablename__ = 'user_followings'
1725 __tablename__ = 'user_followings'
1726 __table_args__ = (
1726 __table_args__ = (
1727 UniqueConstraint('user_id', 'follows_repository_id'),
1727 UniqueConstraint('user_id', 'follows_repository_id'),
1728 UniqueConstraint('user_id', 'follows_user_id'),
1728 UniqueConstraint('user_id', 'follows_user_id'),
1729 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1729 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1730 'mysql_charset': 'utf8'}
1730 'mysql_charset': 'utf8'}
1731 )
1731 )
1732
1732
1733 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1733 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1734 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1734 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1735 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1735 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1736 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1736 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1737 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1737 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1738
1738
1739 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1739 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1740
1740
1741 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1741 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1742 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1742 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1743
1743
1744 @classmethod
1744 @classmethod
1745 def get_repo_followers(cls, repo_id):
1745 def get_repo_followers(cls, repo_id):
1746 return cls.query().filter(cls.follows_repo_id == repo_id)
1746 return cls.query().filter(cls.follows_repo_id == repo_id)
1747
1747
1748
1748
1749 class CacheInvalidation(Base, BaseModel):
1749 class CacheInvalidation(Base, BaseModel):
1750 __tablename__ = 'cache_invalidation'
1750 __tablename__ = 'cache_invalidation'
1751 __table_args__ = (
1751 __table_args__ = (
1752 UniqueConstraint('cache_key'),
1752 UniqueConstraint('cache_key'),
1753 Index('key_idx', 'cache_key'),
1753 Index('key_idx', 'cache_key'),
1754 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1754 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1755 'mysql_charset': 'utf8'},
1755 'mysql_charset': 'utf8'},
1756 )
1756 )
1757 # cache_id, not used
1757 # cache_id, not used
1758 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1758 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1759 # cache_key as created by _get_cache_key
1759 # cache_key as created by _get_cache_key
1760 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1760 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1761 # cache_args is a repo_name
1761 # cache_args is a repo_name
1762 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1762 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1763 # instance sets cache_active True when it is caching,
1763 # instance sets cache_active True when it is caching,
1764 # other instances set cache_active to False to indicate that this cache is invalid
1764 # other instances set cache_active to False to indicate that this cache is invalid
1765 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1765 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1766
1766
1767 def __init__(self, cache_key, repo_name=''):
1767 def __init__(self, cache_key, repo_name=''):
1768 self.cache_key = cache_key
1768 self.cache_key = cache_key
1769 self.cache_args = repo_name
1769 self.cache_args = repo_name
1770 self.cache_active = False
1770 self.cache_active = False
1771
1771
1772 def __unicode__(self):
1772 def __unicode__(self):
1773 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1773 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1774 self.cache_id, self.cache_key, self.cache_active)
1774 self.cache_id, self.cache_key, self.cache_active)
1775
1775
1776 def _cache_key_partition(self):
1776 def _cache_key_partition(self):
1777 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1777 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1778 return prefix, repo_name, suffix
1778 return prefix, repo_name, suffix
1779
1779
1780 def get_prefix(self):
1780 def get_prefix(self):
1781 """
1781 """
1782 get prefix that might have been used in _get_cache_key to
1782 get prefix that might have been used in _get_cache_key to
1783 generate self.cache_key. Only used for informational purposes
1783 generate self.cache_key. Only used for informational purposes
1784 in repo_edit.html.
1784 in repo_edit.html.
1785 """
1785 """
1786 # prefix, repo_name, suffix
1786 # prefix, repo_name, suffix
1787 return self._cache_key_partition()[0]
1787 return self._cache_key_partition()[0]
1788
1788
1789 def get_suffix(self):
1789 def get_suffix(self):
1790 """
1790 """
1791 get suffix that might have been used in _get_cache_key to
1791 get suffix that might have been used in _get_cache_key to
1792 generate self.cache_key. Only used for informational purposes
1792 generate self.cache_key. Only used for informational purposes
1793 in repo_edit.html.
1793 in repo_edit.html.
1794 """
1794 """
1795 # prefix, repo_name, suffix
1795 # prefix, repo_name, suffix
1796 return self._cache_key_partition()[2]
1796 return self._cache_key_partition()[2]
1797
1797
1798 @classmethod
1798 @classmethod
1799 def clear_cache(cls):
1799 def clear_cache(cls):
1800 """
1800 """
1801 Delete all cache keys from database.
1801 Delete all cache keys from database.
1802 Should only be run when all instances are down and all entries thus stale.
1802 Should only be run when all instances are down and all entries thus stale.
1803 """
1803 """
1804 cls.query().delete()
1804 cls.query().delete()
1805 Session().commit()
1805 Session().commit()
1806
1806
1807 @classmethod
1807 @classmethod
1808 def _get_cache_key(cls, key):
1808 def _get_cache_key(cls, key):
1809 """
1809 """
1810 Wrapper for generating a unique cache key for this instance and "key".
1810 Wrapper for generating a unique cache key for this instance and "key".
1811 key must / will start with a repo_name which will be stored in .cache_args .
1811 key must / will start with a repo_name which will be stored in .cache_args .
1812 """
1812 """
1813 import rhodecode
1813 import rhodecode
1814 prefix = rhodecode.CONFIG.get('instance_id', '')
1814 prefix = rhodecode.CONFIG.get('instance_id', '')
1815 return "%s%s" % (prefix, key)
1815 return "%s%s" % (prefix, key)
1816
1816
1817 @classmethod
1817 @classmethod
1818 def set_invalidate(cls, repo_name):
1818 def set_invalidate(cls, repo_name):
1819 """
1819 """
1820 Mark all caches of a repo as invalid in the database.
1820 Mark all caches of a repo as invalid in the database.
1821 """
1821 """
1822 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1822 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1823
1823
1824 try:
1824 try:
1825 for inv_obj in inv_objs:
1825 for inv_obj in inv_objs:
1826 log.debug('marking %s key for invalidation based on repo_name=%s'
1826 log.debug('marking %s key for invalidation based on repo_name=%s'
1827 % (inv_obj, safe_str(repo_name)))
1827 % (inv_obj, safe_str(repo_name)))
1828 inv_obj.cache_active = False
1828 inv_obj.cache_active = False
1829 Session().add(inv_obj)
1829 Session().add(inv_obj)
1830 Session().commit()
1830 Session().commit()
1831 except Exception:
1831 except Exception:
1832 log.error(traceback.format_exc())
1832 log.error(traceback.format_exc())
1833 Session().rollback()
1833 Session().rollback()
1834
1834
1835 @classmethod
1835 @classmethod
1836 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1836 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1837 """
1837 """
1838 Mark this cache key as active and currently cached.
1838 Mark this cache key as active and currently cached.
1839 Return True if the existing cache registration still was valid.
1839 Return True if the existing cache registration still was valid.
1840 Return False to indicate that it had been invalidated and caches should be refreshed.
1840 Return False to indicate that it had been invalidated and caches should be refreshed.
1841 """
1841 """
1842
1842
1843 key = (repo_name + '_' + kind) if kind else repo_name
1843 key = (repo_name + '_' + kind) if kind else repo_name
1844 cache_key = cls._get_cache_key(key)
1844 cache_key = cls._get_cache_key(key)
1845
1845
1846 if valid_cache_keys and cache_key in valid_cache_keys:
1846 if valid_cache_keys and cache_key in valid_cache_keys:
1847 return True
1847 return True
1848
1848
1849 try:
1849 try:
1850 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1850 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1851 if not inv_obj:
1851 if not inv_obj:
1852 inv_obj = CacheInvalidation(cache_key, repo_name)
1852 inv_obj = CacheInvalidation(cache_key, repo_name)
1853 was_valid = inv_obj.cache_active
1853 was_valid = inv_obj.cache_active
1854 inv_obj.cache_active = True
1854 inv_obj.cache_active = True
1855 Session().add(inv_obj)
1855 Session().add(inv_obj)
1856 Session().commit()
1856 Session().commit()
1857 return was_valid
1857 return was_valid
1858 except Exception:
1858 except Exception:
1859 log.error(traceback.format_exc())
1859 log.error(traceback.format_exc())
1860 Session().rollback()
1860 Session().rollback()
1861 return False
1861 return False
1862
1862
1863 @classmethod
1863 @classmethod
1864 def get_valid_cache_keys(cls):
1864 def get_valid_cache_keys(cls):
1865 """
1865 """
1866 Return opaque object with information of which caches still are valid
1866 Return opaque object with information of which caches still are valid
1867 and can be used without checking for invalidation.
1867 and can be used without checking for invalidation.
1868 """
1868 """
1869 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1869 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1870
1870
1871
1871
1872 class ChangesetComment(Base, BaseModel):
1872 class ChangesetComment(Base, BaseModel):
1873 __tablename__ = 'changeset_comments'
1873 __tablename__ = 'changeset_comments'
1874 __table_args__ = (
1874 __table_args__ = (
1875 Index('cc_revision_idx', 'revision'),
1875 Index('cc_revision_idx', 'revision'),
1876 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1876 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1877 'mysql_charset': 'utf8'},
1877 'mysql_charset': 'utf8'},
1878 )
1878 )
1879 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1879 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1880 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1880 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1881 revision = Column('revision', String(40), nullable=True)
1881 revision = Column('revision', String(40), nullable=True)
1882 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1882 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1883 line_no = Column('line_no', Unicode(10), nullable=True)
1883 line_no = Column('line_no', Unicode(10), nullable=True)
1884 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1884 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1885 f_path = Column('f_path', Unicode(1000), nullable=True)
1885 f_path = Column('f_path', Unicode(1000), nullable=True)
1886 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1886 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1887 text = Column('text', UnicodeText(25000), nullable=False)
1887 text = Column('text', UnicodeText(25000), nullable=False)
1888 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1888 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1889 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1889 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1890
1890
1891 author = relationship('User', lazy='joined')
1891 author = relationship('User', lazy='joined')
1892 repo = relationship('Repository')
1892 repo = relationship('Repository')
1893 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1893 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1894 pull_request = relationship('PullRequest', lazy='joined')
1894 pull_request = relationship('PullRequest', lazy='joined')
1895
1895
1896 @classmethod
1896 @classmethod
1897 def get_users(cls, revision=None, pull_request_id=None):
1897 def get_users(cls, revision=None, pull_request_id=None):
1898 """
1898 """
1899 Returns user associated with this ChangesetComment. ie those
1899 Returns user associated with this ChangesetComment. ie those
1900 who actually commented
1900 who actually commented
1901
1901
1902 :param cls:
1902 :param cls:
1903 :param revision:
1903 :param revision:
1904 """
1904 """
1905 q = Session().query(User)\
1905 q = Session().query(User)\
1906 .join(ChangesetComment.author)
1906 .join(ChangesetComment.author)
1907 if revision:
1907 if revision:
1908 q = q.filter(cls.revision == revision)
1908 q = q.filter(cls.revision == revision)
1909 elif pull_request_id:
1909 elif pull_request_id:
1910 q = q.filter(cls.pull_request_id == pull_request_id)
1910 q = q.filter(cls.pull_request_id == pull_request_id)
1911 return q.all()
1911 return q.all()
1912
1912
1913
1913
1914 class ChangesetStatus(Base, BaseModel):
1914 class ChangesetStatus(Base, BaseModel):
1915 __tablename__ = 'changeset_statuses'
1915 __tablename__ = 'changeset_statuses'
1916 __table_args__ = (
1916 __table_args__ = (
1917 Index('cs_revision_idx', 'revision'),
1917 Index('cs_revision_idx', 'revision'),
1918 Index('cs_version_idx', 'version'),
1918 Index('cs_version_idx', 'version'),
1919 UniqueConstraint('repo_id', 'revision', 'version'),
1919 UniqueConstraint('repo_id', 'revision', 'version'),
1920 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1920 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1921 'mysql_charset': 'utf8'}
1921 'mysql_charset': 'utf8'}
1922 )
1922 )
1923 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1923 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1924 STATUS_APPROVED = 'approved'
1924 STATUS_APPROVED = 'approved'
1925 STATUS_REJECTED = 'rejected'
1925 STATUS_REJECTED = 'rejected'
1926 STATUS_UNDER_REVIEW = 'under_review'
1926 STATUS_UNDER_REVIEW = 'under_review'
1927
1927
1928 STATUSES = [
1928 STATUSES = [
1929 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1929 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1930 (STATUS_APPROVED, _("Approved")),
1930 (STATUS_APPROVED, _("Approved")),
1931 (STATUS_REJECTED, _("Rejected")),
1931 (STATUS_REJECTED, _("Rejected")),
1932 (STATUS_UNDER_REVIEW, _("Under Review")),
1932 (STATUS_UNDER_REVIEW, _("Under Review")),
1933 ]
1933 ]
1934
1934
1935 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1935 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1936 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1936 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1937 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1937 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1938 revision = Column('revision', String(40), nullable=False)
1938 revision = Column('revision', String(40), nullable=False)
1939 status = Column('status', String(128), nullable=False, default=DEFAULT)
1939 status = Column('status', String(128), nullable=False, default=DEFAULT)
1940 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1940 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1941 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1941 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1942 version = Column('version', Integer(), nullable=False, default=0)
1942 version = Column('version', Integer(), nullable=False, default=0)
1943 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1943 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1944
1944
1945 author = relationship('User', lazy='joined')
1945 author = relationship('User', lazy='joined')
1946 repo = relationship('Repository')
1946 repo = relationship('Repository')
1947 comment = relationship('ChangesetComment', lazy='joined')
1947 comment = relationship('ChangesetComment', lazy='joined')
1948 pull_request = relationship('PullRequest', lazy='joined')
1948 pull_request = relationship('PullRequest', lazy='joined')
1949
1949
1950 def __unicode__(self):
1950 def __unicode__(self):
1951 return u"<%s('%s:%s')>" % (
1951 return u"<%s('%s:%s')>" % (
1952 self.__class__.__name__,
1952 self.__class__.__name__,
1953 self.status, self.author
1953 self.status, self.author
1954 )
1954 )
1955
1955
1956 @classmethod
1956 @classmethod
1957 def get_status_lbl(cls, value):
1957 def get_status_lbl(cls, value):
1958 return dict(cls.STATUSES).get(value)
1958 return dict(cls.STATUSES).get(value)
1959
1959
1960 @property
1960 @property
1961 def status_lbl(self):
1961 def status_lbl(self):
1962 return ChangesetStatus.get_status_lbl(self.status)
1962 return ChangesetStatus.get_status_lbl(self.status)
1963
1963
1964
1964
1965 class PullRequest(Base, BaseModel):
1965 class PullRequest(Base, BaseModel):
1966 __tablename__ = 'pull_requests'
1966 __tablename__ = 'pull_requests'
1967 __table_args__ = (
1967 __table_args__ = (
1968 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1968 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1969 'mysql_charset': 'utf8'},
1969 'mysql_charset': 'utf8'},
1970 )
1970 )
1971
1971
1972 STATUS_NEW = u'new'
1972 STATUS_NEW = u'new'
1973 STATUS_OPEN = u'open'
1973 STATUS_OPEN = u'open'
1974 STATUS_CLOSED = u'closed'
1974 STATUS_CLOSED = u'closed'
1975
1975
1976 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1976 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1977 title = Column('title', Unicode(256), nullable=True)
1977 title = Column('title', Unicode(256), nullable=True)
1978 description = Column('description', UnicodeText(10240), nullable=True)
1978 description = Column('description', UnicodeText(10240), nullable=True)
1979 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1979 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1980 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1980 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1981 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1981 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1982 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1982 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1983 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1983 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1984 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1984 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1985 org_ref = Column('org_ref', Unicode(256), nullable=False)
1985 org_ref = Column('org_ref', Unicode(256), nullable=False)
1986 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1986 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1987 other_ref = Column('other_ref', Unicode(256), nullable=False)
1987 other_ref = Column('other_ref', Unicode(256), nullable=False)
1988
1988
1989 @hybrid_property
1989 @hybrid_property
1990 def revisions(self):
1990 def revisions(self):
1991 return self._revisions.split(':')
1991 return self._revisions.split(':')
1992
1992
1993 @revisions.setter
1993 @revisions.setter
1994 def revisions(self, val):
1994 def revisions(self, val):
1995 self._revisions = ':'.join(val)
1995 self._revisions = ':'.join(val)
1996
1996
1997 @property
1997 @property
1998 def org_ref_parts(self):
1998 def org_ref_parts(self):
1999 return self.org_ref.split(':')
1999 return self.org_ref.split(':')
2000
2000
2001 @property
2001 @property
2002 def other_ref_parts(self):
2002 def other_ref_parts(self):
2003 return self.other_ref.split(':')
2003 return self.other_ref.split(':')
2004
2004
2005 author = relationship('User', lazy='joined')
2005 author = relationship('User', lazy='joined')
2006 reviewers = relationship('PullRequestReviewers',
2006 reviewers = relationship('PullRequestReviewers',
2007 cascade="all, delete, delete-orphan")
2007 cascade="all, delete, delete-orphan")
2008 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2008 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2009 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2009 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2010 statuses = relationship('ChangesetStatus')
2010 statuses = relationship('ChangesetStatus')
2011 comments = relationship('ChangesetComment',
2011 comments = relationship('ChangesetComment',
2012 cascade="all, delete, delete-orphan")
2012 cascade="all, delete, delete-orphan")
2013
2013
2014 def is_closed(self):
2014 def is_closed(self):
2015 return self.status == self.STATUS_CLOSED
2015 return self.status == self.STATUS_CLOSED
2016
2016
2017 @property
2017 @property
2018 def last_review_status(self):
2018 def last_review_status(self):
2019 return self.statuses[-1].status if self.statuses else ''
2019 return self.statuses[-1].status if self.statuses else ''
2020
2020
2021 def __json__(self):
2021 def __json__(self):
2022 return dict(
2022 return dict(
2023 revisions=self.revisions
2023 revisions=self.revisions
2024 )
2024 )
2025
2025
2026
2026
2027 class PullRequestReviewers(Base, BaseModel):
2027 class PullRequestReviewers(Base, BaseModel):
2028 __tablename__ = 'pull_request_reviewers'
2028 __tablename__ = 'pull_request_reviewers'
2029 __table_args__ = (
2029 __table_args__ = (
2030 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2030 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2031 'mysql_charset': 'utf8'},
2031 'mysql_charset': 'utf8'},
2032 )
2032 )
2033
2033
2034 def __init__(self, user=None, pull_request=None):
2034 def __init__(self, user=None, pull_request=None):
2035 self.user = user
2035 self.user = user
2036 self.pull_request = pull_request
2036 self.pull_request = pull_request
2037
2037
2038 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2038 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2039 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2039 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2040 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2040 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2041
2041
2042 user = relationship('User')
2042 user = relationship('User')
2043 pull_request = relationship('PullRequest')
2043 pull_request = relationship('PullRequest')
2044
2044
2045
2045
2046 class Notification(Base, BaseModel):
2046 class Notification(Base, BaseModel):
2047 __tablename__ = 'notifications'
2047 __tablename__ = 'notifications'
2048 __table_args__ = (
2048 __table_args__ = (
2049 Index('notification_type_idx', 'type'),
2049 Index('notification_type_idx', 'type'),
2050 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2050 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2051 'mysql_charset': 'utf8'},
2051 'mysql_charset': 'utf8'},
2052 )
2052 )
2053
2053
2054 TYPE_CHANGESET_COMMENT = u'cs_comment'
2054 TYPE_CHANGESET_COMMENT = u'cs_comment'
2055 TYPE_MESSAGE = u'message'
2055 TYPE_MESSAGE = u'message'
2056 TYPE_MENTION = u'mention'
2056 TYPE_MENTION = u'mention'
2057 TYPE_REGISTRATION = u'registration'
2057 TYPE_REGISTRATION = u'registration'
2058 TYPE_PULL_REQUEST = u'pull_request'
2058 TYPE_PULL_REQUEST = u'pull_request'
2059 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2059 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2060
2060
2061 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2061 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2062 subject = Column('subject', Unicode(512), nullable=True)
2062 subject = Column('subject', Unicode(512), nullable=True)
2063 body = Column('body', UnicodeText(50000), nullable=True)
2063 body = Column('body', UnicodeText(50000), nullable=True)
2064 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2064 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2065 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2065 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2066 type_ = Column('type', Unicode(256))
2066 type_ = Column('type', Unicode(256))
2067
2067
2068 created_by_user = relationship('User')
2068 created_by_user = relationship('User')
2069 notifications_to_users = relationship('UserNotification', lazy='joined',
2069 notifications_to_users = relationship('UserNotification', lazy='joined',
2070 cascade="all, delete, delete-orphan")
2070 cascade="all, delete, delete-orphan")
2071
2071
2072 @property
2072 @property
2073 def recipients(self):
2073 def recipients(self):
2074 return [x.user for x in UserNotification.query()\
2074 return [x.user for x in UserNotification.query()\
2075 .filter(UserNotification.notification == self)\
2075 .filter(UserNotification.notification == self)\
2076 .order_by(UserNotification.user_id.asc()).all()]
2076 .order_by(UserNotification.user_id.asc()).all()]
2077
2077
2078 @classmethod
2078 @classmethod
2079 def create(cls, created_by, subject, body, recipients, type_=None):
2079 def create(cls, created_by, subject, body, recipients, type_=None):
2080 if type_ is None:
2080 if type_ is None:
2081 type_ = Notification.TYPE_MESSAGE
2081 type_ = Notification.TYPE_MESSAGE
2082
2082
2083 notification = cls()
2083 notification = cls()
2084 notification.created_by_user = created_by
2084 notification.created_by_user = created_by
2085 notification.subject = subject
2085 notification.subject = subject
2086 notification.body = body
2086 notification.body = body
2087 notification.type_ = type_
2087 notification.type_ = type_
2088 notification.created_on = datetime.datetime.now()
2088 notification.created_on = datetime.datetime.now()
2089
2089
2090 for u in recipients:
2090 for u in recipients:
2091 assoc = UserNotification()
2091 assoc = UserNotification()
2092 assoc.notification = notification
2092 assoc.notification = notification
2093 u.notifications.append(assoc)
2093 u.notifications.append(assoc)
2094 Session().add(notification)
2094 Session().add(notification)
2095 return notification
2095 return notification
2096
2096
2097 @property
2097 @property
2098 def description(self):
2098 def description(self):
2099 from rhodecode.model.notification import NotificationModel
2099 from rhodecode.model.notification import NotificationModel
2100 return NotificationModel().make_description(self)
2100 return NotificationModel().make_description(self)
2101
2101
2102
2102
2103 class UserNotification(Base, BaseModel):
2103 class UserNotification(Base, BaseModel):
2104 __tablename__ = 'user_to_notification'
2104 __tablename__ = 'user_to_notification'
2105 __table_args__ = (
2105 __table_args__ = (
2106 UniqueConstraint('user_id', 'notification_id'),
2106 UniqueConstraint('user_id', 'notification_id'),
2107 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2107 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2108 'mysql_charset': 'utf8'}
2108 'mysql_charset': 'utf8'}
2109 )
2109 )
2110 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2110 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2111 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2111 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2112 read = Column('read', Boolean, default=False)
2112 read = Column('read', Boolean, default=False)
2113 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2113 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2114
2114
2115 user = relationship('User', lazy="joined")
2115 user = relationship('User', lazy="joined")
2116 notification = relationship('Notification', lazy="joined",
2116 notification = relationship('Notification', lazy="joined",
2117 order_by=lambda: Notification.created_on.desc(),)
2117 order_by=lambda: Notification.created_on.desc(),)
2118
2118
2119 def mark_as_read(self):
2119 def mark_as_read(self):
2120 self.read = True
2120 self.read = True
2121 Session().add(self)
2121 Session().add(self)
2122
2122
2123
2123
2124 class Gist(Base, BaseModel):
2124 class Gist(Base, BaseModel):
2125 __tablename__ = 'gists'
2125 __tablename__ = 'gists'
2126 __table_args__ = (
2126 __table_args__ = (
2127 Index('g_gist_access_id_idx', 'gist_access_id'),
2127 Index('g_gist_access_id_idx', 'gist_access_id'),
2128 Index('g_created_on_idx', 'created_on'),
2128 Index('g_created_on_idx', 'created_on'),
2129 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2129 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2130 'mysql_charset': 'utf8'}
2130 'mysql_charset': 'utf8'}
2131 )
2131 )
2132 GIST_PUBLIC = u'public'
2132 GIST_PUBLIC = u'public'
2133 GIST_PRIVATE = u'private'
2133 GIST_PRIVATE = u'private'
2134
2134
2135 gist_id = Column('gist_id', Integer(), primary_key=True)
2135 gist_id = Column('gist_id', Integer(), primary_key=True)
2136 gist_access_id = Column('gist_access_id', UnicodeText(1024))
2136 gist_access_id = Column('gist_access_id', UnicodeText(1024))
2137 gist_description = Column('gist_description', UnicodeText(1024))
2137 gist_description = Column('gist_description', UnicodeText(1024))
2138 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
2138 gist_owner = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=True)
2139 gist_expires = Column('gist_expires', Float(), nullable=False)
2139 gist_expires = Column('gist_expires', Float(), nullable=False)
2140 gist_type = Column('gist_type', Unicode(128), nullable=False)
2140 gist_type = Column('gist_type', Unicode(128), nullable=False)
2141 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2141 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2142 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2142 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2143
2143
2144 owner = relationship('User')
2144 owner = relationship('User')
2145
2145
2146 @classmethod
2146 @classmethod
2147 def get_or_404(cls, id_):
2147 def get_or_404(cls, id_):
2148 res = cls.query().filter(cls.gist_access_id == id_).scalar()
2148 res = cls.query().filter(cls.gist_access_id == id_).scalar()
2149 if not res:
2149 if not res:
2150 raise HTTPNotFound
2150 raise HTTPNotFound
2151 return res
2151 return res
2152
2152
2153 @classmethod
2153 @classmethod
2154 def get_by_access_id(cls, gist_access_id):
2154 def get_by_access_id(cls, gist_access_id):
2155 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
2155 return cls.query().filter(cls.gist_access_id == gist_access_id).scalar()
2156
2156
2157 def gist_url(self):
2157 def gist_url(self):
2158 import rhodecode
2158 import rhodecode
2159 alias_url = rhodecode.CONFIG.get('gist_alias_url')
2159 alias_url = rhodecode.CONFIG.get('gist_alias_url')
2160 if alias_url:
2160 if alias_url:
2161 return alias_url.replace('{gistid}', self.gist_access_id)
2161 return alias_url.replace('{gistid}', self.gist_access_id)
2162
2162
2163 from pylons import url
2163 from pylons import url
2164 return url('gist', id=self.gist_access_id, qualified=True)
2164 return url('gist', gist_id=self.gist_access_id, qualified=True)
2165
2165
2166 @classmethod
2166 @classmethod
2167 def base_path(cls):
2167 def base_path(cls):
2168 """
2168 """
2169 Returns base path when all gists are stored
2169 Returns base path when all gists are stored
2170
2170
2171 :param cls:
2171 :param cls:
2172 """
2172 """
2173 from rhodecode.model.gist import GIST_STORE_LOC
2173 from rhodecode.model.gist import GIST_STORE_LOC
2174 q = Session().query(RhodeCodeUi)\
2174 q = Session().query(RhodeCodeUi)\
2175 .filter(RhodeCodeUi.ui_key == URL_SEP)
2175 .filter(RhodeCodeUi.ui_key == URL_SEP)
2176 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
2176 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
2177 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
2177 return os.path.join(q.one().ui_value, GIST_STORE_LOC)
2178
2178
2179 def get_api_data(self):
2179 def get_api_data(self):
2180 """
2180 """
2181 Common function for generating gist related data for API
2181 Common function for generating gist related data for API
2182 """
2182 """
2183 gist = self
2183 gist = self
2184 data = dict(
2184 data = dict(
2185 gist_id=gist.gist_id,
2185 gist_id=gist.gist_id,
2186 type=gist.gist_type,
2186 type=gist.gist_type,
2187 access_id=gist.gist_access_id,
2187 access_id=gist.gist_access_id,
2188 description=gist.gist_description,
2188 description=gist.gist_description,
2189 url=gist.gist_url(),
2189 url=gist.gist_url(),
2190 expires=gist.gist_expires,
2190 expires=gist.gist_expires,
2191 created_on=gist.created_on,
2191 created_on=gist.created_on,
2192 )
2192 )
2193 return data
2193 return data
2194
2194
2195 def __json__(self):
2195 def __json__(self):
2196 data = dict(
2196 data = dict(
2197 )
2197 )
2198 data.update(self.get_api_data())
2198 data.update(self.get_api_data())
2199 return data
2199 return data
2200 ## SCM functions
2200 ## SCM functions
2201
2201
2202 @property
2202 @property
2203 def scm_instance(self):
2203 def scm_instance(self):
2204 from rhodecode.lib.vcs import get_repo
2204 from rhodecode.lib.vcs import get_repo
2205 base_path = self.base_path()
2205 base_path = self.base_path()
2206 return get_repo(os.path.join(*map(safe_str,
2206 return get_repo(os.path.join(*map(safe_str,
2207 [base_path, self.gist_access_id])))
2207 [base_path, self.gist_access_id])))
2208
2208
2209
2209
2210 class DbMigrateVersion(Base, BaseModel):
2210 class DbMigrateVersion(Base, BaseModel):
2211 __tablename__ = 'db_migrate_version'
2211 __tablename__ = 'db_migrate_version'
2212 __table_args__ = (
2212 __table_args__ = (
2213 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2213 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2214 'mysql_charset': 'utf8'},
2214 'mysql_charset': 'utf8'},
2215 )
2215 )
2216 repository_id = Column('repository_id', String(250), primary_key=True)
2216 repository_id = Column('repository_id', String(250), primary_key=True)
2217 repository_path = Column('repository_path', Text)
2217 repository_path = Column('repository_path', Text)
2218 version = Column('version', Integer)
2218 version = Column('version', Integer)
@@ -1,71 +1,71 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Gists')} &middot; ${c.rhodecode_name}
5 ${_('Gists')} &middot; ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 %if c.show_private:
9 %if c.show_private:
10 ${_('Private Gists for user %s') % c.rhodecode_user.username}
10 ${_('Private Gists for user %s') % c.rhodecode_user.username}
11 %elif c.show_public:
11 %elif c.show_public:
12 ${_('Public Gists for user %s') % c.rhodecode_user.username}
12 ${_('Public Gists for user %s') % c.rhodecode_user.username}
13 %else:
13 %else:
14 ${_('Public Gists')}
14 ${_('Public Gists')}
15 %endif
15 %endif
16 - ${c.gists_pager.item_count}
16 - ${c.gists_pager.item_count}
17 </%def>
17 </%def>
18
18
19 <%def name="page_nav()">
19 <%def name="page_nav()">
20 ${self.menu('gists')}
20 ${self.menu('gists')}
21 </%def>
21 </%def>
22
22
23 <%def name="main()">
23 <%def name="main()">
24 <div class="box">
24 <div class="box">
25 <!-- box / title -->
25 <!-- box / title -->
26 <div class="title">
26 <div class="title">
27 ${self.breadcrumbs()}
27 ${self.breadcrumbs()}
28 %if c.rhodecode_user.username != 'default':
28 %if c.rhodecode_user.username != 'default':
29 <ul class="links">
29 <ul class="links">
30 <li>
30 <li>
31 <span>${h.link_to(_(u'Create new gist'), h.url('new_gist'))}</span>
31 <span>${h.link_to(_(u'Create new gist'), h.url('new_gist'))}</span>
32 </li>
32 </li>
33 </ul>
33 </ul>
34 %endif
34 %endif
35 </div>
35 </div>
36 %if c.gists_pager.item_count>0:
36 %if c.gists_pager.item_count>0:
37 % for gist in c.gists_pager:
37 % for gist in c.gists_pager:
38 <div class="gist-item" style="padding:10px 20px 10px 15px">
38 <div class="gist-item" style="padding:10px 20px 10px 15px">
39
39
40 <div class="gravatar">
40 <div class="gravatar">
41 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(gist.owner.full_contact),28)}"/>
41 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(gist.owner.full_contact),28)}"/>
42 </div>
42 </div>
43 <div title="${gist.owner.full_contact}" class="user" style="font-size: 16px">
43 <div title="${gist.owner.full_contact}" class="user" style="font-size: 16px">
44 <b>${h.person(gist.owner.full_contact)}</b> /
44 <b>${h.person(gist.owner.full_contact)}</b> /
45 <b><a href="${h.url('gist',id=gist.gist_access_id)}">gist:${gist.gist_access_id}</a></b>
45 <b><a href="${h.url('gist',gist_id=gist.gist_access_id)}">gist:${gist.gist_access_id}</a></b>
46 </div>
46 </div>
47 <div style="padding: 4px 0px 0px 0px">
47 <div style="padding: 4px 0px 0px 0px">
48 ${_('Created')} ${h.age(gist.created_on)} /
48 ${_('Created')} ${h.age(gist.created_on)} /
49 <span style="color: #AAA">
49 <span style="color: #AAA">
50 %if gist.gist_expires == -1:
50 %if gist.gist_expires == -1:
51 ${_('Expires')}: ${_('never')}
51 ${_('Expires')}: ${_('never')}
52 %else:
52 %else:
53 ${_('Expires')}: ${h.age(h.time_to_datetime(gist.gist_expires))}
53 ${_('Expires')}: ${h.age(h.time_to_datetime(gist.gist_expires))}
54 %endif
54 %endif
55 </span>
55 </span>
56 </div>
56 </div>
57
57
58 <div style="border:0px;padding:10px 0px 0px 40px;color:#AAA">${gist.gist_description}</div>
58 <div style="border:0px;padding:10px 0px 0px 40px;color:#AAA">${gist.gist_description}</div>
59 </div>
59 </div>
60 % endfor
60 % endfor
61
61
62 <div class="notification-paginator">
62 <div class="notification-paginator">
63 <div class="pagination-wh pagination-left">
63 <div class="pagination-wh pagination-left">
64 ${c.gists_pager.pager('$link_previous ~2~ $link_next')}
64 ${c.gists_pager.pager('$link_previous ~2~ $link_next')}
65 </div>
65 </div>
66 </div>
66 </div>
67 %else:
67 %else:
68 <div class="table">${_('There are no gists yet')}</div>
68 <div class="table">${_('There are no gists yet')}</div>
69 %endif
69 %endif
70 </div>
70 </div>
71 </%def>
71 </%def>
@@ -1,92 +1,92 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('gist')}:${c.gist.gist_access_id} &middot; ${c.rhodecode_name}
5 ${_('gist')}:${c.gist.gist_access_id} &middot; ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${_('Gist')} &middot; gist:${c.gist.gist_access_id}
9 ${_('Gist')} &middot; gist:${c.gist.gist_access_id}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('gists')}
13 ${self.menu('gists')}
14 </%def>
14 </%def>
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 %if c.rhodecode_user.username != 'default':
21 %if c.rhodecode_user.username != 'default':
22 <ul class="links">
22 <ul class="links">
23 <li>
23 <li>
24 <span>${h.link_to(_(u'Create new gist'), h.url('new_gist'))}</span>
24 <span>${h.link_to(_(u'Create new gist'), h.url('new_gist'))}</span>
25 </li>
25 </li>
26 </ul>
26 </ul>
27 %endif
27 %endif
28 </div>
28 </div>
29 <div class="table">
29 <div class="table">
30 <div id="files_data">
30 <div id="files_data">
31 <div id="body" class="codeblock">
31 <div id="body" class="codeblock">
32 <div class="code-header">
32 <div class="code-header">
33 <div class="stats">
33 <div class="stats">
34 <div class="left" style="margin: -4px 0px 0px 0px">
34 <div class="left" style="margin: -4px 0px 0px 0px">
35 %if c.gist.gist_type == 'public':
35 %if c.gist.gist_type == 'public':
36 <div class="ui-btn green badge">${_('Public gist')}</div>
36 <div class="ui-btn green badge">${_('Public gist')}</div>
37 %else:
37 %else:
38 <div class="ui-btn yellow badge">${_('Private gist')}</div>
38 <div class="ui-btn yellow badge">${_('Private gist')}</div>
39 %endif
39 %endif
40 </div>
40 </div>
41 <div class="left item ${'' if c.gist.gist_description else 'last'}" style="color: #AAA">
41 <div class="left item ${'' if c.gist.gist_description else 'last'}" style="color: #AAA">
42 %if c.gist.gist_expires == -1:
42 %if c.gist.gist_expires == -1:
43 ${_('Expires')}: ${_('never')}
43 ${_('Expires')}: ${_('never')}
44 %else:
44 %else:
45 ${_('Expires')}: ${h.age(h.time_to_datetime(c.gist.gist_expires))}
45 ${_('Expires')}: ${h.age(h.time_to_datetime(c.gist.gist_expires))}
46 %endif
46 %endif
47 </div>
47 </div>
48 <div class="left item last">
48 <div class="left item last">
49 ${c.gist.gist_description}
49 ${c.gist.gist_description}
50 </div>
50 </div>
51 <div class="buttons">
51 <div class="buttons">
52 ## only owner should see that
52 ## only owner should see that
53 %if h.HasPermissionAny('hg.admin')() or c.gist.gist_owner == c.rhodecode_user.user_id:
53 %if h.HasPermissionAny('hg.admin')() or c.gist.gist_owner == c.rhodecode_user.user_id:
54 ##${h.link_to(_('Edit'),h.url(''),class_="ui-btn")}
54 ##${h.link_to(_('Edit'),h.url(''),class_="ui-btn")}
55 ${h.form(url('gist', id=c.gist.gist_id),method='delete')}
55 ${h.form(url('gist', gist_id=c.gist.gist_id),method='delete')}
56 ${h.submit('remove_gist', _('Delete'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this gist')+"');")}
56 ${h.submit('remove_gist', _('Delete'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this gist')+"');")}
57 ${h.end_form()}
57 ${h.end_form()}
58 %endif
58 %endif
59 </div>
59 </div>
60 </div>
60 </div>
61
61
62 <div class="author">
62 <div class="author">
63 <div class="gravatar">
63 <div class="gravatar">
64 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.file_changeset.author),16)}"/>
64 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.file_changeset.author),16)}"/>
65 </div>
65 </div>
66 <div title="${c.file_changeset.author}" class="user">${h.person(c.file_changeset.author)} - ${_('created')} ${h.age(c.file_changeset.date)}</div>
66 <div title="${c.file_changeset.author}" class="user">${h.person(c.file_changeset.author)} - ${_('created')} ${h.age(c.file_changeset.date)}</div>
67 </div>
67 </div>
68 <div class="commit">${h.urlify_commit(c.file_changeset.message,c.repo_name)}</div>
68 <div class="commit">${h.urlify_commit(c.file_changeset.message,c.repo_name)}</div>
69 </div>
69 </div>
70 </div>
70 </div>
71
71
72 ## iterate over the files
72 ## iterate over the files
73 % for file in c.files:
73 % for file in c.files:
74 <div style="border: 1px solid #EEE;margin-top:20px">
74 <div style="border: 1px solid #EEE;margin-top:20px">
75 <div id="${h.FID('G', file.path)}" class="stats" style="border-bottom: 1px solid #DDD;padding: 8px 14px;">
75 <div id="${h.FID('G', file.path)}" class="stats" style="border-bottom: 1px solid #DDD;padding: 8px 14px;">
76 <a href="${c.gist.gist_url()}">ΒΆ</a>
76 <a href="${c.gist.gist_url()}">ΒΆ</a>
77 <b style="margin:0px 0px 0px 4px">${file.path}</b>
77 <b style="margin:0px 0px 0px 4px">${file.path}</b>
78 ##<div class="buttons">
78 ##<div class="buttons">
79 ## ${h.link_to(_('Show as raw'),h.url(''),class_="ui-btn")}
79 ## ${h.link_to(_('Show as raw'),h.url(''),class_="ui-btn")}
80 ##</div>
80 ##</div>
81 </div>
81 </div>
82 <div class="code-body">
82 <div class="code-body">
83 ${h.pygmentize(file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
83 ${h.pygmentize(file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
84 </div>
84 </div>
85 </div>
85 </div>
86 %endfor
86 %endfor
87 </div>
87 </div>
88 </div>
88 </div>
89
89
90
90
91 </div>
91 </div>
92 </%def>
92 </%def>
@@ -1,160 +1,160 b''
1 import datetime
1 import datetime
2
2
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4 from rhodecode.model.gist import GistModel
4 from rhodecode.model.gist import GistModel
5 from rhodecode.model.meta import Session
5 from rhodecode.model.meta import Session
6 from rhodecode.model.db import User, Gist
6 from rhodecode.model.db import User, Gist
7
7
8
8
9 def _create_gist(f_name, content='some gist', lifetime=-1,
9 def _create_gist(f_name, content='some gist', lifetime=-1,
10 description='gist-desc', gist_type='public',
10 description='gist-desc', gist_type='public',
11 owner=TEST_USER_ADMIN_LOGIN):
11 owner=TEST_USER_ADMIN_LOGIN):
12 gist_mapping = {
12 gist_mapping = {
13 f_name: {'content': content}
13 f_name: {'content': content}
14 }
14 }
15 user = User.get_by_username(owner)
15 user = User.get_by_username(owner)
16 gist = GistModel().create(description, owner=user,
16 gist = GistModel().create(description, owner=user,
17 gist_mapping=gist_mapping, gist_type=gist_type,
17 gist_mapping=gist_mapping, gist_type=gist_type,
18 lifetime=lifetime)
18 lifetime=lifetime)
19 Session().commit()
19 Session().commit()
20 return gist
20 return gist
21
21
22
22
23 class TestGistsController(TestController):
23 class TestGistsController(TestController):
24
24
25 def tearDown(self):
25 def tearDown(self):
26 for g in Gist.get_all():
26 for g in Gist.get_all():
27 GistModel().delete(g)
27 GistModel().delete(g)
28 Session().commit()
28 Session().commit()
29
29
30 def test_index(self):
30 def test_index(self):
31 self.log_user()
31 self.log_user()
32 response = self.app.get(url('gists'))
32 response = self.app.get(url('gists'))
33 # Test response...
33 # Test response...
34 response.mustcontain('There are no gists yet')
34 response.mustcontain('There are no gists yet')
35
35
36 g1 = _create_gist('gist1').gist_access_id
36 g1 = _create_gist('gist1').gist_access_id
37 g2 = _create_gist('gist2', lifetime=1400).gist_access_id
37 g2 = _create_gist('gist2', lifetime=1400).gist_access_id
38 g3 = _create_gist('gist3', description='gist3-desc').gist_access_id
38 g3 = _create_gist('gist3', description='gist3-desc').gist_access_id
39 g4 = _create_gist('gist4', gist_type='private').gist_access_id
39 g4 = _create_gist('gist4', gist_type='private').gist_access_id
40 response = self.app.get(url('gists'))
40 response = self.app.get(url('gists'))
41 # Test response...
41 # Test response...
42 response.mustcontain('gist:%s' % g1)
42 response.mustcontain('gist:%s' % g1)
43 response.mustcontain('gist:%s' % g2)
43 response.mustcontain('gist:%s' % g2)
44 response.mustcontain('Expires: in 23 hours') # we don't care about the end
44 response.mustcontain('Expires: in 23 hours') # we don't care about the end
45 response.mustcontain('gist:%s' % g3)
45 response.mustcontain('gist:%s' % g3)
46 response.mustcontain('gist3-desc')
46 response.mustcontain('gist3-desc')
47 response.mustcontain(no=['gist:%s' % g4])
47 response.mustcontain(no=['gist:%s' % g4])
48
48
49 def test_index_private_gists(self):
49 def test_index_private_gists(self):
50 self.log_user()
50 self.log_user()
51 gist = _create_gist('gist5', gist_type='private')
51 gist = _create_gist('gist5', gist_type='private')
52 response = self.app.get(url('gists', private=1))
52 response = self.app.get(url('gists', private=1))
53 # Test response...
53 # Test response...
54
54
55 #and privates
55 #and privates
56 response.mustcontain('gist:%s' % gist.gist_access_id)
56 response.mustcontain('gist:%s' % gist.gist_access_id)
57
57
58 def test_create_missing_description(self):
58 def test_create_missing_description(self):
59 self.log_user()
59 self.log_user()
60 response = self.app.post(url('gists'),
60 response = self.app.post(url('gists'),
61 params={'lifetime': -1}, status=200)
61 params={'lifetime': -1}, status=200)
62
62
63 response.mustcontain('Missing value')
63 response.mustcontain('Missing value')
64
64
65 def test_create(self):
65 def test_create(self):
66 self.log_user()
66 self.log_user()
67 response = self.app.post(url('gists'),
67 response = self.app.post(url('gists'),
68 params={'lifetime': -1,
68 params={'lifetime': -1,
69 'content': 'gist test',
69 'content': 'gist test',
70 'filename': 'foo',
70 'filename': 'foo',
71 'public': 'public'},
71 'public': 'public'},
72 status=302)
72 status=302)
73 response = response.follow()
73 response = response.follow()
74 response.mustcontain('added file: foo')
74 response.mustcontain('added file: foo')
75 response.mustcontain('gist test')
75 response.mustcontain('gist test')
76 response.mustcontain('<div class="ui-btn green badge">Public gist</div>')
76 response.mustcontain('<div class="ui-btn green badge">Public gist</div>')
77
77
78 def test_create_with_path_with_dirs(self):
78 def test_create_with_path_with_dirs(self):
79 self.log_user()
79 self.log_user()
80 response = self.app.post(url('gists'),
80 response = self.app.post(url('gists'),
81 params={'lifetime': -1,
81 params={'lifetime': -1,
82 'content': 'gist test',
82 'content': 'gist test',
83 'filename': '/home/foo',
83 'filename': '/home/foo',
84 'public': 'public'},
84 'public': 'public'},
85 status=200)
85 status=200)
86 response.mustcontain('Filename cannot be inside a directory')
86 response.mustcontain('Filename cannot be inside a directory')
87
87
88 def test_access_expired_gist(self):
88 def test_access_expired_gist(self):
89 self.log_user()
89 self.log_user()
90 gist = _create_gist('never-see-me')
90 gist = _create_gist('never-see-me')
91 gist.gist_expires = 0 # 1970
91 gist.gist_expires = 0 # 1970
92 Session().add(gist)
92 Session().add(gist)
93 Session().commit()
93 Session().commit()
94
94
95 response = self.app.get(url('gist', id=gist.gist_access_id), status=404)
95 response = self.app.get(url('gist', gist_id=gist.gist_access_id), status=404)
96
96
97 def test_create_private(self):
97 def test_create_private(self):
98 self.log_user()
98 self.log_user()
99 response = self.app.post(url('gists'),
99 response = self.app.post(url('gists'),
100 params={'lifetime': -1,
100 params={'lifetime': -1,
101 'content': 'private gist test',
101 'content': 'private gist test',
102 'filename': 'private-foo',
102 'filename': 'private-foo',
103 'private': 'private'},
103 'private': 'private'},
104 status=302)
104 status=302)
105 response = response.follow()
105 response = response.follow()
106 response.mustcontain('added file: private-foo<')
106 response.mustcontain('added file: private-foo<')
107 response.mustcontain('private gist test')
107 response.mustcontain('private gist test')
108 response.mustcontain('<div class="ui-btn yellow badge">Private gist</div>')
108 response.mustcontain('<div class="ui-btn yellow badge">Private gist</div>')
109
109
110 def test_create_with_description(self):
110 def test_create_with_description(self):
111 self.log_user()
111 self.log_user()
112 response = self.app.post(url('gists'),
112 response = self.app.post(url('gists'),
113 params={'lifetime': -1,
113 params={'lifetime': -1,
114 'content': 'gist test',
114 'content': 'gist test',
115 'filename': 'foo-desc',
115 'filename': 'foo-desc',
116 'description': 'gist-desc',
116 'description': 'gist-desc',
117 'public': 'public'},
117 'public': 'public'},
118 status=302)
118 status=302)
119 response = response.follow()
119 response = response.follow()
120 response.mustcontain('added file: foo-desc')
120 response.mustcontain('added file: foo-desc')
121 response.mustcontain('gist test')
121 response.mustcontain('gist test')
122 response.mustcontain('gist-desc')
122 response.mustcontain('gist-desc')
123 response.mustcontain('<div class="ui-btn green badge">Public gist</div>')
123 response.mustcontain('<div class="ui-btn green badge">Public gist</div>')
124
124
125 def test_new(self):
125 def test_new(self):
126 self.log_user()
126 self.log_user()
127 response = self.app.get(url('new_gist'))
127 response = self.app.get(url('new_gist'))
128
128
129 def test_update(self):
129 def test_update(self):
130 self.skipTest('not implemented')
130 self.skipTest('not implemented')
131 response = self.app.put(url('gist', id=1))
131 response = self.app.put(url('gist', gist_id=1))
132
132
133 def test_delete(self):
133 def test_delete(self):
134 self.log_user()
134 self.log_user()
135 gist = _create_gist('delete-me')
135 gist = _create_gist('delete-me')
136 response = self.app.delete(url('gist', id=gist.gist_id))
136 response = self.app.delete(url('gist', gist_id=gist.gist_id))
137 self.checkSessionFlash(response, 'Deleted gist %s' % gist.gist_id)
137 self.checkSessionFlash(response, 'Deleted gist %s' % gist.gist_id)
138
138
139 def test_delete_normal_user_his_gist(self):
139 def test_delete_normal_user_his_gist(self):
140 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
140 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
141 gist = _create_gist('delete-me', owner=TEST_USER_REGULAR_LOGIN)
141 gist = _create_gist('delete-me', owner=TEST_USER_REGULAR_LOGIN)
142 response = self.app.delete(url('gist', id=gist.gist_id))
142 response = self.app.delete(url('gist', gist_id=gist.gist_id))
143 self.checkSessionFlash(response, 'Deleted gist %s' % gist.gist_id)
143 self.checkSessionFlash(response, 'Deleted gist %s' % gist.gist_id)
144
144
145 def test_delete_normal_user_not_his_own_gist(self):
145 def test_delete_normal_user_not_his_own_gist(self):
146 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
146 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
147 gist = _create_gist('delete-me')
147 gist = _create_gist('delete-me')
148 response = self.app.delete(url('gist', id=gist.gist_id), status=403)
148 response = self.app.delete(url('gist', gist_id=gist.gist_id), status=403)
149
149
150 def test_show(self):
150 def test_show(self):
151 gist = _create_gist('gist-show-me')
151 gist = _create_gist('gist-show-me')
152 response = self.app.get(url('gist', id=gist.gist_access_id))
152 response = self.app.get(url('gist', gist_id=gist.gist_access_id))
153 response.mustcontain('added file: gist-show-me<')
153 response.mustcontain('added file: gist-show-me<')
154 response.mustcontain('test_admin (RhodeCode Admin) - created')
154 response.mustcontain('test_admin (RhodeCode Admin) - created')
155 response.mustcontain('gist-desc')
155 response.mustcontain('gist-desc')
156 response.mustcontain('<div class="ui-btn green badge">Public gist</div>')
156 response.mustcontain('<div class="ui-btn green badge">Public gist</div>')
157
157
158 def test_edit(self):
158 def test_edit(self):
159 self.skipTest('not implemented')
159 self.skipTest('not implemented')
160 response = self.app.get(url('edit_gist', id=1))
160 response = self.app.get(url('edit_gist', gist_id=1))
General Comments 0
You need to be logged in to leave comments. Login now