##// END OF EJS Templates
added action loggers to following repositories,...
marcink -
r735:dbec976d beta
parent child Browse files
Show More
@@ -1,290 +1,290 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # repos controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 7, 2010
22 22 admin controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from operator import itemgetter
27 27 from paste.httpexceptions import HTTPInternalServerError
28 28 from pylons import request, response, session, tmpl_context as c, url
29 29 from pylons.controllers.util import abort, redirect
30 30 from pylons.i18n.translation import _
31 31 from rhodecode.lib import helpers as h
32 32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
33 33 HasPermissionAnyDecorator
34 34 from rhodecode.lib.base import BaseController, render
35 35 from rhodecode.lib.utils import invalidate_cache, action_logger
36 36 from rhodecode.model.db import User
37 37 from rhodecode.model.forms import RepoForm
38 38 from rhodecode.model.scm import ScmModel
39 39 from rhodecode.model.repo import RepoModel
40 40 import formencode
41 41 import logging
42 42 import traceback
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 class ReposController(BaseController):
47 47 """REST Controller styled on the Atom Publishing Protocol"""
48 48 # To properly map this controller, ensure your config/routing.py
49 49 # file has a resource setup:
50 50 # map.resource('repo', 'repos')
51 51
52 52 @LoginRequired()
53 53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
54 54 def __before__(self):
55 55 c.admin_user = session.get('admin_user')
56 56 c.admin_username = session.get('admin_username')
57 57 super(ReposController, self).__before__()
58 58
59 59 @HasPermissionAllDecorator('hg.admin')
60 60 def index(self, format='html'):
61 61 """GET /repos: All items in the collection"""
62 62 # url('repos')
63 63 cached_repo_list = ScmModel().get_repos()
64 64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
65 65 return render('admin/repos/repos.html')
66 66
67 67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
68 68 def create(self):
69 69 """POST /repos: Create a new item"""
70 70 # url('repos')
71 71 repo_model = RepoModel()
72 72 _form = RepoForm()()
73 73 form_result = {}
74 74 try:
75 75 form_result = _form.to_python(dict(request.POST))
76 76 repo_model.create(form_result, c.rhodecode_user)
77 77 h.flash(_('created repository %s') % form_result['repo_name'],
78 78 category='success')
79 79
80 80 if request.POST.get('user_created'):
81 81 action_logger(self.rhodecode_user, 'user_created_repo',
82 82 form_result['repo_name'], '', self.sa)
83 83 else:
84 84 action_logger(self.rhodecode_user, 'admin_created_repo',
85 85 form_result['repo_name'], '', self.sa)
86 86
87 87 except formencode.Invalid, errors:
88 88 c.new_repo = errors.value['repo_name']
89 89
90 90 if request.POST.get('user_created'):
91 91 r = render('admin/repos/repo_add_create_repository.html')
92 92 else:
93 93 r = render('admin/repos/repo_add.html')
94 94
95 95 return htmlfill.render(
96 96 r,
97 97 defaults=errors.value,
98 98 errors=errors.error_dict or {},
99 99 prefix_error=False,
100 100 encoding="UTF-8")
101 101
102 102 except Exception:
103 103 log.error(traceback.format_exc())
104 104 msg = _('error occured during creation of repository %s') \
105 105 % form_result.get('repo_name')
106 106 h.flash(msg, category='error')
107 107 if request.POST.get('user_created'):
108 108 return redirect(url('home'))
109 109 return redirect(url('repos'))
110 110
111 111 @HasPermissionAllDecorator('hg.admin')
112 112 def new(self, format='html'):
113 113 """GET /repos/new: Form to create a new item"""
114 114 new_repo = request.GET.get('repo', '')
115 115 c.new_repo = h.repo_name_slug(new_repo)
116 116
117 117 return render('admin/repos/repo_add.html')
118 118
119 119 @HasPermissionAllDecorator('hg.admin')
120 120 def update(self, repo_name):
121 121 """PUT /repos/repo_name: Update an existing item"""
122 122 # Forms posted to this method should contain a hidden field:
123 123 # <input type="hidden" name="_method" value="PUT" />
124 124 # Or using helpers:
125 125 # h.form(url('repo', repo_name=ID),
126 126 # method='put')
127 127 # url('repo', repo_name=ID)
128 128 repo_model = RepoModel()
129 129 changed_name = repo_name
130 130 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
131 131
132 132 try:
133 133 form_result = _form.to_python(dict(request.POST))
134 134 repo_model.update(repo_name, form_result)
135 135 invalidate_cache('get_repo_cached_%s' % repo_name)
136 136 h.flash(_('Repository %s updated successfully' % repo_name),
137 137 category='success')
138 138 changed_name = form_result['repo_name']
139 139 action_logger(self.rhodecode_user, 'admin_updated_repo',
140 140 changed_name, '', self.sa)
141 141
142 142 except formencode.Invalid, errors:
143 c.repo_info = repo_model.get(repo_name)
143 c.repo_info = repo_model.get_by_repo_name(repo_name)
144 144 c.users_array = repo_model.get_users_js()
145 145 errors.value.update({'user':c.repo_info.user.username})
146 146 return htmlfill.render(
147 147 render('admin/repos/repo_edit.html'),
148 148 defaults=errors.value,
149 149 errors=errors.error_dict or {},
150 150 prefix_error=False,
151 151 encoding="UTF-8")
152 152
153 153 except Exception:
154 154 log.error(traceback.format_exc())
155 155 h.flash(_('error occurred during update of repository %s') \
156 156 % repo_name, category='error')
157 157
158 158 return redirect(url('edit_repo', repo_name=changed_name))
159 159
160 160 @HasPermissionAllDecorator('hg.admin')
161 161 def delete(self, repo_name):
162 162 """DELETE /repos/repo_name: Delete an existing item"""
163 163 # Forms posted to this method should contain a hidden field:
164 164 # <input type="hidden" name="_method" value="DELETE" />
165 165 # Or using helpers:
166 166 # h.form(url('repo', repo_name=ID),
167 167 # method='delete')
168 168 # url('repo', repo_name=ID)
169 169
170 170 repo_model = RepoModel()
171 repo = repo_model.get(repo_name)
171 repo = repo_model.get_by_repo_name(repo_name)
172 172 if not repo:
173 173 h.flash(_('%s repository is not mapped to db perhaps'
174 174 ' it was moved or renamed from the filesystem'
175 175 ' please run the application again'
176 176 ' in order to rescan repositories') % repo_name,
177 177 category='error')
178 178
179 179 return redirect(url('repos'))
180 180 try:
181 181 action_logger(self.rhodecode_user, 'admin_deleted_repo',
182 182 repo_name, '', self.sa)
183 183 repo_model.delete(repo)
184 184 invalidate_cache('get_repo_cached_%s' % repo_name)
185 185 h.flash(_('deleted repository %s') % repo_name, category='success')
186 186
187 187 except Exception, e:
188 188 log.error(traceback.format_exc())
189 189 h.flash(_('An error occured during deletion of %s') % repo_name,
190 190 category='error')
191 191
192 192 return redirect(url('repos'))
193 193
194 194 @HasPermissionAllDecorator('hg.admin')
195 195 def delete_perm_user(self, repo_name):
196 196 """
197 197 DELETE an existing repository permission user
198 198 :param repo_name:
199 199 """
200 200
201 201 try:
202 202 repo_model = RepoModel()
203 203 repo_model.delete_perm_user(request.POST, repo_name)
204 204 except Exception, e:
205 205 h.flash(_('An error occured during deletion of repository user'),
206 206 category='error')
207 207 raise HTTPInternalServerError()
208 208
209 209 @HasPermissionAllDecorator('hg.admin')
210 210 def repo_stats(self, repo_name):
211 211 """
212 212 DELETE an existing repository statistics
213 213 :param repo_name:
214 214 """
215 215
216 216 try:
217 217 repo_model = RepoModel()
218 218 repo_model.delete_stats(repo_name)
219 219 except Exception, e:
220 220 h.flash(_('An error occured during deletion of repository stats'),
221 221 category='error')
222 222 return redirect(url('edit_repo', repo_name=repo_name))
223 223
224 224 @HasPermissionAllDecorator('hg.admin')
225 225 def repo_cache(self, repo_name):
226 226 """
227 227 INVALIDATE exisitings repository cache
228 228 :param repo_name:
229 229 """
230 230
231 231 try:
232 232 ScmModel().mark_for_invalidation(repo_name)
233 233 except Exception, e:
234 234 h.flash(_('An error occurred during cache invalidation'),
235 235 category='error')
236 236 return redirect(url('edit_repo', repo_name=repo_name))
237 237
238 238 @HasPermissionAllDecorator('hg.admin')
239 239 def show(self, repo_name, format='html'):
240 240 """GET /repos/repo_name: Show a specific item"""
241 241 # url('repo', repo_name=ID)
242 242
243 243 @HasPermissionAllDecorator('hg.admin')
244 244 def edit(self, repo_name, format='html'):
245 245 """GET /repos/repo_name/edit: Form to edit an existing item"""
246 246 # url('edit_repo', repo_name=ID)
247 247 repo_model = RepoModel()
248 c.repo_info = repo = repo_model.get(repo_name)
248 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
249 249 if repo.stats:
250 250 last_rev = repo.stats.stat_on_revision
251 251 else:
252 252 last_rev = 0
253 253 c.stats_revision = last_rev
254 254 r = ScmModel().get(repo_name)
255 255 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
256 256
257 257 if last_rev == 0:
258 258 c.stats_percentage = 0
259 259 else:
260 260 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
261 261
262 262
263 263 if not repo:
264 264 h.flash(_('%s repository is not mapped to db perhaps'
265 265 ' it was created or renamed from the filesystem'
266 266 ' please run the application again'
267 267 ' in order to rescan repositories') % repo_name,
268 268 category='error')
269 269
270 270 return redirect(url('repos'))
271 271 defaults = c.repo_info.__dict__
272 272 if c.repo_info.user:
273 273 defaults.update({'user':c.repo_info.user.username})
274 274 else:
275 275 replacement_user = self.sa.query(User)\
276 276 .filter(User.admin == True).first().username
277 277 defaults.update({'user':replacement_user})
278 278
279 279 c.users_array = repo_model.get_users_js()
280 280
281 281 for p in c.repo_info.repo_to_perm:
282 282 defaults.update({'perm_%s' % p.user.username:
283 283 p.permission.permission_name})
284 284
285 285 return htmlfill.render(
286 286 render('admin/repos/repo_edit.html'),
287 287 defaults=defaults,
288 288 encoding="UTF-8",
289 289 force_defaults=False
290 290 )
@@ -1,84 +1,84 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # journal controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on November 21, 2010
22 22 journal controller for pylons
23 23 @author: marcink
24 24 """
25 25
26 26 from pylons import request, response, session, tmpl_context as c, url
27 27 from pylons.controllers.util import abort, redirect
28 28 from rhodecode.lib.auth import LoginRequired
29 29 from rhodecode.lib.base import BaseController, render
30 30 from rhodecode.lib.helpers import get_token
31 from rhodecode.lib.utils import action_logger
31 32 from rhodecode.model.db import UserLog, UserFollowing
32 33 from rhodecode.model.scm import ScmModel
33 34 import logging
34 35 from paste.httpexceptions import HTTPInternalServerError, HTTPNotFound
35 36
36 37 log = logging.getLogger(__name__)
37 38
38 39 class JournalController(BaseController):
39 40
40 41
41 42 @LoginRequired()
42 43 def __before__(self):
43 44 super(JournalController, self).__before__()
44 45
45 46 def index(self):
46 47 # Return a rendered template
47 48
48 49 c.following = self.sa.query(UserFollowing)\
49 50 .filter(UserFollowing.user_id == c.rhodecode_user.user_id).all()
50 51
51 52
52 53 c.journal = self.sa.query(UserLog)\
53 54 .order_by(UserLog.action_date.desc())\
54 55 .all()
55 56 return render('/journal.html')
56 57
57 58
58 59 def toggle_following(self):
59 print c.rhodecode_user
60 60
61 61 if request.POST.get('auth_token') == get_token():
62 62 scm_model = ScmModel()
63 63
64 64 user_id = request.POST.get('follows_user_id')
65 65 if user_id:
66 66 try:
67 67 scm_model.toggle_following_user(user_id,
68 68 c.rhodecode_user.user_id)
69 69 return 'ok'
70 70 except:
71 71 raise HTTPInternalServerError()
72 72
73 73 repo_id = request.POST.get('follows_repo_id')
74 74 if repo_id:
75 75 try:
76 76 scm_model.toggle_following_repo(repo_id,
77 77 c.rhodecode_user.user_id)
78 78 return 'ok'
79 79 except:
80 80 raise HTTPInternalServerError()
81 81
82 82
83 83
84 84 raise HTTPInternalServerError()
@@ -1,178 +1,178 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # settings controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on June 30, 2010
22 22 settings controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from pylons import tmpl_context as c, request, url
27 27 from pylons.controllers.util import redirect
28 28 from pylons.i18n.translation import _
29 29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 30 from rhodecode.lib.base import BaseController, render
31 31 from rhodecode.lib.utils import invalidate_cache, action_logger
32 32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
33 33 from rhodecode.model.repo import RepoModel
34 34 import formencode
35 35 import logging
36 36 import rhodecode.lib.helpers as h
37 37 import traceback
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41 class SettingsController(BaseController):
42 42
43 43 @LoginRequired()
44 44 @HasRepoPermissionAllDecorator('repository.admin')
45 45 def __before__(self):
46 46 super(SettingsController, self).__before__()
47 47
48 48 def index(self, repo_name):
49 49 repo_model = RepoModel()
50 c.repo_info = repo = repo_model.get(repo_name)
50 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
51 51 if not repo:
52 52 h.flash(_('%s repository is not mapped to db perhaps'
53 53 ' it was created or renamed from the filesystem'
54 54 ' please run the application again'
55 55 ' in order to rescan repositories') % repo_name,
56 56 category='error')
57 57
58 58 return redirect(url('home'))
59 59 defaults = c.repo_info.__dict__
60 60 defaults.update({'user':c.repo_info.user.username})
61 61 c.users_array = repo_model.get_users_js()
62 62
63 63 for p in c.repo_info.repo_to_perm:
64 64 defaults.update({'perm_%s' % p.user.username:
65 65 p.permission.permission_name})
66 66
67 67 return htmlfill.render(
68 68 render('settings/repo_settings.html'),
69 69 defaults=defaults,
70 70 encoding="UTF-8",
71 71 force_defaults=False
72 72 )
73 73
74 74 def update(self, repo_name):
75 75 repo_model = RepoModel()
76 76 changed_name = repo_name
77 77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
78 78 try:
79 79 form_result = _form.to_python(dict(request.POST))
80 80 repo_model.update(repo_name, form_result)
81 81 invalidate_cache('get_repo_cached_%s' % repo_name)
82 82 h.flash(_('Repository %s updated successfully' % repo_name),
83 83 category='success')
84 84 changed_name = form_result['repo_name']
85 85 action_logger(self.rhodecode_user, 'user_updated_repo',
86 86 changed_name, '', self.sa)
87 87 except formencode.Invalid, errors:
88 c.repo_info = repo_model.get(repo_name)
88 c.repo_info = repo_model.get_by_repo_name(repo_name)
89 89 c.users_array = repo_model.get_users_js()
90 90 errors.value.update({'user':c.repo_info.user.username})
91 91 return htmlfill.render(
92 92 render('settings/repo_settings.html'),
93 93 defaults=errors.value,
94 94 errors=errors.error_dict or {},
95 95 prefix_error=False,
96 96 encoding="UTF-8")
97 97 except Exception:
98 98 log.error(traceback.format_exc())
99 99 h.flash(_('error occurred during update of repository %s') \
100 100 % repo_name, category='error')
101 101
102 102 return redirect(url('repo_settings_home', repo_name=changed_name))
103 103
104 104
105 105
106 106 def delete(self, repo_name):
107 107 """DELETE /repos/repo_name: Delete an existing item"""
108 108 # Forms posted to this method should contain a hidden field:
109 109 # <input type="hidden" name="_method" value="DELETE" />
110 110 # Or using helpers:
111 111 # h.form(url('repo_settings_delete', repo_name=ID),
112 112 # method='delete')
113 113 # url('repo_settings_delete', repo_name=ID)
114 114
115 115 repo_model = RepoModel()
116 repo = repo_model.get(repo_name)
116 repo = repo_model.get_by_repo_name(repo_name)
117 117 if not repo:
118 118 h.flash(_('%s repository is not mapped to db perhaps'
119 119 ' it was moved or renamed from the filesystem'
120 120 ' please run the application again'
121 121 ' in order to rescan repositories') % repo_name,
122 122 category='error')
123 123
124 124 return redirect(url('home'))
125 125 try:
126 126 action_logger(self.rhodecode_user, 'user_deleted_repo',
127 127 repo_name, '', self.sa)
128 128 repo_model.delete(repo)
129 129 invalidate_cache('get_repo_cached_%s' % repo_name)
130 130 h.flash(_('deleted repository %s') % repo_name, category='success')
131 131 except Exception:
132 132 h.flash(_('An error occurred during deletion of %s') % repo_name,
133 133 category='error')
134 134
135 135 return redirect(url('home'))
136 136
137 137 def fork(self, repo_name):
138 138 repo_model = RepoModel()
139 c.repo_info = repo = repo_model.get(repo_name)
139 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
140 140 if not repo:
141 141 h.flash(_('%s repository is not mapped to db perhaps'
142 142 ' it was created or renamed from the filesystem'
143 143 ' please run the application again'
144 144 ' in order to rescan repositories') % repo_name,
145 145 category='error')
146 146
147 147 return redirect(url('home'))
148 148
149 149 return render('settings/repo_fork.html')
150 150
151 151
152 152
153 153 def fork_create(self, repo_name):
154 154 repo_model = RepoModel()
155 c.repo_info = repo_model.get(repo_name)
155 c.repo_info = repo_model.get_by_repo_name(repo_name)
156 156 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
157 157 form_result = {}
158 158 try:
159 159 form_result = _form.to_python(dict(request.POST))
160 160 form_result.update({'repo_name':repo_name})
161 161 repo_model.create_fork(form_result, c.rhodecode_user)
162 162 h.flash(_('forked %s repository as %s') \
163 163 % (repo_name, form_result['fork_name']),
164 164 category='success')
165 165 action_logger(self.rhodecode_user,
166 166 'user_forked_repo:%s' % form_result['fork_name'],
167 167 repo_name, '', self.sa)
168 168 except formencode.Invalid, errors:
169 169 c.new_repo = errors.value['fork_name']
170 170 r = render('settings/repo_fork.html')
171 171
172 172 return htmlfill.render(
173 173 r,
174 174 defaults=errors.value,
175 175 errors=errors.error_dict or {},
176 176 prefix_error=False,
177 177 encoding="UTF-8")
178 178 return redirect(url('home'))
@@ -1,508 +1,511 b''
1 1 """Helper functions
2 2
3 3 Consists of functions to typically be used within templates, but also
4 4 available to Controllers. This module is available to both as 'h'.
5 5 """
6 6 import random
7 7 import hashlib
8 8 from pygments.formatters import HtmlFormatter
9 9 from pygments import highlight as code_highlight
10 10 from pylons import url, app_globals as g
11 11 from pylons.i18n.translation import _, ungettext
12 12 from vcs.utils.annotate import annotate_highlight
13 13 from webhelpers.html import literal, HTML, escape
14 14 from webhelpers.html.tools import *
15 15 from webhelpers.html.builder import make_tag
16 16 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
17 17 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
18 18 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
19 19 password, textarea, title, ul, xml_declaration, radio
20 20 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
21 21 mail_to, strip_links, strip_tags, tag_re
22 22 from webhelpers.number import format_byte_size, format_bit_size
23 23 from webhelpers.pylonslib import Flash as _Flash
24 24 from webhelpers.pylonslib.secure_form import secure_form
25 25 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
26 26 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
27 27 replace_whitespace, urlify, truncate, wrap_paragraphs
28 28 from webhelpers.date import time_ago_in_words
29 29
30 30 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
31 31 convert_boolean_attrs, NotGiven
32 32
33 33 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
34 34 _set_input_attrs(attrs, type, name, value)
35 35 _set_id_attr(attrs, id, name)
36 36 convert_boolean_attrs(attrs, ["disabled"])
37 37 return HTML.input(**attrs)
38 38
39 39 reset = _reset
40 40
41 41
42 42 def get_token():
43 43 """Return the current authentication token, creating one if one doesn't
44 44 already exist.
45 45 """
46 46 token_key = "_authentication_token"
47 47 from pylons import session
48 48 if not token_key in session:
49 49 try:
50 50 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
51 51 except AttributeError: # Python < 2.4
52 52 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
53 53 session[token_key] = token
54 54 if hasattr(session, 'save'):
55 55 session.save()
56 56 return session[token_key]
57 57
58 58
59 59 #Custom helpers here :)
60 60 class _Link(object):
61 61 '''
62 62 Make a url based on label and url with help of url_for
63 63 :param label:name of link if not defined url is used
64 64 :param url: the url for link
65 65 '''
66 66
67 67 def __call__(self, label='', *url_, **urlargs):
68 68 if label is None or '':
69 69 label = url
70 70 link_fn = link_to(label, url(*url_, **urlargs))
71 71 return link_fn
72 72
73 73 link = _Link()
74 74
75 75 class _GetError(object):
76 76
77 77 def __call__(self, field_name, form_errors):
78 78 tmpl = """<span class="error_msg">%s</span>"""
79 79 if form_errors and form_errors.has_key(field_name):
80 80 return literal(tmpl % form_errors.get(field_name))
81 81
82 82 get_error = _GetError()
83 83
84 84 def recursive_replace(str, replace=' '):
85 85 """
86 86 Recursive replace of given sign to just one instance
87 87 :param str: given string
88 88 :param replace:char to find and replace multiple instances
89 89
90 90 Examples::
91 91 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
92 92 'Mighty-Mighty-Bo-sstones'
93 93 """
94 94
95 95 if str.find(replace * 2) == -1:
96 96 return str
97 97 else:
98 98 str = str.replace(replace * 2, replace)
99 99 return recursive_replace(str, replace)
100 100
101 101 class _ToolTip(object):
102 102
103 103 def __call__(self, tooltip_title, trim_at=50):
104 104 """
105 105 Special function just to wrap our text into nice formatted autowrapped
106 106 text
107 107 :param tooltip_title:
108 108 """
109 109
110 110 return wrap_paragraphs(escape(tooltip_title), trim_at)\
111 111 .replace('\n', '<br/>')
112 112
113 113 def activate(self):
114 114 """
115 115 Adds tooltip mechanism to the given Html all tooltips have to have
116 116 set class tooltip and set attribute tooltip_title.
117 117 Then a tooltip will be generated based on that
118 118 All with yui js tooltip
119 119 """
120 120
121 121 js = '''
122 122 YAHOO.util.Event.onDOMReady(function(){
123 123 function toolTipsId(){
124 124 var ids = [];
125 125 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
126 126
127 127 for (var i = 0; i < tts.length; i++) {
128 128 //if element doesn't not have and id autgenerate one for tooltip
129 129
130 130 if (!tts[i].id){
131 131 tts[i].id='tt'+i*100;
132 132 }
133 133 ids.push(tts[i].id);
134 134 }
135 135 return ids
136 136 };
137 137 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
138 138 context: toolTipsId(),
139 139 monitorresize:false,
140 140 xyoffset :[0,0],
141 141 autodismissdelay:300000,
142 142 hidedelay:5,
143 143 showdelay:20,
144 144 });
145 145
146 146 //Mouse Over event disabled for new repositories since they don't
147 147 //have last commit message
148 148 myToolTips.contextMouseOverEvent.subscribe(
149 149 function(type, args) {
150 150 var context = args[0];
151 151 var txt = context.getAttribute('tooltip_title');
152 152 if(txt){
153 153 return true;
154 154 }
155 155 else{
156 156 return false;
157 157 }
158 158 });
159 159
160 160
161 161 // Set the text for the tooltip just before we display it. Lazy method
162 162 myToolTips.contextTriggerEvent.subscribe(
163 163 function(type, args) {
164 164
165 165
166 166 var context = args[0];
167 167
168 168 var txt = context.getAttribute('tooltip_title');
169 169 this.cfg.setProperty("text", txt);
170 170
171 171
172 172 // positioning of tooltip
173 173 var tt_w = this.element.clientWidth;
174 174 var tt_h = this.element.clientHeight;
175 175
176 176 var context_w = context.offsetWidth;
177 177 var context_h = context.offsetHeight;
178 178
179 179 var pos_x = YAHOO.util.Dom.getX(context);
180 180 var pos_y = YAHOO.util.Dom.getY(context);
181 181
182 182 var display_strategy = 'top';
183 183 var xy_pos = [0,0];
184 184 switch (display_strategy){
185 185
186 186 case 'top':
187 187 var cur_x = (pos_x+context_w/2)-(tt_w/2);
188 188 var cur_y = pos_y-tt_h-4;
189 189 xy_pos = [cur_x,cur_y];
190 190 break;
191 191 case 'bottom':
192 192 var cur_x = (pos_x+context_w/2)-(tt_w/2);
193 193 var cur_y = pos_y+context_h+4;
194 194 xy_pos = [cur_x,cur_y];
195 195 break;
196 196 case 'left':
197 197 var cur_x = (pos_x-tt_w-4);
198 198 var cur_y = pos_y-((tt_h/2)-context_h/2);
199 199 xy_pos = [cur_x,cur_y];
200 200 break;
201 201 case 'right':
202 202 var cur_x = (pos_x+context_w+4);
203 203 var cur_y = pos_y-((tt_h/2)-context_h/2);
204 204 xy_pos = [cur_x,cur_y];
205 205 break;
206 206 default:
207 207 var cur_x = (pos_x+context_w/2)-(tt_w/2);
208 208 var cur_y = pos_y-tt_h-4;
209 209 xy_pos = [cur_x,cur_y];
210 210 break;
211 211
212 212 }
213 213
214 214 this.cfg.setProperty("xy",xy_pos);
215 215
216 216 });
217 217
218 218 //Mouse out
219 219 myToolTips.contextMouseOutEvent.subscribe(
220 220 function(type, args) {
221 221 var context = args[0];
222 222
223 223 });
224 224 });
225 225 '''
226 226 return literal(js)
227 227
228 228 tooltip = _ToolTip()
229 229
230 230 class _FilesBreadCrumbs(object):
231 231
232 232 def __call__(self, repo_name, rev, paths):
233 233 url_l = [link_to(repo_name, url('files_home',
234 234 repo_name=repo_name,
235 235 revision=rev, f_path=''))]
236 236 paths_l = paths.split('/')
237 237
238 238 for cnt, p in enumerate(paths_l, 1):
239 239 if p != '':
240 240 url_l.append(link_to(p, url('files_home',
241 241 repo_name=repo_name,
242 242 revision=rev,
243 243 f_path='/'.join(paths_l[:cnt]))))
244 244
245 245 return literal('/'.join(url_l))
246 246
247 247 files_breadcrumbs = _FilesBreadCrumbs()
248 248 class CodeHtmlFormatter(HtmlFormatter):
249 249
250 250 def wrap(self, source, outfile):
251 251 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
252 252
253 253 def _wrap_code(self, source):
254 254 for cnt, it in enumerate(source, 1):
255 255 i, t = it
256 256 t = '<div id="#S-%s">%s</div>' % (cnt, t)
257 257 yield i, t
258 258 def pygmentize(filenode, **kwargs):
259 259 """
260 260 pygmentize function using pygments
261 261 :param filenode:
262 262 """
263 263 return literal(code_highlight(filenode.content,
264 264 filenode.lexer, CodeHtmlFormatter(**kwargs)))
265 265
266 266 def pygmentize_annotation(filenode, **kwargs):
267 267 """
268 268 pygmentize function for annotation
269 269 :param filenode:
270 270 """
271 271
272 272 color_dict = {}
273 273 def gen_color():
274 274 """generator for getting 10k of evenly distibuted colors using hsv color
275 275 and golden ratio.
276 276 """
277 277 import colorsys
278 278 n = 10000
279 279 golden_ratio = 0.618033988749895
280 280 h = 0.22717784590367374
281 281 #generate 10k nice web friendly colors in the same order
282 282 for c in xrange(n):
283 283 h += golden_ratio
284 284 h %= 1
285 285 HSV_tuple = [h, 0.95, 0.95]
286 286 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
287 287 yield map(lambda x:str(int(x * 256)), RGB_tuple)
288 288
289 289 cgenerator = gen_color()
290 290
291 291 def get_color_string(cs):
292 292 if color_dict.has_key(cs):
293 293 col = color_dict[cs]
294 294 else:
295 295 col = color_dict[cs] = cgenerator.next()
296 296 return "color: rgb(%s)! important;" % (', '.join(col))
297 297
298 298 def url_func(changeset):
299 299 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
300 300 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
301 301
302 302 tooltip_html = tooltip_html % (changeset.author,
303 303 changeset.date,
304 304 tooltip(changeset.message))
305 305 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
306 306 short_id(changeset.raw_id))
307 307 uri = link_to(
308 308 lnk_format,
309 309 url('changeset_home', repo_name=changeset.repository.name,
310 310 revision=changeset.raw_id),
311 311 style=get_color_string(changeset.raw_id),
312 312 class_='tooltip',
313 313 tooltip_title=tooltip_html
314 314 )
315 315
316 316 uri += '\n'
317 317 return uri
318 318 return literal(annotate_highlight(filenode, url_func, **kwargs))
319 319
320 320 def repo_name_slug(value):
321 321 """Return slug of name of repository
322 322 This function is called on each creation/modification
323 323 of repository to prevent bad names in repo
324 324 """
325 325 slug = remove_formatting(value)
326 326 slug = strip_tags(slug)
327 327
328 328 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
329 329 slug = slug.replace(c, '-')
330 330 slug = recursive_replace(slug, '-')
331 331 slug = collapse(slug, '-')
332 332 return slug
333 333
334 334 def get_changeset_safe(repo, rev):
335 335 from vcs.backends.base import BaseRepository
336 336 from vcs.exceptions import RepositoryError
337 337 if not isinstance(repo, BaseRepository):
338 338 raise Exception('You must pass an Repository '
339 339 'object as first argument got %s', type(repo))
340 340
341 341 try:
342 342 cs = repo.get_changeset(rev)
343 343 except RepositoryError:
344 344 from rhodecode.lib.utils import EmptyChangeset
345 345 cs = EmptyChangeset()
346 346 return cs
347 347
348 348
349 349 flash = _Flash()
350 350
351 351
352 352 #==============================================================================
353 353 # MERCURIAL FILTERS available via h.
354 354 #==============================================================================
355 355 from mercurial import util
356 356 from mercurial.templatefilters import person as _person
357 357
358 358
359 359
360 360 def _age(curdate):
361 361 """turns a datetime into an age string."""
362 362
363 363 if not curdate:
364 364 return ''
365 365
366 366 from datetime import timedelta, datetime
367 367
368 368 agescales = [("year", 3600 * 24 * 365),
369 369 ("month", 3600 * 24 * 30),
370 370 ("day", 3600 * 24),
371 371 ("hour", 3600),
372 372 ("minute", 60),
373 373 ("second", 1), ]
374 374
375 375 age = datetime.now() - curdate
376 376 age_seconds = (age.days * agescales[2][1]) + age.seconds
377 377 pos = 1
378 378 for scale in agescales:
379 379 if scale[1] <= age_seconds:
380 380 if pos == 6:pos = 5
381 381 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
382 382 pos += 1
383 383
384 384 return _('just now')
385 385
386 386 age = lambda x:_age(x)
387 387 capitalize = lambda x: x.capitalize()
388 388 email = util.email
389 389 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
390 390 person = lambda x: _person(x)
391 391 short_id = lambda x: x[:12]
392 392
393 393
394 394 def bool2icon(value):
395 395 """
396 396 Returns True/False values represented as small html image of true/false
397 397 icons
398 398 :param value: bool value
399 399 """
400 400
401 401 if value is True:
402 402 return HTML.tag('img', src="/images/icons/accept.png", alt=_('True'))
403 403
404 404 if value is False:
405 405 return HTML.tag('img', src="/images/icons/cancel.png", alt=_('False'))
406 406
407 407 return value
408 408
409 409
410 410 def action_parser(user_log):
411 411 """
412 412 This helper will map the specified string action into translated
413 413 fancy names with icons and links
414 414
415 415 @param action:
416 416 """
417 417 action = user_log.action
418 418 action_params = None
419 419
420 420 x = action.split(':')
421 421
422 422 if len(x) > 1:
423 423 action, action_params = x
424 424
425 425 def get_cs_links():
426 426 if action == 'push':
427 427 revs_limit = 5
428 428 revs = action_params.split(',')
429 429 cs_links = " " + ', '.join ([link(rev,
430 430 url('changeset_home',
431 431 repo_name=user_log.repository.repo_name,
432 432 revision=rev)) for rev in revs[:revs_limit] ])
433 433 if len(revs) > revs_limit:
434 434 html_tmpl = '<span title="%s"> %s </span>'
435 435 cs_links += html_tmpl % (', '.join(r for r in revs[revs_limit:]),
436 436 _('and %s more revisions') \
437 437 % (len(revs) - revs_limit))
438 438
439 439 return literal(cs_links)
440 440 return ''
441 441
442 442 def get_fork_name():
443 443 if action == 'user_forked_repo':
444 444 from rhodecode.model.scm import ScmModel
445 445 repo_name = action_params
446 446 repo = ScmModel().get(repo_name)
447 447 if repo is None:
448 448 return repo_name
449 449 return link_to(action_params, url('summary_home',
450 450 repo_name=repo.name,),
451 451 title=repo.dbrepo.description)
452 452 return ''
453 453 map = {'user_deleted_repo':_('User deleted repository'),
454 454 'user_created_repo':_('User created repository'),
455 455 'user_forked_repo':_('User forked repository as: ') + get_fork_name(),
456 456 'user_updated_repo':_('User updated repository'),
457 457 'admin_deleted_repo':_('Admin delete repository'),
458 458 'admin_created_repo':_('Admin created repository'),
459 459 'admin_forked_repo':_('Admin forked repository'),
460 460 'admin_updated_repo':_('Admin updated repository'),
461 461 'push':_('Pushed') + get_cs_links(),
462 'pull':_('Pulled'), }
462 'pull':_('Pulled'),
463 'started_following_repo':_('User started following repository'),
464 'stopped_following_repo':_('User stopped following repository'),
465 }
463 466
464 467 return map.get(action, action)
465 468
466 469
467 470 #==============================================================================
468 471 # PERMS
469 472 #==============================================================================
470 473 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
471 474 HasRepoPermissionAny, HasRepoPermissionAll
472 475
473 476 #==============================================================================
474 477 # GRAVATAR URL
475 478 #==============================================================================
476 479 import hashlib
477 480 import urllib
478 481 from pylons import request
479 482
480 483 def gravatar_url(email_address, size=30):
481 484 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
482 485 default = 'identicon'
483 486 baseurl_nossl = "http://www.gravatar.com/avatar/"
484 487 baseurl_ssl = "https://secure.gravatar.com/avatar/"
485 488 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
486 489
487 490
488 491 # construct the url
489 492 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
490 493 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
491 494
492 495 return gravatar_url
493 496
494 497 def safe_unicode(str):
495 498 """safe unicode function. In case of UnicodeDecode error we try to return
496 499 unicode with errors replace, if this failes we return unicode with
497 500 string_escape decoding """
498 501
499 502 try:
500 503 u_str = unicode(str)
501 504 except UnicodeDecodeError:
502 505 try:
503 506 u_str = unicode(str, 'utf-8', 'replace')
504 507 except UnicodeDecodeError:
505 508 #incase we have a decode error just represent as byte string
506 509 u_str = unicode(str(str).encode('string_escape'))
507 510
508 511 return u_str
@@ -1,580 +1,585 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Utilities for RhodeCode
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19 """
20 20 Created on April 18, 2010
21 21 Utilities for RhodeCode
22 22 @author: marcink
23 23 """
24 24
25 25 from UserDict import DictMixin
26 26 from mercurial import ui, config, hg
27 27 from mercurial.error import RepoError
28 28 from rhodecode.model import meta
29 29 from rhodecode.model.caching_query import FromCache
30 30 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, \
31 31 UserLog
32 32 from rhodecode.model.repo import RepoModel
33 33 from rhodecode.model.user import UserModel
34 34 from vcs.backends.base import BaseChangeset
35 35 from paste.script import command
36 36 import ConfigParser
37 37 from vcs.utils.lazy import LazyProperty
38 38 import traceback
39 39 import datetime
40 40 import logging
41 41 import os
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 def get_repo_slug(request):
47 47 return request.environ['pylons.routes_dict'].get('repo_name')
48 48
49 49 def is_mercurial(environ):
50 50 """
51 51 Returns True if request's target is mercurial server - header
52 52 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
53 53 """
54 54 http_accept = environ.get('HTTP_ACCEPT')
55 55 if http_accept and http_accept.startswith('application/mercurial'):
56 56 return True
57 57 return False
58 58
59 59 def is_git(environ):
60 60 """
61 61 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
62 62 then have git client version given.
63 63
64 64 :param environ:
65 65 """
66 66 http_user_agent = environ.get('HTTP_USER_AGENT')
67 67 if http_user_agent and http_user_agent.startswith('git'):
68 68 return True
69 69 return False
70 70
71 71 def action_logger(user, action, repo, ipaddr='', sa=None):
72 72 """
73 Action logger for various action made by users
73 Action logger for various actions made by users
74 74
75 :param user: user that made this action, can be a string unique username or
75 :param user: user that made this action, can be a unique username string or
76 76 object containing user_id attribute
77 77 :param action: action to log, should be on of predefined unique actions for
78 78 easy translations
79 :param repo: repository that action was made on
79 :param repo: string name of repository or object containing repo_id,
80 that action was made on
80 81 :param ipaddr: optional ip address from what the action was made
81 82 :param sa: optional sqlalchemy session
82 83
83 84 """
84 85
85 86 if not sa:
86 87 sa = meta.Session()
87 88
88 89 try:
90 um = UserModel()
89 91 if hasattr(user, 'user_id'):
90 92 user_obj = user
91 93 elif isinstance(user, basestring):
92 user_obj = UserModel().get_by_username(user, cache=False)
94 user_obj = um.get_by_username(user, cache=False)
93 95 else:
94 96 raise Exception('You have to provide user object or username')
95 97
96 98
97 if repo:
99 rm = RepoModel()
100 if hasattr(repo, 'repo_id'):
101 repo_obj = rm.get(repo.repo_id, cache=False)
102 repo_name = repo_obj.repo_name
103 elif isinstance(repo, basestring):
98 104 repo_name = repo.lstrip('/')
99
100 repository = RepoModel().get(repo_name, cache=False)
101 if not repository:
102 raise Exception('You have to provide valid repository')
105 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
103 106 else:
104 107 raise Exception('You have to provide repository to action logger')
105 108
106 109
107 110 user_log = UserLog()
108 111 user_log.user_id = user_obj.user_id
109 112 user_log.action = action
113
114 user_log.repository_id = repo_obj.repo_id
110 115 user_log.repository_name = repo_name
111 user_log.repository = repository
116
112 117 user_log.action_date = datetime.datetime.now()
113 118 user_log.user_ip = ipaddr
114 119 sa.add(user_log)
115 120 sa.commit()
116 121
117 122 log.info('Adding user %s, action %s on %s',
118 123 user_obj.username, action, repo)
119 124 except:
120 125 log.error(traceback.format_exc())
121 126 sa.rollback()
122 127
123 128 def get_repos(path, recursive=False, initial=False):
124 129 """
125 130 Scans given path for repos and return (name,(type,path)) tuple
126 131 :param prefix:
127 132 :param path:
128 133 :param recursive:
129 134 :param initial:
130 135 """
131 136 from vcs.utils.helpers import get_scm
132 137 from vcs.exceptions import VCSError
133 138
134 139 try:
135 140 scm = get_scm(path)
136 141 except:
137 142 pass
138 143 else:
139 144 raise Exception('The given path %s should not be a repository got %s',
140 145 path, scm)
141 146
142 147 for dirpath in os.listdir(path):
143 148 try:
144 149 yield dirpath, get_scm(os.path.join(path, dirpath))
145 150 except VCSError:
146 151 pass
147 152
148 153 if __name__ == '__main__':
149 154 get_repos('', '/home/marcink/workspace-python')
150 155
151 156
152 157 def check_repo_fast(repo_name, base_path):
153 158 if os.path.isdir(os.path.join(base_path, repo_name)):return False
154 159 return True
155 160
156 161 def check_repo(repo_name, base_path, verify=True):
157 162
158 163 repo_path = os.path.join(base_path, repo_name)
159 164
160 165 try:
161 166 if not check_repo_fast(repo_name, base_path):
162 167 return False
163 168 r = hg.repository(ui.ui(), repo_path)
164 169 if verify:
165 170 hg.verify(r)
166 171 #here we hnow that repo exists it was verified
167 172 log.info('%s repo is already created', repo_name)
168 173 return False
169 174 except RepoError:
170 175 #it means that there is no valid repo there...
171 176 log.info('%s repo is free for creation', repo_name)
172 177 return True
173 178
174 179 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
175 180 while True:
176 181 ok = raw_input(prompt)
177 182 if ok in ('y', 'ye', 'yes'): return True
178 183 if ok in ('n', 'no', 'nop', 'nope'): return False
179 184 retries = retries - 1
180 185 if retries < 0: raise IOError
181 186 print complaint
182 187
183 188 def get_hg_ui_cached():
184 189 try:
185 190 sa = meta.Session
186 191 ret = sa.query(RhodeCodeUi)\
187 192 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
188 193 .all()
189 194 except:
190 195 pass
191 196 finally:
192 197 meta.Session.remove()
193 198 return ret
194 199
195 200
196 201 def get_hg_settings():
197 202 try:
198 203 sa = meta.Session()
199 204 ret = sa.query(RhodeCodeSettings)\
200 205 .options(FromCache("sql_cache_short", "get_hg_settings"))\
201 206 .all()
202 207 except:
203 208 pass
204 209 finally:
205 210 meta.Session.remove()
206 211
207 212 if not ret:
208 213 raise Exception('Could not get application settings !')
209 214 settings = {}
210 215 for each in ret:
211 216 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
212 217
213 218 return settings
214 219
215 220 def get_hg_ui_settings():
216 221 try:
217 222 sa = meta.Session()
218 223 ret = sa.query(RhodeCodeUi).all()
219 224 except:
220 225 pass
221 226 finally:
222 227 meta.Session.remove()
223 228
224 229 if not ret:
225 230 raise Exception('Could not get application ui settings !')
226 231 settings = {}
227 232 for each in ret:
228 233 k = each.ui_key
229 234 v = each.ui_value
230 235 if k == '/':
231 236 k = 'root_path'
232 237
233 238 if k.find('.') != -1:
234 239 k = k.replace('.', '_')
235 240
236 241 if each.ui_section == 'hooks':
237 242 v = each.ui_active
238 243
239 244 settings[each.ui_section + '_' + k] = v
240 245
241 246 return settings
242 247
243 248 #propagated from mercurial documentation
244 249 ui_sections = ['alias', 'auth',
245 250 'decode/encode', 'defaults',
246 251 'diff', 'email',
247 252 'extensions', 'format',
248 253 'merge-patterns', 'merge-tools',
249 254 'hooks', 'http_proxy',
250 255 'smtp', 'patch',
251 256 'paths', 'profiling',
252 257 'server', 'trusted',
253 258 'ui', 'web', ]
254 259
255 260 def make_ui(read_from='file', path=None, checkpaths=True):
256 261 """
257 262 A function that will read python rc files or database
258 263 and make an mercurial ui object from read options
259 264
260 265 :param path: path to mercurial config file
261 266 :param checkpaths: check the path
262 267 :param read_from: read from 'file' or 'db'
263 268 """
264 269
265 270 baseui = ui.ui()
266 271
267 272 #clean the baseui object
268 273 baseui._ocfg = config.config()
269 274 baseui._ucfg = config.config()
270 275 baseui._tcfg = config.config()
271 276
272 277 if read_from == 'file':
273 278 if not os.path.isfile(path):
274 279 log.warning('Unable to read config file %s' % path)
275 280 return False
276 281 log.debug('reading hgrc from %s', path)
277 282 cfg = config.config()
278 283 cfg.read(path)
279 284 for section in ui_sections:
280 285 for k, v in cfg.items(section):
281 286 log.debug('settings ui from file[%s]%s:%s', section, k, v)
282 287 baseui.setconfig(section, k, v)
283 288
284 289
285 290 elif read_from == 'db':
286 291 hg_ui = get_hg_ui_cached()
287 292 for ui_ in hg_ui:
288 293 if ui_.ui_active:
289 294 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
290 295 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
291 296 return baseui
292 297
293 298
294 299 def set_rhodecode_config(config):
295 300 hgsettings = get_hg_settings()
296 301
297 302 for k, v in hgsettings.items():
298 303 config[k] = v
299 304
300 305 def invalidate_cache(cache_key, *args):
301 306 """
302 307 Puts cache invalidation task into db for
303 308 further global cache invalidation
304 309 """
305 310 from rhodecode.model.scm import ScmModel
306 311
307 312 if cache_key.startswith('get_repo_cached_'):
308 313 name = cache_key.split('get_repo_cached_')[-1]
309 314 ScmModel().mark_for_invalidation(name)
310 315
311 316 class EmptyChangeset(BaseChangeset):
312 317 """
313 318 An dummy empty changeset. It's possible to pass hash when creating
314 319 an EmptyChangeset
315 320 """
316 321
317 322 def __init__(self, cs='0' * 40):
318 323 self._empty_cs = cs
319 324 self.revision = -1
320 325 self.message = ''
321 326 self.author = ''
322 327 self.date = ''
323 328
324 329 @LazyProperty
325 330 def raw_id(self):
326 331 """
327 332 Returns raw string identifying this changeset, useful for web
328 333 representation.
329 334 """
330 335 return self._empty_cs
331 336
332 337 @LazyProperty
333 338 def short_id(self):
334 339 return self.raw_id[:12]
335 340
336 341 def get_file_changeset(self, path):
337 342 return self
338 343
339 344 def get_file_content(self, path):
340 345 return u''
341 346
342 347 def get_file_size(self, path):
343 348 return 0
344 349
345 350 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
346 351 """
347 352 maps all found repositories into db
348 353 """
349 354
350 355 sa = meta.Session()
351 356 rm = RepoModel()
352 357 user = sa.query(User).filter(User.admin == True).first()
353 358
354 359 for name, repo in initial_repo_list.items():
355 if not rm.get(name, cache=False):
360 if not rm.get_by_repo_name(name, cache=False):
356 361 log.info('repository %s not found creating default', name)
357 362
358 363 form_data = {
359 364 'repo_name':name,
360 365 'repo_type':repo.alias,
361 366 'description':repo.description \
362 367 if repo.description != 'unknown' else \
363 368 '%s repository' % name,
364 369 'private':False
365 370 }
366 371 rm.create(form_data, user, just_db=True)
367 372
368 373 if remove_obsolete:
369 374 #remove from database those repositories that are not in the filesystem
370 375 for repo in sa.query(Repository).all():
371 376 if repo.repo_name not in initial_repo_list.keys():
372 377 sa.delete(repo)
373 378 sa.commit()
374 379
375 380 class OrderedDict(dict, DictMixin):
376 381
377 382 def __init__(self, *args, **kwds):
378 383 if len(args) > 1:
379 384 raise TypeError('expected at most 1 arguments, got %d' % len(args))
380 385 try:
381 386 self.__end
382 387 except AttributeError:
383 388 self.clear()
384 389 self.update(*args, **kwds)
385 390
386 391 def clear(self):
387 392 self.__end = end = []
388 393 end += [None, end, end] # sentinel node for doubly linked list
389 394 self.__map = {} # key --> [key, prev, next]
390 395 dict.clear(self)
391 396
392 397 def __setitem__(self, key, value):
393 398 if key not in self:
394 399 end = self.__end
395 400 curr = end[1]
396 401 curr[2] = end[1] = self.__map[key] = [key, curr, end]
397 402 dict.__setitem__(self, key, value)
398 403
399 404 def __delitem__(self, key):
400 405 dict.__delitem__(self, key)
401 406 key, prev, next = self.__map.pop(key)
402 407 prev[2] = next
403 408 next[1] = prev
404 409
405 410 def __iter__(self):
406 411 end = self.__end
407 412 curr = end[2]
408 413 while curr is not end:
409 414 yield curr[0]
410 415 curr = curr[2]
411 416
412 417 def __reversed__(self):
413 418 end = self.__end
414 419 curr = end[1]
415 420 while curr is not end:
416 421 yield curr[0]
417 422 curr = curr[1]
418 423
419 424 def popitem(self, last=True):
420 425 if not self:
421 426 raise KeyError('dictionary is empty')
422 427 if last:
423 428 key = reversed(self).next()
424 429 else:
425 430 key = iter(self).next()
426 431 value = self.pop(key)
427 432 return key, value
428 433
429 434 def __reduce__(self):
430 435 items = [[k, self[k]] for k in self]
431 436 tmp = self.__map, self.__end
432 437 del self.__map, self.__end
433 438 inst_dict = vars(self).copy()
434 439 self.__map, self.__end = tmp
435 440 if inst_dict:
436 441 return (self.__class__, (items,), inst_dict)
437 442 return self.__class__, (items,)
438 443
439 444 def keys(self):
440 445 return list(self)
441 446
442 447 setdefault = DictMixin.setdefault
443 448 update = DictMixin.update
444 449 pop = DictMixin.pop
445 450 values = DictMixin.values
446 451 items = DictMixin.items
447 452 iterkeys = DictMixin.iterkeys
448 453 itervalues = DictMixin.itervalues
449 454 iteritems = DictMixin.iteritems
450 455
451 456 def __repr__(self):
452 457 if not self:
453 458 return '%s()' % (self.__class__.__name__,)
454 459 return '%s(%r)' % (self.__class__.__name__, self.items())
455 460
456 461 def copy(self):
457 462 return self.__class__(self)
458 463
459 464 @classmethod
460 465 def fromkeys(cls, iterable, value=None):
461 466 d = cls()
462 467 for key in iterable:
463 468 d[key] = value
464 469 return d
465 470
466 471 def __eq__(self, other):
467 472 if isinstance(other, OrderedDict):
468 473 return len(self) == len(other) and self.items() == other.items()
469 474 return dict.__eq__(self, other)
470 475
471 476 def __ne__(self, other):
472 477 return not self == other
473 478
474 479
475 480 #===============================================================================
476 481 # TEST FUNCTIONS AND CREATORS
477 482 #===============================================================================
478 483 def create_test_index(repo_location, full_index):
479 484 """Makes default test index
480 485 :param repo_location:
481 486 :param full_index:
482 487 """
483 488 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
484 489 from rhodecode.lib.pidlock import DaemonLock, LockHeld
485 490 import shutil
486 491
487 492 index_location = os.path.join(repo_location, 'index')
488 493 if os.path.exists(index_location):
489 494 shutil.rmtree(index_location)
490 495
491 496 try:
492 497 l = DaemonLock()
493 498 WhooshIndexingDaemon(index_location=index_location,
494 499 repo_location=repo_location)\
495 500 .run(full_index=full_index)
496 501 l.release()
497 502 except LockHeld:
498 503 pass
499 504
500 505 def create_test_env(repos_test_path, config):
501 506 """Makes a fresh database and
502 507 install test repository into tmp dir
503 508 """
504 509 from rhodecode.lib.db_manage import DbManage
505 510 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
506 511 HG_FORK, GIT_FORK, TESTS_TMP_PATH
507 512 import tarfile
508 513 import shutil
509 514 from os.path import dirname as dn, join as jn, abspath
510 515
511 516 log = logging.getLogger('TestEnvCreator')
512 517 # create logger
513 518 log.setLevel(logging.DEBUG)
514 519 log.propagate = True
515 520 # create console handler and set level to debug
516 521 ch = logging.StreamHandler()
517 522 ch.setLevel(logging.DEBUG)
518 523
519 524 # create formatter
520 525 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
521 526
522 527 # add formatter to ch
523 528 ch.setFormatter(formatter)
524 529
525 530 # add ch to logger
526 531 log.addHandler(ch)
527 532
528 533 #PART ONE create db
529 534 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
530 535 log.debug('making test db %s', dbname)
531 536
532 537 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
533 538 tests=True)
534 539 dbmanage.create_tables(override=True)
535 540 dbmanage.config_prompt(repos_test_path)
536 541 dbmanage.create_default_user()
537 542 dbmanage.admin_prompt()
538 543 dbmanage.create_permissions()
539 544 dbmanage.populate_default_permissions()
540 545
541 546 #PART TWO make test repo
542 547 log.debug('making test vcs repositories')
543 548
544 549 #remove old one from previos tests
545 550 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
546 551
547 552 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
548 553 log.debug('removing %s', r)
549 554 shutil.rmtree(jn(TESTS_TMP_PATH, r))
550 555
551 556 #CREATE DEFAULT HG REPOSITORY
552 557 cur_dir = dn(dn(abspath(__file__)))
553 558 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
554 559 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
555 560 tar.close()
556 561
557 562 class UpgradeDb(command.Command):
558 563 """Command used for paster to upgrade our database to newer version
559 564 """
560 565
561 566 max_args = 1
562 567 min_args = 1
563 568
564 569 usage = "CONFIG_FILE"
565 570 summary = "Upgrades current db to newer version given configuration file"
566 571 group_name = "RhodeCode"
567 572
568 573 parser = command.Command.standard_parser(verbose=True)
569 574
570 575 parser.add_option('--sql',
571 576 action='store_true',
572 577 dest='just_sql',
573 578 help="Prints upgrade sql for further investigation",
574 579 default=False)
575 580 def command(self):
576 581 config_name = self.args[0]
577 582 p = config_name.split('/')
578 583 root = '.' if len(p) == 1 else '/'.join(p[:-1])
579 584 config = ConfigParser.ConfigParser({'here':root})
580 585 config.read(config_name)
@@ -1,438 +1,438 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 from formencode import All
23 23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 24 Email, Bool, StringBoolean
25 25 from pylons import session
26 26 from pylons.i18n.translation import _
27 27 from rhodecode.lib.auth import authfunc, get_crypt_password
28 28 from rhodecode.lib.exceptions import LdapImportError
29 29 from rhodecode.model import meta
30 30 from rhodecode.model.user import UserModel
31 31 from rhodecode.model.repo import RepoModel
32 32 from rhodecode.model.db import User
33 33 from webhelpers.pylonslib.secure_form import authentication_token
34 34 from rhodecode import BACKENDS
35 35 import formencode
36 36 import logging
37 37 import os
38 38 import rhodecode.lib.helpers as h
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42 #this is needed to translate the messages using _() in validators
43 43 class State_obj(object):
44 44 _ = staticmethod(_)
45 45
46 46 #===============================================================================
47 47 # VALIDATORS
48 48 #===============================================================================
49 49 class ValidAuthToken(formencode.validators.FancyValidator):
50 50 messages = {'invalid_token':_('Token mismatch')}
51 51
52 52 def validate_python(self, value, state):
53 53
54 54 if value != authentication_token():
55 55 raise formencode.Invalid(self.message('invalid_token', state,
56 56 search_number=value), value, state)
57 57
58 58 def ValidUsername(edit, old_data):
59 59 class _ValidUsername(formencode.validators.FancyValidator):
60 60
61 61 def validate_python(self, value, state):
62 62 if value in ['default', 'new_user']:
63 63 raise formencode.Invalid(_('Invalid username'), value, state)
64 64 #check if user is unique
65 65 old_un = None
66 66 if edit:
67 67 old_un = UserModel().get(old_data.get('user_id')).username
68 68
69 69 if old_un != value or not edit:
70 70 if UserModel().get_by_username(value, cache=False):
71 71 raise formencode.Invalid(_('This username already exists') ,
72 72 value, state)
73 73
74 74 return _ValidUsername
75 75
76 76 class ValidPassword(formencode.validators.FancyValidator):
77 77
78 78 def to_python(self, value, state):
79 79
80 80 if value:
81 81
82 82 if value.get('password'):
83 83 try:
84 84 value['password'] = get_crypt_password(value['password'])
85 85 except UnicodeEncodeError:
86 86 e_dict = {'password':_('Invalid characters in password')}
87 87 raise formencode.Invalid('', value, state, error_dict=e_dict)
88 88
89 89 if value.get('password_confirmation'):
90 90 try:
91 91 value['password_confirmation'] = \
92 92 get_crypt_password(value['password_confirmation'])
93 93 except UnicodeEncodeError:
94 94 e_dict = {'password_confirmation':_('Invalid characters in password')}
95 95 raise formencode.Invalid('', value, state, error_dict=e_dict)
96 96
97 97 if value.get('new_password'):
98 98 try:
99 99 value['new_password'] = \
100 100 get_crypt_password(value['new_password'])
101 101 except UnicodeEncodeError:
102 102 e_dict = {'new_password':_('Invalid characters in password')}
103 103 raise formencode.Invalid('', value, state, error_dict=e_dict)
104 104
105 105 return value
106 106
107 107 class ValidPasswordsMatch(formencode.validators.FancyValidator):
108 108
109 109 def validate_python(self, value, state):
110 110
111 111 if value['password'] != value['password_confirmation']:
112 112 e_dict = {'password_confirmation':
113 113 _('Password do not match')}
114 114 raise formencode.Invalid('', value, state, error_dict=e_dict)
115 115
116 116 class ValidAuth(formencode.validators.FancyValidator):
117 117 messages = {
118 118 'invalid_password':_('invalid password'),
119 119 'invalid_login':_('invalid user name'),
120 120 'disabled_account':_('Your account is disabled')
121 121
122 122 }
123 123 #error mapping
124 124 e_dict = {'username':messages['invalid_login'],
125 125 'password':messages['invalid_password']}
126 126 e_dict_disable = {'username':messages['disabled_account']}
127 127
128 128 def validate_python(self, value, state):
129 129 password = value['password']
130 130 username = value['username']
131 131 user = UserModel().get_by_username(username)
132 132
133 133 if authfunc(None, username, password):
134 134 return value
135 135 else:
136 136 if user and user.active is False:
137 137 log.warning('user %s is disabled', username)
138 138 raise formencode.Invalid(self.message('disabled_account',
139 139 state=State_obj),
140 140 value, state,
141 141 error_dict=self.e_dict_disable)
142 142 else:
143 143 log.warning('user %s not authenticated', username)
144 144 raise formencode.Invalid(self.message('invalid_password',
145 145 state=State_obj), value, state,
146 146 error_dict=self.e_dict)
147 147
148 148 class ValidRepoUser(formencode.validators.FancyValidator):
149 149
150 150 def to_python(self, value, state):
151 151 sa = meta.Session()
152 152 try:
153 153 self.user_db = sa.query(User)\
154 154 .filter(User.active == True)\
155 155 .filter(User.username == value).one()
156 156 except Exception:
157 157 raise formencode.Invalid(_('This username is not valid'),
158 158 value, state)
159 159 finally:
160 160 meta.Session.remove()
161 161
162 162 return self.user_db.user_id
163 163
164 164 def ValidRepoName(edit, old_data):
165 165 class _ValidRepoName(formencode.validators.FancyValidator):
166 166
167 167 def to_python(self, value, state):
168 168 slug = h.repo_name_slug(value)
169 169 if slug in ['_admin']:
170 170 raise formencode.Invalid(_('This repository name is disallowed'),
171 171 value, state)
172 172 if old_data.get('repo_name') != value or not edit:
173 if RepoModel().get(slug, cache=False):
173 if RepoModel().get_by_repo_name(slug, cache=False):
174 174 raise formencode.Invalid(_('This repository already exists') ,
175 175 value, state)
176 176 return slug
177 177
178 178
179 179 return _ValidRepoName
180 180
181 181 def ValidForkType(old_data):
182 182 class _ValidForkType(formencode.validators.FancyValidator):
183 183
184 184 def to_python(self, value, state):
185 185 if old_data['repo_type'] != value:
186 186 raise formencode.Invalid(_('Fork have to be the same type as original'), value, state)
187 187 return value
188 188 return _ValidForkType
189 189
190 190 class ValidPerms(formencode.validators.FancyValidator):
191 191 messages = {'perm_new_user_name':_('This username is not valid')}
192 192
193 193 def to_python(self, value, state):
194 194 perms_update = []
195 195 perms_new = []
196 196 #build a list of permission to update and new permission to create
197 197 for k, v in value.items():
198 198 if k.startswith('perm_'):
199 199 if k.startswith('perm_new_user'):
200 200 new_perm = value.get('perm_new_user', False)
201 201 new_user = value.get('perm_new_user_name', False)
202 202 if new_user and new_perm:
203 203 if (new_user, new_perm) not in perms_new:
204 204 perms_new.append((new_user, new_perm))
205 205 else:
206 206 usr = k[5:]
207 207 if usr == 'default':
208 208 if value['private']:
209 209 #set none for default when updating to private repo
210 210 v = 'repository.none'
211 211 perms_update.append((usr, v))
212 212 value['perms_updates'] = perms_update
213 213 value['perms_new'] = perms_new
214 214 sa = meta.Session
215 215 for k, v in perms_new:
216 216 try:
217 217 self.user_db = sa.query(User)\
218 218 .filter(User.active == True)\
219 219 .filter(User.username == k).one()
220 220 except Exception:
221 221 msg = self.message('perm_new_user_name',
222 222 state=State_obj)
223 223 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
224 224 return value
225 225
226 226 class ValidSettings(formencode.validators.FancyValidator):
227 227
228 228 def to_python(self, value, state):
229 229 #settings form can't edit user
230 230 if value.has_key('user'):
231 231 del['value']['user']
232 232
233 233 return value
234 234
235 235 class ValidPath(formencode.validators.FancyValidator):
236 236 def to_python(self, value, state):
237 237
238 238 if not os.path.isdir(value):
239 239 msg = _('This is not a valid path')
240 240 raise formencode.Invalid(msg, value, state,
241 241 error_dict={'paths_root_path':msg})
242 242 return value
243 243
244 244 def UniqSystemEmail(old_data):
245 245 class _UniqSystemEmail(formencode.validators.FancyValidator):
246 246 def to_python(self, value, state):
247 247 if old_data.get('email') != value:
248 248 sa = meta.Session()
249 249 try:
250 250 user = sa.query(User).filter(User.email == value).scalar()
251 251 if user:
252 252 raise formencode.Invalid(_("That e-mail address is already taken") ,
253 253 value, state)
254 254 finally:
255 255 meta.Session.remove()
256 256
257 257 return value
258 258
259 259 return _UniqSystemEmail
260 260
261 261 class ValidSystemEmail(formencode.validators.FancyValidator):
262 262 def to_python(self, value, state):
263 263 sa = meta.Session
264 264 try:
265 265 user = sa.query(User).filter(User.email == value).scalar()
266 266 if user is None:
267 267 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
268 268 value, state)
269 269 finally:
270 270 meta.Session.remove()
271 271
272 272 return value
273 273
274 274 class LdapLibValidator(formencode.validators.FancyValidator):
275 275
276 276 def to_python(self, value, state):
277 277
278 278 try:
279 279 import ldap
280 280 except ImportError:
281 281 raise LdapImportError
282 282 return value
283 283
284 284 #===============================================================================
285 285 # FORMS
286 286 #===============================================================================
287 287 class LoginForm(formencode.Schema):
288 288 allow_extra_fields = True
289 289 filter_extra_fields = True
290 290 username = UnicodeString(
291 291 strip=True,
292 292 min=1,
293 293 not_empty=True,
294 294 messages={
295 295 'empty':_('Please enter a login'),
296 296 'tooShort':_('Enter a value %(min)i characters long or more')}
297 297 )
298 298
299 299 password = UnicodeString(
300 300 strip=True,
301 301 min=6,
302 302 not_empty=True,
303 303 messages={
304 304 'empty':_('Please enter a password'),
305 305 'tooShort':_('Enter %(min)i characters or more')}
306 306 )
307 307
308 308
309 309 #chained validators have access to all data
310 310 chained_validators = [ValidAuth]
311 311
312 312 def UserForm(edit=False, old_data={}):
313 313 class _UserForm(formencode.Schema):
314 314 allow_extra_fields = True
315 315 filter_extra_fields = True
316 316 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
317 317 if edit:
318 318 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
319 319 admin = StringBoolean(if_missing=False)
320 320 else:
321 321 password = All(UnicodeString(strip=True, min=6, not_empty=True))
322 322 active = StringBoolean(if_missing=False)
323 323 name = UnicodeString(strip=True, min=1, not_empty=True)
324 324 lastname = UnicodeString(strip=True, min=1, not_empty=True)
325 325 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
326 326
327 327 chained_validators = [ValidPassword]
328 328
329 329 return _UserForm
330 330
331 331 def RegisterForm(edit=False, old_data={}):
332 332 class _RegisterForm(formencode.Schema):
333 333 allow_extra_fields = True
334 334 filter_extra_fields = True
335 335 username = All(ValidUsername(edit, old_data), UnicodeString(strip=True, min=1, not_empty=True))
336 336 password = All(UnicodeString(strip=True, min=6, not_empty=True))
337 337 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
338 338 active = StringBoolean(if_missing=False)
339 339 name = UnicodeString(strip=True, min=1, not_empty=True)
340 340 lastname = UnicodeString(strip=True, min=1, not_empty=True)
341 341 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
342 342
343 343 chained_validators = [ValidPasswordsMatch, ValidPassword]
344 344
345 345 return _RegisterForm
346 346
347 347 def PasswordResetForm():
348 348 class _PasswordResetForm(formencode.Schema):
349 349 allow_extra_fields = True
350 350 filter_extra_fields = True
351 351 email = All(ValidSystemEmail(), Email(not_empty=True))
352 352 return _PasswordResetForm
353 353
354 354 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
355 355 class _RepoForm(formencode.Schema):
356 356 allow_extra_fields = True
357 357 filter_extra_fields = False
358 358 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
359 359 description = UnicodeString(strip=True, min=1, not_empty=True)
360 360 private = StringBoolean(if_missing=False)
361 361 repo_type = OneOf(supported_backends)
362 362 if edit:
363 363 user = All(Int(not_empty=True), ValidRepoUser)
364 364
365 365 chained_validators = [ValidPerms]
366 366 return _RepoForm
367 367
368 368 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
369 369 class _RepoForkForm(formencode.Schema):
370 370 allow_extra_fields = True
371 371 filter_extra_fields = False
372 372 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
373 373 description = UnicodeString(strip=True, min=1, not_empty=True)
374 374 private = StringBoolean(if_missing=False)
375 375 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
376 376 return _RepoForkForm
377 377
378 378 def RepoSettingsForm(edit=False, old_data={}):
379 379 class _RepoForm(formencode.Schema):
380 380 allow_extra_fields = True
381 381 filter_extra_fields = False
382 382 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
383 383 description = UnicodeString(strip=True, min=1, not_empty=True)
384 384 private = StringBoolean(if_missing=False)
385 385
386 386 chained_validators = [ValidPerms, ValidSettings]
387 387 return _RepoForm
388 388
389 389
390 390 def ApplicationSettingsForm():
391 391 class _ApplicationSettingsForm(formencode.Schema):
392 392 allow_extra_fields = True
393 393 filter_extra_fields = False
394 394 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
395 395 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
396 396
397 397 return _ApplicationSettingsForm
398 398
399 399 def ApplicationUiSettingsForm():
400 400 class _ApplicationUiSettingsForm(formencode.Schema):
401 401 allow_extra_fields = True
402 402 filter_extra_fields = False
403 403 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
404 404 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
405 405 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
406 406 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
407 407 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
408 408 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
409 409
410 410 return _ApplicationUiSettingsForm
411 411
412 412 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
413 413 class _DefaultPermissionsForm(formencode.Schema):
414 414 allow_extra_fields = True
415 415 filter_extra_fields = True
416 416 overwrite_default = StringBoolean(if_missing=False)
417 417 anonymous = OneOf(['True', 'False'], if_missing=False)
418 418 default_perm = OneOf(perms_choices)
419 419 default_register = OneOf(register_choices)
420 420 default_create = OneOf(create_choices)
421 421
422 422 return _DefaultPermissionsForm
423 423
424 424
425 425 def LdapSettingsForm():
426 426 class _LdapSettingsForm(formencode.Schema):
427 427 allow_extra_fields = True
428 428 filter_extra_fields = True
429 429 pre_validators = [LdapLibValidator]
430 430 ldap_active = StringBoolean(if_missing=False)
431 431 ldap_host = UnicodeString(strip=True,)
432 432 ldap_port = Number(strip=True,)
433 433 ldap_ldaps = StringBoolean(if_missing=False)
434 434 ldap_dn_user = UnicodeString(strip=True,)
435 435 ldap_dn_pass = UnicodeString(strip=True,)
436 436 ldap_base_dn = UnicodeString(strip=True,)
437 437
438 438 return _LdapSettingsForm
@@ -1,221 +1,235 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # model for handling repositories actions
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 # This program is free software; you can redistribute it and/or
6 6 # modify it under the terms of the GNU General Public License
7 7 # as published by the Free Software Foundation; version 2
8 8 # of the License or (at your opinion) any later version of the license.
9 9 #
10 10 # This program is distributed in the hope that it will be useful,
11 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 13 # GNU General Public License for more details.
14 14 #
15 15 # You should have received a copy of the GNU General Public License
16 16 # along with this program; if not, write to the Free Software
17 17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 18 # MA 02110-1301, USA.
19 19 """
20 20 Created on Jun 5, 2010
21 21 model for handling repositories actions
22 22 :author: marcink
23 23 """
24 24 from vcs.backends import get_repo, get_backend
25 25 from datetime import datetime
26 26 from pylons import app_globals as g
27 27 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
28 28 Statistics
29 29 from rhodecode.model.meta import Session
30 30 from rhodecode.model.user import UserModel
31 31 from rhodecode.model.caching_query import FromCache
32 32 import logging
33 33 import os
34 34 import shutil
35 35 import traceback
36 36 log = logging.getLogger(__name__)
37 37
38 38 class RepoModel(object):
39 39
40 40 def __init__(self):
41 41 self.sa = Session()
42 42
43 43 def get(self, repo_id, cache=False):
44 44 repo = self.sa.query(Repository)\
45 .filter(Repository.repo_name == repo_id)
45 .filter(Repository.repo_id == repo_id)
46 46
47 47 if cache:
48 48 repo = repo.options(FromCache("sql_cache_short",
49 "get_repo_%s" % repo))
49 "get_repo_%s" % repo_id))
50 50 return repo.scalar()
51 51
52
53 def get_by_repo_name(self, repo_name, cache=False):
54 repo = self.sa.query(Repository)\
55 .filter(Repository.repo_name == repo_name)
56
57 if cache:
58 repo = repo.options(FromCache("sql_cache_short",
59 "get_repo_%s" % repo_name))
60 return repo.scalar()
61
52 62 def get_users_js(self):
53 63
54 64 users = self.sa.query(User).filter(User.active == True).all()
55 65 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
56 66 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
57 67 u.lastname, u.username)
58 68 for u in users])
59 69 return users_array
60 70
61 71
62 72 def update(self, repo_name, form_data):
63 73 try:
64 74
65 75 #update permissions
66 76 for username, perm in form_data['perms_updates']:
67 77 r2p = self.sa.query(RepoToPerm)\
68 .filter(RepoToPerm.user == UserModel().get_by_username(username, cache=False))\
69 .filter(RepoToPerm.repository == self.get(repo_name))\
78 .filter(RepoToPerm.user == UserModel()\
79 .get_by_username(username, cache=False))\
80 .filter(RepoToPerm.repository == \
81 self.get_by_repo_name(repo_name))\
70 82 .one()
71 83
72 84 r2p.permission_id = self.sa.query(Permission).filter(
73 Permission.permission_name ==
85 Permission.permission_name ==
74 86 perm).one().permission_id
75 87 self.sa.add(r2p)
76 88
77 89 #set new permissions
78 90 for username, perm in form_data['perms_new']:
79 91 r2p = RepoToPerm()
80 r2p.repository = self.get(repo_name)
92 r2p.repository = self.get_by_repo_name(repo_name)
81 93 r2p.user = UserModel().get_by_username(username, cache=False)
82 94
83 95 r2p.permission_id = self.sa.query(Permission).filter(
84 96 Permission.permission_name == perm)\
85 97 .one().permission_id
86 98 self.sa.add(r2p)
87 99
88 100 #update current repo
89 cur_repo = self.get(repo_name, cache=False)
101 cur_repo = self.get_by_repo_name(repo_name, cache=False)
90 102
91 103 for k, v in form_data.items():
92 104 if k == 'user':
93 105 cur_repo.user_id = v
94 106 else:
95 107 setattr(cur_repo, k, v)
96 108
97 109 self.sa.add(cur_repo)
98 110
99 111 if repo_name != form_data['repo_name']:
100 112 #rename our data
101 113 self.__rename_repo(repo_name, form_data['repo_name'])
102 114
103 115 self.sa.commit()
104 116 except:
105 117 log.error(traceback.format_exc())
106 118 self.sa.rollback()
107 119 raise
108 120
109 121 def create(self, form_data, cur_user, just_db=False, fork=False):
110 122 try:
111 123 if fork:
112 124 #force str since hg doesn't go with unicode
113 125 repo_name = str(form_data['fork_name'])
114 126 org_name = str(form_data['repo_name'])
115 127
116 128 else:
117 129 org_name = repo_name = str(form_data['repo_name'])
118 130 new_repo = Repository()
119 131 for k, v in form_data.items():
120 132 if k == 'repo_name':
121 133 v = repo_name
122 134 setattr(new_repo, k, v)
123 135
124 136 if fork:
125 137 parent_repo = self.sa.query(Repository)\
126 138 .filter(Repository.repo_name == org_name).scalar()
127 139 new_repo.fork = parent_repo
128 140
129 141 new_repo.user_id = cur_user.user_id
130 142 self.sa.add(new_repo)
131 143
132 144 #create default permission
133 145 repo_to_perm = RepoToPerm()
134 146 default = 'repository.read'
135 147 for p in UserModel().get_by_username('default', cache=False).user_perms:
136 148 if p.permission.permission_name.startswith('repository.'):
137 149 default = p.permission.permission_name
138 150 break
139 151
140 152 default_perm = 'repository.none' if form_data['private'] else default
141 153
142 154 repo_to_perm.permission_id = self.sa.query(Permission)\
143 155 .filter(Permission.permission_name == default_perm)\
144 156 .one().permission_id
145 157
146 158 repo_to_perm.repository_id = new_repo.repo_id
147 159 repo_to_perm.user_id = UserModel().get_by_username('default', cache=False).user_id
148 160
149 161 self.sa.add(repo_to_perm)
150 162 self.sa.commit()
151 163 if not just_db:
152 164 self.__create_repo(repo_name, form_data['repo_type'])
153 165 except:
154 166 log.error(traceback.format_exc())
155 167 self.sa.rollback()
156 168 raise
157 169
158 170 def create_fork(self, form_data, cur_user):
159 171 from rhodecode.lib.celerylib import tasks, run_task
160 172 run_task(tasks.create_repo_fork, form_data, cur_user)
161 173
162 174 def delete(self, repo):
163 175 try:
164 176 self.sa.delete(repo)
165 177 self.__delete_repo(repo)
166 178 self.sa.commit()
167 179 except:
168 180 log.error(traceback.format_exc())
169 181 self.sa.rollback()
170 182 raise
171 183
172 184 def delete_perm_user(self, form_data, repo_name):
173 185 try:
174 186 self.sa.query(RepoToPerm)\
175 .filter(RepoToPerm.repository == self.get(repo_name))\
187 .filter(RepoToPerm.repository \
188 == self.get_by_repo_name(repo_name))\
176 189 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
177 190 self.sa.commit()
178 191 except:
179 192 log.error(traceback.format_exc())
180 193 self.sa.rollback()
181 194 raise
182 195
183 196 def delete_stats(self, repo_name):
184 197 try:
185 198 self.sa.query(Statistics)\
186 .filter(Statistics.repository == self.get(repo_name)).delete()
199 .filter(Statistics.repository == \
200 self.get_by_repo_name(repo_name)).delete()
187 201 self.sa.commit()
188 202 except:
189 203 log.error(traceback.format_exc())
190 204 self.sa.rollback()
191 205 raise
192 206
193 207
194 208 def __create_repo(self, repo_name, alias):
195 209 from rhodecode.lib.utils import check_repo
196 210 repo_path = os.path.join(g.base_path, repo_name)
197 211 if check_repo(repo_name, g.base_path):
198 212 log.info('creating repo %s in %s', repo_name, repo_path)
199 213 backend = get_backend(alias)
200 214 backend(repo_path, create=True)
201 215
202 216 def __rename_repo(self, old, new):
203 217 log.info('renaming repo from %s to %s', old, new)
204 218
205 219 old_path = os.path.join(g.base_path, old)
206 220 new_path = os.path.join(g.base_path, new)
207 221 if os.path.isdir(new_path):
208 222 raise Exception('Was trying to rename to already existing dir %s',
209 223 new_path)
210 224 shutil.move(old_path, new_path)
211 225
212 226 def __delete_repo(self, repo):
213 227 rm_path = os.path.join(g.base_path, repo.repo_name)
214 228 log.info("Removing %s", rm_path)
215 229 #disable hg/git
216 230 alias = repo.repo_type
217 231 shutil.move(os.path.join(rm_path, '.%s' % alias),
218 232 os.path.join(rm_path, 'rm__.%s' % alias))
219 233 #disable repo
220 234 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
221 235 % (datetime.today(), repo.repo_name)))
@@ -1,327 +1,343 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for RhodeCode
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 9, 2010
22 22 Model for RhodeCode
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region, region_invalidate
26 26 from mercurial import ui
27 27 from rhodecode import BACKENDS
28 28 from rhodecode.lib import helpers as h
29 29 from rhodecode.lib.auth import HasRepoPermissionAny
30 from rhodecode.lib.utils import get_repos, make_ui
30 from rhodecode.lib.utils import get_repos, make_ui, action_logger
31 31 from rhodecode.model import meta
32 32 from rhodecode.model.db import Repository, User, RhodeCodeUi, CacheInvalidation, \
33 33 UserFollowing
34 34 from rhodecode.model.caching_query import FromCache
35 35 from sqlalchemy.orm import joinedload
36 36 from sqlalchemy.orm.session import make_transient
37 37 from vcs import get_backend
38 38 from vcs.utils.helpers import get_scm
39 39 from vcs.exceptions import RepositoryError, VCSError
40 40 from vcs.utils.lazy import LazyProperty
41 41 import traceback
42 42 import logging
43 43 import os
44 44 import time
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 class UserTemp(object):
49 def __init__(self, user_id):
50 self.user_id = user_id
51
52 class RepoTemp(object):
53 def __init__(self, repo_id):
54 self.repo_id = repo_id
55
56
48 57 class ScmModel(object):
49 58 """
50 59 Mercurial Model
51 60 """
52 61
53 62 def __init__(self):
54 63 self.sa = meta.Session()
55 64
56 65 @LazyProperty
57 66 def repos_path(self):
58 67 """
59 68 Get's the repositories root path from database
60 69 """
61 70 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
62 71
63 72 return q.ui_value
64 73
65 74 def repo_scan(self, repos_path, baseui, initial=False):
66 75 """
67 76 Listing of repositories in given path. This path should not be a
68 77 repository itself. Return a dictionary of repository objects
69 78
70 79 :param repos_path: path to directory containing repositories
71 80 :param baseui
72 81 :param initial: initial scan
73 82 """
74 83 log.info('scanning for repositories in %s', repos_path)
75 84
76 85 if not isinstance(baseui, ui.ui):
77 86 baseui = make_ui('db')
78 87 repos_list = {}
79 88
80 89 for name, path in get_repos(repos_path):
81 90 try:
82 91 if repos_list.has_key(name):
83 92 raise RepositoryError('Duplicate repository name %s '
84 93 'found in %s' % (name, path))
85 94 else:
86 95
87 96 klass = get_backend(path[0])
88 97
89 98 if path[0] == 'hg' and path[0] in BACKENDS.keys():
90 99 repos_list[name] = klass(path[1], baseui=baseui)
91 100
92 101 if path[0] == 'git' and path[0] in BACKENDS.keys():
93 102 repos_list[name] = klass(path[1])
94 103 except OSError:
95 104 continue
96 105
97 106 return repos_list
98 107
99 108 def get_repos(self, all_repos=None):
100 109 """
101 110 Get all repos from db and for each repo create it's backend instance.
102 111 and fill that backed with information from database
103 112
104 113 :param all_repos: give specific repositories list, good for filtering
105 114 """
106 115 if not all_repos:
107 116 all_repos = self.sa.query(Repository)\
108 117 .order_by(Repository.repo_name).all()
109 118
110 119 invalidation_list = [str(x.cache_key) for x in \
111 120 self.sa.query(CacheInvalidation.cache_key)\
112 121 .filter(CacheInvalidation.cache_active == False)\
113 122 .all()]
114 123
115 124 for r in all_repos:
116 125
117 126 repo = self.get(r.repo_name, invalidation_list)
118 127
119 128 if repo is not None:
120 129 last_change = repo.last_change
121 130 tip = h.get_changeset_safe(repo, 'tip')
122 131
123 132 tmp_d = {}
124 133 tmp_d['name'] = repo.name
125 134 tmp_d['name_sort'] = tmp_d['name'].lower()
126 135 tmp_d['description'] = repo.dbrepo.description
127 136 tmp_d['description_sort'] = tmp_d['description']
128 137 tmp_d['last_change'] = last_change
129 138 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
130 139 tmp_d['tip'] = tip.raw_id
131 140 tmp_d['tip_sort'] = tip.revision
132 141 tmp_d['rev'] = tip.revision
133 142 tmp_d['contact'] = repo.dbrepo.user.full_contact
134 143 tmp_d['contact_sort'] = tmp_d['contact']
135 144 tmp_d['repo_archives'] = list(repo._get_archives())
136 145 tmp_d['last_msg'] = tip.message
137 146 tmp_d['repo'] = repo
138 147 yield tmp_d
139 148
140 149 def get_repo(self, repo_name):
141 150 return self.get(repo_name)
142 151
143 152 def get(self, repo_name, invalidation_list=None):
144 153 """
145 154 Get's repository from given name, creates BackendInstance and
146 155 propagates it's data from database with all additional information
147 156 :param repo_name:
148 157 """
149 158 if not HasRepoPermissionAny('repository.read', 'repository.write',
150 159 'repository.admin')(repo_name, 'get repo check'):
151 160 return
152 161
153 162 @cache_region('long_term')
154 163 def _get_repo(repo_name):
155 164
156 165 repo_path = os.path.join(self.repos_path, repo_name)
157 166 alias = get_scm(repo_path)[0]
158 167
159 168 log.debug('Creating instance of %s repository', alias)
160 169 backend = get_backend(alias)
161 170
162 171 #TODO: get the baseui from somewhere for this
163 172 if alias == 'hg':
164 173 from pylons import app_globals as g
165 174 repo = backend(repo_path, create=False, baseui=g.baseui)
166 175 #skip hidden web repository
167 176 if repo._get_hidden():
168 177 return
169 178 else:
170 179 repo = backend(repo_path, create=False)
171 180
172 181 dbrepo = self.sa.query(Repository)\
173 182 .options(joinedload(Repository.fork))\
174 183 .options(joinedload(Repository.user))\
175 184 .filter(Repository.repo_name == repo_name)\
176 185 .scalar()
177 186 make_transient(dbrepo)
178 187 repo.dbrepo = dbrepo
179 188 return repo
180 189
181 190 pre_invalidate = True
182 191 if invalidation_list:
183 192 pre_invalidate = repo_name in invalidation_list
184 193
185 194 if pre_invalidate:
186 195 invalidate = self._should_invalidate(repo_name)
187 196
188 197 if invalidate:
189 198 log.info('invalidating cache for repository %s', repo_name)
190 199 region_invalidate(_get_repo, None, repo_name)
191 200 self._mark_invalidated(invalidate)
192 201
193 202 return _get_repo(repo_name)
194 203
195 204
196 205
197 206 def mark_for_invalidation(self, repo_name):
198 207 """
199 208 Puts cache invalidation task into db for
200 209 further global cache invalidation
201 210
202 211 :param repo_name: this repo that should invalidation take place
203 212 """
204 213 log.debug('marking %s for invalidation', repo_name)
205 214 cache = self.sa.query(CacheInvalidation)\
206 215 .filter(CacheInvalidation.cache_key == repo_name).scalar()
207 216
208 217 if cache:
209 218 #mark this cache as inactive
210 219 cache.cache_active = False
211 220 else:
212 221 log.debug('cache key not found in invalidation db -> creating one')
213 222 cache = CacheInvalidation(repo_name)
214 223
215 224 try:
216 225 self.sa.add(cache)
217 226 self.sa.commit()
218 227 except:
219 228 log.error(traceback.format_exc())
220 229 self.sa.rollback()
221 230
222 231
223 232 def toggle_following_repo(self, follow_repo_id, user_id):
224 233
225 234 f = self.sa.query(UserFollowing)\
226 235 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
227 236 .filter(UserFollowing.user_id == user_id).scalar()
228 237
229 238 if f is not None:
239
230 240 try:
231 241 self.sa.delete(f)
232 242 self.sa.commit()
243 action_logger(UserTemp(user_id),
244 'stopped_following_repo',
245 RepoTemp(follow_repo_id))
233 246 return
234 247 except:
235 248 log.error(traceback.format_exc())
236 249 self.sa.rollback()
237 250 raise
238 251
239 252
240 253 try:
241 254 f = UserFollowing()
242 255 f.user_id = user_id
243 256 f.follows_repo_id = follow_repo_id
244 257 self.sa.add(f)
245 258 self.sa.commit()
259 action_logger(UserTemp(user_id),
260 'started_following_repo',
261 RepoTemp(follow_repo_id))
246 262 except:
247 263 log.error(traceback.format_exc())
248 264 self.sa.rollback()
249 265 raise
250 266
251 267 def toggle_following_user(self, follow_user_id , user_id):
252 268 f = self.sa.query(UserFollowing)\
253 269 .filter(UserFollowing.follows_user_id == follow_user_id)\
254 270 .filter(UserFollowing.user_id == user_id).scalar()
255 271
256 272 if f is not None:
257 273 try:
258 274 self.sa.delete(f)
259 275 self.sa.commit()
260 276 return
261 277 except:
262 278 log.error(traceback.format_exc())
263 279 self.sa.rollback()
264 280 raise
265 281
266 282 try:
267 283 f = UserFollowing()
268 284 f.user_id = user_id
269 285 f.follows_user_id = follow_user_id
270 286 self.sa.add(f)
271 287 self.sa.commit()
272 288 except:
273 289 log.error(traceback.format_exc())
274 290 self.sa.rollback()
275 291 raise
276 292
277 293 def is_following_repo(self, repo_name, user_id):
278 294 r = self.sa.query(Repository)\
279 295 .filter(Repository.repo_name == repo_name).scalar()
280 296
281 297 f = self.sa.query(UserFollowing)\
282 298 .filter(UserFollowing.follows_repository == r)\
283 299 .filter(UserFollowing.user_id == user_id).scalar()
284 300
285 301 return f is not None
286 302
287 303 def is_following_user(self, username, user_id):
288 304 u = self.sa.query(User)\
289 305 .filter(User.username == username).scalar()
290 306
291 307 f = self.sa.query(UserFollowing)\
292 308 .filter(UserFollowing.follows_user == u)\
293 309 .filter(UserFollowing.user_id == user_id).scalar()
294 310
295 311 return f is not None
296 312
297 313
298 314 def _should_invalidate(self, repo_name):
299 315 """
300 316 Looks up database for invalidation signals for this repo_name
301 317 :param repo_name:
302 318 """
303 319
304 320 ret = self.sa.query(CacheInvalidation)\
305 321 .options(FromCache('sql_cache_short',
306 322 'get_invalidation_%s' % repo_name))\
307 323 .filter(CacheInvalidation.cache_key == repo_name)\
308 324 .filter(CacheInvalidation.cache_active == False)\
309 325 .scalar()
310 326
311 327 return ret
312 328
313 329 def _mark_invalidated(self, cache_key):
314 330 """
315 331 Marks all occurences of cache to invaldation as already invalidated
316 332 @param repo_name:
317 333 """
318 334 if cache_key:
319 335 log.debug('marking %s as already invalidated', cache_key)
320 336 try:
321 337 cache_key.cache_active = True
322 338 self.sa.add(cache_key)
323 339 self.sa.commit()
324 340 except:
325 341 log.error(traceback.format_exc())
326 342 self.sa.rollback()
327 343
General Comments 0
You need to be logged in to leave comments. Login now