##// END OF EJS Templates
Added basic models for saving open pull requests...
marcink -
r2434:f2946967 codereview
parent child Browse files
Show More
@@ -0,0 +1,79 b''
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.model.pull_reuquest
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 pull request model for RhodeCode
7
8 :created_on: Jun 6, 2012
9 :author: marcink
10 :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
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
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
22 #
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/>.
25
26 import logging
27 from pylons.i18n.translation import _
28
29 from rhodecode.lib import helpers as h
30 from rhodecode.model import BaseModel
31 from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification
32 from rhodecode.model.notification import NotificationModel
33 from rhodecode.lib.utils2 import safe_unicode
34
35 log = logging.getLogger(__name__)
36
37
38 class PullRequestModel(BaseModel):
39
40 def create(self, created_by, org_repo, org_ref, other_repo,
41 other_ref, revisions, reviewers, title, description=None):
42
43 new = PullRequest()
44 new.org_repo = self._get_repo(org_repo)
45 new.org_ref = org_ref
46 new.other_repo = self._get_repo(other_repo)
47 new.other_ref = other_ref
48 new.revisions = revisions
49 new.title = title
50 new.description = description
51
52 self.sa.add(new)
53
54 #members
55 for member in reviewers:
56 _usr = self._get_user(member)
57 reviewer = PullRequestReviewers(_usr, new)
58 self.sa.add(reviewer)
59
60 #notification to reviewers
61 notif = NotificationModel()
62 created_by_user = self._get_user(created_by)
63 subject = safe_unicode(
64 h.link_to(
65 _('%(user)s wants you to review pull request #%(pr_id)s') % \
66 {'user': created_by_user.username,
67 'pr_id': new.pull_request_id},
68 h.url('pullrequest_show', repo_name=other_repo,
69 pull_request_id=new.pull_request_id,
70 qualified=True,
71 )
72 )
73 )
74 body = description
75 notif.create(created_by=created_by, subject=subject, body=body,
76 recipients=reviewers,
77 type_=Notification.TYPE_PULL_REQUEST,)
78
79 return new
@@ -0,0 +1,32 b''
1 <%inherit file="/base/base.html"/>
2
3 <%def name="title()">
4 ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id}
5 </%def>
6
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 &raquo;
12 ${_('Pull request #%s') % c.pull_request.pull_request_id}
13 </%def>
14
15 <%def name="main()">
16
17 <div class="box">
18 <!-- box / title -->
19 <div class="title">
20 ${self.breadcrumbs()}
21 </div>
22
23 pull request ${c.pull_request} overview...
24
25 </div>
26
27 <script type="text/javascript">
28
29
30 </script>
31
32 </%def>
@@ -1,535 +1,546 b''
1 """
1 """
2 Routes configuration
2 Routes configuration
3
3
4 The more specific and detailed routes should be defined first so they
4 The more specific and detailed routes should be defined first so they
5 may take precedent over the more generic routes. For more information
5 may take precedent over the more generic routes. For more information
6 refer to the routes manual at http://routes.groovie.org/docs/
6 refer to the routes manual at http://routes.groovie.org/docs/
7 """
7 """
8 from __future__ import with_statement
8 from __future__ import with_statement
9 from routes import Mapper
9 from routes import Mapper
10
10
11 # prefix for non repository related links needs to be prefixed with `/`
11 # prefix for non repository related links needs to be prefixed with `/`
12 ADMIN_PREFIX = '/_admin'
12 ADMIN_PREFIX = '/_admin'
13
13
14
14
15 def make_map(config):
15 def make_map(config):
16 """Create, configure and return the routes Mapper"""
16 """Create, configure and return the routes Mapper"""
17 rmap = Mapper(directory=config['pylons.paths']['controllers'],
17 rmap = Mapper(directory=config['pylons.paths']['controllers'],
18 always_scan=config['debug'])
18 always_scan=config['debug'])
19 rmap.minimization = False
19 rmap.minimization = False
20 rmap.explicit = False
20 rmap.explicit = False
21
21
22 from rhodecode.lib.utils import is_valid_repo
22 from rhodecode.lib.utils import is_valid_repo
23 from rhodecode.lib.utils import is_valid_repos_group
23 from rhodecode.lib.utils import is_valid_repos_group
24
24
25 def check_repo(environ, match_dict):
25 def check_repo(environ, match_dict):
26 """
26 """
27 check for valid repository for proper 404 handling
27 check for valid repository for proper 404 handling
28
28
29 :param environ:
29 :param environ:
30 :param match_dict:
30 :param match_dict:
31 """
31 """
32 from rhodecode.model.db import Repository
32 from rhodecode.model.db import Repository
33 repo_name = match_dict.get('repo_name')
33 repo_name = match_dict.get('repo_name')
34
34
35 try:
35 try:
36 by_id = repo_name.split('_')
36 by_id = repo_name.split('_')
37 if len(by_id) == 2 and by_id[1].isdigit():
37 if len(by_id) == 2 and by_id[1].isdigit():
38 repo_name = Repository.get(by_id[1]).repo_name
38 repo_name = Repository.get(by_id[1]).repo_name
39 match_dict['repo_name'] = repo_name
39 match_dict['repo_name'] = repo_name
40 except:
40 except:
41 pass
41 pass
42
42
43 return is_valid_repo(repo_name, config['base_path'])
43 return is_valid_repo(repo_name, config['base_path'])
44
44
45 def check_group(environ, match_dict):
45 def check_group(environ, match_dict):
46 """
46 """
47 check for valid repositories group for proper 404 handling
47 check for valid repositories group for proper 404 handling
48
48
49 :param environ:
49 :param environ:
50 :param match_dict:
50 :param match_dict:
51 """
51 """
52 repos_group_name = match_dict.get('group_name')
52 repos_group_name = match_dict.get('group_name')
53
53
54 return is_valid_repos_group(repos_group_name, config['base_path'])
54 return is_valid_repos_group(repos_group_name, config['base_path'])
55
55
56 def check_int(environ, match_dict):
56 def check_int(environ, match_dict):
57 return match_dict.get('id').isdigit()
57 return match_dict.get('id').isdigit()
58
58
59 # The ErrorController route (handles 404/500 error pages); it should
59 # The ErrorController route (handles 404/500 error pages); it should
60 # likely stay at the top, ensuring it can always be resolved
60 # likely stay at the top, ensuring it can always be resolved
61 rmap.connect('/error/{action}', controller='error')
61 rmap.connect('/error/{action}', controller='error')
62 rmap.connect('/error/{action}/{id}', controller='error')
62 rmap.connect('/error/{action}/{id}', controller='error')
63
63
64 #==========================================================================
64 #==========================================================================
65 # CUSTOM ROUTES HERE
65 # CUSTOM ROUTES HERE
66 #==========================================================================
66 #==========================================================================
67
67
68 #MAIN PAGE
68 #MAIN PAGE
69 rmap.connect('home', '/', controller='home', action='index')
69 rmap.connect('home', '/', controller='home', action='index')
70 rmap.connect('repo_switcher', '/repos', controller='home',
70 rmap.connect('repo_switcher', '/repos', controller='home',
71 action='repo_switcher')
71 action='repo_switcher')
72 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
72 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
73 controller='home', action='branch_tag_switcher')
73 controller='home', action='branch_tag_switcher')
74 rmap.connect('bugtracker',
74 rmap.connect('bugtracker',
75 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
75 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
76 _static=True)
76 _static=True)
77 rmap.connect('rst_help',
77 rmap.connect('rst_help',
78 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
78 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
79 _static=True)
79 _static=True)
80 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
80 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
81
81
82 #ADMIN REPOSITORY REST ROUTES
82 #ADMIN REPOSITORY REST ROUTES
83 with rmap.submapper(path_prefix=ADMIN_PREFIX,
83 with rmap.submapper(path_prefix=ADMIN_PREFIX,
84 controller='admin/repos') as m:
84 controller='admin/repos') as m:
85 m.connect("repos", "/repos",
85 m.connect("repos", "/repos",
86 action="create", conditions=dict(method=["POST"]))
86 action="create", conditions=dict(method=["POST"]))
87 m.connect("repos", "/repos",
87 m.connect("repos", "/repos",
88 action="index", conditions=dict(method=["GET"]))
88 action="index", conditions=dict(method=["GET"]))
89 m.connect("formatted_repos", "/repos.{format}",
89 m.connect("formatted_repos", "/repos.{format}",
90 action="index",
90 action="index",
91 conditions=dict(method=["GET"]))
91 conditions=dict(method=["GET"]))
92 m.connect("new_repo", "/repos/new",
92 m.connect("new_repo", "/repos/new",
93 action="new", conditions=dict(method=["GET"]))
93 action="new", conditions=dict(method=["GET"]))
94 m.connect("formatted_new_repo", "/repos/new.{format}",
94 m.connect("formatted_new_repo", "/repos/new.{format}",
95 action="new", conditions=dict(method=["GET"]))
95 action="new", conditions=dict(method=["GET"]))
96 m.connect("/repos/{repo_name:.*}",
96 m.connect("/repos/{repo_name:.*}",
97 action="update", conditions=dict(method=["PUT"],
97 action="update", conditions=dict(method=["PUT"],
98 function=check_repo))
98 function=check_repo))
99 m.connect("/repos/{repo_name:.*}",
99 m.connect("/repos/{repo_name:.*}",
100 action="delete", conditions=dict(method=["DELETE"],
100 action="delete", conditions=dict(method=["DELETE"],
101 function=check_repo))
101 function=check_repo))
102 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
102 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
103 action="edit", conditions=dict(method=["GET"],
103 action="edit", conditions=dict(method=["GET"],
104 function=check_repo))
104 function=check_repo))
105 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
105 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
106 action="edit", conditions=dict(method=["GET"],
106 action="edit", conditions=dict(method=["GET"],
107 function=check_repo))
107 function=check_repo))
108 m.connect("repo", "/repos/{repo_name:.*}",
108 m.connect("repo", "/repos/{repo_name:.*}",
109 action="show", conditions=dict(method=["GET"],
109 action="show", conditions=dict(method=["GET"],
110 function=check_repo))
110 function=check_repo))
111 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
111 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
112 action="show", conditions=dict(method=["GET"],
112 action="show", conditions=dict(method=["GET"],
113 function=check_repo))
113 function=check_repo))
114 #ajax delete repo perm user
114 #ajax delete repo perm user
115 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
115 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
116 action="delete_perm_user",
116 action="delete_perm_user",
117 conditions=dict(method=["DELETE"], function=check_repo))
117 conditions=dict(method=["DELETE"], function=check_repo))
118
118
119 #ajax delete repo perm users_group
119 #ajax delete repo perm users_group
120 m.connect('delete_repo_users_group',
120 m.connect('delete_repo_users_group',
121 "/repos_delete_users_group/{repo_name:.*}",
121 "/repos_delete_users_group/{repo_name:.*}",
122 action="delete_perm_users_group",
122 action="delete_perm_users_group",
123 conditions=dict(method=["DELETE"], function=check_repo))
123 conditions=dict(method=["DELETE"], function=check_repo))
124
124
125 #settings actions
125 #settings actions
126 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
126 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
127 action="repo_stats", conditions=dict(method=["DELETE"],
127 action="repo_stats", conditions=dict(method=["DELETE"],
128 function=check_repo))
128 function=check_repo))
129 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
129 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
130 action="repo_cache", conditions=dict(method=["DELETE"],
130 action="repo_cache", conditions=dict(method=["DELETE"],
131 function=check_repo))
131 function=check_repo))
132 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*}",
132 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*}",
133 action="repo_public_journal", conditions=dict(method=["PUT"],
133 action="repo_public_journal", conditions=dict(method=["PUT"],
134 function=check_repo))
134 function=check_repo))
135 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
135 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
136 action="repo_pull", conditions=dict(method=["PUT"],
136 action="repo_pull", conditions=dict(method=["PUT"],
137 function=check_repo))
137 function=check_repo))
138 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*}",
138 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*}",
139 action="repo_as_fork", conditions=dict(method=["PUT"],
139 action="repo_as_fork", conditions=dict(method=["PUT"],
140 function=check_repo))
140 function=check_repo))
141
141
142 with rmap.submapper(path_prefix=ADMIN_PREFIX,
142 with rmap.submapper(path_prefix=ADMIN_PREFIX,
143 controller='admin/repos_groups') as m:
143 controller='admin/repos_groups') as m:
144 m.connect("repos_groups", "/repos_groups",
144 m.connect("repos_groups", "/repos_groups",
145 action="create", conditions=dict(method=["POST"]))
145 action="create", conditions=dict(method=["POST"]))
146 m.connect("repos_groups", "/repos_groups",
146 m.connect("repos_groups", "/repos_groups",
147 action="index", conditions=dict(method=["GET"]))
147 action="index", conditions=dict(method=["GET"]))
148 m.connect("formatted_repos_groups", "/repos_groups.{format}",
148 m.connect("formatted_repos_groups", "/repos_groups.{format}",
149 action="index", conditions=dict(method=["GET"]))
149 action="index", conditions=dict(method=["GET"]))
150 m.connect("new_repos_group", "/repos_groups/new",
150 m.connect("new_repos_group", "/repos_groups/new",
151 action="new", conditions=dict(method=["GET"]))
151 action="new", conditions=dict(method=["GET"]))
152 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
152 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
153 action="new", conditions=dict(method=["GET"]))
153 action="new", conditions=dict(method=["GET"]))
154 m.connect("update_repos_group", "/repos_groups/{id}",
154 m.connect("update_repos_group", "/repos_groups/{id}",
155 action="update", conditions=dict(method=["PUT"],
155 action="update", conditions=dict(method=["PUT"],
156 function=check_int))
156 function=check_int))
157 m.connect("delete_repos_group", "/repos_groups/{id}",
157 m.connect("delete_repos_group", "/repos_groups/{id}",
158 action="delete", conditions=dict(method=["DELETE"],
158 action="delete", conditions=dict(method=["DELETE"],
159 function=check_int))
159 function=check_int))
160 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
160 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
161 action="edit", conditions=dict(method=["GET"],
161 action="edit", conditions=dict(method=["GET"],
162 function=check_int))
162 function=check_int))
163 m.connect("formatted_edit_repos_group",
163 m.connect("formatted_edit_repos_group",
164 "/repos_groups/{id}.{format}/edit",
164 "/repos_groups/{id}.{format}/edit",
165 action="edit", conditions=dict(method=["GET"],
165 action="edit", conditions=dict(method=["GET"],
166 function=check_int))
166 function=check_int))
167 m.connect("repos_group", "/repos_groups/{id}",
167 m.connect("repos_group", "/repos_groups/{id}",
168 action="show", conditions=dict(method=["GET"],
168 action="show", conditions=dict(method=["GET"],
169 function=check_int))
169 function=check_int))
170 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
170 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
171 action="show", conditions=dict(method=["GET"],
171 action="show", conditions=dict(method=["GET"],
172 function=check_int))
172 function=check_int))
173 # ajax delete repos group perm user
173 # ajax delete repos group perm user
174 m.connect('delete_repos_group_user_perm',
174 m.connect('delete_repos_group_user_perm',
175 "/delete_repos_group_user_perm/{group_name:.*}",
175 "/delete_repos_group_user_perm/{group_name:.*}",
176 action="delete_repos_group_user_perm",
176 action="delete_repos_group_user_perm",
177 conditions=dict(method=["DELETE"], function=check_group))
177 conditions=dict(method=["DELETE"], function=check_group))
178
178
179 # ajax delete repos group perm users_group
179 # ajax delete repos group perm users_group
180 m.connect('delete_repos_group_users_group_perm',
180 m.connect('delete_repos_group_users_group_perm',
181 "/delete_repos_group_users_group_perm/{group_name:.*}",
181 "/delete_repos_group_users_group_perm/{group_name:.*}",
182 action="delete_repos_group_users_group_perm",
182 action="delete_repos_group_users_group_perm",
183 conditions=dict(method=["DELETE"], function=check_group))
183 conditions=dict(method=["DELETE"], function=check_group))
184
184
185 #ADMIN USER REST ROUTES
185 #ADMIN USER REST ROUTES
186 with rmap.submapper(path_prefix=ADMIN_PREFIX,
186 with rmap.submapper(path_prefix=ADMIN_PREFIX,
187 controller='admin/users') as m:
187 controller='admin/users') as m:
188 m.connect("users", "/users",
188 m.connect("users", "/users",
189 action="create", conditions=dict(method=["POST"]))
189 action="create", conditions=dict(method=["POST"]))
190 m.connect("users", "/users",
190 m.connect("users", "/users",
191 action="index", conditions=dict(method=["GET"]))
191 action="index", conditions=dict(method=["GET"]))
192 m.connect("formatted_users", "/users.{format}",
192 m.connect("formatted_users", "/users.{format}",
193 action="index", conditions=dict(method=["GET"]))
193 action="index", conditions=dict(method=["GET"]))
194 m.connect("new_user", "/users/new",
194 m.connect("new_user", "/users/new",
195 action="new", conditions=dict(method=["GET"]))
195 action="new", conditions=dict(method=["GET"]))
196 m.connect("formatted_new_user", "/users/new.{format}",
196 m.connect("formatted_new_user", "/users/new.{format}",
197 action="new", conditions=dict(method=["GET"]))
197 action="new", conditions=dict(method=["GET"]))
198 m.connect("update_user", "/users/{id}",
198 m.connect("update_user", "/users/{id}",
199 action="update", conditions=dict(method=["PUT"]))
199 action="update", conditions=dict(method=["PUT"]))
200 m.connect("delete_user", "/users/{id}",
200 m.connect("delete_user", "/users/{id}",
201 action="delete", conditions=dict(method=["DELETE"]))
201 action="delete", conditions=dict(method=["DELETE"]))
202 m.connect("edit_user", "/users/{id}/edit",
202 m.connect("edit_user", "/users/{id}/edit",
203 action="edit", conditions=dict(method=["GET"]))
203 action="edit", conditions=dict(method=["GET"]))
204 m.connect("formatted_edit_user",
204 m.connect("formatted_edit_user",
205 "/users/{id}.{format}/edit",
205 "/users/{id}.{format}/edit",
206 action="edit", conditions=dict(method=["GET"]))
206 action="edit", conditions=dict(method=["GET"]))
207 m.connect("user", "/users/{id}",
207 m.connect("user", "/users/{id}",
208 action="show", conditions=dict(method=["GET"]))
208 action="show", conditions=dict(method=["GET"]))
209 m.connect("formatted_user", "/users/{id}.{format}",
209 m.connect("formatted_user", "/users/{id}.{format}",
210 action="show", conditions=dict(method=["GET"]))
210 action="show", conditions=dict(method=["GET"]))
211
211
212 #EXTRAS USER ROUTES
212 #EXTRAS USER ROUTES
213 m.connect("user_perm", "/users_perm/{id}",
213 m.connect("user_perm", "/users_perm/{id}",
214 action="update_perm", conditions=dict(method=["PUT"]))
214 action="update_perm", conditions=dict(method=["PUT"]))
215 m.connect("user_emails", "/users_emails/{id}",
215 m.connect("user_emails", "/users_emails/{id}",
216 action="add_email", conditions=dict(method=["PUT"]))
216 action="add_email", conditions=dict(method=["PUT"]))
217 m.connect("user_emails_delete", "/users_emails/{id}",
217 m.connect("user_emails_delete", "/users_emails/{id}",
218 action="delete_email", conditions=dict(method=["DELETE"]))
218 action="delete_email", conditions=dict(method=["DELETE"]))
219
219
220 #ADMIN USERS GROUPS REST ROUTES
220 #ADMIN USERS GROUPS REST ROUTES
221 with rmap.submapper(path_prefix=ADMIN_PREFIX,
221 with rmap.submapper(path_prefix=ADMIN_PREFIX,
222 controller='admin/users_groups') as m:
222 controller='admin/users_groups') as m:
223 m.connect("users_groups", "/users_groups",
223 m.connect("users_groups", "/users_groups",
224 action="create", conditions=dict(method=["POST"]))
224 action="create", conditions=dict(method=["POST"]))
225 m.connect("users_groups", "/users_groups",
225 m.connect("users_groups", "/users_groups",
226 action="index", conditions=dict(method=["GET"]))
226 action="index", conditions=dict(method=["GET"]))
227 m.connect("formatted_users_groups", "/users_groups.{format}",
227 m.connect("formatted_users_groups", "/users_groups.{format}",
228 action="index", conditions=dict(method=["GET"]))
228 action="index", conditions=dict(method=["GET"]))
229 m.connect("new_users_group", "/users_groups/new",
229 m.connect("new_users_group", "/users_groups/new",
230 action="new", conditions=dict(method=["GET"]))
230 action="new", conditions=dict(method=["GET"]))
231 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
231 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
232 action="new", conditions=dict(method=["GET"]))
232 action="new", conditions=dict(method=["GET"]))
233 m.connect("update_users_group", "/users_groups/{id}",
233 m.connect("update_users_group", "/users_groups/{id}",
234 action="update", conditions=dict(method=["PUT"]))
234 action="update", conditions=dict(method=["PUT"]))
235 m.connect("delete_users_group", "/users_groups/{id}",
235 m.connect("delete_users_group", "/users_groups/{id}",
236 action="delete", conditions=dict(method=["DELETE"]))
236 action="delete", conditions=dict(method=["DELETE"]))
237 m.connect("edit_users_group", "/users_groups/{id}/edit",
237 m.connect("edit_users_group", "/users_groups/{id}/edit",
238 action="edit", conditions=dict(method=["GET"]))
238 action="edit", conditions=dict(method=["GET"]))
239 m.connect("formatted_edit_users_group",
239 m.connect("formatted_edit_users_group",
240 "/users_groups/{id}.{format}/edit",
240 "/users_groups/{id}.{format}/edit",
241 action="edit", conditions=dict(method=["GET"]))
241 action="edit", conditions=dict(method=["GET"]))
242 m.connect("users_group", "/users_groups/{id}",
242 m.connect("users_group", "/users_groups/{id}",
243 action="show", conditions=dict(method=["GET"]))
243 action="show", conditions=dict(method=["GET"]))
244 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
244 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
245 action="show", conditions=dict(method=["GET"]))
245 action="show", conditions=dict(method=["GET"]))
246
246
247 #EXTRAS USER ROUTES
247 #EXTRAS USER ROUTES
248 m.connect("users_group_perm", "/users_groups_perm/{id}",
248 m.connect("users_group_perm", "/users_groups_perm/{id}",
249 action="update_perm", conditions=dict(method=["PUT"]))
249 action="update_perm", conditions=dict(method=["PUT"]))
250
250
251 #ADMIN GROUP REST ROUTES
251 #ADMIN GROUP REST ROUTES
252 rmap.resource('group', 'groups',
252 rmap.resource('group', 'groups',
253 controller='admin/groups', path_prefix=ADMIN_PREFIX)
253 controller='admin/groups', path_prefix=ADMIN_PREFIX)
254
254
255 #ADMIN PERMISSIONS REST ROUTES
255 #ADMIN PERMISSIONS REST ROUTES
256 rmap.resource('permission', 'permissions',
256 rmap.resource('permission', 'permissions',
257 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
257 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
258
258
259 ##ADMIN LDAP SETTINGS
259 ##ADMIN LDAP SETTINGS
260 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
260 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
261 controller='admin/ldap_settings', action='ldap_settings',
261 controller='admin/ldap_settings', action='ldap_settings',
262 conditions=dict(method=["POST"]))
262 conditions=dict(method=["POST"]))
263
263
264 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
264 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
265 controller='admin/ldap_settings')
265 controller='admin/ldap_settings')
266
266
267 #ADMIN SETTINGS REST ROUTES
267 #ADMIN SETTINGS REST ROUTES
268 with rmap.submapper(path_prefix=ADMIN_PREFIX,
268 with rmap.submapper(path_prefix=ADMIN_PREFIX,
269 controller='admin/settings') as m:
269 controller='admin/settings') as m:
270 m.connect("admin_settings", "/settings",
270 m.connect("admin_settings", "/settings",
271 action="create", conditions=dict(method=["POST"]))
271 action="create", conditions=dict(method=["POST"]))
272 m.connect("admin_settings", "/settings",
272 m.connect("admin_settings", "/settings",
273 action="index", conditions=dict(method=["GET"]))
273 action="index", conditions=dict(method=["GET"]))
274 m.connect("formatted_admin_settings", "/settings.{format}",
274 m.connect("formatted_admin_settings", "/settings.{format}",
275 action="index", conditions=dict(method=["GET"]))
275 action="index", conditions=dict(method=["GET"]))
276 m.connect("admin_new_setting", "/settings/new",
276 m.connect("admin_new_setting", "/settings/new",
277 action="new", conditions=dict(method=["GET"]))
277 action="new", conditions=dict(method=["GET"]))
278 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
278 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
279 action="new", conditions=dict(method=["GET"]))
279 action="new", conditions=dict(method=["GET"]))
280 m.connect("/settings/{setting_id}",
280 m.connect("/settings/{setting_id}",
281 action="update", conditions=dict(method=["PUT"]))
281 action="update", conditions=dict(method=["PUT"]))
282 m.connect("/settings/{setting_id}",
282 m.connect("/settings/{setting_id}",
283 action="delete", conditions=dict(method=["DELETE"]))
283 action="delete", conditions=dict(method=["DELETE"]))
284 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
284 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
285 action="edit", conditions=dict(method=["GET"]))
285 action="edit", conditions=dict(method=["GET"]))
286 m.connect("formatted_admin_edit_setting",
286 m.connect("formatted_admin_edit_setting",
287 "/settings/{setting_id}.{format}/edit",
287 "/settings/{setting_id}.{format}/edit",
288 action="edit", conditions=dict(method=["GET"]))
288 action="edit", conditions=dict(method=["GET"]))
289 m.connect("admin_setting", "/settings/{setting_id}",
289 m.connect("admin_setting", "/settings/{setting_id}",
290 action="show", conditions=dict(method=["GET"]))
290 action="show", conditions=dict(method=["GET"]))
291 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
291 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
292 action="show", conditions=dict(method=["GET"]))
292 action="show", conditions=dict(method=["GET"]))
293 m.connect("admin_settings_my_account", "/my_account",
293 m.connect("admin_settings_my_account", "/my_account",
294 action="my_account", conditions=dict(method=["GET"]))
294 action="my_account", conditions=dict(method=["GET"]))
295 m.connect("admin_settings_my_account_update", "/my_account_update",
295 m.connect("admin_settings_my_account_update", "/my_account_update",
296 action="my_account_update", conditions=dict(method=["PUT"]))
296 action="my_account_update", conditions=dict(method=["PUT"]))
297 m.connect("admin_settings_create_repository", "/create_repository",
297 m.connect("admin_settings_create_repository", "/create_repository",
298 action="create_repository", conditions=dict(method=["GET"]))
298 action="create_repository", conditions=dict(method=["GET"]))
299
299
300 #NOTIFICATION REST ROUTES
300 #NOTIFICATION REST ROUTES
301 with rmap.submapper(path_prefix=ADMIN_PREFIX,
301 with rmap.submapper(path_prefix=ADMIN_PREFIX,
302 controller='admin/notifications') as m:
302 controller='admin/notifications') as m:
303 m.connect("notifications", "/notifications",
303 m.connect("notifications", "/notifications",
304 action="create", conditions=dict(method=["POST"]))
304 action="create", conditions=dict(method=["POST"]))
305 m.connect("notifications", "/notifications",
305 m.connect("notifications", "/notifications",
306 action="index", conditions=dict(method=["GET"]))
306 action="index", conditions=dict(method=["GET"]))
307 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
307 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
308 action="mark_all_read", conditions=dict(method=["GET"]))
308 action="mark_all_read", conditions=dict(method=["GET"]))
309 m.connect("formatted_notifications", "/notifications.{format}",
309 m.connect("formatted_notifications", "/notifications.{format}",
310 action="index", conditions=dict(method=["GET"]))
310 action="index", conditions=dict(method=["GET"]))
311 m.connect("new_notification", "/notifications/new",
311 m.connect("new_notification", "/notifications/new",
312 action="new", conditions=dict(method=["GET"]))
312 action="new", conditions=dict(method=["GET"]))
313 m.connect("formatted_new_notification", "/notifications/new.{format}",
313 m.connect("formatted_new_notification", "/notifications/new.{format}",
314 action="new", conditions=dict(method=["GET"]))
314 action="new", conditions=dict(method=["GET"]))
315 m.connect("/notification/{notification_id}",
315 m.connect("/notification/{notification_id}",
316 action="update", conditions=dict(method=["PUT"]))
316 action="update", conditions=dict(method=["PUT"]))
317 m.connect("/notification/{notification_id}",
317 m.connect("/notification/{notification_id}",
318 action="delete", conditions=dict(method=["DELETE"]))
318 action="delete", conditions=dict(method=["DELETE"]))
319 m.connect("edit_notification", "/notification/{notification_id}/edit",
319 m.connect("edit_notification", "/notification/{notification_id}/edit",
320 action="edit", conditions=dict(method=["GET"]))
320 action="edit", conditions=dict(method=["GET"]))
321 m.connect("formatted_edit_notification",
321 m.connect("formatted_edit_notification",
322 "/notification/{notification_id}.{format}/edit",
322 "/notification/{notification_id}.{format}/edit",
323 action="edit", conditions=dict(method=["GET"]))
323 action="edit", conditions=dict(method=["GET"]))
324 m.connect("notification", "/notification/{notification_id}",
324 m.connect("notification", "/notification/{notification_id}",
325 action="show", conditions=dict(method=["GET"]))
325 action="show", conditions=dict(method=["GET"]))
326 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
326 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
327 action="show", conditions=dict(method=["GET"]))
327 action="show", conditions=dict(method=["GET"]))
328
328
329 #ADMIN MAIN PAGES
329 #ADMIN MAIN PAGES
330 with rmap.submapper(path_prefix=ADMIN_PREFIX,
330 with rmap.submapper(path_prefix=ADMIN_PREFIX,
331 controller='admin/admin') as m:
331 controller='admin/admin') as m:
332 m.connect('admin_home', '', action='index')
332 m.connect('admin_home', '', action='index')
333 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
333 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
334 action='add_repo')
334 action='add_repo')
335
335
336 #==========================================================================
336 #==========================================================================
337 # API V2
337 # API V2
338 #==========================================================================
338 #==========================================================================
339 with rmap.submapper(path_prefix=ADMIN_PREFIX,
339 with rmap.submapper(path_prefix=ADMIN_PREFIX,
340 controller='api/api') as m:
340 controller='api/api') as m:
341 m.connect('api', '/api')
341 m.connect('api', '/api')
342
342
343 #USER JOURNAL
343 #USER JOURNAL
344 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
344 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
345 controller='journal', action='index')
345 controller='journal', action='index')
346 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
346 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
347 controller='journal', action='journal_rss')
347 controller='journal', action='journal_rss')
348 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
348 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
349 controller='journal', action='journal_atom')
349 controller='journal', action='journal_atom')
350
350
351 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
351 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
352 controller='journal', action="public_journal")
352 controller='journal', action="public_journal")
353
353
354 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
354 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
355 controller='journal', action="public_journal_rss")
355 controller='journal', action="public_journal_rss")
356
356
357 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
357 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
358 controller='journal', action="public_journal_rss")
358 controller='journal', action="public_journal_rss")
359
359
360 rmap.connect('public_journal_atom',
360 rmap.connect('public_journal_atom',
361 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
361 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
362 action="public_journal_atom")
362 action="public_journal_atom")
363
363
364 rmap.connect('public_journal_atom_old',
364 rmap.connect('public_journal_atom_old',
365 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
365 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
366 action="public_journal_atom")
366 action="public_journal_atom")
367
367
368 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
368 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
369 controller='journal', action='toggle_following',
369 controller='journal', action='toggle_following',
370 conditions=dict(method=["POST"]))
370 conditions=dict(method=["POST"]))
371
371
372 #SEARCH
372 #SEARCH
373 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
373 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
374 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
374 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
375 controller='search')
375 controller='search')
376
376
377 #LOGIN/LOGOUT/REGISTER/SIGN IN
377 #LOGIN/LOGOUT/REGISTER/SIGN IN
378 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
378 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
379 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
379 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
380 action='logout')
380 action='logout')
381
381
382 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
382 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
383 action='register')
383 action='register')
384
384
385 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
385 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
386 controller='login', action='password_reset')
386 controller='login', action='password_reset')
387
387
388 rmap.connect('reset_password_confirmation',
388 rmap.connect('reset_password_confirmation',
389 '%s/password_reset_confirmation' % ADMIN_PREFIX,
389 '%s/password_reset_confirmation' % ADMIN_PREFIX,
390 controller='login', action='password_reset_confirmation')
390 controller='login', action='password_reset_confirmation')
391
391
392 #FEEDS
392 #FEEDS
393 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
393 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
394 controller='feed', action='rss',
394 controller='feed', action='rss',
395 conditions=dict(function=check_repo))
395 conditions=dict(function=check_repo))
396
396
397 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
397 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
398 controller='feed', action='atom',
398 controller='feed', action='atom',
399 conditions=dict(function=check_repo))
399 conditions=dict(function=check_repo))
400
400
401 #==========================================================================
401 #==========================================================================
402 # REPOSITORY ROUTES
402 # REPOSITORY ROUTES
403 #==========================================================================
403 #==========================================================================
404 rmap.connect('summary_home', '/{repo_name:.*}',
404 rmap.connect('summary_home', '/{repo_name:.*}',
405 controller='summary',
405 controller='summary',
406 conditions=dict(function=check_repo))
406 conditions=dict(function=check_repo))
407
407
408 rmap.connect('repos_group_home', '/{group_name:.*}',
408 rmap.connect('repos_group_home', '/{group_name:.*}',
409 controller='admin/repos_groups', action="show_by_name",
409 controller='admin/repos_groups', action="show_by_name",
410 conditions=dict(function=check_group))
410 conditions=dict(function=check_group))
411
411
412 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
412 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
413 controller='changeset', revision='tip',
413 controller='changeset', revision='tip',
414 conditions=dict(function=check_repo))
414 conditions=dict(function=check_repo))
415
415
416 rmap.connect('changeset_comment',
416 rmap.connect('changeset_comment',
417 '/{repo_name:.*}/changeset/{revision}/comment',
417 '/{repo_name:.*}/changeset/{revision}/comment',
418 controller='changeset', revision='tip', action='comment',
418 controller='changeset', revision='tip', action='comment',
419 conditions=dict(function=check_repo))
419 conditions=dict(function=check_repo))
420
420
421 rmap.connect('changeset_comment_delete',
421 rmap.connect('changeset_comment_delete',
422 '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
422 '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
423 controller='changeset', action='delete_comment',
423 controller='changeset', action='delete_comment',
424 conditions=dict(function=check_repo, method=["DELETE"]))
424 conditions=dict(function=check_repo, method=["DELETE"]))
425
425
426 rmap.connect('raw_changeset_home',
426 rmap.connect('raw_changeset_home',
427 '/{repo_name:.*}/raw-changeset/{revision}',
427 '/{repo_name:.*}/raw-changeset/{revision}',
428 controller='changeset', action='raw_changeset',
428 controller='changeset', action='raw_changeset',
429 revision='tip', conditions=dict(function=check_repo))
429 revision='tip', conditions=dict(function=check_repo))
430
430
431 rmap.connect('compare_url',
431 rmap.connect('compare_url',
432 '/{repo_name:.*}/compare/{org_ref_type}@{org_ref}...{other_ref_type}@{other_ref}',
432 '/{repo_name:.*}/compare/{org_ref_type}@{org_ref}...{other_ref_type}@{other_ref}',
433 controller='compare', action='index',
433 controller='compare', action='index',
434 conditions=dict(function=check_repo),
434 conditions=dict(function=check_repo),
435 requirements=dict(org_ref_type='(branch|book|tag)',
435 requirements=dict(org_ref_type='(branch|book|tag)',
436 other_ref_type='(branch|book|tag)'))
436 other_ref_type='(branch|book|tag)'))
437
437
438 rmap.connect('pullrequest_home',
438 rmap.connect('pullrequest_home',
439 '/{repo_name:.*}/pull-request/new',
439 '/{repo_name:.*}/pull-request/new', controller='pullrequests',
440 controller='pullrequests', action='index',
440 action='index', conditions=dict(function=check_repo,
441 conditions=dict(function=check_repo))
441 method=["GET"]))
442
443 rmap.connect('pullrequest',
444 '/{repo_name:.*}/pull-request/new', controller='pullrequests',
445 action='create', conditions=dict(function=check_repo,
446 method=["POST"]))
447
448 rmap.connect('pullrequest_show',
449 '/{repo_name:.*}/pull-request/{pull_request_id}',
450 controller='pullrequests',
451 action='show', conditions=dict(function=check_repo,
452 method=["GET"]))
442
453
443 rmap.connect('summary_home', '/{repo_name:.*}/summary',
454 rmap.connect('summary_home', '/{repo_name:.*}/summary',
444 controller='summary', conditions=dict(function=check_repo))
455 controller='summary', conditions=dict(function=check_repo))
445
456
446 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
457 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
447 controller='shortlog', conditions=dict(function=check_repo))
458 controller='shortlog', conditions=dict(function=check_repo))
448
459
449 rmap.connect('branches_home', '/{repo_name:.*}/branches',
460 rmap.connect('branches_home', '/{repo_name:.*}/branches',
450 controller='branches', conditions=dict(function=check_repo))
461 controller='branches', conditions=dict(function=check_repo))
451
462
452 rmap.connect('tags_home', '/{repo_name:.*}/tags',
463 rmap.connect('tags_home', '/{repo_name:.*}/tags',
453 controller='tags', conditions=dict(function=check_repo))
464 controller='tags', conditions=dict(function=check_repo))
454
465
455 rmap.connect('bookmarks_home', '/{repo_name:.*}/bookmarks',
466 rmap.connect('bookmarks_home', '/{repo_name:.*}/bookmarks',
456 controller='bookmarks', conditions=dict(function=check_repo))
467 controller='bookmarks', conditions=dict(function=check_repo))
457
468
458 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
469 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
459 controller='changelog', conditions=dict(function=check_repo))
470 controller='changelog', conditions=dict(function=check_repo))
460
471
461 rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
472 rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
462 controller='changelog', action='changelog_details',
473 controller='changelog', action='changelog_details',
463 conditions=dict(function=check_repo))
474 conditions=dict(function=check_repo))
464
475
465 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
476 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
466 controller='files', revision='tip', f_path='',
477 controller='files', revision='tip', f_path='',
467 conditions=dict(function=check_repo))
478 conditions=dict(function=check_repo))
468
479
469 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
480 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
470 controller='files', action='diff', revision='tip', f_path='',
481 controller='files', action='diff', revision='tip', f_path='',
471 conditions=dict(function=check_repo))
482 conditions=dict(function=check_repo))
472
483
473 rmap.connect('files_rawfile_home',
484 rmap.connect('files_rawfile_home',
474 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
485 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
475 controller='files', action='rawfile', revision='tip',
486 controller='files', action='rawfile', revision='tip',
476 f_path='', conditions=dict(function=check_repo))
487 f_path='', conditions=dict(function=check_repo))
477
488
478 rmap.connect('files_raw_home',
489 rmap.connect('files_raw_home',
479 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
490 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
480 controller='files', action='raw', revision='tip', f_path='',
491 controller='files', action='raw', revision='tip', f_path='',
481 conditions=dict(function=check_repo))
492 conditions=dict(function=check_repo))
482
493
483 rmap.connect('files_annotate_home',
494 rmap.connect('files_annotate_home',
484 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
495 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
485 controller='files', action='index', revision='tip',
496 controller='files', action='index', revision='tip',
486 f_path='', annotate=True, conditions=dict(function=check_repo))
497 f_path='', annotate=True, conditions=dict(function=check_repo))
487
498
488 rmap.connect('files_edit_home',
499 rmap.connect('files_edit_home',
489 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
500 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
490 controller='files', action='edit', revision='tip',
501 controller='files', action='edit', revision='tip',
491 f_path='', conditions=dict(function=check_repo))
502 f_path='', conditions=dict(function=check_repo))
492
503
493 rmap.connect('files_add_home',
504 rmap.connect('files_add_home',
494 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
505 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
495 controller='files', action='add', revision='tip',
506 controller='files', action='add', revision='tip',
496 f_path='', conditions=dict(function=check_repo))
507 f_path='', conditions=dict(function=check_repo))
497
508
498 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
509 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
499 controller='files', action='archivefile',
510 controller='files', action='archivefile',
500 conditions=dict(function=check_repo))
511 conditions=dict(function=check_repo))
501
512
502 rmap.connect('files_nodelist_home',
513 rmap.connect('files_nodelist_home',
503 '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
514 '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
504 controller='files', action='nodelist',
515 controller='files', action='nodelist',
505 conditions=dict(function=check_repo))
516 conditions=dict(function=check_repo))
506
517
507 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
518 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
508 controller='settings', action="delete",
519 controller='settings', action="delete",
509 conditions=dict(method=["DELETE"], function=check_repo))
520 conditions=dict(method=["DELETE"], function=check_repo))
510
521
511 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
522 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
512 controller='settings', action="update",
523 controller='settings', action="update",
513 conditions=dict(method=["PUT"], function=check_repo))
524 conditions=dict(method=["PUT"], function=check_repo))
514
525
515 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
526 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
516 controller='settings', action='index',
527 controller='settings', action='index',
517 conditions=dict(function=check_repo))
528 conditions=dict(function=check_repo))
518
529
519 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
530 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
520 controller='forks', action='fork_create',
531 controller='forks', action='fork_create',
521 conditions=dict(function=check_repo, method=["POST"]))
532 conditions=dict(function=check_repo, method=["POST"]))
522
533
523 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
534 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
524 controller='forks', action='fork',
535 controller='forks', action='fork',
525 conditions=dict(function=check_repo))
536 conditions=dict(function=check_repo))
526
537
527 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
538 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
528 controller='forks', action='forks',
539 controller='forks', action='forks',
529 conditions=dict(function=check_repo))
540 conditions=dict(function=check_repo))
530
541
531 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
542 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
532 controller='followers', action='followers',
543 controller='followers', action='followers',
533 conditions=dict(function=check_repo))
544 conditions=dict(function=check_repo))
534
545
535 return rmap
546 return rmap
@@ -1,143 +1,145 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.compare
3 rhodecode.controllers.compare
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 compare controller for pylons showoing differences between two
6 compare controller for pylons showoing differences between two
7 repos, branches, bookmarks or tips
7 repos, branches, bookmarks or tips
8
8
9 :created_on: May 6, 2012
9 :created_on: May 6, 2012
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import binascii
28 import binascii
29
29
30 from webob.exc import HTTPNotFound
30 from webob.exc import HTTPNotFound
31 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33
33
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.base import BaseRepoController, render
35 from rhodecode.lib.base import BaseRepoController, render
36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib import diffs
37 from rhodecode.lib import diffs
38
38
39 from rhodecode.model.db import Repository
39 from rhodecode.model.db import Repository
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 class CompareController(BaseRepoController):
44 class CompareController(BaseRepoController):
45
45
46 @LoginRequired()
46 @LoginRequired()
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
48 'repository.admin')
48 'repository.admin')
49 def __before__(self):
49 def __before__(self):
50 super(CompareController, self).__before__()
50 super(CompareController, self).__before__()
51
51
52 def _get_discovery(self, org_repo, org_ref, other_repo, other_ref):
52 def _get_discovery(self, org_repo, org_ref, other_repo, other_ref):
53 from mercurial import discovery
53 from mercurial import discovery
54 other = org_repo._repo
54 other = org_repo._repo
55 repo = other_repo._repo
55 repo = other_repo._repo
56 tip = other[org_ref[1]]
56 tip = other[org_ref[1]]
57 log.debug('Doing discovery for %s@%s vs %s@%s' % (
57 log.debug('Doing discovery for %s@%s vs %s@%s' % (
58 org_repo, org_ref, other_repo, other_ref)
58 org_repo, org_ref, other_repo, other_ref)
59 )
59 )
60 log.debug('Filter heads are %s[%s]' % (tip, org_ref[1]))
60 log.debug('Filter heads are %s[%s]' % (tip, org_ref[1]))
61 tmp = discovery.findcommonincoming(
61 tmp = discovery.findcommonincoming(
62 repo=repo, # other_repo we check for incoming
62 repo=repo, # other_repo we check for incoming
63 remote=other, # org_repo source for incoming
63 remote=other, # org_repo source for incoming
64 heads=[tip.node()],
64 heads=[tip.node()],
65 force=False
65 force=False
66 )
66 )
67 return tmp
67 return tmp
68
68
69 def _get_changesets(self, org_repo, org_ref, other_repo, other_ref, tmp):
69 def _get_changesets(self, org_repo, org_ref, other_repo, other_ref, tmp):
70 changesets = []
70 changesets = []
71 #case two independent repos
71 #case two independent repos
72 if org_repo != other_repo:
72 if org_repo != other_repo:
73 common, incoming, rheads = tmp
73 common, incoming, rheads = tmp
74
74
75 if not incoming:
75 if not incoming:
76 revs = []
76 revs = []
77 else:
77 else:
78 revs = org_repo._repo.changelog.findmissing(common, rheads)
78 revs = org_repo._repo.changelog.findmissing(common, rheads)
79
79
80 for cs in reversed(map(binascii.hexlify, revs)):
80 for cs in reversed(map(binascii.hexlify, revs)):
81 changesets.append(org_repo.get_changeset(cs))
81 changesets.append(org_repo.get_changeset(cs))
82 else:
82 else:
83 revs = ['ancestors(%s) and not ancestors(%s)' % (org_ref[1],
83 revs = ['ancestors(%s) and not ancestors(%s)' % (org_ref[1],
84 other_ref[1])]
84 other_ref[1])]
85 from mercurial import scmutil
85 from mercurial import scmutil
86 out = scmutil.revrange(org_repo._repo, revs)
86 out = scmutil.revrange(org_repo._repo, revs)
87 for cs in reversed(out):
87 for cs in reversed(out):
88 changesets.append(org_repo.get_changeset(cs))
88 changesets.append(org_repo.get_changeset(cs))
89
89
90 return changesets
90 return changesets
91
91
92 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
92 def index(self, org_ref_type, org_ref, other_ref_type, other_ref):
93
93
94 org_repo = c.rhodecode_db_repo.repo_name
94 org_repo = c.rhodecode_db_repo.repo_name
95 org_ref = (org_ref_type, org_ref)
95 org_ref = (org_ref_type, org_ref)
96 other_ref = (other_ref_type, other_ref)
96 other_ref = (other_ref_type, other_ref)
97 other_repo = request.GET.get('repo', org_repo)
97 other_repo = request.GET.get('repo', org_repo)
98
98
99 c.swap_url = h.url('compare_url', repo_name=other_repo,
99 c.swap_url = h.url('compare_url', repo_name=other_repo,
100 org_ref_type=other_ref[0], org_ref=other_ref[1],
100 org_ref_type=other_ref[0], org_ref=other_ref[1],
101 other_ref_type=org_ref[0], other_ref=org_ref[1],
101 other_ref_type=org_ref[0], other_ref=org_ref[1],
102 repo=org_repo)
102 repo=org_repo)
103
103
104 c.org_repo = org_repo = Repository.get_by_repo_name(org_repo)
104 c.org_repo = org_repo = Repository.get_by_repo_name(org_repo)
105 c.other_repo = other_repo = Repository.get_by_repo_name(other_repo)
105 c.other_repo = other_repo = Repository.get_by_repo_name(other_repo)
106
106
107 if c.org_repo is None or c.other_repo is None:
107 if c.org_repo is None or c.other_repo is None:
108 log.error('Could not found repo %s or %s' % (org_repo, other_repo))
108 log.error('Could not found repo %s or %s' % (org_repo, other_repo))
109 raise HTTPNotFound
109 raise HTTPNotFound
110
110
111 discovery_data = self._get_discovery(org_repo.scm_instance,
111 discovery_data = self._get_discovery(org_repo.scm_instance,
112 org_ref,
112 org_ref,
113 other_repo.scm_instance,
113 other_repo.scm_instance,
114 other_ref)
114 other_ref)
115 c.cs_ranges = self._get_changesets(org_repo.scm_instance,
115 c.cs_ranges = self._get_changesets(org_repo.scm_instance,
116 org_ref,
116 org_ref,
117 other_repo.scm_instance,
117 other_repo.scm_instance,
118 other_ref,
118 other_ref,
119 discovery_data)
119 discovery_data)
120
120
121 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
121 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
122 c.cs_ranges])
122 c.cs_ranges])
123 # defines that we need hidden inputs with changesets
124 c.as_form = request.GET.get('as_form', False)
123 if request.environ.get('HTTP_X_PARTIAL_XHR'):
125 if request.environ.get('HTTP_X_PARTIAL_XHR'):
124 return render('compare/compare_cs.html')
126 return render('compare/compare_cs.html')
125
127
126 c.org_ref = org_ref[1]
128 c.org_ref = org_ref[1]
127 c.other_ref = other_ref[1]
129 c.other_ref = other_ref[1]
128 # diff needs to have swapped org with other to generate proper diff
130 # diff needs to have swapped org with other to generate proper diff
129 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
131 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
130 discovery_data)
132 discovery_data)
131 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
133 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
132 _parsed = diff_processor.prepare()
134 _parsed = diff_processor.prepare()
133
135
134 c.files = []
136 c.files = []
135 c.changes = {}
137 c.changes = {}
136
138
137 for f in _parsed:
139 for f in _parsed:
138 fid = h.FID('', f['filename'])
140 fid = h.FID('', f['filename'])
139 c.files.append([fid, f['operation'], f['filename'], f['stats']])
141 c.files.append([fid, f['operation'], f['filename'], f['stats']])
140 diff = diff_processor.as_html(enable_comments=False, diff_lines=[f])
142 diff = diff_processor.as_html(enable_comments=False, diff_lines=[f])
141 c.changes[fid] = [f['operation'], f['filename'], diff]
143 c.changes[fid] = [f['operation'], f['filename'], diff]
142
144
143 return render('compare/compare_diff.html')
145 return render('compare/compare_diff.html')
@@ -1,90 +1,133 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.pullrequests
3 rhodecode.controllers.pullrequests
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 pull requests controller for rhodecode for initializing pull requests
6 pull requests controller for rhodecode for initializing pull requests
7
7
8 :created_on: May 7, 2012
8 :created_on: May 7, 2012
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 import logging
25 import logging
26 import traceback
26 import traceback
27
27
28 from pylons import request, response, session, tmpl_context as c, url
28 from pylons import request, response, session, tmpl_context as c, url
29 from pylons.controllers.util import abort, redirect
29 from pylons.controllers.util import abort, redirect
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31
31
32 from rhodecode.lib.base import BaseRepoController, render
32 from rhodecode.lib.base import BaseRepoController, render
33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.model.db import User
34 from rhodecode.lib import helpers as h
35 from rhodecode.model.db import User, PullRequest
36 from rhodecode.model.pull_request import PullRequestModel
37 from rhodecode.model.meta import Session
35
38
36 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
37
40
38
41
39 class PullrequestsController(BaseRepoController):
42 class PullrequestsController(BaseRepoController):
40
43
41 @LoginRequired()
44 @LoginRequired()
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
45 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 'repository.admin')
46 'repository.admin')
44 def __before__(self):
47 def __before__(self):
45 super(PullrequestsController, self).__before__()
48 super(PullrequestsController, self).__before__()
46
49
47 def _get_repo_refs(self, repo):
50 def _get_repo_refs(self, repo):
48 hist_l = []
51 hist_l = []
49
52
50 branches_group = ([('branch:' + k, k) for k in repo.branches.keys()],
53 branches_group = ([('branch:' + k, k) for k in repo.branches.keys()],
51 _("Branches"))
54 _("Branches"))
52 bookmarks_group = ([('book:' + k, k) for k in repo.bookmarks.keys()],
55 bookmarks_group = ([('book:' + k, k) for k in repo.bookmarks.keys()],
53 _("Bookmarks"))
56 _("Bookmarks"))
54 tags_group = ([('tag:' + k, k) for k in repo.tags.keys()],
57 tags_group = ([('tag:' + k, k) for k in repo.tags.keys()],
55 _("Tags"))
58 _("Tags"))
56
59
57 hist_l.append(bookmarks_group)
60 hist_l.append(bookmarks_group)
58 hist_l.append(branches_group)
61 hist_l.append(branches_group)
59 hist_l.append(tags_group)
62 hist_l.append(tags_group)
60
63
61 return hist_l
64 return hist_l
62
65
63 def index(self):
66 def index(self):
64 org_repo = c.rhodecode_db_repo
67 org_repo = c.rhodecode_db_repo
65 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
68 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
66 c.org_repos = []
69 c.org_repos = []
67 c.other_repos = []
70 c.other_repos = []
68 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
71 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
69 org_repo.user.username, c.repo_name))
72 org_repo.user.username, c.repo_name))
70 )
73 )
71
74
72 c.other_refs = c.org_refs
75 c.other_refs = c.org_refs
73 c.other_repos.extend(c.org_repos)
76 c.other_repos.extend(c.org_repos)
74
77 c.default_pull_request = org_repo.repo_name
75 #gather forks and add to this list
78 #gather forks and add to this list
76 for fork in org_repo.forks:
79 for fork in org_repo.forks:
77 c.other_repos.append((fork.repo_name, '%s/%s' % (
80 c.other_repos.append((fork.repo_name, '%s/%s' % (
78 fork.user.username, fork.repo_name))
81 fork.user.username, fork.repo_name))
79 )
82 )
80 #add parents of this fork also
83 #add parents of this fork also
81 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
84 if org_repo.parent:
82 org_repo.parent.user.username,
85 c.default_pull_request = org_repo.parent.repo_name
83 org_repo.parent.repo_name))
86 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
84 )
87 org_repo.parent.user.username,
88 org_repo.parent.repo_name))
89 )
85
90
86 #TODO: maybe the owner should be default ?
91 #TODO: maybe the owner should be default ?
87 c.review_members = []
92 c.review_members = []
88 c.available_members = [(x.user_id, x.username) for x in
93 c.available_members = []
89 User.query().filter(User.username != 'default').all()]
94 for u in User.query().filter(User.username != 'default').all():
95 uname = u.username
96 if org_repo.user == u:
97 uname = _('%s (owner)' % u.username)
98 # auto add owner to pull-request recipients
99 c.review_members.append([u.user_id, uname])
100 c.available_members.append([u.user_id, uname])
90 return render('/pullrequests/pullrequest.html')
101 return render('/pullrequests/pullrequest.html')
102
103 def create(self, repo_name):
104 req_p = request.POST
105 org_repo = req_p['org_repo']
106 org_ref = req_p['org_ref']
107 other_repo = req_p['other_repo']
108 other_ref = req_p['other_ref']
109 revisions = req_p.getall('revisions')
110 reviewers = req_p.getall('review_members')
111 #TODO: wrap this into a FORM !!!
112
113 title = req_p['pullrequest_title']
114 description = req_p['pullrequest_desc']
115
116 try:
117 model = PullRequestModel()
118 model.create(self.rhodecode_user.user_id, org_repo,
119 org_ref, other_repo, other_ref, revisions,
120 reviewers, title, description)
121 Session.commit()
122 h.flash(_('Pull request send'), category='success')
123 except Exception:
124 raise
125 h.flash(_('Error occured during sending pull request'),
126 category='error')
127 log.error(traceback.format_exc())
128
129 return redirect(url('changelog_home', repo_name=repo_name))
130
131 def show(self, repo_name, pull_request_id):
132 c.pull_request = PullRequest.get(pull_request_id)
133 return render('/pullrequests/pullrequest_show.html')
@@ -1,157 +1,158 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.comment
3 rhodecode.model.comment
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 comments model for RhodeCode
6 comments model for RhodeCode
7
7
8 :created_on: Nov 11, 2011
8 :created_on: Nov 11, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from sqlalchemy.util.compat import defaultdict
30 from sqlalchemy.util.compat import defaultdict
31
31
32 from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
32 from rhodecode.lib.utils2 import extract_mentioned_users, safe_unicode
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.model import BaseModel
34 from rhodecode.model import BaseModel
35 from rhodecode.model.db import ChangesetComment, User, Repository, Notification
35 from rhodecode.model.db import ChangesetComment, User, Repository, Notification
36 from rhodecode.model.notification import NotificationModel
36 from rhodecode.model.notification import NotificationModel
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 class ChangesetCommentsModel(BaseModel):
41 class ChangesetCommentsModel(BaseModel):
42
42
43 def __get_changeset_comment(self, changeset_comment):
43 def __get_changeset_comment(self, changeset_comment):
44 return self._get_instance(ChangesetComment, changeset_comment)
44 return self._get_instance(ChangesetComment, changeset_comment)
45
45
46 def _extract_mentions(self, s):
46 def _extract_mentions(self, s):
47 user_objects = []
47 user_objects = []
48 for username in extract_mentioned_users(s):
48 for username in extract_mentioned_users(s):
49 user_obj = User.get_by_username(username, case_insensitive=True)
49 user_obj = User.get_by_username(username, case_insensitive=True)
50 if user_obj:
50 if user_obj:
51 user_objects.append(user_obj)
51 user_objects.append(user_obj)
52 return user_objects
52 return user_objects
53
53
54 def create(self, text, repo_id, user_id, revision, f_path=None,
54 def create(self, text, repo_id, user_id, revision, f_path=None,
55 line_no=None, status_change=None):
55 line_no=None, status_change=None):
56 """
56 """
57 Creates new comment for changeset. IF status_change is not none
57 Creates new comment for changeset. IF status_change is not none
58 this comment is associated with a status change of changeset
58 this comment is associated with a status change of changeset
59
59
60 :param text:
60 :param text:
61 :param repo_id:
61 :param repo_id:
62 :param user_id:
62 :param user_id:
63 :param revision:
63 :param revision:
64 :param f_path:
64 :param f_path:
65 :param line_no:
65 :param line_no:
66 :param status_change:
66 :param status_change:
67 """
67 """
68
68
69 if text:
69 if text:
70 repo = Repository.get(repo_id)
70 repo = Repository.get(repo_id)
71 cs = repo.scm_instance.get_changeset(revision)
71 cs = repo.scm_instance.get_changeset(revision)
72 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
72 desc = "%s - %s" % (cs.short_id, h.shorter(cs.message, 256))
73 author_email = cs.author_email
73 author_email = cs.author_email
74 comment = ChangesetComment()
74 comment = ChangesetComment()
75 comment.repo = repo
75 comment.repo = repo
76 comment.user_id = user_id
76 comment.user_id = user_id
77 comment.revision = revision
77 comment.revision = revision
78 comment.text = text
78 comment.text = text
79 comment.f_path = f_path
79 comment.f_path = f_path
80 comment.line_no = line_no
80 comment.line_no = line_no
81
81
82 self.sa.add(comment)
82 self.sa.add(comment)
83 self.sa.flush()
83 self.sa.flush()
84 # make notification
84 # make notification
85 line = ''
85 line = ''
86 if line_no:
86 if line_no:
87 line = _('on line %s') % line_no
87 line = _('on line %s') % line_no
88 subj = safe_unicode(
88 subj = safe_unicode(
89 h.link_to('Re commit: %(commit_desc)s %(line)s' % \
89 h.link_to('Re commit: %(commit_desc)s %(line)s' % \
90 {'commit_desc': desc, 'line': line},
90 {'commit_desc': desc, 'line': line},
91 h.url('changeset_home', repo_name=repo.repo_name,
91 h.url('changeset_home', repo_name=repo.repo_name,
92 revision=revision,
92 revision=revision,
93 anchor='comment-%s' % comment.comment_id,
93 anchor='comment-%s' % comment.comment_id,
94 qualified=True,
94 qualified=True,
95 )
95 )
96 )
96 )
97 )
97 )
98
98
99 body = text
99 body = text
100
100
101 # get the current participants of this changeset
101 # get the current participants of this changeset
102 recipients = ChangesetComment.get_users(revision=revision)
102 recipients = ChangesetComment.get_users(revision=revision)
103
103
104 # add changeset author if it's in rhodecode system
104 # add changeset author if it's in rhodecode system
105 recipients += [User.get_by_email(author_email)]
105 recipients += [User.get_by_email(author_email)]
106
106
107 # create notification objects, and emails
107 # create notification objects, and emails
108 NotificationModel().create(
108 NotificationModel().create(
109 created_by=user_id, subject=subj, body=body,
109 created_by=user_id, subject=subj, body=body,
110 recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT,
110 recipients=recipients, type_=Notification.TYPE_CHANGESET_COMMENT,
111 email_kwargs={'status_change': status_change}
111 email_kwargs={'status_change': status_change}
112 )
112 )
113
113
114 mention_recipients = set(self._extract_mentions(body))\
114 mention_recipients = set(self._extract_mentions(body))\
115 .difference(recipients)
115 .difference(recipients)
116 if mention_recipients:
116 if mention_recipients:
117 subj = _('[Mention]') + ' ' + subj
117 subj = _('[Mention]') + ' ' + subj
118 NotificationModel().create(
118 NotificationModel().create(
119 created_by=user_id, subject=subj, body=body,
119 created_by=user_id, subject=subj, body=body,
120 recipients=mention_recipients,
120 recipients=mention_recipients,
121 type_=Notification.TYPE_CHANGESET_COMMENT
121 type_=Notification.TYPE_CHANGESET_COMMENT,
122 email_kwargs={'status_change': status_change}
122 )
123 )
123
124
124 return comment
125 return comment
125
126
126 def delete(self, comment):
127 def delete(self, comment):
127 """
128 """
128 Deletes given comment
129 Deletes given comment
129
130
130 :param comment_id:
131 :param comment_id:
131 """
132 """
132 comment = self.__get_changeset_comment(comment)
133 comment = self.__get_changeset_comment(comment)
133 self.sa.delete(comment)
134 self.sa.delete(comment)
134
135
135 return comment
136 return comment
136
137
137 def get_comments(self, repo_id, revision):
138 def get_comments(self, repo_id, revision):
138 return ChangesetComment.query()\
139 return ChangesetComment.query()\
139 .filter(ChangesetComment.repo_id == repo_id)\
140 .filter(ChangesetComment.repo_id == repo_id)\
140 .filter(ChangesetComment.revision == revision)\
141 .filter(ChangesetComment.revision == revision)\
141 .filter(ChangesetComment.line_no == None)\
142 .filter(ChangesetComment.line_no == None)\
142 .filter(ChangesetComment.f_path == None).all()
143 .filter(ChangesetComment.f_path == None).all()
143
144
144 def get_inline_comments(self, repo_id, revision):
145 def get_inline_comments(self, repo_id, revision):
145 comments = self.sa.query(ChangesetComment)\
146 comments = self.sa.query(ChangesetComment)\
146 .filter(ChangesetComment.repo_id == repo_id)\
147 .filter(ChangesetComment.repo_id == repo_id)\
147 .filter(ChangesetComment.revision == revision)\
148 .filter(ChangesetComment.revision == revision)\
148 .filter(ChangesetComment.line_no != None)\
149 .filter(ChangesetComment.line_no != None)\
149 .filter(ChangesetComment.f_path != None)\
150 .filter(ChangesetComment.f_path != None)\
150 .order_by(ChangesetComment.comment_id.asc())\
151 .order_by(ChangesetComment.comment_id.asc())\
151 .all()
152 .all()
152
153
153 paths = defaultdict(lambda: defaultdict(list))
154 paths = defaultdict(lambda: defaultdict(list))
154
155
155 for co in comments:
156 for co in comments:
156 paths[co.f_path][co.line_no].append(co)
157 paths[co.f_path][co.line_no].append(co)
157 return paths.items()
158 return paths.items()
@@ -1,1471 +1,1527 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 logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 from collections import defaultdict
31 from collections import defaultdict
32
32
33 from sqlalchemy import *
33 from sqlalchemy import *
34 from sqlalchemy.ext.hybrid import hybrid_property
34 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
35 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.exc import DatabaseError
36 from sqlalchemy.exc import DatabaseError
37 from beaker.cache import cache_region, region_invalidate
37 from beaker.cache import cache_region, region_invalidate
38
38
39 from pylons.i18n.translation import lazy_ugettext as _
39 from pylons.i18n.translation import lazy_ugettext as _
40
40
41 from rhodecode.lib.vcs import get_backend
41 from rhodecode.lib.vcs import get_backend
42 from rhodecode.lib.vcs.utils.helpers import get_scm
42 from rhodecode.lib.vcs.utils.helpers import get_scm
43 from rhodecode.lib.vcs.exceptions import VCSError
43 from rhodecode.lib.vcs.exceptions import VCSError
44 from rhodecode.lib.vcs.utils.lazy import LazyProperty
44 from rhodecode.lib.vcs.utils.lazy import LazyProperty
45
45
46 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
46 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
47 safe_unicode
47 safe_unicode
48 from rhodecode.lib.compat import json
48 from rhodecode.lib.compat import json
49 from rhodecode.lib.caching_query import FromCache
49 from rhodecode.lib.caching_query import FromCache
50
50
51 from rhodecode.model.meta import Base, Session
51 from rhodecode.model.meta import Base, Session
52
52
53
53
54 URL_SEP = '/'
54 URL_SEP = '/'
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57 #==============================================================================
57 #==============================================================================
58 # BASE CLASSES
58 # BASE CLASSES
59 #==============================================================================
59 #==============================================================================
60
60
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
61 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
62
62
63
63
64 class ModelSerializer(json.JSONEncoder):
64 class ModelSerializer(json.JSONEncoder):
65 """
65 """
66 Simple Serializer for JSON,
66 Simple Serializer for JSON,
67
67
68 usage::
68 usage::
69
69
70 to make object customized for serialization implement a __json__
70 to make object customized for serialization implement a __json__
71 method that will return a dict for serialization into json
71 method that will return a dict for serialization into json
72
72
73 example::
73 example::
74
74
75 class Task(object):
75 class Task(object):
76
76
77 def __init__(self, name, value):
77 def __init__(self, name, value):
78 self.name = name
78 self.name = name
79 self.value = value
79 self.value = value
80
80
81 def __json__(self):
81 def __json__(self):
82 return dict(name=self.name,
82 return dict(name=self.name,
83 value=self.value)
83 value=self.value)
84
84
85 """
85 """
86
86
87 def default(self, obj):
87 def default(self, obj):
88
88
89 if hasattr(obj, '__json__'):
89 if hasattr(obj, '__json__'):
90 return obj.__json__()
90 return obj.__json__()
91 else:
91 else:
92 return json.JSONEncoder.default(self, obj)
92 return json.JSONEncoder.default(self, obj)
93
93
94
94
95 class BaseModel(object):
95 class BaseModel(object):
96 """
96 """
97 Base Model for all classess
97 Base Model for all classess
98 """
98 """
99
99
100 @classmethod
100 @classmethod
101 def _get_keys(cls):
101 def _get_keys(cls):
102 """return column names for this model """
102 """return column names for this model """
103 return class_mapper(cls).c.keys()
103 return class_mapper(cls).c.keys()
104
104
105 def get_dict(self):
105 def get_dict(self):
106 """
106 """
107 return dict with keys and values corresponding
107 return dict with keys and values corresponding
108 to this model data """
108 to this model data """
109
109
110 d = {}
110 d = {}
111 for k in self._get_keys():
111 for k in self._get_keys():
112 d[k] = getattr(self, k)
112 d[k] = getattr(self, k)
113
113
114 # also use __json__() if present to get additional fields
114 # also use __json__() if present to get additional fields
115 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
115 for k, val in getattr(self, '__json__', lambda: {})().iteritems():
116 d[k] = val
116 d[k] = val
117 return d
117 return d
118
118
119 def get_appstruct(self):
119 def get_appstruct(self):
120 """return list with keys and values tupples corresponding
120 """return list with keys and values tupples corresponding
121 to this model data """
121 to this model data """
122
122
123 l = []
123 l = []
124 for k in self._get_keys():
124 for k in self._get_keys():
125 l.append((k, getattr(self, k),))
125 l.append((k, getattr(self, k),))
126 return l
126 return l
127
127
128 def populate_obj(self, populate_dict):
128 def populate_obj(self, populate_dict):
129 """populate model with data from given populate_dict"""
129 """populate model with data from given populate_dict"""
130
130
131 for k in self._get_keys():
131 for k in self._get_keys():
132 if k in populate_dict:
132 if k in populate_dict:
133 setattr(self, k, populate_dict[k])
133 setattr(self, k, populate_dict[k])
134
134
135 @classmethod
135 @classmethod
136 def query(cls):
136 def query(cls):
137 return Session.query(cls)
137 return Session.query(cls)
138
138
139 @classmethod
139 @classmethod
140 def get(cls, id_):
140 def get(cls, id_):
141 if id_:
141 if id_:
142 return cls.query().get(id_)
142 return cls.query().get(id_)
143
143
144 @classmethod
144 @classmethod
145 def getAll(cls):
145 def getAll(cls):
146 return cls.query().all()
146 return cls.query().all()
147
147
148 @classmethod
148 @classmethod
149 def delete(cls, id_):
149 def delete(cls, id_):
150 obj = cls.query().get(id_)
150 obj = cls.query().get(id_)
151 Session.delete(obj)
151 Session.delete(obj)
152
152
153 def __repr__(self):
153 def __repr__(self):
154 if hasattr(self, '__unicode__'):
154 if hasattr(self, '__unicode__'):
155 # python repr needs to return str
155 # python repr needs to return str
156 return safe_str(self.__unicode__())
156 return safe_str(self.__unicode__())
157 return '<DB:%s>' % (self.__class__.__name__)
157 return '<DB:%s>' % (self.__class__.__name__)
158
158
159
159
160 class RhodeCodeSetting(Base, BaseModel):
160 class RhodeCodeSetting(Base, BaseModel):
161 __tablename__ = 'rhodecode_settings'
161 __tablename__ = 'rhodecode_settings'
162 __table_args__ = (
162 __table_args__ = (
163 UniqueConstraint('app_settings_name'),
163 UniqueConstraint('app_settings_name'),
164 {'extend_existing': True, 'mysql_engine': 'InnoDB',
164 {'extend_existing': True, 'mysql_engine': 'InnoDB',
165 'mysql_charset': 'utf8'}
165 'mysql_charset': 'utf8'}
166 )
166 )
167 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
168 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
168 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
169 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
169 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
170
170
171 def __init__(self, k='', v=''):
171 def __init__(self, k='', v=''):
172 self.app_settings_name = k
172 self.app_settings_name = k
173 self.app_settings_value = v
173 self.app_settings_value = v
174
174
175 @validates('_app_settings_value')
175 @validates('_app_settings_value')
176 def validate_settings_value(self, key, val):
176 def validate_settings_value(self, key, val):
177 assert type(val) == unicode
177 assert type(val) == unicode
178 return val
178 return val
179
179
180 @hybrid_property
180 @hybrid_property
181 def app_settings_value(self):
181 def app_settings_value(self):
182 v = self._app_settings_value
182 v = self._app_settings_value
183 if self.app_settings_name == 'ldap_active':
183 if self.app_settings_name == 'ldap_active':
184 v = str2bool(v)
184 v = str2bool(v)
185 return v
185 return v
186
186
187 @app_settings_value.setter
187 @app_settings_value.setter
188 def app_settings_value(self, val):
188 def app_settings_value(self, val):
189 """
189 """
190 Setter that will always make sure we use unicode in app_settings_value
190 Setter that will always make sure we use unicode in app_settings_value
191
191
192 :param val:
192 :param val:
193 """
193 """
194 self._app_settings_value = safe_unicode(val)
194 self._app_settings_value = safe_unicode(val)
195
195
196 def __unicode__(self):
196 def __unicode__(self):
197 return u"<%s('%s:%s')>" % (
197 return u"<%s('%s:%s')>" % (
198 self.__class__.__name__,
198 self.__class__.__name__,
199 self.app_settings_name, self.app_settings_value
199 self.app_settings_name, self.app_settings_value
200 )
200 )
201
201
202 @classmethod
202 @classmethod
203 def get_by_name(cls, ldap_key):
203 def get_by_name(cls, ldap_key):
204 return cls.query()\
204 return cls.query()\
205 .filter(cls.app_settings_name == ldap_key).scalar()
205 .filter(cls.app_settings_name == ldap_key).scalar()
206
206
207 @classmethod
207 @classmethod
208 def get_app_settings(cls, cache=False):
208 def get_app_settings(cls, cache=False):
209
209
210 ret = cls.query()
210 ret = cls.query()
211
211
212 if cache:
212 if cache:
213 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
213 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
214
214
215 if not ret:
215 if not ret:
216 raise Exception('Could not get application settings !')
216 raise Exception('Could not get application settings !')
217 settings = {}
217 settings = {}
218 for each in ret:
218 for each in ret:
219 settings['rhodecode_' + each.app_settings_name] = \
219 settings['rhodecode_' + each.app_settings_name] = \
220 each.app_settings_value
220 each.app_settings_value
221
221
222 return settings
222 return settings
223
223
224 @classmethod
224 @classmethod
225 def get_ldap_settings(cls, cache=False):
225 def get_ldap_settings(cls, cache=False):
226 ret = cls.query()\
226 ret = cls.query()\
227 .filter(cls.app_settings_name.startswith('ldap_')).all()
227 .filter(cls.app_settings_name.startswith('ldap_')).all()
228 fd = {}
228 fd = {}
229 for row in ret:
229 for row in ret:
230 fd.update({row.app_settings_name: row.app_settings_value})
230 fd.update({row.app_settings_name: row.app_settings_value})
231
231
232 return fd
232 return fd
233
233
234
234
235 class RhodeCodeUi(Base, BaseModel):
235 class RhodeCodeUi(Base, BaseModel):
236 __tablename__ = 'rhodecode_ui'
236 __tablename__ = 'rhodecode_ui'
237 __table_args__ = (
237 __table_args__ = (
238 UniqueConstraint('ui_key'),
238 UniqueConstraint('ui_key'),
239 {'extend_existing': True, 'mysql_engine': 'InnoDB',
239 {'extend_existing': True, 'mysql_engine': 'InnoDB',
240 'mysql_charset': 'utf8'}
240 'mysql_charset': 'utf8'}
241 )
241 )
242
242
243 HOOK_UPDATE = 'changegroup.update'
243 HOOK_UPDATE = 'changegroup.update'
244 HOOK_REPO_SIZE = 'changegroup.repo_size'
244 HOOK_REPO_SIZE = 'changegroup.repo_size'
245 HOOK_PUSH = 'changegroup.push_logger'
245 HOOK_PUSH = 'changegroup.push_logger'
246 HOOK_PULL = 'preoutgoing.pull_logger'
246 HOOK_PULL = 'preoutgoing.pull_logger'
247
247
248 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
248 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
249 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
249 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
250 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
250 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
251 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
251 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
252 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
252 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
253
253
254 @classmethod
254 @classmethod
255 def get_by_key(cls, key):
255 def get_by_key(cls, key):
256 return cls.query().filter(cls.ui_key == key)
256 return cls.query().filter(cls.ui_key == key)
257
257
258 @classmethod
258 @classmethod
259 def get_builtin_hooks(cls):
259 def get_builtin_hooks(cls):
260 q = cls.query()
260 q = cls.query()
261 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
261 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
262 cls.HOOK_REPO_SIZE,
262 cls.HOOK_REPO_SIZE,
263 cls.HOOK_PUSH, cls.HOOK_PULL]))
263 cls.HOOK_PUSH, cls.HOOK_PULL]))
264 return q.all()
264 return q.all()
265
265
266 @classmethod
266 @classmethod
267 def get_custom_hooks(cls):
267 def get_custom_hooks(cls):
268 q = cls.query()
268 q = cls.query()
269 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
269 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
270 cls.HOOK_REPO_SIZE,
270 cls.HOOK_REPO_SIZE,
271 cls.HOOK_PUSH, cls.HOOK_PULL]))
271 cls.HOOK_PUSH, cls.HOOK_PULL]))
272 q = q.filter(cls.ui_section == 'hooks')
272 q = q.filter(cls.ui_section == 'hooks')
273 return q.all()
273 return q.all()
274
274
275 @classmethod
275 @classmethod
276 def get_repos_location(cls):
276 def get_repos_location(cls):
277 return cls.get_by_key('/').one().ui_value
277 return cls.get_by_key('/').one().ui_value
278
278
279 @classmethod
279 @classmethod
280 def create_or_update_hook(cls, key, val):
280 def create_or_update_hook(cls, key, val):
281 new_ui = cls.get_by_key(key).scalar() or cls()
281 new_ui = cls.get_by_key(key).scalar() or cls()
282 new_ui.ui_section = 'hooks'
282 new_ui.ui_section = 'hooks'
283 new_ui.ui_active = True
283 new_ui.ui_active = True
284 new_ui.ui_key = key
284 new_ui.ui_key = key
285 new_ui.ui_value = val
285 new_ui.ui_value = val
286
286
287 Session.add(new_ui)
287 Session.add(new_ui)
288
288
289
289
290 class User(Base, BaseModel):
290 class User(Base, BaseModel):
291 __tablename__ = 'users'
291 __tablename__ = 'users'
292 __table_args__ = (
292 __table_args__ = (
293 UniqueConstraint('username'), UniqueConstraint('email'),
293 UniqueConstraint('username'), UniqueConstraint('email'),
294 {'extend_existing': True, 'mysql_engine': 'InnoDB',
294 {'extend_existing': True, 'mysql_engine': 'InnoDB',
295 'mysql_charset': 'utf8'}
295 'mysql_charset': 'utf8'}
296 )
296 )
297 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
297 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
298 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
298 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
299 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
300 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
300 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
301 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
301 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
302 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
302 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
303 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
304 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
304 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
305 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
305 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
306 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
306 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
307 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
307 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
308
308
309 user_log = relationship('UserLog', cascade='all')
309 user_log = relationship('UserLog', cascade='all')
310 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
310 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
311
311
312 repositories = relationship('Repository')
312 repositories = relationship('Repository')
313 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
313 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
314 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
314 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
315 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
315 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
316
316
317 group_member = relationship('UsersGroupMember', cascade='all')
317 group_member = relationship('UsersGroupMember', cascade='all')
318
318
319 notifications = relationship('UserNotification', cascade='all')
319 notifications = relationship('UserNotification', cascade='all')
320 # notifications assigned to this user
320 # notifications assigned to this user
321 user_created_notifications = relationship('Notification', cascade='all')
321 user_created_notifications = relationship('Notification', cascade='all')
322 # comments created by this user
322 # comments created by this user
323 user_comments = relationship('ChangesetComment', cascade='all')
323 user_comments = relationship('ChangesetComment', cascade='all')
324
324
325 @hybrid_property
325 @hybrid_property
326 def email(self):
326 def email(self):
327 return self._email
327 return self._email
328
328
329 @email.setter
329 @email.setter
330 def email(self, val):
330 def email(self, val):
331 self._email = val.lower() if val else None
331 self._email = val.lower() if val else None
332
332
333 @property
333 @property
334 def full_name(self):
334 def full_name(self):
335 return '%s %s' % (self.name, self.lastname)
335 return '%s %s' % (self.name, self.lastname)
336
336
337 @property
337 @property
338 def full_name_or_username(self):
338 def full_name_or_username(self):
339 return ('%s %s' % (self.name, self.lastname)
339 return ('%s %s' % (self.name, self.lastname)
340 if (self.name and self.lastname) else self.username)
340 if (self.name and self.lastname) else self.username)
341
341
342 @property
342 @property
343 def full_contact(self):
343 def full_contact(self):
344 return '%s %s <%s>' % (self.name, self.lastname, self.email)
344 return '%s %s <%s>' % (self.name, self.lastname, self.email)
345
345
346 @property
346 @property
347 def short_contact(self):
347 def short_contact(self):
348 return '%s %s' % (self.name, self.lastname)
348 return '%s %s' % (self.name, self.lastname)
349
349
350 @property
350 @property
351 def is_admin(self):
351 def is_admin(self):
352 return self.admin
352 return self.admin
353
353
354 def __unicode__(self):
354 def __unicode__(self):
355 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
355 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
356 self.user_id, self.username)
356 self.user_id, self.username)
357
357
358 @classmethod
358 @classmethod
359 def get_by_username(cls, username, case_insensitive=False, cache=False):
359 def get_by_username(cls, username, case_insensitive=False, cache=False):
360 if case_insensitive:
360 if case_insensitive:
361 q = cls.query().filter(cls.username.ilike(username))
361 q = cls.query().filter(cls.username.ilike(username))
362 else:
362 else:
363 q = cls.query().filter(cls.username == username)
363 q = cls.query().filter(cls.username == username)
364
364
365 if cache:
365 if cache:
366 q = q.options(FromCache(
366 q = q.options(FromCache(
367 "sql_cache_short",
367 "sql_cache_short",
368 "get_user_%s" % _hash_key(username)
368 "get_user_%s" % _hash_key(username)
369 )
369 )
370 )
370 )
371 return q.scalar()
371 return q.scalar()
372
372
373 @classmethod
373 @classmethod
374 def get_by_api_key(cls, api_key, cache=False):
374 def get_by_api_key(cls, api_key, cache=False):
375 q = cls.query().filter(cls.api_key == api_key)
375 q = cls.query().filter(cls.api_key == api_key)
376
376
377 if cache:
377 if cache:
378 q = q.options(FromCache("sql_cache_short",
378 q = q.options(FromCache("sql_cache_short",
379 "get_api_key_%s" % api_key))
379 "get_api_key_%s" % api_key))
380 return q.scalar()
380 return q.scalar()
381
381
382 @classmethod
382 @classmethod
383 def get_by_email(cls, email, case_insensitive=False, cache=False):
383 def get_by_email(cls, email, case_insensitive=False, cache=False):
384 if case_insensitive:
384 if case_insensitive:
385 q = cls.query().filter(cls.email.ilike(email))
385 q = cls.query().filter(cls.email.ilike(email))
386 else:
386 else:
387 q = cls.query().filter(cls.email == email)
387 q = cls.query().filter(cls.email == email)
388
388
389 if cache:
389 if cache:
390 q = q.options(FromCache("sql_cache_short",
390 q = q.options(FromCache("sql_cache_short",
391 "get_email_key_%s" % email))
391 "get_email_key_%s" % email))
392
392
393 ret = q.scalar()
393 ret = q.scalar()
394 if ret is None:
394 if ret is None:
395 q = UserEmailMap.query()
395 q = UserEmailMap.query()
396 # try fetching in alternate email map
396 # try fetching in alternate email map
397 if case_insensitive:
397 if case_insensitive:
398 q = q.filter(UserEmailMap.email.ilike(email))
398 q = q.filter(UserEmailMap.email.ilike(email))
399 else:
399 else:
400 q = q.filter(UserEmailMap.email == email)
400 q = q.filter(UserEmailMap.email == email)
401 q = q.options(joinedload(UserEmailMap.user))
401 q = q.options(joinedload(UserEmailMap.user))
402 if cache:
402 if cache:
403 q = q.options(FromCache("sql_cache_short",
403 q = q.options(FromCache("sql_cache_short",
404 "get_email_map_key_%s" % email))
404 "get_email_map_key_%s" % email))
405 ret = getattr(q.scalar(), 'user', None)
405 ret = getattr(q.scalar(), 'user', None)
406
406
407 return ret
407 return ret
408
408
409 def update_lastlogin(self):
409 def update_lastlogin(self):
410 """Update user lastlogin"""
410 """Update user lastlogin"""
411 self.last_login = datetime.datetime.now()
411 self.last_login = datetime.datetime.now()
412 Session.add(self)
412 Session.add(self)
413 log.debug('updated user %s lastlogin' % self.username)
413 log.debug('updated user %s lastlogin' % self.username)
414
414
415 def __json__(self):
415 def __json__(self):
416 return dict(
416 return dict(
417 user_id=self.user_id,
417 user_id=self.user_id,
418 first_name=self.name,
418 first_name=self.name,
419 last_name=self.lastname,
419 last_name=self.lastname,
420 email=self.email,
420 email=self.email,
421 full_name=self.full_name,
421 full_name=self.full_name,
422 full_name_or_username=self.full_name_or_username,
422 full_name_or_username=self.full_name_or_username,
423 short_contact=self.short_contact,
423 short_contact=self.short_contact,
424 full_contact=self.full_contact
424 full_contact=self.full_contact
425 )
425 )
426
426
427
427
428 class UserEmailMap(Base, BaseModel):
428 class UserEmailMap(Base, BaseModel):
429 __tablename__ = 'user_email_map'
429 __tablename__ = 'user_email_map'
430 __table_args__ = (
430 __table_args__ = (
431 UniqueConstraint('email'),
431 UniqueConstraint('email'),
432 {'extend_existing': True, 'mysql_engine':'InnoDB',
432 {'extend_existing': True, 'mysql_engine':'InnoDB',
433 'mysql_charset': 'utf8'}
433 'mysql_charset': 'utf8'}
434 )
434 )
435 __mapper_args__ = {}
435 __mapper_args__ = {}
436
436
437 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
437 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
438 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
438 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
439 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
439 _email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
440
440
441 user = relationship('User')
441 user = relationship('User')
442
442
443 @validates('_email')
443 @validates('_email')
444 def validate_email(self, key, email):
444 def validate_email(self, key, email):
445 # check if this email is not main one
445 # check if this email is not main one
446 main_email = Session.query(User).filter(User.email == email).scalar()
446 main_email = Session.query(User).filter(User.email == email).scalar()
447 if main_email is not None:
447 if main_email is not None:
448 raise AttributeError('email %s is present is user table' % email)
448 raise AttributeError('email %s is present is user table' % email)
449 return email
449 return email
450
450
451 @hybrid_property
451 @hybrid_property
452 def email(self):
452 def email(self):
453 return self._email
453 return self._email
454
454
455 @email.setter
455 @email.setter
456 def email(self, val):
456 def email(self, val):
457 self._email = val.lower() if val else None
457 self._email = val.lower() if val else None
458
458
459
459
460 class UserLog(Base, BaseModel):
460 class UserLog(Base, BaseModel):
461 __tablename__ = 'user_logs'
461 __tablename__ = 'user_logs'
462 __table_args__ = (
462 __table_args__ = (
463 {'extend_existing': True, 'mysql_engine': 'InnoDB',
463 {'extend_existing': True, 'mysql_engine': 'InnoDB',
464 'mysql_charset': 'utf8'},
464 'mysql_charset': 'utf8'},
465 )
465 )
466 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
466 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
467 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
468 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
468 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
469 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
469 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
470 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
470 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
471 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
471 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
472 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
472 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
473
473
474 @property
474 @property
475 def action_as_day(self):
475 def action_as_day(self):
476 return datetime.date(*self.action_date.timetuple()[:3])
476 return datetime.date(*self.action_date.timetuple()[:3])
477
477
478 user = relationship('User')
478 user = relationship('User')
479 repository = relationship('Repository', cascade='')
479 repository = relationship('Repository', cascade='')
480
480
481
481
482 class UsersGroup(Base, BaseModel):
482 class UsersGroup(Base, BaseModel):
483 __tablename__ = 'users_groups'
483 __tablename__ = 'users_groups'
484 __table_args__ = (
484 __table_args__ = (
485 {'extend_existing': True, 'mysql_engine': 'InnoDB',
485 {'extend_existing': True, 'mysql_engine': 'InnoDB',
486 'mysql_charset': 'utf8'},
486 'mysql_charset': 'utf8'},
487 )
487 )
488
488
489 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
489 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
490 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
490 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
491 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
491 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
492
492
493 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
493 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
494 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
494 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
495 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
495 users_group_repo_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
496
496
497 def __unicode__(self):
497 def __unicode__(self):
498 return u'<userGroup(%s)>' % (self.users_group_name)
498 return u'<userGroup(%s)>' % (self.users_group_name)
499
499
500 @classmethod
500 @classmethod
501 def get_by_group_name(cls, group_name, cache=False,
501 def get_by_group_name(cls, group_name, cache=False,
502 case_insensitive=False):
502 case_insensitive=False):
503 if case_insensitive:
503 if case_insensitive:
504 q = cls.query().filter(cls.users_group_name.ilike(group_name))
504 q = cls.query().filter(cls.users_group_name.ilike(group_name))
505 else:
505 else:
506 q = cls.query().filter(cls.users_group_name == group_name)
506 q = cls.query().filter(cls.users_group_name == group_name)
507 if cache:
507 if cache:
508 q = q.options(FromCache(
508 q = q.options(FromCache(
509 "sql_cache_short",
509 "sql_cache_short",
510 "get_user_%s" % _hash_key(group_name)
510 "get_user_%s" % _hash_key(group_name)
511 )
511 )
512 )
512 )
513 return q.scalar()
513 return q.scalar()
514
514
515 @classmethod
515 @classmethod
516 def get(cls, users_group_id, cache=False):
516 def get(cls, users_group_id, cache=False):
517 users_group = cls.query()
517 users_group = cls.query()
518 if cache:
518 if cache:
519 users_group = users_group.options(FromCache("sql_cache_short",
519 users_group = users_group.options(FromCache("sql_cache_short",
520 "get_users_group_%s" % users_group_id))
520 "get_users_group_%s" % users_group_id))
521 return users_group.get(users_group_id)
521 return users_group.get(users_group_id)
522
522
523
523
524 class UsersGroupMember(Base, BaseModel):
524 class UsersGroupMember(Base, BaseModel):
525 __tablename__ = 'users_groups_members'
525 __tablename__ = 'users_groups_members'
526 __table_args__ = (
526 __table_args__ = (
527 {'extend_existing': True, 'mysql_engine': 'InnoDB',
527 {'extend_existing': True, 'mysql_engine': 'InnoDB',
528 'mysql_charset': 'utf8'},
528 'mysql_charset': 'utf8'},
529 )
529 )
530
530
531 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
531 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
532 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
532 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
533 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
533 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
534
534
535 user = relationship('User', lazy='joined')
535 user = relationship('User', lazy='joined')
536 users_group = relationship('UsersGroup')
536 users_group = relationship('UsersGroup')
537
537
538 def __init__(self, gr_id='', u_id=''):
538 def __init__(self, gr_id='', u_id=''):
539 self.users_group_id = gr_id
539 self.users_group_id = gr_id
540 self.user_id = u_id
540 self.user_id = u_id
541
541
542
542
543 class Repository(Base, BaseModel):
543 class Repository(Base, BaseModel):
544 __tablename__ = 'repositories'
544 __tablename__ = 'repositories'
545 __table_args__ = (
545 __table_args__ = (
546 UniqueConstraint('repo_name'),
546 UniqueConstraint('repo_name'),
547 {'extend_existing': True, 'mysql_engine': 'InnoDB',
547 {'extend_existing': True, 'mysql_engine': 'InnoDB',
548 'mysql_charset': 'utf8'},
548 'mysql_charset': 'utf8'},
549 )
549 )
550
550
551 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
551 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
552 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
552 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
553 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
553 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
554 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
554 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
555 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
555 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
556 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
556 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
557 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
557 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
558 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
558 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
559 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
559 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
560 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
560 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
561
561
562 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
562 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
563 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
563 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
564
564
565 user = relationship('User')
565 user = relationship('User')
566 fork = relationship('Repository', remote_side=repo_id)
566 fork = relationship('Repository', remote_side=repo_id)
567 group = relationship('RepoGroup')
567 group = relationship('RepoGroup')
568 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
568 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
569 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
569 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
570 stats = relationship('Statistics', cascade='all', uselist=False)
570 stats = relationship('Statistics', cascade='all', uselist=False)
571
571
572 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
572 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
573
573
574 logs = relationship('UserLog')
574 logs = relationship('UserLog')
575 comments = relationship('ChangesetComment')
575 comments = relationship('ChangesetComment')
576
576
577 def __unicode__(self):
577 def __unicode__(self):
578 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
578 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
579 self.repo_name)
579 self.repo_name)
580
580
581 @classmethod
581 @classmethod
582 def url_sep(cls):
582 def url_sep(cls):
583 return URL_SEP
583 return URL_SEP
584
584
585 @classmethod
585 @classmethod
586 def get_by_repo_name(cls, repo_name):
586 def get_by_repo_name(cls, repo_name):
587 q = Session.query(cls).filter(cls.repo_name == repo_name)
587 q = Session.query(cls).filter(cls.repo_name == repo_name)
588 q = q.options(joinedload(Repository.fork))\
588 q = q.options(joinedload(Repository.fork))\
589 .options(joinedload(Repository.user))\
589 .options(joinedload(Repository.user))\
590 .options(joinedload(Repository.group))
590 .options(joinedload(Repository.group))
591 return q.scalar()
591 return q.scalar()
592
592
593 @classmethod
593 @classmethod
594 def get_by_full_path(cls, repo_full_path):
594 def get_by_full_path(cls, repo_full_path):
595 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
595 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
596 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
596 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
597
597
598 @classmethod
598 @classmethod
599 def get_repo_forks(cls, repo_id):
599 def get_repo_forks(cls, repo_id):
600 return cls.query().filter(Repository.fork_id == repo_id)
600 return cls.query().filter(Repository.fork_id == repo_id)
601
601
602 @classmethod
602 @classmethod
603 def base_path(cls):
603 def base_path(cls):
604 """
604 """
605 Returns base path when all repos are stored
605 Returns base path when all repos are stored
606
606
607 :param cls:
607 :param cls:
608 """
608 """
609 q = Session.query(RhodeCodeUi)\
609 q = Session.query(RhodeCodeUi)\
610 .filter(RhodeCodeUi.ui_key == cls.url_sep())
610 .filter(RhodeCodeUi.ui_key == cls.url_sep())
611 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
611 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
612 return q.one().ui_value
612 return q.one().ui_value
613
613
614 @property
614 @property
615 def forks(self):
615 def forks(self):
616 """
616 """
617 Return forks of this repo
617 Return forks of this repo
618 """
618 """
619 return Repository.get_repo_forks(self.repo_id)
619 return Repository.get_repo_forks(self.repo_id)
620
620
621 @property
621 @property
622 def parent(self):
622 def parent(self):
623 """
623 """
624 Returns fork parent
624 Returns fork parent
625 """
625 """
626 return self.fork
626 return self.fork
627
627
628 @property
628 @property
629 def just_name(self):
629 def just_name(self):
630 return self.repo_name.split(Repository.url_sep())[-1]
630 return self.repo_name.split(Repository.url_sep())[-1]
631
631
632 @property
632 @property
633 def groups_with_parents(self):
633 def groups_with_parents(self):
634 groups = []
634 groups = []
635 if self.group is None:
635 if self.group is None:
636 return groups
636 return groups
637
637
638 cur_gr = self.group
638 cur_gr = self.group
639 groups.insert(0, cur_gr)
639 groups.insert(0, cur_gr)
640 while 1:
640 while 1:
641 gr = getattr(cur_gr, 'parent_group', None)
641 gr = getattr(cur_gr, 'parent_group', None)
642 cur_gr = cur_gr.parent_group
642 cur_gr = cur_gr.parent_group
643 if gr is None:
643 if gr is None:
644 break
644 break
645 groups.insert(0, gr)
645 groups.insert(0, gr)
646
646
647 return groups
647 return groups
648
648
649 @property
649 @property
650 def groups_and_repo(self):
650 def groups_and_repo(self):
651 return self.groups_with_parents, self.just_name
651 return self.groups_with_parents, self.just_name
652
652
653 @LazyProperty
653 @LazyProperty
654 def repo_path(self):
654 def repo_path(self):
655 """
655 """
656 Returns base full path for that repository means where it actually
656 Returns base full path for that repository means where it actually
657 exists on a filesystem
657 exists on a filesystem
658 """
658 """
659 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
659 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
660 Repository.url_sep())
660 Repository.url_sep())
661 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
661 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
662 return q.one().ui_value
662 return q.one().ui_value
663
663
664 @property
664 @property
665 def repo_full_path(self):
665 def repo_full_path(self):
666 p = [self.repo_path]
666 p = [self.repo_path]
667 # we need to split the name by / since this is how we store the
667 # we need to split the name by / since this is how we store the
668 # names in the database, but that eventually needs to be converted
668 # names in the database, but that eventually needs to be converted
669 # into a valid system path
669 # into a valid system path
670 p += self.repo_name.split(Repository.url_sep())
670 p += self.repo_name.split(Repository.url_sep())
671 return os.path.join(*p)
671 return os.path.join(*p)
672
672
673 def get_new_name(self, repo_name):
673 def get_new_name(self, repo_name):
674 """
674 """
675 returns new full repository name based on assigned group and new new
675 returns new full repository name based on assigned group and new new
676
676
677 :param group_name:
677 :param group_name:
678 """
678 """
679 path_prefix = self.group.full_path_splitted if self.group else []
679 path_prefix = self.group.full_path_splitted if self.group else []
680 return Repository.url_sep().join(path_prefix + [repo_name])
680 return Repository.url_sep().join(path_prefix + [repo_name])
681
681
682 @property
682 @property
683 def _ui(self):
683 def _ui(self):
684 """
684 """
685 Creates an db based ui object for this repository
685 Creates an db based ui object for this repository
686 """
686 """
687 from mercurial import ui
687 from mercurial import ui
688 from mercurial import config
688 from mercurial import config
689 baseui = ui.ui()
689 baseui = ui.ui()
690
690
691 #clean the baseui object
691 #clean the baseui object
692 baseui._ocfg = config.config()
692 baseui._ocfg = config.config()
693 baseui._ucfg = config.config()
693 baseui._ucfg = config.config()
694 baseui._tcfg = config.config()
694 baseui._tcfg = config.config()
695
695
696 ret = RhodeCodeUi.query()\
696 ret = RhodeCodeUi.query()\
697 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
697 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
698
698
699 hg_ui = ret
699 hg_ui = ret
700 for ui_ in hg_ui:
700 for ui_ in hg_ui:
701 if ui_.ui_active:
701 if ui_.ui_active:
702 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
702 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
703 ui_.ui_key, ui_.ui_value)
703 ui_.ui_key, ui_.ui_value)
704 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
704 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
705
705
706 return baseui
706 return baseui
707
707
708 @classmethod
708 @classmethod
709 def is_valid(cls, repo_name):
709 def is_valid(cls, repo_name):
710 """
710 """
711 returns True if given repo name is a valid filesystem repository
711 returns True if given repo name is a valid filesystem repository
712
712
713 :param cls:
713 :param cls:
714 :param repo_name:
714 :param repo_name:
715 """
715 """
716 from rhodecode.lib.utils import is_valid_repo
716 from rhodecode.lib.utils import is_valid_repo
717
717
718 return is_valid_repo(repo_name, cls.base_path())
718 return is_valid_repo(repo_name, cls.base_path())
719
719
720 #==========================================================================
720 #==========================================================================
721 # SCM PROPERTIES
721 # SCM PROPERTIES
722 #==========================================================================
722 #==========================================================================
723
723
724 def get_changeset(self, rev=None):
724 def get_changeset(self, rev=None):
725 return get_changeset_safe(self.scm_instance, rev)
725 return get_changeset_safe(self.scm_instance, rev)
726
726
727 @property
727 @property
728 def tip(self):
728 def tip(self):
729 return self.get_changeset('tip')
729 return self.get_changeset('tip')
730
730
731 @property
731 @property
732 def author(self):
732 def author(self):
733 return self.tip.author
733 return self.tip.author
734
734
735 @property
735 @property
736 def last_change(self):
736 def last_change(self):
737 return self.scm_instance.last_change
737 return self.scm_instance.last_change
738
738
739 def comments(self, revisions=None):
739 def comments(self, revisions=None):
740 """
740 """
741 Returns comments for this repository grouped by revisions
741 Returns comments for this repository grouped by revisions
742
742
743 :param revisions: filter query by revisions only
743 :param revisions: filter query by revisions only
744 """
744 """
745 cmts = ChangesetComment.query()\
745 cmts = ChangesetComment.query()\
746 .filter(ChangesetComment.repo == self)
746 .filter(ChangesetComment.repo == self)
747 if revisions:
747 if revisions:
748 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
748 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
749 grouped = defaultdict(list)
749 grouped = defaultdict(list)
750 for cmt in cmts.all():
750 for cmt in cmts.all():
751 grouped[cmt.revision].append(cmt)
751 grouped[cmt.revision].append(cmt)
752 return grouped
752 return grouped
753
753
754 def statuses(self, revisions=None):
754 def statuses(self, revisions=None):
755 """
755 """
756 Returns statuses for this repository
756 Returns statuses for this repository
757
757
758 :param revisions: list of revisions to get statuses for
758 :param revisions: list of revisions to get statuses for
759 :type revisions: list
759 :type revisions: list
760 """
760 """
761
761
762 statuses = ChangesetStatus.query()\
762 statuses = ChangesetStatus.query()\
763 .filter(ChangesetStatus.repo == self)\
763 .filter(ChangesetStatus.repo == self)\
764 .filter(ChangesetStatus.version == 0)
764 .filter(ChangesetStatus.version == 0)
765 if revisions:
765 if revisions:
766 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
766 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
767 grouped = {}
767 grouped = {}
768 for stat in statuses.all():
768 for stat in statuses.all():
769 grouped[stat.revision] = [str(stat.status), stat.status_lbl]
769 grouped[stat.revision] = [str(stat.status), stat.status_lbl]
770 return grouped
770 return grouped
771
771
772 #==========================================================================
772 #==========================================================================
773 # SCM CACHE INSTANCE
773 # SCM CACHE INSTANCE
774 #==========================================================================
774 #==========================================================================
775
775
776 @property
776 @property
777 def invalidate(self):
777 def invalidate(self):
778 return CacheInvalidation.invalidate(self.repo_name)
778 return CacheInvalidation.invalidate(self.repo_name)
779
779
780 def set_invalidate(self):
780 def set_invalidate(self):
781 """
781 """
782 set a cache for invalidation for this instance
782 set a cache for invalidation for this instance
783 """
783 """
784 CacheInvalidation.set_invalidate(self.repo_name)
784 CacheInvalidation.set_invalidate(self.repo_name)
785
785
786 @LazyProperty
786 @LazyProperty
787 def scm_instance(self):
787 def scm_instance(self):
788 return self.__get_instance()
788 return self.__get_instance()
789
789
790 def scm_instance_cached(self, cache_map=None):
790 def scm_instance_cached(self, cache_map=None):
791 @cache_region('long_term')
791 @cache_region('long_term')
792 def _c(repo_name):
792 def _c(repo_name):
793 return self.__get_instance()
793 return self.__get_instance()
794 rn = self.repo_name
794 rn = self.repo_name
795 log.debug('Getting cached instance of repo')
795 log.debug('Getting cached instance of repo')
796
796
797 if cache_map:
797 if cache_map:
798 # get using prefilled cache_map
798 # get using prefilled cache_map
799 invalidate_repo = cache_map[self.repo_name]
799 invalidate_repo = cache_map[self.repo_name]
800 if invalidate_repo:
800 if invalidate_repo:
801 invalidate_repo = (None if invalidate_repo.cache_active
801 invalidate_repo = (None if invalidate_repo.cache_active
802 else invalidate_repo)
802 else invalidate_repo)
803 else:
803 else:
804 # get from invalidate
804 # get from invalidate
805 invalidate_repo = self.invalidate
805 invalidate_repo = self.invalidate
806
806
807 if invalidate_repo is not None:
807 if invalidate_repo is not None:
808 region_invalidate(_c, None, rn)
808 region_invalidate(_c, None, rn)
809 # update our cache
809 # update our cache
810 CacheInvalidation.set_valid(invalidate_repo.cache_key)
810 CacheInvalidation.set_valid(invalidate_repo.cache_key)
811 return _c(rn)
811 return _c(rn)
812
812
813 def __get_instance(self):
813 def __get_instance(self):
814 repo_full_path = self.repo_full_path
814 repo_full_path = self.repo_full_path
815 try:
815 try:
816 alias = get_scm(repo_full_path)[0]
816 alias = get_scm(repo_full_path)[0]
817 log.debug('Creating instance of %s repository' % alias)
817 log.debug('Creating instance of %s repository' % alias)
818 backend = get_backend(alias)
818 backend = get_backend(alias)
819 except VCSError:
819 except VCSError:
820 log.error(traceback.format_exc())
820 log.error(traceback.format_exc())
821 log.error('Perhaps this repository is in db and not in '
821 log.error('Perhaps this repository is in db and not in '
822 'filesystem run rescan repositories with '
822 'filesystem run rescan repositories with '
823 '"destroy old data " option from admin panel')
823 '"destroy old data " option from admin panel')
824 return
824 return
825
825
826 if alias == 'hg':
826 if alias == 'hg':
827
827
828 repo = backend(safe_str(repo_full_path), create=False,
828 repo = backend(safe_str(repo_full_path), create=False,
829 baseui=self._ui)
829 baseui=self._ui)
830 # skip hidden web repository
830 # skip hidden web repository
831 if repo._get_hidden():
831 if repo._get_hidden():
832 return
832 return
833 else:
833 else:
834 repo = backend(repo_full_path, create=False)
834 repo = backend(repo_full_path, create=False)
835
835
836 return repo
836 return repo
837
837
838
838
839 class RepoGroup(Base, BaseModel):
839 class RepoGroup(Base, BaseModel):
840 __tablename__ = 'groups'
840 __tablename__ = 'groups'
841 __table_args__ = (
841 __table_args__ = (
842 UniqueConstraint('group_name', 'group_parent_id'),
842 UniqueConstraint('group_name', 'group_parent_id'),
843 CheckConstraint('group_id != group_parent_id'),
843 CheckConstraint('group_id != group_parent_id'),
844 {'extend_existing': True, 'mysql_engine': 'InnoDB',
844 {'extend_existing': True, 'mysql_engine': 'InnoDB',
845 'mysql_charset': 'utf8'},
845 'mysql_charset': 'utf8'},
846 )
846 )
847 __mapper_args__ = {'order_by': 'group_name'}
847 __mapper_args__ = {'order_by': 'group_name'}
848
848
849 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
849 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
850 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
850 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
851 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
851 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
852 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
852 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
853
853
854 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
854 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
855 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
855 users_group_to_perm = relationship('UsersGroupRepoGroupToPerm', cascade='all')
856
856
857 parent_group = relationship('RepoGroup', remote_side=group_id)
857 parent_group = relationship('RepoGroup', remote_side=group_id)
858
858
859 def __init__(self, group_name='', parent_group=None):
859 def __init__(self, group_name='', parent_group=None):
860 self.group_name = group_name
860 self.group_name = group_name
861 self.parent_group = parent_group
861 self.parent_group = parent_group
862
862
863 def __unicode__(self):
863 def __unicode__(self):
864 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
864 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
865 self.group_name)
865 self.group_name)
866
866
867 @classmethod
867 @classmethod
868 def groups_choices(cls):
868 def groups_choices(cls):
869 from webhelpers.html import literal as _literal
869 from webhelpers.html import literal as _literal
870 repo_groups = [('', '')]
870 repo_groups = [('', '')]
871 sep = ' &raquo; '
871 sep = ' &raquo; '
872 _name = lambda k: _literal(sep.join(k))
872 _name = lambda k: _literal(sep.join(k))
873
873
874 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
874 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
875 for x in cls.query().all()])
875 for x in cls.query().all()])
876
876
877 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
877 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
878 return repo_groups
878 return repo_groups
879
879
880 @classmethod
880 @classmethod
881 def url_sep(cls):
881 def url_sep(cls):
882 return URL_SEP
882 return URL_SEP
883
883
884 @classmethod
884 @classmethod
885 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
885 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
886 if case_insensitive:
886 if case_insensitive:
887 gr = cls.query()\
887 gr = cls.query()\
888 .filter(cls.group_name.ilike(group_name))
888 .filter(cls.group_name.ilike(group_name))
889 else:
889 else:
890 gr = cls.query()\
890 gr = cls.query()\
891 .filter(cls.group_name == group_name)
891 .filter(cls.group_name == group_name)
892 if cache:
892 if cache:
893 gr = gr.options(FromCache(
893 gr = gr.options(FromCache(
894 "sql_cache_short",
894 "sql_cache_short",
895 "get_group_%s" % _hash_key(group_name)
895 "get_group_%s" % _hash_key(group_name)
896 )
896 )
897 )
897 )
898 return gr.scalar()
898 return gr.scalar()
899
899
900 @property
900 @property
901 def parents(self):
901 def parents(self):
902 parents_recursion_limit = 5
902 parents_recursion_limit = 5
903 groups = []
903 groups = []
904 if self.parent_group is None:
904 if self.parent_group is None:
905 return groups
905 return groups
906 cur_gr = self.parent_group
906 cur_gr = self.parent_group
907 groups.insert(0, cur_gr)
907 groups.insert(0, cur_gr)
908 cnt = 0
908 cnt = 0
909 while 1:
909 while 1:
910 cnt += 1
910 cnt += 1
911 gr = getattr(cur_gr, 'parent_group', None)
911 gr = getattr(cur_gr, 'parent_group', None)
912 cur_gr = cur_gr.parent_group
912 cur_gr = cur_gr.parent_group
913 if gr is None:
913 if gr is None:
914 break
914 break
915 if cnt == parents_recursion_limit:
915 if cnt == parents_recursion_limit:
916 # this will prevent accidental infinit loops
916 # this will prevent accidental infinit loops
917 log.error('group nested more than %s' %
917 log.error('group nested more than %s' %
918 parents_recursion_limit)
918 parents_recursion_limit)
919 break
919 break
920
920
921 groups.insert(0, gr)
921 groups.insert(0, gr)
922 return groups
922 return groups
923
923
924 @property
924 @property
925 def children(self):
925 def children(self):
926 return RepoGroup.query().filter(RepoGroup.parent_group == self)
926 return RepoGroup.query().filter(RepoGroup.parent_group == self)
927
927
928 @property
928 @property
929 def name(self):
929 def name(self):
930 return self.group_name.split(RepoGroup.url_sep())[-1]
930 return self.group_name.split(RepoGroup.url_sep())[-1]
931
931
932 @property
932 @property
933 def full_path(self):
933 def full_path(self):
934 return self.group_name
934 return self.group_name
935
935
936 @property
936 @property
937 def full_path_splitted(self):
937 def full_path_splitted(self):
938 return self.group_name.split(RepoGroup.url_sep())
938 return self.group_name.split(RepoGroup.url_sep())
939
939
940 @property
940 @property
941 def repositories(self):
941 def repositories(self):
942 return Repository.query()\
942 return Repository.query()\
943 .filter(Repository.group == self)\
943 .filter(Repository.group == self)\
944 .order_by(Repository.repo_name)
944 .order_by(Repository.repo_name)
945
945
946 @property
946 @property
947 def repositories_recursive_count(self):
947 def repositories_recursive_count(self):
948 cnt = self.repositories.count()
948 cnt = self.repositories.count()
949
949
950 def children_count(group):
950 def children_count(group):
951 cnt = 0
951 cnt = 0
952 for child in group.children:
952 for child in group.children:
953 cnt += child.repositories.count()
953 cnt += child.repositories.count()
954 cnt += children_count(child)
954 cnt += children_count(child)
955 return cnt
955 return cnt
956
956
957 return cnt + children_count(self)
957 return cnt + children_count(self)
958
958
959 def get_new_name(self, group_name):
959 def get_new_name(self, group_name):
960 """
960 """
961 returns new full group name based on parent and new name
961 returns new full group name based on parent and new name
962
962
963 :param group_name:
963 :param group_name:
964 """
964 """
965 path_prefix = (self.parent_group.full_path_splitted if
965 path_prefix = (self.parent_group.full_path_splitted if
966 self.parent_group else [])
966 self.parent_group else [])
967 return RepoGroup.url_sep().join(path_prefix + [group_name])
967 return RepoGroup.url_sep().join(path_prefix + [group_name])
968
968
969
969
970 class Permission(Base, BaseModel):
970 class Permission(Base, BaseModel):
971 __tablename__ = 'permissions'
971 __tablename__ = 'permissions'
972 __table_args__ = (
972 __table_args__ = (
973 {'extend_existing': True, 'mysql_engine': 'InnoDB',
973 {'extend_existing': True, 'mysql_engine': 'InnoDB',
974 'mysql_charset': 'utf8'},
974 'mysql_charset': 'utf8'},
975 )
975 )
976 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
976 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
977 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
977 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
978 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
978 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
979
979
980 def __unicode__(self):
980 def __unicode__(self):
981 return u"<%s('%s:%s')>" % (
981 return u"<%s('%s:%s')>" % (
982 self.__class__.__name__, self.permission_id, self.permission_name
982 self.__class__.__name__, self.permission_id, self.permission_name
983 )
983 )
984
984
985 @classmethod
985 @classmethod
986 def get_by_key(cls, key):
986 def get_by_key(cls, key):
987 return cls.query().filter(cls.permission_name == key).scalar()
987 return cls.query().filter(cls.permission_name == key).scalar()
988
988
989 @classmethod
989 @classmethod
990 def get_default_perms(cls, default_user_id):
990 def get_default_perms(cls, default_user_id):
991 q = Session.query(UserRepoToPerm, Repository, cls)\
991 q = Session.query(UserRepoToPerm, Repository, cls)\
992 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
992 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
993 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
993 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
994 .filter(UserRepoToPerm.user_id == default_user_id)
994 .filter(UserRepoToPerm.user_id == default_user_id)
995
995
996 return q.all()
996 return q.all()
997
997
998 @classmethod
998 @classmethod
999 def get_default_group_perms(cls, default_user_id):
999 def get_default_group_perms(cls, default_user_id):
1000 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
1000 q = Session.query(UserRepoGroupToPerm, RepoGroup, cls)\
1001 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1001 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1002 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1002 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1003 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1003 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1004
1004
1005 return q.all()
1005 return q.all()
1006
1006
1007
1007
1008 class UserRepoToPerm(Base, BaseModel):
1008 class UserRepoToPerm(Base, BaseModel):
1009 __tablename__ = 'repo_to_perm'
1009 __tablename__ = 'repo_to_perm'
1010 __table_args__ = (
1010 __table_args__ = (
1011 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1011 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1012 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1012 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1013 'mysql_charset': 'utf8'}
1013 'mysql_charset': 'utf8'}
1014 )
1014 )
1015 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1015 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1016 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1016 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1017 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1017 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1018 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1018 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1019
1019
1020 user = relationship('User')
1020 user = relationship('User')
1021 repository = relationship('Repository')
1021 repository = relationship('Repository')
1022 permission = relationship('Permission')
1022 permission = relationship('Permission')
1023
1023
1024 @classmethod
1024 @classmethod
1025 def create(cls, user, repository, permission):
1025 def create(cls, user, repository, permission):
1026 n = cls()
1026 n = cls()
1027 n.user = user
1027 n.user = user
1028 n.repository = repository
1028 n.repository = repository
1029 n.permission = permission
1029 n.permission = permission
1030 Session.add(n)
1030 Session.add(n)
1031 return n
1031 return n
1032
1032
1033 def __unicode__(self):
1033 def __unicode__(self):
1034 return u'<user:%s => %s >' % (self.user, self.repository)
1034 return u'<user:%s => %s >' % (self.user, self.repository)
1035
1035
1036
1036
1037 class UserToPerm(Base, BaseModel):
1037 class UserToPerm(Base, BaseModel):
1038 __tablename__ = 'user_to_perm'
1038 __tablename__ = 'user_to_perm'
1039 __table_args__ = (
1039 __table_args__ = (
1040 UniqueConstraint('user_id', 'permission_id'),
1040 UniqueConstraint('user_id', 'permission_id'),
1041 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1041 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1042 'mysql_charset': 'utf8'}
1042 'mysql_charset': 'utf8'}
1043 )
1043 )
1044 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1044 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1045 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1045 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1046 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1046 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1047
1047
1048 user = relationship('User')
1048 user = relationship('User')
1049 permission = relationship('Permission', lazy='joined')
1049 permission = relationship('Permission', lazy='joined')
1050
1050
1051
1051
1052 class UsersGroupRepoToPerm(Base, BaseModel):
1052 class UsersGroupRepoToPerm(Base, BaseModel):
1053 __tablename__ = 'users_group_repo_to_perm'
1053 __tablename__ = 'users_group_repo_to_perm'
1054 __table_args__ = (
1054 __table_args__ = (
1055 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1055 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1056 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1056 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1057 'mysql_charset': 'utf8'}
1057 'mysql_charset': 'utf8'}
1058 )
1058 )
1059 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1059 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1060 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1060 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1061 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1061 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1062 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1062 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1063
1063
1064 users_group = relationship('UsersGroup')
1064 users_group = relationship('UsersGroup')
1065 permission = relationship('Permission')
1065 permission = relationship('Permission')
1066 repository = relationship('Repository')
1066 repository = relationship('Repository')
1067
1067
1068 @classmethod
1068 @classmethod
1069 def create(cls, users_group, repository, permission):
1069 def create(cls, users_group, repository, permission):
1070 n = cls()
1070 n = cls()
1071 n.users_group = users_group
1071 n.users_group = users_group
1072 n.repository = repository
1072 n.repository = repository
1073 n.permission = permission
1073 n.permission = permission
1074 Session.add(n)
1074 Session.add(n)
1075 return n
1075 return n
1076
1076
1077 def __unicode__(self):
1077 def __unicode__(self):
1078 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1078 return u'<userGroup:%s => %s >' % (self.users_group, self.repository)
1079
1079
1080
1080
1081 class UsersGroupToPerm(Base, BaseModel):
1081 class UsersGroupToPerm(Base, BaseModel):
1082 __tablename__ = 'users_group_to_perm'
1082 __tablename__ = 'users_group_to_perm'
1083 __table_args__ = (
1083 __table_args__ = (
1084 UniqueConstraint('users_group_id', 'permission_id',),
1084 UniqueConstraint('users_group_id', 'permission_id',),
1085 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1085 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1086 'mysql_charset': 'utf8'}
1086 'mysql_charset': 'utf8'}
1087 )
1087 )
1088 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1088 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1089 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1089 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1090 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1090 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1091
1091
1092 users_group = relationship('UsersGroup')
1092 users_group = relationship('UsersGroup')
1093 permission = relationship('Permission')
1093 permission = relationship('Permission')
1094
1094
1095
1095
1096 class UserRepoGroupToPerm(Base, BaseModel):
1096 class UserRepoGroupToPerm(Base, BaseModel):
1097 __tablename__ = 'user_repo_group_to_perm'
1097 __tablename__ = 'user_repo_group_to_perm'
1098 __table_args__ = (
1098 __table_args__ = (
1099 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1099 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1100 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1100 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1101 'mysql_charset': 'utf8'}
1101 'mysql_charset': 'utf8'}
1102 )
1102 )
1103
1103
1104 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1104 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1105 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1105 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1106 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1106 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1107 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1107 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1108
1108
1109 user = relationship('User')
1109 user = relationship('User')
1110 group = relationship('RepoGroup')
1110 group = relationship('RepoGroup')
1111 permission = relationship('Permission')
1111 permission = relationship('Permission')
1112
1112
1113
1113
1114 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1114 class UsersGroupRepoGroupToPerm(Base, BaseModel):
1115 __tablename__ = 'users_group_repo_group_to_perm'
1115 __tablename__ = 'users_group_repo_group_to_perm'
1116 __table_args__ = (
1116 __table_args__ = (
1117 UniqueConstraint('users_group_id', 'group_id'),
1117 UniqueConstraint('users_group_id', 'group_id'),
1118 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1118 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1119 'mysql_charset': 'utf8'}
1119 'mysql_charset': 'utf8'}
1120 )
1120 )
1121
1121
1122 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)
1122 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)
1123 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1123 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1124 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1124 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1125 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1125 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1126
1126
1127 users_group = relationship('UsersGroup')
1127 users_group = relationship('UsersGroup')
1128 permission = relationship('Permission')
1128 permission = relationship('Permission')
1129 group = relationship('RepoGroup')
1129 group = relationship('RepoGroup')
1130
1130
1131
1131
1132 class Statistics(Base, BaseModel):
1132 class Statistics(Base, BaseModel):
1133 __tablename__ = 'statistics'
1133 __tablename__ = 'statistics'
1134 __table_args__ = (
1134 __table_args__ = (
1135 UniqueConstraint('repository_id'),
1135 UniqueConstraint('repository_id'),
1136 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1136 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1137 'mysql_charset': 'utf8'}
1137 'mysql_charset': 'utf8'}
1138 )
1138 )
1139 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1139 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1140 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1140 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1141 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1141 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1142 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1142 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1143 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1143 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1144 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1144 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1145
1145
1146 repository = relationship('Repository', single_parent=True)
1146 repository = relationship('Repository', single_parent=True)
1147
1147
1148
1148
1149 class UserFollowing(Base, BaseModel):
1149 class UserFollowing(Base, BaseModel):
1150 __tablename__ = 'user_followings'
1150 __tablename__ = 'user_followings'
1151 __table_args__ = (
1151 __table_args__ = (
1152 UniqueConstraint('user_id', 'follows_repository_id'),
1152 UniqueConstraint('user_id', 'follows_repository_id'),
1153 UniqueConstraint('user_id', 'follows_user_id'),
1153 UniqueConstraint('user_id', 'follows_user_id'),
1154 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1154 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1155 'mysql_charset': 'utf8'}
1155 'mysql_charset': 'utf8'}
1156 )
1156 )
1157
1157
1158 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1158 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1159 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1159 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1160 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1160 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1161 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1161 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1162 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1162 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1163
1163
1164 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1164 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1165
1165
1166 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1166 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1167 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1167 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1168
1168
1169 @classmethod
1169 @classmethod
1170 def get_repo_followers(cls, repo_id):
1170 def get_repo_followers(cls, repo_id):
1171 return cls.query().filter(cls.follows_repo_id == repo_id)
1171 return cls.query().filter(cls.follows_repo_id == repo_id)
1172
1172
1173
1173
1174 class CacheInvalidation(Base, BaseModel):
1174 class CacheInvalidation(Base, BaseModel):
1175 __tablename__ = 'cache_invalidation'
1175 __tablename__ = 'cache_invalidation'
1176 __table_args__ = (
1176 __table_args__ = (
1177 UniqueConstraint('cache_key'),
1177 UniqueConstraint('cache_key'),
1178 Index('key_idx', 'cache_key'),
1178 Index('key_idx', 'cache_key'),
1179 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1179 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1180 'mysql_charset': 'utf8'},
1180 'mysql_charset': 'utf8'},
1181 )
1181 )
1182 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1182 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1183 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1183 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1184 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1184 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1185 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1185 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1186
1186
1187 def __init__(self, cache_key, cache_args=''):
1187 def __init__(self, cache_key, cache_args=''):
1188 self.cache_key = cache_key
1188 self.cache_key = cache_key
1189 self.cache_args = cache_args
1189 self.cache_args = cache_args
1190 self.cache_active = False
1190 self.cache_active = False
1191
1191
1192 def __unicode__(self):
1192 def __unicode__(self):
1193 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1193 return u"<%s('%s:%s')>" % (self.__class__.__name__,
1194 self.cache_id, self.cache_key)
1194 self.cache_id, self.cache_key)
1195
1195
1196 @classmethod
1196 @classmethod
1197 def clear_cache(cls):
1197 def clear_cache(cls):
1198 cls.query().delete()
1198 cls.query().delete()
1199
1199
1200 @classmethod
1200 @classmethod
1201 def _get_key(cls, key):
1201 def _get_key(cls, key):
1202 """
1202 """
1203 Wrapper for generating a key, together with a prefix
1203 Wrapper for generating a key, together with a prefix
1204
1204
1205 :param key:
1205 :param key:
1206 """
1206 """
1207 import rhodecode
1207 import rhodecode
1208 prefix = ''
1208 prefix = ''
1209 iid = rhodecode.CONFIG.get('instance_id')
1209 iid = rhodecode.CONFIG.get('instance_id')
1210 if iid:
1210 if iid:
1211 prefix = iid
1211 prefix = iid
1212 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1212 return "%s%s" % (prefix, key), prefix, key.rstrip('_README')
1213
1213
1214 @classmethod
1214 @classmethod
1215 def get_by_key(cls, key):
1215 def get_by_key(cls, key):
1216 return cls.query().filter(cls.cache_key == key).scalar()
1216 return cls.query().filter(cls.cache_key == key).scalar()
1217
1217
1218 @classmethod
1218 @classmethod
1219 def _get_or_create_key(cls, key, prefix, org_key):
1219 def _get_or_create_key(cls, key, prefix, org_key):
1220 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1220 inv_obj = Session.query(cls).filter(cls.cache_key == key).scalar()
1221 if not inv_obj:
1221 if not inv_obj:
1222 try:
1222 try:
1223 inv_obj = CacheInvalidation(key, org_key)
1223 inv_obj = CacheInvalidation(key, org_key)
1224 Session.add(inv_obj)
1224 Session.add(inv_obj)
1225 Session.commit()
1225 Session.commit()
1226 except Exception:
1226 except Exception:
1227 log.error(traceback.format_exc())
1227 log.error(traceback.format_exc())
1228 Session.rollback()
1228 Session.rollback()
1229 return inv_obj
1229 return inv_obj
1230
1230
1231 @classmethod
1231 @classmethod
1232 def invalidate(cls, key):
1232 def invalidate(cls, key):
1233 """
1233 """
1234 Returns Invalidation object if this given key should be invalidated
1234 Returns Invalidation object if this given key should be invalidated
1235 None otherwise. `cache_active = False` means that this cache
1235 None otherwise. `cache_active = False` means that this cache
1236 state is not valid and needs to be invalidated
1236 state is not valid and needs to be invalidated
1237
1237
1238 :param key:
1238 :param key:
1239 """
1239 """
1240
1240
1241 key, _prefix, _org_key = cls._get_key(key)
1241 key, _prefix, _org_key = cls._get_key(key)
1242 inv = cls._get_or_create_key(key, _prefix, _org_key)
1242 inv = cls._get_or_create_key(key, _prefix, _org_key)
1243
1243
1244 if inv and inv.cache_active is False:
1244 if inv and inv.cache_active is False:
1245 return inv
1245 return inv
1246
1246
1247 @classmethod
1247 @classmethod
1248 def set_invalidate(cls, key):
1248 def set_invalidate(cls, key):
1249 """
1249 """
1250 Mark this Cache key for invalidation
1250 Mark this Cache key for invalidation
1251
1251
1252 :param key:
1252 :param key:
1253 """
1253 """
1254
1254
1255 key, _prefix, _org_key = cls._get_key(key)
1255 key, _prefix, _org_key = cls._get_key(key)
1256 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1256 inv_objs = Session.query(cls).filter(cls.cache_args == _org_key).all()
1257 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1257 log.debug('marking %s key[s] %s for invalidation' % (len(inv_objs),
1258 _org_key))
1258 _org_key))
1259 try:
1259 try:
1260 for inv_obj in inv_objs:
1260 for inv_obj in inv_objs:
1261 if inv_obj:
1261 if inv_obj:
1262 inv_obj.cache_active = False
1262 inv_obj.cache_active = False
1263
1263
1264 Session.add(inv_obj)
1264 Session.add(inv_obj)
1265 Session.commit()
1265 Session.commit()
1266 except Exception:
1266 except Exception:
1267 log.error(traceback.format_exc())
1267 log.error(traceback.format_exc())
1268 Session.rollback()
1268 Session.rollback()
1269
1269
1270 @classmethod
1270 @classmethod
1271 def set_valid(cls, key):
1271 def set_valid(cls, key):
1272 """
1272 """
1273 Mark this cache key as active and currently cached
1273 Mark this cache key as active and currently cached
1274
1274
1275 :param key:
1275 :param key:
1276 """
1276 """
1277 inv_obj = cls.get_by_key(key)
1277 inv_obj = cls.get_by_key(key)
1278 inv_obj.cache_active = True
1278 inv_obj.cache_active = True
1279 Session.add(inv_obj)
1279 Session.add(inv_obj)
1280 Session.commit()
1280 Session.commit()
1281
1281
1282 @classmethod
1282 @classmethod
1283 def get_cache_map(cls):
1283 def get_cache_map(cls):
1284
1284
1285 class cachemapdict(dict):
1285 class cachemapdict(dict):
1286
1286
1287 def __init__(self, *args, **kwargs):
1287 def __init__(self, *args, **kwargs):
1288 fixkey = kwargs.get('fixkey')
1288 fixkey = kwargs.get('fixkey')
1289 if fixkey:
1289 if fixkey:
1290 del kwargs['fixkey']
1290 del kwargs['fixkey']
1291 self.fixkey = fixkey
1291 self.fixkey = fixkey
1292 super(cachemapdict, self).__init__(*args, **kwargs)
1292 super(cachemapdict, self).__init__(*args, **kwargs)
1293
1293
1294 def __getattr__(self, name):
1294 def __getattr__(self, name):
1295 key = name
1295 key = name
1296 if self.fixkey:
1296 if self.fixkey:
1297 key, _prefix, _org_key = cls._get_key(key)
1297 key, _prefix, _org_key = cls._get_key(key)
1298 if key in self.__dict__:
1298 if key in self.__dict__:
1299 return self.__dict__[key]
1299 return self.__dict__[key]
1300 else:
1300 else:
1301 return self[key]
1301 return self[key]
1302
1302
1303 def __getitem__(self, key):
1303 def __getitem__(self, key):
1304 if self.fixkey:
1304 if self.fixkey:
1305 key, _prefix, _org_key = cls._get_key(key)
1305 key, _prefix, _org_key = cls._get_key(key)
1306 try:
1306 try:
1307 return super(cachemapdict, self).__getitem__(key)
1307 return super(cachemapdict, self).__getitem__(key)
1308 except KeyError:
1308 except KeyError:
1309 return
1309 return
1310
1310
1311 cache_map = cachemapdict(fixkey=True)
1311 cache_map = cachemapdict(fixkey=True)
1312 for obj in cls.query().all():
1312 for obj in cls.query().all():
1313 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1313 cache_map[obj.cache_key] = cachemapdict(obj.get_dict())
1314 return cache_map
1314 return cache_map
1315
1315
1316
1316
1317 class ChangesetComment(Base, BaseModel):
1317 class ChangesetComment(Base, BaseModel):
1318 __tablename__ = 'changeset_comments'
1318 __tablename__ = 'changeset_comments'
1319 __table_args__ = (
1319 __table_args__ = (
1320 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1320 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1321 'mysql_charset': 'utf8'},
1321 'mysql_charset': 'utf8'},
1322 )
1322 )
1323 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1323 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1324 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1324 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1325 revision = Column('revision', String(40), nullable=False)
1325 revision = Column('revision', String(40), nullable=False)
1326 line_no = Column('line_no', Unicode(10), nullable=True)
1326 line_no = Column('line_no', Unicode(10), nullable=True)
1327 f_path = Column('f_path', Unicode(1000), nullable=True)
1327 f_path = Column('f_path', Unicode(1000), nullable=True)
1328 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1328 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1329 text = Column('text', Unicode(25000), nullable=False)
1329 text = Column('text', Unicode(25000), nullable=False)
1330 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1330 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1331
1331
1332 author = relationship('User', lazy='joined')
1332 author = relationship('User', lazy='joined')
1333 repo = relationship('Repository')
1333 repo = relationship('Repository')
1334 status_change = relationship('ChangesetStatus', uselist=False)
1334 status_change = relationship('ChangesetStatus', uselist=False)
1335
1335
1336 @classmethod
1336 @classmethod
1337 def get_users(cls, revision):
1337 def get_users(cls, revision):
1338 """
1338 """
1339 Returns user associated with this changesetComment. ie those
1339 Returns user associated with this changesetComment. ie those
1340 who actually commented
1340 who actually commented
1341
1341
1342 :param cls:
1342 :param cls:
1343 :param revision:
1343 :param revision:
1344 """
1344 """
1345 return Session.query(User)\
1345 return Session.query(User)\
1346 .filter(cls.revision == revision)\
1346 .filter(cls.revision == revision)\
1347 .join(ChangesetComment.author).all()
1347 .join(ChangesetComment.author).all()
1348
1348
1349
1349
1350 class ChangesetStatus(Base, BaseModel):
1350 class ChangesetStatus(Base, BaseModel):
1351 __tablename__ = 'changeset_statuses'
1351 __tablename__ = 'changeset_statuses'
1352 __table_args__ = (
1352 __table_args__ = (
1353 UniqueConstraint('repo_id', 'revision', 'version'),
1353 UniqueConstraint('repo_id', 'revision', 'version'),
1354 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1354 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1355 'mysql_charset': 'utf8'}
1355 'mysql_charset': 'utf8'}
1356 )
1356 )
1357
1357
1358 STATUSES = [
1358 STATUSES = [
1359 ('not_reviewed', _("Not Reviewed")), # (no icon) and default
1359 ('not_reviewed', _("Not Reviewed")), # (no icon) and default
1360 ('approved', _("Approved")),
1360 ('approved', _("Approved")),
1361 ('rejected', _("Rejected")),
1361 ('rejected', _("Rejected")),
1362 ('under_review', _("Under Review")),
1362 ('under_review', _("Under Review")),
1363 ]
1363 ]
1364 DEFAULT = STATUSES[0][0]
1364 DEFAULT = STATUSES[0][0]
1365
1365
1366 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1366 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1367 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1367 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1368 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1368 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1369 revision = Column('revision', String(40), nullable=False)
1369 revision = Column('revision', String(40), nullable=False)
1370 status = Column('status', String(128), nullable=False, default=DEFAULT)
1370 status = Column('status', String(128), nullable=False, default=DEFAULT)
1371 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1371 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1372 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1372 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1373 version = Column('version', Integer(), nullable=False, default=0)
1373 version = Column('version', Integer(), nullable=False, default=0)
1374 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1375
1374 author = relationship('User', lazy='joined')
1376 author = relationship('User', lazy='joined')
1375 repo = relationship('Repository')
1377 repo = relationship('Repository')
1376 comment = relationship('ChangesetComment', lazy='joined')
1378 comment = relationship('ChangesetComment', lazy='joined')
1379 pull_request = relationship('PullRequest', lazy='joined')
1377
1380
1378 @classmethod
1381 @classmethod
1379 def get_status_lbl(cls, value):
1382 def get_status_lbl(cls, value):
1380 return dict(cls.STATUSES).get(value)
1383 return dict(cls.STATUSES).get(value)
1381
1384
1382 @property
1385 @property
1383 def status_lbl(self):
1386 def status_lbl(self):
1384 return ChangesetStatus.get_status_lbl(self.status)
1387 return ChangesetStatus.get_status_lbl(self.status)
1385
1388
1386
1389
1390 class PullRequest(Base, BaseModel):
1391 __tablename__ = 'pull_requests'
1392 __table_args__ = (
1393 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1394 'mysql_charset': 'utf8'},
1395 )
1396
1397 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1398 title = Column('title', Unicode(256), nullable=True)
1399 description = Column('description', Unicode(10240), nullable=True)
1400 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1401 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1402 org_ref = Column('org_ref', Unicode(256), nullable=False)
1403 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1404 other_ref = Column('other_ref', Unicode(256), nullable=False)
1405
1406 @hybrid_property
1407 def revisions(self):
1408 return self._revisions.split(':')
1409
1410 @revisions.setter
1411 def revisions(self, val):
1412 self._revisions = ':'.join(val)
1413
1414 reviewers = relationship('PullRequestReviewers')
1415 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
1416 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
1417
1418 def __json__(self):
1419 return dict(
1420 revisions=self.revisions
1421 )
1422
1423
1424 class PullRequestReviewers(Base, BaseModel):
1425 __tablename__ = 'pull_request_reviewers'
1426 __table_args__ = (
1427 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1428 'mysql_charset': 'utf8'},
1429 )
1430
1431 def __init__(self, user=None, pull_request=None):
1432 self.user = user
1433 self.pull_request = pull_request
1434
1435 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
1436 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
1437 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
1438
1439 user = relationship('User')
1440 pull_request = relationship('PullRequest')
1441
1442
1387 class Notification(Base, BaseModel):
1443 class Notification(Base, BaseModel):
1388 __tablename__ = 'notifications'
1444 __tablename__ = 'notifications'
1389 __table_args__ = (
1445 __table_args__ = (
1390 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1446 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1391 'mysql_charset': 'utf8'},
1447 'mysql_charset': 'utf8'},
1392 )
1448 )
1393
1449
1394 TYPE_CHANGESET_COMMENT = u'cs_comment'
1450 TYPE_CHANGESET_COMMENT = u'cs_comment'
1395 TYPE_MESSAGE = u'message'
1451 TYPE_MESSAGE = u'message'
1396 TYPE_MENTION = u'mention'
1452 TYPE_MENTION = u'mention'
1397 TYPE_REGISTRATION = u'registration'
1453 TYPE_REGISTRATION = u'registration'
1398 TYPE_PULL_REQUEST = u'pull_request'
1454 TYPE_PULL_REQUEST = u'pull_request'
1399
1455
1400 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1456 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1401 subject = Column('subject', Unicode(512), nullable=True)
1457 subject = Column('subject', Unicode(512), nullable=True)
1402 body = Column('body', Unicode(50000), nullable=True)
1458 body = Column('body', Unicode(50000), nullable=True)
1403 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1459 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1404 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1460 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1405 type_ = Column('type', Unicode(256))
1461 type_ = Column('type', Unicode(256))
1406
1462
1407 created_by_user = relationship('User')
1463 created_by_user = relationship('User')
1408 notifications_to_users = relationship('UserNotification', lazy='joined',
1464 notifications_to_users = relationship('UserNotification', lazy='joined',
1409 cascade="all, delete, delete-orphan")
1465 cascade="all, delete, delete-orphan")
1410
1466
1411 @property
1467 @property
1412 def recipients(self):
1468 def recipients(self):
1413 return [x.user for x in UserNotification.query()\
1469 return [x.user for x in UserNotification.query()\
1414 .filter(UserNotification.notification == self)\
1470 .filter(UserNotification.notification == self)\
1415 .order_by(UserNotification.user).all()]
1471 .order_by(UserNotification.user).all()]
1416
1472
1417 @classmethod
1473 @classmethod
1418 def create(cls, created_by, subject, body, recipients, type_=None):
1474 def create(cls, created_by, subject, body, recipients, type_=None):
1419 if type_ is None:
1475 if type_ is None:
1420 type_ = Notification.TYPE_MESSAGE
1476 type_ = Notification.TYPE_MESSAGE
1421
1477
1422 notification = cls()
1478 notification = cls()
1423 notification.created_by_user = created_by
1479 notification.created_by_user = created_by
1424 notification.subject = subject
1480 notification.subject = subject
1425 notification.body = body
1481 notification.body = body
1426 notification.type_ = type_
1482 notification.type_ = type_
1427 notification.created_on = datetime.datetime.now()
1483 notification.created_on = datetime.datetime.now()
1428
1484
1429 for u in recipients:
1485 for u in recipients:
1430 assoc = UserNotification()
1486 assoc = UserNotification()
1431 assoc.notification = notification
1487 assoc.notification = notification
1432 u.notifications.append(assoc)
1488 u.notifications.append(assoc)
1433 Session.add(notification)
1489 Session.add(notification)
1434 return notification
1490 return notification
1435
1491
1436 @property
1492 @property
1437 def description(self):
1493 def description(self):
1438 from rhodecode.model.notification import NotificationModel
1494 from rhodecode.model.notification import NotificationModel
1439 return NotificationModel().make_description(self)
1495 return NotificationModel().make_description(self)
1440
1496
1441
1497
1442 class UserNotification(Base, BaseModel):
1498 class UserNotification(Base, BaseModel):
1443 __tablename__ = 'user_to_notification'
1499 __tablename__ = 'user_to_notification'
1444 __table_args__ = (
1500 __table_args__ = (
1445 UniqueConstraint('user_id', 'notification_id'),
1501 UniqueConstraint('user_id', 'notification_id'),
1446 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1502 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1447 'mysql_charset': 'utf8'}
1503 'mysql_charset': 'utf8'}
1448 )
1504 )
1449 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1505 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
1450 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1506 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1451 read = Column('read', Boolean, default=False)
1507 read = Column('read', Boolean, default=False)
1452 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1508 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1453
1509
1454 user = relationship('User', lazy="joined")
1510 user = relationship('User', lazy="joined")
1455 notification = relationship('Notification', lazy="joined",
1511 notification = relationship('Notification', lazy="joined",
1456 order_by=lambda: Notification.created_on.desc(),)
1512 order_by=lambda: Notification.created_on.desc(),)
1457
1513
1458 def mark_as_read(self):
1514 def mark_as_read(self):
1459 self.read = True
1515 self.read = True
1460 Session.add(self)
1516 Session.add(self)
1461
1517
1462
1518
1463 class DbMigrateVersion(Base, BaseModel):
1519 class DbMigrateVersion(Base, BaseModel):
1464 __tablename__ = 'db_migrate_version'
1520 __tablename__ = 'db_migrate_version'
1465 __table_args__ = (
1521 __table_args__ = (
1466 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1522 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1467 'mysql_charset': 'utf8'},
1523 'mysql_charset': 'utf8'},
1468 )
1524 )
1469 repository_id = Column('repository_id', String(250), primary_key=True)
1525 repository_id = Column('repository_id', String(250), primary_key=True)
1470 repository_path = Column('repository_path', Text)
1526 repository_path = Column('repository_path', Text)
1471 version = Column('version', Integer)
1527 version = Column('version', Integer)
@@ -1,255 +1,256 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.notification
3 rhodecode.model.notification
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Model for notifications
6 Model for notifications
7
7
8
8
9 :created_on: Nov 20, 2011
9 :created_on: Nov 20, 2011
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import datetime
30 import datetime
31
31
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33
33
34 import rhodecode
34 import rhodecode
35 from rhodecode.config.conf import DATETIME_FORMAT
35 from rhodecode.config.conf import DATETIME_FORMAT
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.model import BaseModel
37 from rhodecode.model import BaseModel
38 from rhodecode.model.db import Notification, User, UserNotification
38 from rhodecode.model.db import Notification, User, UserNotification
39 from sqlalchemy.orm import joinedload
39 from sqlalchemy.orm import joinedload
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 class NotificationModel(BaseModel):
44 class NotificationModel(BaseModel):
45
45
46 def __get_notification(self, notification):
46 def __get_notification(self, notification):
47 if isinstance(notification, Notification):
47 if isinstance(notification, Notification):
48 return notification
48 return notification
49 elif isinstance(notification, (int, long)):
49 elif isinstance(notification, (int, long)):
50 return Notification.get(notification)
50 return Notification.get(notification)
51 else:
51 else:
52 if notification:
52 if notification:
53 raise Exception('notification must be int, long or Instance'
53 raise Exception('notification must be int, long or Instance'
54 ' of Notification got %s' % type(notification))
54 ' of Notification got %s' % type(notification))
55
55
56 def create(self, created_by, subject, body, recipients=None,
56 def create(self, created_by, subject, body, recipients=None,
57 type_=Notification.TYPE_MESSAGE, with_email=True,
57 type_=Notification.TYPE_MESSAGE, with_email=True,
58 email_kwargs={}):
58 email_kwargs={}):
59 """
59 """
60
60
61 Creates notification of given type
61 Creates notification of given type
62
62
63 :param created_by: int, str or User instance. User who created this
63 :param created_by: int, str or User instance. User who created this
64 notification
64 notification
65 :param subject:
65 :param subject:
66 :param body:
66 :param body:
67 :param recipients: list of int, str or User objects, when None
67 :param recipients: list of int, str or User objects, when None
68 is given send to all admins
68 is given send to all admins
69 :param type_: type of notification
69 :param type_: type of notification
70 :param with_email: send email with this notification
70 :param with_email: send email with this notification
71 :param email_kwargs: additional dict to pass as args to email template
71 :param email_kwargs: additional dict to pass as args to email template
72 """
72 """
73 from rhodecode.lib.celerylib import tasks, run_task
73 from rhodecode.lib.celerylib import tasks, run_task
74
74
75 if recipients and not getattr(recipients, '__iter__', False):
75 if recipients and not getattr(recipients, '__iter__', False):
76 raise Exception('recipients must be a list of iterable')
76 raise Exception('recipients must be a list of iterable')
77
77
78 created_by_obj = self._get_user(created_by)
78 created_by_obj = self._get_user(created_by)
79
79
80 if recipients:
80 if recipients:
81 recipients_objs = []
81 recipients_objs = []
82 for u in recipients:
82 for u in recipients:
83 obj = self._get_user(u)
83 obj = self._get_user(u)
84 if obj:
84 if obj:
85 recipients_objs.append(obj)
85 recipients_objs.append(obj)
86 recipients_objs = set(recipients_objs)
86 recipients_objs = set(recipients_objs)
87 log.debug('sending notifications %s to %s' % (
87 log.debug('sending notifications %s to %s' % (
88 type_, recipients_objs)
88 type_, recipients_objs)
89 )
89 )
90 else:
90 else:
91 # empty recipients means to all admins
91 # empty recipients means to all admins
92 recipients_objs = User.query().filter(User.admin == True).all()
92 recipients_objs = User.query().filter(User.admin == True).all()
93 log.debug('sending notifications %s to admins: %s' % (
93 log.debug('sending notifications %s to admins: %s' % (
94 type_, recipients_objs)
94 type_, recipients_objs)
95 )
95 )
96 notif = Notification.create(
96 notif = Notification.create(
97 created_by=created_by_obj, subject=subject,
97 created_by=created_by_obj, subject=subject,
98 body=body, recipients=recipients_objs, type_=type_
98 body=body, recipients=recipients_objs, type_=type_
99 )
99 )
100
100
101 if with_email is False:
101 if with_email is False:
102 return notif
102 return notif
103
103
104 #don't send email to person who created this comment
104 #don't send email to person who created this comment
105 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
105 rec_objs = set(recipients_objs).difference(set([created_by_obj]))
106
106
107 # send email with notification to all other participants
107 # send email with notification to all other participants
108 for rec in rec_objs:
108 for rec in rec_objs:
109 email_subject = NotificationModel().make_description(notif, False)
109 email_subject = NotificationModel().make_description(notif, False)
110 type_ = type_
110 type_ = type_
111 email_body = body
111 email_body = body
112 ## this is passed into template
112 ## this is passed into template
113 kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
113 kwargs = {'subject': subject, 'body': h.rst_w_mentions(body)}
114 kwargs.update(email_kwargs)
114 kwargs.update(email_kwargs)
115 email_body_html = EmailNotificationModel()\
115 email_body_html = EmailNotificationModel()\
116 .get_email_tmpl(type_, **kwargs)
116 .get_email_tmpl(type_, **kwargs)
117
117
118 run_task(tasks.send_email, rec.email, email_subject, email_body,
118 run_task(tasks.send_email, rec.email, email_subject, email_body,
119 email_body_html)
119 email_body_html)
120
120
121 return notif
121 return notif
122
122
123 def delete(self, user, notification):
123 def delete(self, user, notification):
124 # we don't want to remove actual notification just the assignment
124 # we don't want to remove actual notification just the assignment
125 try:
125 try:
126 notification = self.__get_notification(notification)
126 notification = self.__get_notification(notification)
127 user = self._get_user(user)
127 user = self._get_user(user)
128 if notification and user:
128 if notification and user:
129 obj = UserNotification.query()\
129 obj = UserNotification.query()\
130 .filter(UserNotification.user == user)\
130 .filter(UserNotification.user == user)\
131 .filter(UserNotification.notification
131 .filter(UserNotification.notification
132 == notification)\
132 == notification)\
133 .one()
133 .one()
134 self.sa.delete(obj)
134 self.sa.delete(obj)
135 return True
135 return True
136 except Exception:
136 except Exception:
137 log.error(traceback.format_exc())
137 log.error(traceback.format_exc())
138 raise
138 raise
139
139
140 def get_for_user(self, user, filter_=None):
140 def get_for_user(self, user, filter_=None):
141 """
141 """
142 Get mentions for given user, filter them if filter dict is given
142 Get mentions for given user, filter them if filter dict is given
143
143
144 :param user:
144 :param user:
145 :type user:
145 :type user:
146 :param filter:
146 :param filter:
147 """
147 """
148 user = self._get_user(user)
148 user = self._get_user(user)
149
149
150 q = UserNotification.query()\
150 q = UserNotification.query()\
151 .filter(UserNotification.user == user)\
151 .filter(UserNotification.user == user)\
152 .join((Notification, UserNotification.notification_id ==
152 .join((Notification, UserNotification.notification_id ==
153 Notification.notification_id))
153 Notification.notification_id))
154
154
155 if filter_:
155 if filter_:
156 q = q.filter(Notification.type_ == filter_.get('type'))
156 q = q.filter(Notification.type_ == filter_.get('type'))
157
157
158 return q.all()
158 return q.all()
159
159
160 def mark_all_read_for_user(self, user, filter_=None):
160 def mark_all_read_for_user(self, user, filter_=None):
161 user = self._get_user(user)
161 user = self._get_user(user)
162 q = UserNotification.query()\
162 q = UserNotification.query()\
163 .filter(UserNotification.user == user)\
163 .filter(UserNotification.user == user)\
164 .filter(UserNotification.read == False)\
164 .filter(UserNotification.read == False)\
165 .join((Notification, UserNotification.notification_id ==
165 .join((Notification, UserNotification.notification_id ==
166 Notification.notification_id))
166 Notification.notification_id))
167 if filter_:
167 if filter_:
168 q = q.filter(Notification.type_ == filter_.get('type'))
168 q = q.filter(Notification.type_ == filter_.get('type'))
169
169
170 # this is a little inefficient but sqlalchemy doesn't support
170 # this is a little inefficient but sqlalchemy doesn't support
171 # update on joined tables :(
171 # update on joined tables :(
172 for obj in q.all():
172 for obj in q.all():
173 obj.read = True
173 obj.read = True
174 self.sa.add(obj)
174 self.sa.add(obj)
175
175
176 def get_unread_cnt_for_user(self, user):
176 def get_unread_cnt_for_user(self, user):
177 user = self._get_user(user)
177 user = self._get_user(user)
178 return UserNotification.query()\
178 return UserNotification.query()\
179 .filter(UserNotification.read == False)\
179 .filter(UserNotification.read == False)\
180 .filter(UserNotification.user == user).count()
180 .filter(UserNotification.user == user).count()
181
181
182 def get_unread_for_user(self, user):
182 def get_unread_for_user(self, user):
183 user = self._get_user(user)
183 user = self._get_user(user)
184 return [x.notification for x in UserNotification.query()\
184 return [x.notification for x in UserNotification.query()\
185 .filter(UserNotification.read == False)\
185 .filter(UserNotification.read == False)\
186 .filter(UserNotification.user == user).all()]
186 .filter(UserNotification.user == user).all()]
187
187
188 def get_user_notification(self, user, notification):
188 def get_user_notification(self, user, notification):
189 user = self._get_user(user)
189 user = self._get_user(user)
190 notification = self.__get_notification(notification)
190 notification = self.__get_notification(notification)
191
191
192 return UserNotification.query()\
192 return UserNotification.query()\
193 .filter(UserNotification.notification == notification)\
193 .filter(UserNotification.notification == notification)\
194 .filter(UserNotification.user == user).scalar()
194 .filter(UserNotification.user == user).scalar()
195
195
196 def make_description(self, notification, show_age=True):
196 def make_description(self, notification, show_age=True):
197 """
197 """
198 Creates a human readable description based on properties
198 Creates a human readable description based on properties
199 of notification object
199 of notification object
200 """
200 """
201
201
202 _map = {
202 _map = {
203 notification.TYPE_CHANGESET_COMMENT: _('commented on commit'),
203 notification.TYPE_CHANGESET_COMMENT: _('commented on commit'),
204 notification.TYPE_MESSAGE: _('sent message'),
204 notification.TYPE_MESSAGE: _('sent message'),
205 notification.TYPE_MENTION: _('mentioned you'),
205 notification.TYPE_MENTION: _('mentioned you'),
206 notification.TYPE_REGISTRATION: _('registered in RhodeCode'),
206 notification.TYPE_REGISTRATION: _('registered in RhodeCode'),
207 notification.TYPE_PULL_REQUEST: _('opened new pull request')
207 notification.TYPE_PULL_REQUEST: _('opened new pull request')
208 }
208 }
209
209
210 tmpl = "%(user)s %(action)s %(when)s"
210 tmpl = "%(user)s %(action)s %(when)s"
211 if show_age:
211 if show_age:
212 when = h.age(notification.created_on)
212 when = h.age(notification.created_on)
213 else:
213 else:
214 DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT)
214 DTF = lambda d: datetime.datetime.strftime(d, DATETIME_FORMAT)
215 when = DTF(notification.created_on)
215 when = DTF(notification.created_on)
216
216
217 data = dict(
217 data = dict(
218 user=notification.created_by_user.username,
218 user=notification.created_by_user.username,
219 action=_map[notification.type_], when=when,
219 action=_map[notification.type_], when=when,
220 )
220 )
221 return tmpl % data
221 return tmpl % data
222
222
223
223
224 class EmailNotificationModel(BaseModel):
224 class EmailNotificationModel(BaseModel):
225
225
226 TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
226 TYPE_CHANGESET_COMMENT = Notification.TYPE_CHANGESET_COMMENT
227 TYPE_PASSWORD_RESET = 'passoword_link'
227 TYPE_PASSWORD_RESET = 'passoword_link'
228 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
228 TYPE_REGISTRATION = Notification.TYPE_REGISTRATION
229 TYPE_PULL_REQUEST = Notification.TYPE_PULL_REQUEST
229 TYPE_DEFAULT = 'default'
230 TYPE_DEFAULT = 'default'
230
231
231 def __init__(self):
232 def __init__(self):
232 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
233 self._template_root = rhodecode.CONFIG['pylons.paths']['templates'][0]
233 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
234 self._tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
234
235
235 self.email_types = {
236 self.email_types = {
236 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
237 self.TYPE_CHANGESET_COMMENT: 'email_templates/changeset_comment.html',
237 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
238 self.TYPE_PASSWORD_RESET: 'email_templates/password_reset.html',
238 self.TYPE_REGISTRATION: 'email_templates/registration.html',
239 self.TYPE_REGISTRATION: 'email_templates/registration.html',
239 self.TYPE_DEFAULT: 'email_templates/default.html'
240 self.TYPE_DEFAULT: 'email_templates/default.html'
240 }
241 }
241
242
242 def get_email_tmpl(self, type_, **kwargs):
243 def get_email_tmpl(self, type_, **kwargs):
243 """
244 """
244 return generated template for email based on given type
245 return generated template for email based on given type
245
246
246 :param type_:
247 :param type_:
247 """
248 """
248
249
249 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
250 base = self.email_types.get(type_, self.email_types[self.TYPE_DEFAULT])
250 email_template = self._tmpl_lookup.get_template(base)
251 email_template = self._tmpl_lookup.get_template(base)
251 # translator inject
252 # translator inject
252 _kwargs = {'_': _}
253 _kwargs = {'_': _}
253 _kwargs.update(kwargs)
254 _kwargs.update(kwargs)
254 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
255 log.debug('rendering tmpl %s with kwargs %s' % (base, _kwargs))
255 return email_template.render(**_kwargs)
256 return email_template.render(**_kwargs)
@@ -1,23 +1,27 b''
1 ## Changesets table !
1 ## Changesets table !
2 <div class="container">
2 <div class="container">
3 <table class="compare_view_commits noborder">
3 <table class="compare_view_commits noborder">
4 %if not c.cs_ranges:
4 %if not c.cs_ranges:
5 <tr><td>${_('No changesets')}</td></tr>
5 <tr><td>${_('No changesets')}</td></tr>
6 %else:
6 %else:
7 %for cnt, cs in enumerate(c.cs_ranges):
7 %for cnt, cs in enumerate(c.cs_ranges):
8 <tr>
8 <tr>
9 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
9 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
10 <td>
10 <td>
11 %if cs.raw_id in c.statuses:
11 %if cs.raw_id in c.statuses:
12 <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div>
12 <div title="${c.statuses[cs.raw_id][1]}" class="changeset-status-ico"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses[cs.raw_id][0])}" /></div>
13 %endif
13 %endif
14 </td>
14 </td>
15 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
15 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}
16 %if c.as_form:
17 ${h.hidden('revisions',cs.raw_id)}
18 %endif
19 </td>
16 <td><div class="author">${h.person(cs.author)}</div></td>
20 <td><div class="author">${h.person(cs.author)}</div></td>
17 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
21 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
18 <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
22 <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
19 </tr>
23 </tr>
20 %endfor
24 %endfor
21 %endif
25 %endif
22 </table>
26 </table>
23 </div> No newline at end of file
27 </div>
@@ -1,77 +1,77 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 ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
5 ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(u'Home',h.url('/'))}
9 ${h.link_to(u'Home',h.url('/'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 &raquo;
12 &raquo;
13 ${_('Compare')}
13 ${_('Compare')}
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('changelog')}
17 ${self.menu('changelog')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box">
21 <div class="box">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 <div class="table">
26 <div class="table">
27 <div id="body" class="diffblock">
27 <div id="body" class="diffblock">
28 <div class="code-header cv">
28 <div class="code-header cv">
29 <h3 class="code-header-title">${_('Compare View')}</h3>
29 <h3 class="code-header-title">${_('Compare View')}</h3>
30 <div>
30 <div>
31 ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)} <a href="${c.swap_url}">[swap]</a>
31 ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)} <a href="${c.swap_url}">[swap]</a>
32 </div>
32 </div>
33 </div>
33 </div>
34 </div>
34 </div>
35 <div id="changeset_compare_view_content">
35 <div id="changeset_compare_view_content">
36 ##CS
36 ##CS
37 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Changesets')}</div>
37 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Outgoing changesets')}</div>
38 <%include file="compare_cs.html" />
38 <%include file="compare_cs.html" />
39
39
40 ## FILES
40 ## FILES
41 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
41 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
42 <div class="cs_files">
42 <div class="cs_files">
43 %for fid, change, f, stat in c.files:
43 %for fid, change, f, stat in c.files:
44 <div class="cs_${change}">
44 <div class="cs_${change}">
45 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
45 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
46 <div class="changes">${h.fancy_file_stats(stat)}</div>
46 <div class="changes">${h.fancy_file_stats(stat)}</div>
47 </div>
47 </div>
48 %endfor
48 %endfor
49 </div>
49 </div>
50 </div>
50 </div>
51 </div>
51 </div>
52
52
53 ## diff block
53 ## diff block
54 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
54 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
55 %for fid, change, f, stat in c.files:
55 %for fid, change, f, stat in c.files:
56 ${diff_block.diff_block_simple([c.changes[fid]])}
56 ${diff_block.diff_block_simple([c.changes[fid]])}
57 %endfor
57 %endfor
58
58
59 <script type="text/javascript">
59 <script type="text/javascript">
60
60
61 YUE.onDOMReady(function(){
61 YUE.onDOMReady(function(){
62
62
63 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
63 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
64 var act = e.currentTarget.nextElementSibling;
64 var act = e.currentTarget.nextElementSibling;
65
65
66 if(YUD.hasClass(act,'active')){
66 if(YUD.hasClass(act,'active')){
67 YUD.removeClass(act,'active');
67 YUD.removeClass(act,'active');
68 YUD.setStyle(act,'display','none');
68 YUD.setStyle(act,'display','none');
69 }else{
69 }else{
70 YUD.addClass(act,'active');
70 YUD.addClass(act,'active');
71 YUD.setStyle(act,'display','');
71 YUD.setStyle(act,'display','');
72 }
72 }
73 });
73 });
74 })
74 })
75 </script>
75 </script>
76 </div>
76 </div>
77 </%def>
77 </%def>
@@ -1,192 +1,192 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('Pull request')}
4 ${c.repo_name} ${_('New pull request')}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('Pull request')}
12 ${_('New pull request')}
13 </%def>
13 </%def>
14
14
15 <%def name="main()">
15 <%def name="main()">
16
16
17 <div class="box">
17 <div class="box">
18 <!-- box / title -->
18 <!-- box / title -->
19 <div class="title">
19 <div class="title">
20 ${self.breadcrumbs()}
20 ${self.breadcrumbs()}
21 </div>
21 </div>
22 ${h.form(url('#'),method='put', id='pull_request_form')}
22 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
23 <div style="float:left;padding:30px">
23 <div style="float:left;padding:0px 30px 30px 30px">
24 <div style="padding:0px 5px 5px 5px">
25 <span>
26 <a id="refresh" href="#">
27 <img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
28 ${_('refresh overview')}
29 </a>
30 </span>
31 </div>
24 ##ORG
32 ##ORG
25 <div style="float:left">
33 <div style="float:left">
26 <div class="fork_user">
34 <div class="fork_user">
27 <div class="gravatar">
35 <div class="gravatar">
28 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
36 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
29 </div>
37 </div>
30 <span style="font-size: 20px">
38 <span style="font-size: 20px">
31 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
39 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
32 </span>
40 </span>
33 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
41 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
34 </div>
42 </div>
35 <div style="clear:both;padding-top: 10px"></div>
43 <div style="clear:both;padding-top: 10px"></div>
36 </div>
44 </div>
37 <div style="float:left;font-size:24px;padding:0px 20px">
45 <div style="float:left;font-size:24px;padding:0px 20px">
38 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
46 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
39 </div>
47 </div>
40
48
41 ##OTHER, most Probably the PARENT OF THIS FORK
49 ##OTHER, most Probably the PARENT OF THIS FORK
42 <div style="float:left">
50 <div style="float:left">
43 <div class="fork_user">
51 <div class="fork_user">
44 <div class="gravatar">
52 <div class="gravatar">
45 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
53 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
46 </div>
54 </div>
47 <span style="font-size: 20px">
55 <span style="font-size: 20px">
48 ${h.select('other_repo','',c.other_repos,class_='refs')}:${h.select('other_ref','',c.other_refs,class_='refs')}
56 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref','',c.other_refs,class_='refs')}
49 </span>
57 </span>
50 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
58 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
51 </div>
59 </div>
52 <div style="clear:both;padding-top: 10px"></div>
60 <div style="clear:both;padding-top: 10px"></div>
53 </div>
61 </div>
54 <div style="float:left;padding:5px 5px 5px 15px">
62 <div style="clear:both;padding-top: 10px"></div>
55 <span>
63 ## overview pulled by ajax
56 <a id="refresh" href="#">
64 <div style="float:left" id="pull_request_overview"></div>
57 <img class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
58 ${_('refresh overview')}
59 </a>
60 </span>
61 </div>
62 <div style="clear:both;padding-top: 10px"></div>
63 <div style="float:left" id="pull_request_overview">
64 </div>
65 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
65 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
66 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
66 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
67 </div>
67 </div>
68 </div>
68 </div>
69 <div style="float:left; border-left:1px dashed #eee">
69 <div style="float:left; border-left:1px dashed #eee">
70 <h4>${_('Pull request reviewers')}</h4>
70 <h4>${_('Pull request reviewers')}</h4>
71 <div id="reviewers" style="padding:0px 0px 0px 15px">
71 <div id="reviewers" style="padding:0px 0px 0px 15px">
72 ##TODO: make this nicer :)
72 ##TODO: make this nicer :)
73 <table class="table noborder">
73 <table class="table noborder">
74 <tr>
74 <tr>
75 <td>
75 <td>
76 <div>
76 <div>
77 <div style="float:left">
77 <div style="float:left">
78 <div class="text" style="padding: 0px 0px 6px;">${_('Choosen reviewers')}</div>
78 <div class="text" style="padding: 0px 0px 6px;">${_('Chosen reviewers')}</div>
79 ${h.select('review_members',[x[0] for x in c.review_members],c.review_members,multiple=True,size=8,style="min-width:210px")}
79 ${h.select('review_members',[x[0] for x in c.review_members],c.review_members,multiple=True,size=8,style="min-width:210px")}
80 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
80 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
81 ${_('Remove all elements')}
81 ${_('Remove all elements')}
82 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
82 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
83 </div>
83 </div>
84 </div>
84 </div>
85 <div style="float:left;width:20px;padding-top:50px">
85 <div style="float:left;width:20px;padding-top:50px">
86 <img alt="add" id="add_element"
86 <img alt="add" id="add_element"
87 style="padding:2px;cursor:pointer"
87 style="padding:2px;cursor:pointer"
88 src="${h.url('/images/icons/arrow_left.png')}"/>
88 src="${h.url('/images/icons/arrow_left.png')}"/>
89 <br />
89 <br />
90 <img alt="remove" id="remove_element"
90 <img alt="remove" id="remove_element"
91 style="padding:2px;cursor:pointer"
91 style="padding:2px;cursor:pointer"
92 src="${h.url('/images/icons/arrow_right.png')}"/>
92 src="${h.url('/images/icons/arrow_right.png')}"/>
93 </div>
93 </div>
94 <div style="float:left">
94 <div style="float:left">
95 <div class="text" style="padding: 0px 0px 6px;">${_('Available reviewers')}</div>
95 <div class="text" style="padding: 0px 0px 6px;">${_('Available reviewers')}</div>
96 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
96 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
97 <div id="add_all_elements" style="cursor:pointer;text-align:center">
97 <div id="add_all_elements" style="cursor:pointer;text-align:center">
98 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
98 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
99 ${_('Add all elements')}
99 ${_('Add all elements')}
100 </div>
100 </div>
101 </div>
101 </div>
102 </div>
102 </div>
103 </td>
103 </td>
104 </tr>
104 </tr>
105 </table>
105 </table>
106 </div>
106 </div>
107 </div>
107 </div>
108 <h3>${_('Create new pull request')}</h3>
108 <h3>${_('Create new pull request')}</h3>
109
109
110 <div class="form">
110 <div class="form">
111 <!-- fields -->
111 <!-- fields -->
112
112
113 <div class="fields">
113 <div class="fields">
114
114
115 <div class="field">
115 <div class="field">
116 <div class="label">
116 <div class="label">
117 <label for="pullrequest_title">${_('Title')}:</label>
117 <label for="pullrequest_title">${_('Title')}:</label>
118 </div>
118 </div>
119 <div class="input">
119 <div class="input">
120 ${h.text('pullrequest_title',size=30)}
120 ${h.text('pullrequest_title',size=30)}
121 </div>
121 </div>
122 </div>
122 </div>
123
123
124 <div class="field">
124 <div class="field">
125 <div class="label label-textarea">
125 <div class="label label-textarea">
126 <label for="pullrequest_desc">${_('description')}:</label>
126 <label for="pullrequest_desc">${_('description')}:</label>
127 </div>
127 </div>
128 <div class="textarea text-area editor">
128 <div class="textarea text-area editor">
129 ${h.textarea('pullrequest_desc',size=30)}
129 ${h.textarea('pullrequest_desc',size=30)}
130 </div>
130 </div>
131 </div>
131 </div>
132
132
133 <div class="buttons">
133 <div class="buttons">
134 ${h.submit('save',_('Send pull request'),class_="ui-button")}
134 ${h.submit('save',_('Send pull request'),class_="ui-button")}
135 ${h.reset('reset',_('Reset'),class_="ui-button")}
135 ${h.reset('reset',_('Reset'),class_="ui-button")}
136 </div>
136 </div>
137 </div>
137 </div>
138 </div>
138 </div>
139 ${h.end_form()}
139 ${h.end_form()}
140
140
141 </div>
141 </div>
142
142
143 <script type="text/javascript">
143 <script type="text/javascript">
144 MultiSelectWidget('review_members','available_members','pull_request_form');
144 MultiSelectWidget('review_members','available_members','pull_request_form');
145
145
146 var loadPreview = function(){
146 var loadPreview = function(){
147 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
147 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
148 var url = "${h.url('compare_url',
148 var url = "${h.url('compare_url',
149 repo_name='org_repo',
149 repo_name='org_repo',
150 org_ref_type='branch', org_ref='org_ref',
150 org_ref_type='branch', org_ref='org_ref',
151 other_ref_type='branch', other_ref='other_ref',
151 other_ref_type='branch', other_ref='other_ref',
152 repo='other_repo')}";
152 repo='other_repo',
153
153 as_form=True)}";
154
154 var select_refs = YUQ('#pull_request_form select.refs')
155 var select_refs = YUQ('#pull_request_form select.refs')
155
156
156 for(var i=0;i<select_refs.length;i++){
157 for(var i=0;i<select_refs.length;i++){
157 var select_ref = select_refs[i];
158 var select_ref = select_refs[i];
158 var select_ref_data = select_ref.value.split(':');
159 var select_ref_data = select_ref.value.split(':');
159 var key = null;
160 var key = null;
160 var val = null;
161 var val = null;
161 if(select_ref_data.length>1){
162 if(select_ref_data.length>1){
162 key = select_ref.name+"_type";
163 key = select_ref.name+"_type";
163 val = select_ref_data[0];
164 val = select_ref_data[0];
164 url = url.replace(key,val);
165 url = url.replace(key,val);
165
166
166 key = select_ref.name;
167 key = select_ref.name;
167 val = select_ref_data[1];
168 val = select_ref_data[1];
168 url = url.replace(key,val);
169 url = url.replace(key,val);
169
170
170 }else{
171 }else{
171 key = select_ref.name;
172 key = select_ref.name;
172 val = select_ref.value;
173 val = select_ref.value;
173 url = url.replace(key,val);
174 url = url.replace(key,val);
174 }
175 }
175 }
176 }
176
177
177 ypjax(url,'pull_request_overview', function(data){
178 ypjax(url,'pull_request_overview', function(data){
178 YUD.get('pull_request_overview_url').href = url;
179 YUD.get('pull_request_overview_url').href = url;
179 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
180 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
180 })
181 })
181 }
182 }
182 YUE.on('refresh','click',function(e){
183 YUE.on('refresh','click',function(e){
183 loadPreview()
184 loadPreview()
184 })
185 })
185
186
186 //lazy load after 0.5
187 //lazy load overview after 0.5s
187
188 setTimeout(loadPreview, 500)
188 setTimeout(loadPreview,500)
189
189
190 </script>
190 </script>
191
191
192 </%def>
192 </%def>
@@ -1,7 +1,52 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2
2
3
3 class TestCompareController(TestController):
4 class TestCompareController(TestController):
4
5
5 def test_index(self):
6 def test_index_tag(self):
6 response = self.app.get(url(controller='compare', action='index'))
7 self.log_user()
7 # Test response...
8 tag1='0.1.3'
9 tag2='0.1.2'
10 response = self.app.get(url(controller='compare', action='index',
11 repo_name=HG_REPO,
12 org_ref_type="tag",
13 org_ref=tag1,
14 other_ref_type="tag",
15 other_ref=tag2,
16 ))
17 response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, tag1, HG_REPO, tag2))
18 ## outgoing changesets between tags
19 response.mustcontain('''<a href="/%s/changeset/17544fbfcd33ffb439e2b728b5d526b1ef30bfcf">r120:17544fbfcd33</a>''' % HG_REPO)
20 response.mustcontain('''<a href="/%s/changeset/36e0fc9d2808c5022a24f49d6658330383ed8666">r119:36e0fc9d2808</a>''' % HG_REPO)
21 response.mustcontain('''<a href="/%s/changeset/bb1a3ab98cc45cb934a77dcabf87a5a598b59e97">r118:bb1a3ab98cc4</a>''' % HG_REPO)
22 response.mustcontain('''<a href="/%s/changeset/41fda979f02fda216374bf8edac4e83f69e7581c">r117:41fda979f02f</a>''' % HG_REPO)
23 response.mustcontain('''<a href="/%s/changeset/9749bfbfc0d2eba208d7947de266303b67c87cda">r116:9749bfbfc0d2</a>''' % HG_REPO)
24 response.mustcontain('''<a href="/%s/changeset/70d4cef8a37657ee4cf5aabb3bd9f68879769816">r115:70d4cef8a376</a>''' % HG_REPO)
25 response.mustcontain('''<a href="/%s/changeset/c5ddebc06eaaba3010c2d66ea6ec9d074eb0f678">r112:c5ddebc06eaa</a>''' % HG_REPO)
26
27 ## files diff
28 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--1c5cf9e91c12">docs/api/utils/index.rst</a></div>''' % (HG_REPO, tag1, tag2))
29 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--e3305437df55">test_and_report.sh</a></div>''' % (HG_REPO, tag1, tag2))
30 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--c8e92ef85cd1">.hgignore</a></div>''' % (HG_REPO, tag1, tag2))
31 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--6e08b694d687">.hgtags</a></div>''' % (HG_REPO, tag1, tag2))
32 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2c14b00f3393">docs/api/index.rst</a></div>''' % (HG_REPO, tag1, tag2))
33 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--430ccbc82bdf">vcs/__init__.py</a></div>''' % (HG_REPO, tag1, tag2))
34 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--9c390eb52cd6">vcs/backends/hg.py</a></div>''' % (HG_REPO, tag1, tag2))
35 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--ebb592c595c0">vcs/utils/__init__.py</a></div>''' % (HG_REPO, tag1, tag2))
36 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--7abc741b5052">vcs/utils/annotate.py</a></div>''' % (HG_REPO, tag1, tag2))
37 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--2ef0ef106c56">vcs/utils/diffs.py</a></div>''' % (HG_REPO, tag1, tag2))
38 response.mustcontain('''<div class="node"><a href="/%s/compare/tag@%s...tag@%s#C--3150cb87d4b7">vcs/utils/lazy.py</a></div>''' % (HG_REPO, tag1, tag2))
39
40 def test_index_branch(self):
41 self.log_user()
42 response = self.app.get(url(controller='compare', action='index',
43 repo_name=HG_REPO,
44 org_ref_type="branch",
45 org_ref='default',
46 other_ref_type="branch",
47 other_ref='default',
48 ))
49
50 response.mustcontain('%s@default -> %s@default' % (HG_REPO, HG_REPO))
51 # branch are equal
52 response.mustcontain('<tr><td>No changesets</td></tr>')
@@ -1,7 +1,9 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2
2
3
3 class TestPullrequestsController(TestController):
4 class TestPullrequestsController(TestController):
4
5
5 def test_index(self):
6 def test_index(self):
6 response = self.app.get(url(controller='pullrequests', action='index'))
7 self.log_user()
7 # Test response...
8 response = self.app.get(url(controller='pullrequests', action='index',
9 repo_name=HG_REPO))
@@ -1,760 +1,760 b''
1 import os
1 import os
2 import unittest
2 import unittest
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4
4
5 from rhodecode.model.repos_group import ReposGroupModel
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.repo import RepoModel
6 from rhodecode.model.repo import RepoModel
7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
8 UsersGroup, UsersGroupMember, Permission, UsersGroupRepoGroupToPerm,\
8 UsersGroup, UsersGroupMember, Permission, UsersGroupRepoGroupToPerm,\
9 Repository, UserEmailMap
9 Repository, UserEmailMap
10 from sqlalchemy.exc import IntegrityError, DatabaseError
10 from sqlalchemy.exc import IntegrityError, DatabaseError
11 from rhodecode.model.user import UserModel
11 from rhodecode.model.user import UserModel
12
12
13 from rhodecode.model.meta import Session
13 from rhodecode.model.meta import Session
14 from rhodecode.model.notification import NotificationModel
14 from rhodecode.model.notification import NotificationModel
15 from rhodecode.model.users_group import UsersGroupModel
15 from rhodecode.model.users_group import UsersGroupModel
16 from rhodecode.lib.auth import AuthUser
16 from rhodecode.lib.auth import AuthUser
17
17
18
18
19 def _make_group(path, desc='desc', parent_id=None,
19 def _make_group(path, desc='desc', parent_id=None,
20 skip_if_exists=False):
20 skip_if_exists=False):
21
21
22 gr = RepoGroup.get_by_group_name(path)
22 gr = RepoGroup.get_by_group_name(path)
23 if gr and skip_if_exists:
23 if gr and skip_if_exists:
24 return gr
24 return gr
25
25
26 gr = ReposGroupModel().create(path, desc, parent_id)
26 gr = ReposGroupModel().create(path, desc, parent_id)
27 return gr
27 return gr
28
28
29
29
30 class TestReposGroups(unittest.TestCase):
30 class TestReposGroups(unittest.TestCase):
31
31
32 def setUp(self):
32 def setUp(self):
33 self.g1 = _make_group('test1', skip_if_exists=True)
33 self.g1 = _make_group('test1', skip_if_exists=True)
34 Session.commit()
34 Session.commit()
35 self.g2 = _make_group('test2', skip_if_exists=True)
35 self.g2 = _make_group('test2', skip_if_exists=True)
36 Session.commit()
36 Session.commit()
37 self.g3 = _make_group('test3', skip_if_exists=True)
37 self.g3 = _make_group('test3', skip_if_exists=True)
38 Session.commit()
38 Session.commit()
39
39
40 def tearDown(self):
40 def tearDown(self):
41 print 'out'
41 print 'out'
42
42
43 def __check_path(self, *path):
43 def __check_path(self, *path):
44 """
44 """
45 Checks the path for existance !
45 Checks the path for existance !
46 """
46 """
47 path = [TESTS_TMP_PATH] + list(path)
47 path = [TESTS_TMP_PATH] + list(path)
48 path = os.path.join(*path)
48 path = os.path.join(*path)
49 return os.path.isdir(path)
49 return os.path.isdir(path)
50
50
51 def _check_folders(self):
51 def _check_folders(self):
52 print os.listdir(TESTS_TMP_PATH)
52 print os.listdir(TESTS_TMP_PATH)
53
53
54 def __delete_group(self, id_):
54 def __delete_group(self, id_):
55 ReposGroupModel().delete(id_)
55 ReposGroupModel().delete(id_)
56
56
57 def __update_group(self, id_, path, desc='desc', parent_id=None):
57 def __update_group(self, id_, path, desc='desc', parent_id=None):
58 form_data = dict(
58 form_data = dict(
59 group_name=path,
59 group_name=path,
60 group_description=desc,
60 group_description=desc,
61 group_parent_id=parent_id,
61 group_parent_id=parent_id,
62 perms_updates=[],
62 perms_updates=[],
63 perms_new=[]
63 perms_new=[]
64 )
64 )
65 gr = ReposGroupModel().update(id_, form_data)
65 gr = ReposGroupModel().update(id_, form_data)
66 return gr
66 return gr
67
67
68 def test_create_group(self):
68 def test_create_group(self):
69 g = _make_group('newGroup')
69 g = _make_group('newGroup')
70 self.assertEqual(g.full_path, 'newGroup')
70 self.assertEqual(g.full_path, 'newGroup')
71
71
72 self.assertTrue(self.__check_path('newGroup'))
72 self.assertTrue(self.__check_path('newGroup'))
73
73
74 def test_create_same_name_group(self):
74 def test_create_same_name_group(self):
75 self.assertRaises(IntegrityError, lambda:_make_group('newGroup'))
75 self.assertRaises(IntegrityError, lambda:_make_group('newGroup'))
76 Session.rollback()
76 Session.rollback()
77
77
78 def test_same_subgroup(self):
78 def test_same_subgroup(self):
79 sg1 = _make_group('sub1', parent_id=self.g1.group_id)
79 sg1 = _make_group('sub1', parent_id=self.g1.group_id)
80 self.assertEqual(sg1.parent_group, self.g1)
80 self.assertEqual(sg1.parent_group, self.g1)
81 self.assertEqual(sg1.full_path, 'test1/sub1')
81 self.assertEqual(sg1.full_path, 'test1/sub1')
82 self.assertTrue(self.__check_path('test1', 'sub1'))
82 self.assertTrue(self.__check_path('test1', 'sub1'))
83
83
84 ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
84 ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
85 self.assertEqual(ssg1.parent_group, sg1)
85 self.assertEqual(ssg1.parent_group, sg1)
86 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
86 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
87 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
87 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
88
88
89 def test_remove_group(self):
89 def test_remove_group(self):
90 sg1 = _make_group('deleteme')
90 sg1 = _make_group('deleteme')
91 self.__delete_group(sg1.group_id)
91 self.__delete_group(sg1.group_id)
92
92
93 self.assertEqual(RepoGroup.get(sg1.group_id), None)
93 self.assertEqual(RepoGroup.get(sg1.group_id), None)
94 self.assertFalse(self.__check_path('deteteme'))
94 self.assertFalse(self.__check_path('deteteme'))
95
95
96 sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
96 sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
97 self.__delete_group(sg1.group_id)
97 self.__delete_group(sg1.group_id)
98
98
99 self.assertEqual(RepoGroup.get(sg1.group_id), None)
99 self.assertEqual(RepoGroup.get(sg1.group_id), None)
100 self.assertFalse(self.__check_path('test1', 'deteteme'))
100 self.assertFalse(self.__check_path('test1', 'deteteme'))
101
101
102 def test_rename_single_group(self):
102 def test_rename_single_group(self):
103 sg1 = _make_group('initial')
103 sg1 = _make_group('initial')
104
104
105 new_sg1 = self.__update_group(sg1.group_id, 'after')
105 new_sg1 = self.__update_group(sg1.group_id, 'after')
106 self.assertTrue(self.__check_path('after'))
106 self.assertTrue(self.__check_path('after'))
107 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
107 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
108
108
109 def test_update_group_parent(self):
109 def test_update_group_parent(self):
110
110
111 sg1 = _make_group('initial', parent_id=self.g1.group_id)
111 sg1 = _make_group('initial', parent_id=self.g1.group_id)
112
112
113 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
113 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
114 self.assertTrue(self.__check_path('test1', 'after'))
114 self.assertTrue(self.__check_path('test1', 'after'))
115 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
115 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
116
116
117 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
117 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
118 self.assertTrue(self.__check_path('test3', 'after'))
118 self.assertTrue(self.__check_path('test3', 'after'))
119 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
119 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
120
120
121 new_sg1 = self.__update_group(sg1.group_id, 'hello')
121 new_sg1 = self.__update_group(sg1.group_id, 'hello')
122 self.assertTrue(self.__check_path('hello'))
122 self.assertTrue(self.__check_path('hello'))
123
123
124 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
124 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
125
125
126 def test_subgrouping_with_repo(self):
126 def test_subgrouping_with_repo(self):
127
127
128 g1 = _make_group('g1')
128 g1 = _make_group('g1')
129 g2 = _make_group('g2')
129 g2 = _make_group('g2')
130
130
131 # create new repo
131 # create new repo
132 form_data = dict(repo_name='john',
132 form_data = dict(repo_name='john',
133 repo_name_full='john',
133 repo_name_full='john',
134 fork_name=None,
134 fork_name=None,
135 description=None,
135 description=None,
136 repo_group=None,
136 repo_group=None,
137 private=False,
137 private=False,
138 repo_type='hg',
138 repo_type='hg',
139 clone_uri=None)
139 clone_uri=None)
140 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
140 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
141 r = RepoModel().create(form_data, cur_user)
141 r = RepoModel().create(form_data, cur_user)
142
142
143 self.assertEqual(r.repo_name, 'john')
143 self.assertEqual(r.repo_name, 'john')
144
144
145 # put repo into group
145 # put repo into group
146 form_data = form_data
146 form_data = form_data
147 form_data['repo_group'] = g1.group_id
147 form_data['repo_group'] = g1.group_id
148 form_data['perms_new'] = []
148 form_data['perms_new'] = []
149 form_data['perms_updates'] = []
149 form_data['perms_updates'] = []
150 RepoModel().update(r.repo_name, form_data)
150 RepoModel().update(r.repo_name, form_data)
151 self.assertEqual(r.repo_name, 'g1/john')
151 self.assertEqual(r.repo_name, 'g1/john')
152
152
153 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
153 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
154 self.assertTrue(self.__check_path('g2', 'g1'))
154 self.assertTrue(self.__check_path('g2', 'g1'))
155
155
156 # test repo
156 # test repo
157 self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1', r.just_name]))
157 self.assertEqual(r.repo_name, RepoGroup.url_sep().join(['g2', 'g1', r.just_name]))
158
158
159 def test_move_to_root(self):
159 def test_move_to_root(self):
160 g1 = _make_group('t11')
160 g1 = _make_group('t11')
161 Session.commit()
161 Session.commit()
162 g2 = _make_group('t22', parent_id=g1.group_id)
162 g2 = _make_group('t22', parent_id=g1.group_id)
163 Session.commit()
163 Session.commit()
164
164
165 self.assertEqual(g2.full_path, 't11/t22')
165 self.assertEqual(g2.full_path, 't11/t22')
166 self.assertTrue(self.__check_path('t11', 't22'))
166 self.assertTrue(self.__check_path('t11', 't22'))
167
167
168 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
168 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
169 Session.commit()
169 Session.commit()
170
170
171 self.assertEqual(g2.group_name, 'g22')
171 self.assertEqual(g2.group_name, 'g22')
172 # we moved out group from t1 to '' so it's full path should be 'g2'
172 # we moved out group from t1 to '' so it's full path should be 'g2'
173 self.assertEqual(g2.full_path, 'g22')
173 self.assertEqual(g2.full_path, 'g22')
174 self.assertFalse(self.__check_path('t11', 't22'))
174 self.assertFalse(self.__check_path('t11', 't22'))
175 self.assertTrue(self.__check_path('g22'))
175 self.assertTrue(self.__check_path('g22'))
176
176
177
177
178 class TestUser(unittest.TestCase):
178 class TestUser(unittest.TestCase):
179 def __init__(self, methodName='runTest'):
179 def __init__(self, methodName='runTest'):
180 Session.remove()
180 Session.remove()
181 super(TestUser, self).__init__(methodName=methodName)
181 super(TestUser, self).__init__(methodName=methodName)
182
182
183 def test_create_and_remove(self):
183 def test_create_and_remove(self):
184 usr = UserModel().create_or_update(username=u'test_user',
184 usr = UserModel().create_or_update(username=u'test_user',
185 password=u'qweqwe',
185 password=u'qweqwe',
186 email=u'u232@rhodecode.org',
186 email=u'u232@rhodecode.org',
187 name=u'u1', lastname=u'u1')
187 name=u'u1', lastname=u'u1')
188 Session.commit()
188 Session.commit()
189 self.assertEqual(User.get_by_username(u'test_user'), usr)
189 self.assertEqual(User.get_by_username(u'test_user'), usr)
190
190
191 # make users group
191 # make users group
192 users_group = UsersGroupModel().create('some_example_group')
192 users_group = UsersGroupModel().create('some_example_group')
193 Session.commit()
193 Session.commit()
194
194
195 UsersGroupModel().add_user_to_group(users_group, usr)
195 UsersGroupModel().add_user_to_group(users_group, usr)
196 Session.commit()
196 Session.commit()
197
197
198 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
198 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
199 self.assertEqual(UsersGroupMember.query().count(), 1)
199 self.assertEqual(UsersGroupMember.query().count(), 1)
200 UserModel().delete(usr.user_id)
200 UserModel().delete(usr.user_id)
201 Session.commit()
201 Session.commit()
202
202
203 self.assertEqual(UsersGroupMember.query().all(), [])
203 self.assertEqual(UsersGroupMember.query().all(), [])
204
204
205 def test_additonal_email_as_main(self):
205 def test_additonal_email_as_main(self):
206 usr = UserModel().create_or_update(username=u'test_user',
206 usr = UserModel().create_or_update(username=u'test_user',
207 password=u'qweqwe',
207 password=u'qweqwe',
208 email=u'main_email@rhodecode.org',
208 email=u'main_email@rhodecode.org',
209 name=u'u1', lastname=u'u1')
209 name=u'u1', lastname=u'u1')
210 Session.commit()
210 Session.commit()
211
211
212 def do():
212 def do():
213 m = UserEmailMap()
213 m = UserEmailMap()
214 m.email = u'main_email@rhodecode.org'
214 m.email = u'main_email@rhodecode.org'
215 m.user = usr
215 m.user = usr
216 Session.add(m)
216 Session.add(m)
217 Session.commit()
217 Session.commit()
218 self.assertRaises(AttributeError, do)
218 self.assertRaises(AttributeError, do)
219
219
220 UserModel().delete(usr.user_id)
220 UserModel().delete(usr.user_id)
221 Session.commit()
221 Session.commit()
222
222
223 def test_extra_email_map(self):
223 def test_extra_email_map(self):
224 usr = UserModel().create_or_update(username=u'test_user',
224 usr = UserModel().create_or_update(username=u'test_user',
225 password=u'qweqwe',
225 password=u'qweqwe',
226 email=u'main_email@rhodecode.org',
226 email=u'main_email@rhodecode.org',
227 name=u'u1', lastname=u'u1')
227 name=u'u1', lastname=u'u1')
228 Session.commit()
228 Session.commit()
229
229
230 m = UserEmailMap()
230 m = UserEmailMap()
231 m.email = u'main_email2@rhodecode.org'
231 m.email = u'main_email2@rhodecode.org'
232 m.user = usr
232 m.user = usr
233 Session.add(m)
233 Session.add(m)
234 Session.commit()
234 Session.commit()
235
235
236 u = User.get_by_email(email='main_email@rhodecode.org')
236 u = User.get_by_email(email='main_email@rhodecode.org')
237 self.assertEqual(usr.user_id, u.user_id)
237 self.assertEqual(usr.user_id, u.user_id)
238 self.assertEqual(usr.username, u.username)
238 self.assertEqual(usr.username, u.username)
239
239
240 u = User.get_by_email(email='main_email2@rhodecode.org')
240 u = User.get_by_email(email='main_email2@rhodecode.org')
241 self.assertEqual(usr.user_id, u.user_id)
241 self.assertEqual(usr.user_id, u.user_id)
242 self.assertEqual(usr.username, u.username)
242 self.assertEqual(usr.username, u.username)
243 u = User.get_by_email(email='main_email3@rhodecode.org')
243 u = User.get_by_email(email='main_email3@rhodecode.org')
244 self.assertEqual(None, u)
244 self.assertEqual(None, u)
245
245
246 UserModel().delete(usr.user_id)
246 UserModel().delete(usr.user_id)
247 Session.commit()
247 Session.commit()
248
248
249
249
250 class TestNotifications(unittest.TestCase):
250 class TestNotifications(unittest.TestCase):
251
251
252 def __init__(self, methodName='runTest'):
252 def __init__(self, methodName='runTest'):
253 Session.remove()
253 Session.remove()
254 self.u1 = UserModel().create_or_update(username=u'u1',
254 self.u1 = UserModel().create_or_update(username=u'u1',
255 password=u'qweqwe',
255 password=u'qweqwe',
256 email=u'u1@rhodecode.org',
256 email=u'u1@rhodecode.org',
257 name=u'u1', lastname=u'u1')
257 name=u'u1', lastname=u'u1')
258 Session.commit()
258 Session.commit()
259 self.u1 = self.u1.user_id
259 self.u1 = self.u1.user_id
260
260
261 self.u2 = UserModel().create_or_update(username=u'u2',
261 self.u2 = UserModel().create_or_update(username=u'u2',
262 password=u'qweqwe',
262 password=u'qweqwe',
263 email=u'u2@rhodecode.org',
263 email=u'u2@rhodecode.org',
264 name=u'u2', lastname=u'u3')
264 name=u'u2', lastname=u'u3')
265 Session.commit()
265 Session.commit()
266 self.u2 = self.u2.user_id
266 self.u2 = self.u2.user_id
267
267
268 self.u3 = UserModel().create_or_update(username=u'u3',
268 self.u3 = UserModel().create_or_update(username=u'u3',
269 password=u'qweqwe',
269 password=u'qweqwe',
270 email=u'u3@rhodecode.org',
270 email=u'u3@rhodecode.org',
271 name=u'u3', lastname=u'u3')
271 name=u'u3', lastname=u'u3')
272 Session.commit()
272 Session.commit()
273 self.u3 = self.u3.user_id
273 self.u3 = self.u3.user_id
274
274
275 super(TestNotifications, self).__init__(methodName=methodName)
275 super(TestNotifications, self).__init__(methodName=methodName)
276
276
277 def _clean_notifications(self):
277 def _clean_notifications(self):
278 for n in Notification.query().all():
278 for n in Notification.query().all():
279 Session.delete(n)
279 Session.delete(n)
280
280
281 Session.commit()
281 Session.commit()
282 self.assertEqual(Notification.query().all(), [])
282 self.assertEqual(Notification.query().all(), [])
283
283
284 def tearDown(self):
284 def tearDown(self):
285 self._clean_notifications()
285 self._clean_notifications()
286
286
287 def test_create_notification(self):
287 def test_create_notification(self):
288 self.assertEqual([], Notification.query().all())
288 self.assertEqual([], Notification.query().all())
289 self.assertEqual([], UserNotification.query().all())
289 self.assertEqual([], UserNotification.query().all())
290
290
291 usrs = [self.u1, self.u2]
291 usrs = [self.u1, self.u2]
292 notification = NotificationModel().create(created_by=self.u1,
292 notification = NotificationModel().create(created_by=self.u1,
293 subject=u'subj', body=u'hi there',
293 subject=u'subj', body=u'hi there',
294 recipients=usrs)
294 recipients=usrs)
295 Session.commit()
295 Session.commit()
296 u1 = User.get(self.u1)
296 u1 = User.get(self.u1)
297 u2 = User.get(self.u2)
297 u2 = User.get(self.u2)
298 u3 = User.get(self.u3)
298 u3 = User.get(self.u3)
299 notifications = Notification.query().all()
299 notifications = Notification.query().all()
300 self.assertEqual(len(notifications), 1)
300 self.assertEqual(len(notifications), 1)
301
301
302 unotification = UserNotification.query()\
302 unotification = UserNotification.query()\
303 .filter(UserNotification.notification == notification).all()
303 .filter(UserNotification.notification == notification).all()
304
304
305 self.assertEqual(notifications[0].recipients, [u1, u2])
305 self.assertEqual(notifications[0].recipients, [u1, u2])
306 self.assertEqual(notification.notification_id,
306 self.assertEqual(notification.notification_id,
307 notifications[0].notification_id)
307 notifications[0].notification_id)
308 self.assertEqual(len(unotification), len(usrs))
308 self.assertEqual(len(unotification), len(usrs))
309 self.assertEqual([x.user.user_id for x in unotification], usrs)
309 self.assertEqual([x.user.user_id for x in unotification], usrs)
310
310
311 def test_user_notifications(self):
311 def test_user_notifications(self):
312 self.assertEqual([], Notification.query().all())
312 self.assertEqual([], Notification.query().all())
313 self.assertEqual([], UserNotification.query().all())
313 self.assertEqual([], UserNotification.query().all())
314
314
315 notification1 = NotificationModel().create(created_by=self.u1,
315 notification1 = NotificationModel().create(created_by=self.u1,
316 subject=u'subj', body=u'hi there1',
316 subject=u'subj', body=u'hi there1',
317 recipients=[self.u3])
317 recipients=[self.u3])
318 Session.commit()
318 Session.commit()
319 notification2 = NotificationModel().create(created_by=self.u1,
319 notification2 = NotificationModel().create(created_by=self.u1,
320 subject=u'subj', body=u'hi there2',
320 subject=u'subj', body=u'hi there2',
321 recipients=[self.u3])
321 recipients=[self.u3])
322 Session.commit()
322 Session.commit()
323 u3 = Session.query(User).get(self.u3)
323 u3 = Session.query(User).get(self.u3)
324
324
325 self.assertEqual(sorted([x.notification for x in u3.notifications]),
325 self.assertEqual(sorted([x.notification for x in u3.notifications]),
326 sorted([notification2, notification1]))
326 sorted([notification2, notification1]))
327
327
328 def test_delete_notifications(self):
328 def test_delete_notifications(self):
329 self.assertEqual([], Notification.query().all())
329 self.assertEqual([], Notification.query().all())
330 self.assertEqual([], UserNotification.query().all())
330 self.assertEqual([], UserNotification.query().all())
331
331
332 notification = NotificationModel().create(created_by=self.u1,
332 notification = NotificationModel().create(created_by=self.u1,
333 subject=u'title', body=u'hi there3',
333 subject=u'title', body=u'hi there3',
334 recipients=[self.u3, self.u1, self.u2])
334 recipients=[self.u3, self.u1, self.u2])
335 Session.commit()
335 Session.commit()
336 notifications = Notification.query().all()
336 notifications = Notification.query().all()
337 self.assertTrue(notification in notifications)
337 self.assertTrue(notification in notifications)
338
338
339 Notification.delete(notification.notification_id)
339 Notification.delete(notification.notification_id)
340 Session.commit()
340 Session.commit()
341
341
342 notifications = Notification.query().all()
342 notifications = Notification.query().all()
343 self.assertFalse(notification in notifications)
343 self.assertFalse(notification in notifications)
344
344
345 un = UserNotification.query().filter(UserNotification.notification
345 un = UserNotification.query().filter(UserNotification.notification
346 == notification).all()
346 == notification).all()
347 self.assertEqual(un, [])
347 self.assertEqual(un, [])
348
348
349 def test_delete_association(self):
349 def test_delete_association(self):
350
350
351 self.assertEqual([], Notification.query().all())
351 self.assertEqual([], Notification.query().all())
352 self.assertEqual([], UserNotification.query().all())
352 self.assertEqual([], UserNotification.query().all())
353
353
354 notification = NotificationModel().create(created_by=self.u1,
354 notification = NotificationModel().create(created_by=self.u1,
355 subject=u'title', body=u'hi there3',
355 subject=u'title', body=u'hi there3',
356 recipients=[self.u3, self.u1, self.u2])
356 recipients=[self.u3, self.u1, self.u2])
357 Session.commit()
357 Session.commit()
358
358
359 unotification = UserNotification.query()\
359 unotification = UserNotification.query()\
360 .filter(UserNotification.notification ==
360 .filter(UserNotification.notification ==
361 notification)\
361 notification)\
362 .filter(UserNotification.user_id == self.u3)\
362 .filter(UserNotification.user_id == self.u3)\
363 .scalar()
363 .scalar()
364
364
365 self.assertEqual(unotification.user_id, self.u3)
365 self.assertEqual(unotification.user_id, self.u3)
366
366
367 NotificationModel().delete(self.u3,
367 NotificationModel().delete(self.u3,
368 notification.notification_id)
368 notification.notification_id)
369 Session.commit()
369 Session.commit()
370
370
371 u3notification = UserNotification.query()\
371 u3notification = UserNotification.query()\
372 .filter(UserNotification.notification ==
372 .filter(UserNotification.notification ==
373 notification)\
373 notification)\
374 .filter(UserNotification.user_id == self.u3)\
374 .filter(UserNotification.user_id == self.u3)\
375 .scalar()
375 .scalar()
376
376
377 self.assertEqual(u3notification, None)
377 self.assertEqual(u3notification, None)
378
378
379 # notification object is still there
379 # notification object is still there
380 self.assertEqual(Notification.query().all(), [notification])
380 self.assertEqual(Notification.query().all(), [notification])
381
381
382 #u1 and u2 still have assignments
382 #u1 and u2 still have assignments
383 u1notification = UserNotification.query()\
383 u1notification = UserNotification.query()\
384 .filter(UserNotification.notification ==
384 .filter(UserNotification.notification ==
385 notification)\
385 notification)\
386 .filter(UserNotification.user_id == self.u1)\
386 .filter(UserNotification.user_id == self.u1)\
387 .scalar()
387 .scalar()
388 self.assertNotEqual(u1notification, None)
388 self.assertNotEqual(u1notification, None)
389 u2notification = UserNotification.query()\
389 u2notification = UserNotification.query()\
390 .filter(UserNotification.notification ==
390 .filter(UserNotification.notification ==
391 notification)\
391 notification)\
392 .filter(UserNotification.user_id == self.u2)\
392 .filter(UserNotification.user_id == self.u2)\
393 .scalar()
393 .scalar()
394 self.assertNotEqual(u2notification, None)
394 self.assertNotEqual(u2notification, None)
395
395
396 def test_notification_counter(self):
396 def test_notification_counter(self):
397 self._clean_notifications()
397 self._clean_notifications()
398 self.assertEqual([], Notification.query().all())
398 self.assertEqual([], Notification.query().all())
399 self.assertEqual([], UserNotification.query().all())
399 self.assertEqual([], UserNotification.query().all())
400
400
401 NotificationModel().create(created_by=self.u1,
401 NotificationModel().create(created_by=self.u1,
402 subject=u'title', body=u'hi there_delete',
402 subject=u'title', body=u'hi there_delete',
403 recipients=[self.u3, self.u1])
403 recipients=[self.u3, self.u1])
404 Session.commit()
404 Session.commit()
405
405
406 self.assertEqual(NotificationModel()
406 self.assertEqual(NotificationModel()
407 .get_unread_cnt_for_user(self.u1), 1)
407 .get_unread_cnt_for_user(self.u1), 1)
408 self.assertEqual(NotificationModel()
408 self.assertEqual(NotificationModel()
409 .get_unread_cnt_for_user(self.u2), 0)
409 .get_unread_cnt_for_user(self.u2), 0)
410 self.assertEqual(NotificationModel()
410 self.assertEqual(NotificationModel()
411 .get_unread_cnt_for_user(self.u3), 1)
411 .get_unread_cnt_for_user(self.u3), 1)
412
412
413 notification = NotificationModel().create(created_by=self.u1,
413 notification = NotificationModel().create(created_by=self.u1,
414 subject=u'title', body=u'hi there3',
414 subject=u'title', body=u'hi there3',
415 recipients=[self.u3, self.u1, self.u2])
415 recipients=[self.u3, self.u1, self.u2])
416 Session.commit()
416 Session.commit()
417
417
418 self.assertEqual(NotificationModel()
418 self.assertEqual(NotificationModel()
419 .get_unread_cnt_for_user(self.u1), 2)
419 .get_unread_cnt_for_user(self.u1), 2)
420 self.assertEqual(NotificationModel()
420 self.assertEqual(NotificationModel()
421 .get_unread_cnt_for_user(self.u2), 1)
421 .get_unread_cnt_for_user(self.u2), 1)
422 self.assertEqual(NotificationModel()
422 self.assertEqual(NotificationModel()
423 .get_unread_cnt_for_user(self.u3), 2)
423 .get_unread_cnt_for_user(self.u3), 2)
424
424
425
425
426 class TestUsers(unittest.TestCase):
426 class TestUsers(unittest.TestCase):
427
427
428 def __init__(self, methodName='runTest'):
428 def __init__(self, methodName='runTest'):
429 super(TestUsers, self).__init__(methodName=methodName)
429 super(TestUsers, self).__init__(methodName=methodName)
430
430
431 def setUp(self):
431 def setUp(self):
432 self.u1 = UserModel().create_or_update(username=u'u1',
432 self.u1 = UserModel().create_or_update(username=u'u1',
433 password=u'qweqwe',
433 password=u'qweqwe',
434 email=u'u1@rhodecode.org',
434 email=u'u1@rhodecode.org',
435 name=u'u1', lastname=u'u1')
435 name=u'u1', lastname=u'u1')
436
436
437 def tearDown(self):
437 def tearDown(self):
438 perm = Permission.query().all()
438 perm = Permission.query().all()
439 for p in perm:
439 for p in perm:
440 UserModel().revoke_perm(self.u1, p)
440 UserModel().revoke_perm(self.u1, p)
441
441
442 UserModel().delete(self.u1)
442 UserModel().delete(self.u1)
443 Session.commit()
443 Session.commit()
444
444
445 def test_add_perm(self):
445 def test_add_perm(self):
446 perm = Permission.query().all()[0]
446 perm = Permission.query().all()[0]
447 UserModel().grant_perm(self.u1, perm)
447 UserModel().grant_perm(self.u1, perm)
448 Session.commit()
448 Session.commit()
449 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
449 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
450
450
451 def test_has_perm(self):
451 def test_has_perm(self):
452 perm = Permission.query().all()
452 perm = Permission.query().all()
453 for p in perm:
453 for p in perm:
454 has_p = UserModel().has_perm(self.u1, p)
454 has_p = UserModel().has_perm(self.u1, p)
455 self.assertEqual(False, has_p)
455 self.assertEqual(False, has_p)
456
456
457 def test_revoke_perm(self):
457 def test_revoke_perm(self):
458 perm = Permission.query().all()[0]
458 perm = Permission.query().all()[0]
459 UserModel().grant_perm(self.u1, perm)
459 UserModel().grant_perm(self.u1, perm)
460 Session.commit()
460 Session.commit()
461 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
461 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
462
462
463 #revoke
463 #revoke
464 UserModel().revoke_perm(self.u1, perm)
464 UserModel().revoke_perm(self.u1, perm)
465 Session.commit()
465 Session.commit()
466 self.assertEqual(UserModel().has_perm(self.u1, perm), False)
466 self.assertEqual(UserModel().has_perm(self.u1, perm), False)
467
467
468
468
469 class TestPermissions(unittest.TestCase):
469 class TestPermissions(unittest.TestCase):
470 def __init__(self, methodName='runTest'):
470 def __init__(self, methodName='runTest'):
471 super(TestPermissions, self).__init__(methodName=methodName)
471 super(TestPermissions, self).__init__(methodName=methodName)
472
472
473 def setUp(self):
473 def setUp(self):
474 self.u1 = UserModel().create_or_update(
474 self.u1 = UserModel().create_or_update(
475 username=u'u1', password=u'qweqwe',
475 username=u'u1', password=u'qweqwe',
476 email=u'u1@rhodecode.org', name=u'u1', lastname=u'u1'
476 email=u'u1@rhodecode.org', name=u'u1', lastname=u'u1'
477 )
477 )
478 self.u2 = UserModel().create_or_update(
478 self.u2 = UserModel().create_or_update(
479 username=u'u2', password=u'qweqwe',
479 username=u'u2', password=u'qweqwe',
480 email=u'u2@rhodecode.org', name=u'u2', lastname=u'u2'
480 email=u'u2@rhodecode.org', name=u'u2', lastname=u'u2'
481 )
481 )
482 self.anon = User.get_by_username('default')
482 self.anon = User.get_by_username('default')
483 self.a1 = UserModel().create_or_update(
483 self.a1 = UserModel().create_or_update(
484 username=u'a1', password=u'qweqwe',
484 username=u'a1', password=u'qweqwe',
485 email=u'a1@rhodecode.org', name=u'a1', lastname=u'a1', admin=True
485 email=u'a1@rhodecode.org', name=u'a1', lastname=u'a1', admin=True
486 )
486 )
487 Session.commit()
487 Session.commit()
488
488
489 def tearDown(self):
489 def tearDown(self):
490 if hasattr(self, 'test_repo'):
490 if hasattr(self, 'test_repo'):
491 RepoModel().delete(repo=self.test_repo)
491 RepoModel().delete(repo=self.test_repo)
492 UserModel().delete(self.u1)
492 UserModel().delete(self.u1)
493 UserModel().delete(self.u2)
493 UserModel().delete(self.u2)
494 UserModel().delete(self.a1)
494 UserModel().delete(self.a1)
495 if hasattr(self, 'g1'):
495 if hasattr(self, 'g1'):
496 ReposGroupModel().delete(self.g1.group_id)
496 ReposGroupModel().delete(self.g1.group_id)
497 if hasattr(self, 'g2'):
497 if hasattr(self, 'g2'):
498 ReposGroupModel().delete(self.g2.group_id)
498 ReposGroupModel().delete(self.g2.group_id)
499
499
500 if hasattr(self, 'ug1'):
500 if hasattr(self, 'ug1'):
501 UsersGroupModel().delete(self.ug1, force=True)
501 UsersGroupModel().delete(self.ug1, force=True)
502
502
503 Session.commit()
503 Session.commit()
504
504
505 def test_default_perms_set(self):
505 def test_default_perms_set(self):
506 u1_auth = AuthUser(user_id=self.u1.user_id)
506 u1_auth = AuthUser(user_id=self.u1.user_id)
507 perms = {
507 perms = {
508 'repositories_groups': {},
508 'repositories_groups': {},
509 'global': set([u'hg.create.repository', u'repository.read',
509 'global': set([u'hg.create.repository', u'repository.read',
510 u'hg.register.manual_activate']),
510 u'hg.register.manual_activate']),
511 'repositories': {u'vcs_test_hg': u'repository.read'}
511 'repositories': {u'vcs_test_hg': u'repository.read'}
512 }
512 }
513 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
513 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
514 perms['repositories'][HG_REPO])
514 perms['repositories'][HG_REPO])
515 new_perm = 'repository.write'
515 new_perm = 'repository.write'
516 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
516 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
517 Session.commit()
517 Session.commit()
518
518
519 u1_auth = AuthUser(user_id=self.u1.user_id)
519 u1_auth = AuthUser(user_id=self.u1.user_id)
520 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm)
520 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm)
521
521
522 def test_default_admin_perms_set(self):
522 def test_default_admin_perms_set(self):
523 a1_auth = AuthUser(user_id=self.a1.user_id)
523 a1_auth = AuthUser(user_id=self.a1.user_id)
524 perms = {
524 perms = {
525 'repositories_groups': {},
525 'repositories_groups': {},
526 'global': set([u'hg.admin']),
526 'global': set([u'hg.admin']),
527 'repositories': {u'vcs_test_hg': u'repository.admin'}
527 'repositories': {u'vcs_test_hg': u'repository.admin'}
528 }
528 }
529 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
529 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
530 perms['repositories'][HG_REPO])
530 perms['repositories'][HG_REPO])
531 new_perm = 'repository.write'
531 new_perm = 'repository.write'
532 RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1, perm=new_perm)
532 RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1, perm=new_perm)
533 Session.commit()
533 Session.commit()
534 # cannot really downgrade admins permissions !? they still get's set as
534 # cannot really downgrade admins permissions !? they still get's set as
535 # admin !
535 # admin !
536 u1_auth = AuthUser(user_id=self.a1.user_id)
536 u1_auth = AuthUser(user_id=self.a1.user_id)
537 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
537 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
538 perms['repositories'][HG_REPO])
538 perms['repositories'][HG_REPO])
539
539
540 def test_default_group_perms(self):
540 def test_default_group_perms(self):
541 self.g1 = _make_group('test1', skip_if_exists=True)
541 self.g1 = _make_group('test1', skip_if_exists=True)
542 self.g2 = _make_group('test2', skip_if_exists=True)
542 self.g2 = _make_group('test2', skip_if_exists=True)
543 u1_auth = AuthUser(user_id=self.u1.user_id)
543 u1_auth = AuthUser(user_id=self.u1.user_id)
544 perms = {
544 perms = {
545 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
545 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
546 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
546 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
547 'repositories': {u'vcs_test_hg': u'repository.read'}
547 'repositories': {u'vcs_test_hg': u'repository.read'}
548 }
548 }
549 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
549 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
550 perms['repositories'][HG_REPO])
550 perms['repositories'][HG_REPO])
551 self.assertEqual(u1_auth.permissions['repositories_groups'],
551 self.assertEqual(u1_auth.permissions['repositories_groups'],
552 perms['repositories_groups'])
552 perms['repositories_groups'])
553
553
554 def test_default_admin_group_perms(self):
554 def test_default_admin_group_perms(self):
555 self.g1 = _make_group('test1', skip_if_exists=True)
555 self.g1 = _make_group('test1', skip_if_exists=True)
556 self.g2 = _make_group('test2', skip_if_exists=True)
556 self.g2 = _make_group('test2', skip_if_exists=True)
557 a1_auth = AuthUser(user_id=self.a1.user_id)
557 a1_auth = AuthUser(user_id=self.a1.user_id)
558 perms = {
558 perms = {
559 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
559 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
560 'global': set(['hg.admin']),
560 'global': set(['hg.admin']),
561 'repositories': {u'vcs_test_hg': 'repository.admin'}
561 'repositories': {u'vcs_test_hg': 'repository.admin'}
562 }
562 }
563
563
564 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
564 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
565 perms['repositories'][HG_REPO])
565 perms['repositories'][HG_REPO])
566 self.assertEqual(a1_auth.permissions['repositories_groups'],
566 self.assertEqual(a1_auth.permissions['repositories_groups'],
567 perms['repositories_groups'])
567 perms['repositories_groups'])
568
568
569 def test_propagated_permission_from_users_group(self):
569 def test_propagated_permission_from_users_group(self):
570 # make group
570 # make group
571 self.ug1 = UsersGroupModel().create('G1')
571 self.ug1 = UsersGroupModel().create('G1')
572 # add user to group
572 # add user to group
573 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
573 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
574
574
575 # set permission to lower
575 # set permission to lower
576 new_perm = 'repository.none'
576 new_perm = 'repository.none'
577 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
577 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
578 Session.commit()
578 Session.commit()
579 u1_auth = AuthUser(user_id=self.u1.user_id)
579 u1_auth = AuthUser(user_id=self.u1.user_id)
580 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
580 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
581 new_perm)
581 new_perm)
582
582
583 # grant perm for group this should override permission from user
583 # grant perm for group this should override permission from user
584 new_perm = 'repository.write'
584 new_perm = 'repository.write'
585 RepoModel().grant_users_group_permission(repo=HG_REPO,
585 RepoModel().grant_users_group_permission(repo=HG_REPO,
586 group_name=self.ug1,
586 group_name=self.ug1,
587 perm=new_perm)
587 perm=new_perm)
588 # check perms
588 # check perms
589 u1_auth = AuthUser(user_id=self.u1.user_id)
589 u1_auth = AuthUser(user_id=self.u1.user_id)
590 perms = {
590 perms = {
591 'repositories_groups': {},
591 'repositories_groups': {},
592 'global': set([u'hg.create.repository', u'repository.read',
592 'global': set([u'hg.create.repository', u'repository.read',
593 u'hg.register.manual_activate']),
593 u'hg.register.manual_activate']),
594 'repositories': {u'vcs_test_hg': u'repository.read'}
594 'repositories': {u'vcs_test_hg': u'repository.read'}
595 }
595 }
596 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
596 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
597 new_perm)
597 new_perm)
598 self.assertEqual(u1_auth.permissions['repositories_groups'],
598 self.assertEqual(u1_auth.permissions['repositories_groups'],
599 perms['repositories_groups'])
599 perms['repositories_groups'])
600
600
601 def test_propagated_permission_from_users_group_lower_weight(self):
601 def test_propagated_permission_from_users_group_lower_weight(self):
602 # make group
602 # make group
603 self.ug1 = UsersGroupModel().create('G1')
603 self.ug1 = UsersGroupModel().create('G1')
604 # add user to group
604 # add user to group
605 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
605 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
606
606
607 # set permission to lower
607 # set permission to lower
608 new_perm_h = 'repository.write'
608 new_perm_h = 'repository.write'
609 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
609 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
610 perm=new_perm_h)
610 perm=new_perm_h)
611 Session.commit()
611 Session.commit()
612 u1_auth = AuthUser(user_id=self.u1.user_id)
612 u1_auth = AuthUser(user_id=self.u1.user_id)
613 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
613 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
614 new_perm_h)
614 new_perm_h)
615
615
616 # grant perm for group this should NOT override permission from user
616 # grant perm for group this should NOT override permission from user
617 # since it's lower than granted
617 # since it's lower than granted
618 new_perm_l = 'repository.read'
618 new_perm_l = 'repository.read'
619 RepoModel().grant_users_group_permission(repo=HG_REPO,
619 RepoModel().grant_users_group_permission(repo=HG_REPO,
620 group_name=self.ug1,
620 group_name=self.ug1,
621 perm=new_perm_l)
621 perm=new_perm_l)
622 # check perms
622 # check perms
623 u1_auth = AuthUser(user_id=self.u1.user_id)
623 u1_auth = AuthUser(user_id=self.u1.user_id)
624 perms = {
624 perms = {
625 'repositories_groups': {},
625 'repositories_groups': {},
626 'global': set([u'hg.create.repository', u'repository.read',
626 'global': set([u'hg.create.repository', u'repository.read',
627 u'hg.register.manual_activate']),
627 u'hg.register.manual_activate']),
628 'repositories': {u'vcs_test_hg': u'repository.write'}
628 'repositories': {u'vcs_test_hg': u'repository.write'}
629 }
629 }
630 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
630 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
631 new_perm_h)
631 new_perm_h)
632 self.assertEqual(u1_auth.permissions['repositories_groups'],
632 self.assertEqual(u1_auth.permissions['repositories_groups'],
633 perms['repositories_groups'])
633 perms['repositories_groups'])
634
634
635 def test_repo_in_group_permissions(self):
635 def test_repo_in_group_permissions(self):
636 self.g1 = _make_group('group1', skip_if_exists=True)
636 self.g1 = _make_group('group1', skip_if_exists=True)
637 self.g2 = _make_group('group2', skip_if_exists=True)
637 self.g2 = _make_group('group2', skip_if_exists=True)
638 Session.commit()
638 Session.commit()
639 # both perms should be read !
639 # both perms should be read !
640 u1_auth = AuthUser(user_id=self.u1.user_id)
640 u1_auth = AuthUser(user_id=self.u1.user_id)
641 self.assertEqual(u1_auth.permissions['repositories_groups'],
641 self.assertEqual(u1_auth.permissions['repositories_groups'],
642 {u'group1': u'group.read', u'group2': u'group.read'})
642 {u'group1': u'group.read', u'group2': u'group.read'})
643
643
644 a1_auth = AuthUser(user_id=self.anon.user_id)
644 a1_auth = AuthUser(user_id=self.anon.user_id)
645 self.assertEqual(a1_auth.permissions['repositories_groups'],
645 self.assertEqual(a1_auth.permissions['repositories_groups'],
646 {u'group1': u'group.read', u'group2': u'group.read'})
646 {u'group1': u'group.read', u'group2': u'group.read'})
647
647
648 #Change perms to none for both groups
648 #Change perms to none for both groups
649 ReposGroupModel().grant_user_permission(repos_group=self.g1,
649 ReposGroupModel().grant_user_permission(repos_group=self.g1,
650 user=self.anon,
650 user=self.anon,
651 perm='group.none')
651 perm='group.none')
652 ReposGroupModel().grant_user_permission(repos_group=self.g2,
652 ReposGroupModel().grant_user_permission(repos_group=self.g2,
653 user=self.anon,
653 user=self.anon,
654 perm='group.none')
654 perm='group.none')
655
655
656
656
657 u1_auth = AuthUser(user_id=self.u1.user_id)
657 u1_auth = AuthUser(user_id=self.u1.user_id)
658 self.assertEqual(u1_auth.permissions['repositories_groups'],
658 self.assertEqual(u1_auth.permissions['repositories_groups'],
659 {u'group1': u'group.none', u'group2': u'group.none'})
659 {u'group1': u'group.none', u'group2': u'group.none'})
660
660
661 a1_auth = AuthUser(user_id=self.anon.user_id)
661 a1_auth = AuthUser(user_id=self.anon.user_id)
662 self.assertEqual(a1_auth.permissions['repositories_groups'],
662 self.assertEqual(a1_auth.permissions['repositories_groups'],
663 {u'group1': u'group.none', u'group2': u'group.none'})
663 {u'group1': u'group.none', u'group2': u'group.none'})
664
664
665 # add repo to group
665 # add repo to group
666 form_data = {
666 form_data = {
667 'repo_name':HG_REPO,
667 'repo_name':HG_REPO,
668 'repo_name_full':RepoGroup.url_sep().join([self.g1.group_name,HG_REPO]),
668 'repo_name_full':RepoGroup.url_sep().join([self.g1.group_name,HG_REPO]),
669 'repo_type':'hg',
669 'repo_type':'hg',
670 'clone_uri':'',
670 'clone_uri':'',
671 'repo_group':self.g1.group_id,
671 'repo_group':self.g1.group_id,
672 'description':'desc',
672 'description':'desc',
673 'private':False
673 'private':False
674 }
674 }
675 self.test_repo = RepoModel().create(form_data, cur_user=self.u1)
675 self.test_repo = RepoModel().create(form_data, cur_user=self.u1)
676 Session.commit()
676 Session.commit()
677
677
678 u1_auth = AuthUser(user_id=self.u1.user_id)
678 u1_auth = AuthUser(user_id=self.u1.user_id)
679 self.assertEqual(u1_auth.permissions['repositories_groups'],
679 self.assertEqual(u1_auth.permissions['repositories_groups'],
680 {u'group1': u'group.none', u'group2': u'group.none'})
680 {u'group1': u'group.none', u'group2': u'group.none'})
681
681
682 a1_auth = AuthUser(user_id=self.anon.user_id)
682 a1_auth = AuthUser(user_id=self.anon.user_id)
683 self.assertEqual(a1_auth.permissions['repositories_groups'],
683 self.assertEqual(a1_auth.permissions['repositories_groups'],
684 {u'group1': u'group.none', u'group2': u'group.none'})
684 {u'group1': u'group.none', u'group2': u'group.none'})
685
685
686 #grant permission for u2 !
686 #grant permission for u2 !
687 ReposGroupModel().grant_user_permission(repos_group=self.g1,
687 ReposGroupModel().grant_user_permission(repos_group=self.g1,
688 user=self.u2,
688 user=self.u2,
689 perm='group.read')
689 perm='group.read')
690 ReposGroupModel().grant_user_permission(repos_group=self.g2,
690 ReposGroupModel().grant_user_permission(repos_group=self.g2,
691 user=self.u2,
691 user=self.u2,
692 perm='group.read')
692 perm='group.read')
693 Session.commit()
693 Session.commit()
694 self.assertNotEqual(self.u1, self.u2)
694 self.assertNotEqual(self.u1, self.u2)
695 #u1 and anon should have not change perms while u2 should !
695 #u1 and anon should have not change perms while u2 should !
696 u1_auth = AuthUser(user_id=self.u1.user_id)
696 u1_auth = AuthUser(user_id=self.u1.user_id)
697 self.assertEqual(u1_auth.permissions['repositories_groups'],
697 self.assertEqual(u1_auth.permissions['repositories_groups'],
698 {u'group1': u'group.none', u'group2': u'group.none'})
698 {u'group1': u'group.none', u'group2': u'group.none'})
699
699
700 u2_auth = AuthUser(user_id=self.u2.user_id)
700 u2_auth = AuthUser(user_id=self.u2.user_id)
701 self.assertEqual(u2_auth.permissions['repositories_groups'],
701 self.assertEqual(u2_auth.permissions['repositories_groups'],
702 {u'group1': u'group.read', u'group2': u'group.read'})
702 {u'group1': u'group.read', u'group2': u'group.read'})
703
703
704 a1_auth = AuthUser(user_id=self.anon.user_id)
704 a1_auth = AuthUser(user_id=self.anon.user_id)
705 self.assertEqual(a1_auth.permissions['repositories_groups'],
705 self.assertEqual(a1_auth.permissions['repositories_groups'],
706 {u'group1': u'group.none', u'group2': u'group.none'})
706 {u'group1': u'group.none', u'group2': u'group.none'})
707
707
708 def test_repo_group_user_as_user_group_member(self):
708 def test_repo_group_user_as_user_group_member(self):
709 # create Group1
709 # create Group1
710 self.g1 = _make_group('group1', skip_if_exists=True)
710 self.g1 = _make_group('group1', skip_if_exists=True)
711 Session.commit()
711 Session.commit()
712 a1_auth = AuthUser(user_id=self.anon.user_id)
712 a1_auth = AuthUser(user_id=self.anon.user_id)
713
713
714 self.assertEqual(a1_auth.permissions['repositories_groups'],
714 self.assertEqual(a1_auth.permissions['repositories_groups'],
715 {u'group1': u'group.read'})
715 {u'group1': u'group.read'})
716
716
717 # set default permission to none
717 # set default permission to none
718 ReposGroupModel().grant_user_permission(repos_group=self.g1,
718 ReposGroupModel().grant_user_permission(repos_group=self.g1,
719 user=self.anon,
719 user=self.anon,
720 perm='group.none')
720 perm='group.none')
721 # make group
721 # make group
722 self.ug1 = UsersGroupModel().create('G1')
722 self.ug1 = UsersGroupModel().create('G1')
723 # add user to group
723 # add user to group
724 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
724 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
725 Session.commit()
725 Session.commit()
726
726
727 # check if user is in the group
727 # check if user is in the group
728 membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
728 membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
729 self.assertEqual(membrs, [self.u1.user_id])
729 self.assertEqual(membrs, [self.u1.user_id])
730 # add some user to that group
730 # add some user to that group
731
731
732 # check his permissions
732 # check his permissions
733 a1_auth = AuthUser(user_id=self.anon.user_id)
733 a1_auth = AuthUser(user_id=self.anon.user_id)
734 self.assertEqual(a1_auth.permissions['repositories_groups'],
734 self.assertEqual(a1_auth.permissions['repositories_groups'],
735 {u'group1': u'group.none'})
735 {u'group1': u'group.none'})
736
736
737 u1_auth = AuthUser(user_id=self.u1.user_id)
737 u1_auth = AuthUser(user_id=self.u1.user_id)
738 self.assertEqual(u1_auth.permissions['repositories_groups'],
738 self.assertEqual(u1_auth.permissions['repositories_groups'],
739 {u'group1': u'group.none'})
739 {u'group1': u'group.none'})
740
740
741 # grant ug1 read permissions for
741 # grant ug1 read permissions for
742 ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
742 ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
743 group_name=self.ug1,
743 group_name=self.ug1,
744 perm='group.read')
744 perm='group.read')
745 Session.commit()
745 Session.commit()
746 # check if the
746 # check if the
747 obj = Session.query(UsersGroupRepoGroupToPerm)\
747 obj = Session.query(UsersGroupRepoGroupToPerm)\
748 .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
748 .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
749 .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
749 .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
750 .scalar()
750 .scalar()
751 self.assertEqual(obj.permission.permission_name, 'group.read')
751 self.assertEqual(obj.permission.permission_name, 'group.read')
752
752
753 a1_auth = AuthUser(user_id=self.anon.user_id)
753 a1_auth = AuthUser(user_id=self.anon.user_id)
754
754
755 self.assertEqual(a1_auth.permissions['repositories_groups'],
755 self.assertEqual(a1_auth.permissions['repositories_groups'],
756 {u'group1': u'group.none'})
756 {u'group1': u'group.none'})
757
757
758 u1_auth = AuthUser(user_id=self.u1.user_id)
758 u1_auth = AuthUser(user_id=self.u1.user_id)
759 self.assertEqual(u1_auth.permissions['repositories_groups'],
759 self.assertEqual(u1_auth.permissions['repositories_groups'],
760 {u'group1': u'group.read'})
760 {u'group1': u'group.read'})
General Comments 0
You need to be logged in to leave comments. Login now