##// END OF EJS Templates
merge with default
marcink -
r863:4c123ade merge default
parent child Browse files
Show More
@@ -1,106 +1,105 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 package.rhodecode.controllers.admin.ldap_settings
4 ~~~~~~~~~~~~~~
3 rhodecode.controllers.admin.ldap_settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 ldap controller for RhodeCode
7
7 8 :created_on: Nov 26, 2010
8 9 :author: marcink
9 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 11 :license: GPLv3, see COPYING for more details.
11 12 """
12 13 # This program is free software; you can redistribute it and/or
13 14 # modify it under the terms of the GNU General Public License
14 15 # as published by the Free Software Foundation; version 2
15 16 # of the License or (at your opinion) any later version of the license.
16 17 #
17 18 # This program is distributed in the hope that it will be useful,
18 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 21 # GNU General Public License for more details.
21 22 #
22 23 # You should have received a copy of the GNU General Public License
23 24 # along with this program; if not, write to the Free Software
24 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 26 # MA 02110-1301, USA.
26 27 import logging
27 28 import formencode
28 29 import traceback
29 30
30 31 from formencode import htmlfill
31 32
32 33 from pylons import request, response, session, tmpl_context as c, url
33 34 from pylons.controllers.util import abort, redirect
34 35 from pylons.i18n.translation import _
35 36
36 37 from rhodecode.lib.base import BaseController, render
37 38 from rhodecode.lib import helpers as h
38 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
39 40 from rhodecode.lib.auth_ldap import LdapImportError
40 41 from rhodecode.model.settings import SettingsModel
41 42 from rhodecode.model.forms import LdapSettingsForm
42 43 from sqlalchemy.exc import DatabaseError
43 44
44 45 log = logging.getLogger(__name__)
45 46
46 47
47 48
48 49 class LdapSettingsController(BaseController):
49 50
50 51 @LoginRequired()
51 52 @HasPermissionAllDecorator('hg.admin')
52 53 def __before__(self):
53 54 c.admin_user = session.get('admin_user')
54 55 c.admin_username = session.get('admin_username')
55 56 super(LdapSettingsController, self).__before__()
56 57
57 58 def index(self):
58 59 defaults = SettingsModel().get_ldap_settings()
59 60
60 61 return htmlfill.render(
61 62 render('admin/ldap/ldap.html'),
62 63 defaults=defaults,
63 64 encoding="UTF-8",
64 65 force_defaults=True,)
65 66
66 67 def ldap_settings(self):
67 """
68 POST ldap create and store ldap settings
69 """
68 """POST ldap create and store ldap settings"""
70 69
71 70 settings_model = SettingsModel()
72 71 _form = LdapSettingsForm()()
73 72
74 73 try:
75 74 form_result = _form.to_python(dict(request.POST))
76 75 try:
77 76
78 77 for k, v in form_result.items():
79 78 if k.startswith('ldap_'):
80 79 setting = settings_model.get(k)
81 80 setting.app_settings_value = v
82 81 self.sa.add(setting)
83 82
84 83 self.sa.commit()
85 84 h.flash(_('Ldap settings updated successfully'),
86 85 category='success')
87 86 except (DatabaseError,):
88 87 raise
89 88 except LdapImportError:
90 89 h.flash(_('Unable to activate ldap. The "python-ldap" library '
91 90 'is missing.'), category='warning')
92 91
93 92 except formencode.Invalid, errors:
94 93
95 94 return htmlfill.render(
96 95 render('admin/ldap/ldap.html'),
97 96 defaults=errors.value,
98 97 errors=errors.error_dict or {},
99 98 prefix_error=False,
100 99 encoding="UTF-8")
101 100 except Exception:
102 101 log.error(traceback.format_exc())
103 h.flash(_('error occured during update of ldap settings'),
102 h.flash(_('error occurred during update of ldap settings'),
104 103 category='error')
105 104
106 105 return redirect(url('ldap_home'))
@@ -1,171 +1,171 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.permissions
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 permissions controller for Rhodecode
7 7
8 8 :created_on: Apr 27, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 from formencode import htmlfill
29 29 from pylons import request, session, tmpl_context as c, url
30 30 from pylons.controllers.util import abort, redirect
31 31 from pylons.i18n.translation import _
32 32 from rhodecode.lib import helpers as h
33 33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
34 34 from rhodecode.lib.auth_ldap import LdapImportError
35 35 from rhodecode.lib.base import BaseController, render
36 36 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
37 37 from rhodecode.model.permission import PermissionModel
38 38 from rhodecode.model.settings import SettingsModel
39 39 from rhodecode.model.user import UserModel
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 PermissionsController(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('permission', 'permissions')
51 51
52 52 @LoginRequired()
53 53 @HasPermissionAllDecorator('hg.admin')
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(PermissionsController, self).__before__()
58 58
59 59 self.perms_choices = [('repository.none', _('None'),),
60 60 ('repository.read', _('Read'),),
61 61 ('repository.write', _('Write'),),
62 62 ('repository.admin', _('Admin'),)]
63 63 self.register_choices = [
64 64 ('hg.register.none',
65 65 _('disabled')),
66 66 ('hg.register.manual_activate',
67 67 _('allowed with manual account activation')),
68 68 ('hg.register.auto_activate',
69 69 _('allowed with automatic account activation')), ]
70 70
71 71 self.create_choices = [('hg.create.none', _('Disabled')),
72 72 ('hg.create.repository', _('Enabled'))]
73 73
74 74
75 75 def index(self, format='html'):
76 76 """GET /permissions: All items in the collection"""
77 77 # url('permissions')
78 78
79 79 def create(self):
80 80 """POST /permissions: Create a new item"""
81 81 # url('permissions')
82 82
83 83 def new(self, format='html'):
84 84 """GET /permissions/new: Form to create a new item"""
85 85 # url('new_permission')
86 86
87 87 def update(self, id):
88 88 """PUT /permissions/id: Update an existing item"""
89 89 # Forms posted to this method should contain a hidden field:
90 90 # <input type="hidden" name="_method" value="PUT" />
91 91 # Or using helpers:
92 92 # h.form(url('permission', id=ID),
93 93 # method='put')
94 94 # url('permission', id=ID)
95 95
96 96 permission_model = PermissionModel()
97 97
98 98 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
99 99 [x[0] for x in self.register_choices],
100 100 [x[0] for x in self.create_choices])()
101 101
102 102 try:
103 103 form_result = _form.to_python(dict(request.POST))
104 104 form_result.update({'perm_user_name':id})
105 105 permission_model.update(form_result)
106 106 h.flash(_('Default permissions updated successfully'),
107 107 category='success')
108 108
109 109 except formencode.Invalid, errors:
110 110 c.perms_choices = self.perms_choices
111 111 c.register_choices = self.register_choices
112 112 c.create_choices = self.create_choices
113 113 defaults = errors.value
114 114
115 115 return htmlfill.render(
116 116 render('admin/permissions/permissions.html'),
117 117 defaults=defaults,
118 118 errors=errors.error_dict or {},
119 119 prefix_error=False,
120 120 encoding="UTF-8")
121 121 except Exception:
122 122 log.error(traceback.format_exc())
123 h.flash(_('error occured during update of permissions'),
123 h.flash(_('error occurred during update of permissions'),
124 124 category='error')
125 125
126 126 return redirect(url('edit_permission', id=id))
127 127
128 128
129 129
130 130 def delete(self, id):
131 131 """DELETE /permissions/id: Delete an existing item"""
132 132 # Forms posted to this method should contain a hidden field:
133 133 # <input type="hidden" name="_method" value="DELETE" />
134 134 # Or using helpers:
135 135 # h.form(url('permission', id=ID),
136 136 # method='delete')
137 137 # url('permission', id=ID)
138 138
139 139 def show(self, id, format='html'):
140 140 """GET /permissions/id: Show a specific item"""
141 141 # url('permission', id=ID)
142 142
143 143 def edit(self, id, format='html'):
144 144 """GET /permissions/id/edit: Form to edit an existing item"""
145 145 #url('edit_permission', id=ID)
146 146 c.perms_choices = self.perms_choices
147 147 c.register_choices = self.register_choices
148 148 c.create_choices = self.create_choices
149 149
150 150 if id == 'default':
151 151 default_user = UserModel().get_by_username('default')
152 152 defaults = {'_method':'put',
153 153 'anonymous':default_user.active}
154 154
155 155 for p in default_user.user_perms:
156 156 if p.permission.permission_name.startswith('repository.'):
157 157 defaults['default_perm'] = p.permission.permission_name
158 158
159 159 if p.permission.permission_name.startswith('hg.register.'):
160 160 defaults['default_register'] = p.permission.permission_name
161 161
162 162 if p.permission.permission_name.startswith('hg.create.'):
163 163 defaults['default_create'] = p.permission.permission_name
164 164
165 165 return htmlfill.render(
166 166 render('admin/permissions/permissions.html'),
167 167 defaults=defaults,
168 168 encoding="UTF-8",
169 169 force_defaults=True,)
170 170 else:
171 171 return redirect(url('admin_home'))
@@ -1,313 +1,313 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Admin controller for RhodeCode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import logging
29 29 import traceback
30 30 import formencode
31 31 from operator import itemgetter
32 32 from formencode import htmlfill
33 33
34 34 from paste.httpexceptions import HTTPInternalServerError
35 35 from pylons import request, response, session, tmpl_context as c, url
36 36 from pylons.controllers.util import abort, redirect
37 37 from pylons.i18n.translation import _
38 38
39 39 from rhodecode.lib import helpers as h
40 40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
41 41 HasPermissionAnyDecorator
42 42 from rhodecode.lib.base import BaseController, render
43 43 from rhodecode.lib.utils import invalidate_cache, action_logger
44 44 from rhodecode.model.db import User
45 45 from rhodecode.model.forms import RepoForm
46 46 from rhodecode.model.scm import ScmModel
47 47 from rhodecode.model.repo import RepoModel
48 48
49 49
50 50 log = logging.getLogger(__name__)
51 51
52 52 class ReposController(BaseController):
53 53 """REST Controller styled on the Atom Publishing Protocol"""
54 54 # To properly map this controller, ensure your config/routing.py
55 55 # file has a resource setup:
56 56 # map.resource('repo', 'repos')
57 57
58 58 @LoginRequired()
59 59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 60 def __before__(self):
61 61 c.admin_user = session.get('admin_user')
62 62 c.admin_username = session.get('admin_username')
63 63 super(ReposController, self).__before__()
64 64
65 65 @HasPermissionAllDecorator('hg.admin')
66 66 def index(self, format='html'):
67 67 """GET /repos: All items in the collection"""
68 68 # url('repos')
69 69 cached_repo_list = ScmModel().get_repos()
70 70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
71 71 return render('admin/repos/repos.html')
72 72
73 73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
74 74 def create(self):
75 75 """POST /repos: Create a new item"""
76 76 # url('repos')
77 77 repo_model = RepoModel()
78 78 _form = RepoForm()()
79 79 form_result = {}
80 80 try:
81 81 form_result = _form.to_python(dict(request.POST))
82 82 repo_model.create(form_result, c.rhodecode_user)
83 83 h.flash(_('created repository %s') % form_result['repo_name'],
84 84 category='success')
85 85
86 86 if request.POST.get('user_created'):
87 87 action_logger(self.rhodecode_user, 'user_created_repo',
88 88 form_result['repo_name'], '', self.sa)
89 89 else:
90 90 action_logger(self.rhodecode_user, 'admin_created_repo',
91 91 form_result['repo_name'], '', self.sa)
92 92
93 93 except formencode.Invalid, errors:
94 94 c.new_repo = errors.value['repo_name']
95 95
96 96 if request.POST.get('user_created'):
97 97 r = render('admin/repos/repo_add_create_repository.html')
98 98 else:
99 99 r = render('admin/repos/repo_add.html')
100 100
101 101 return htmlfill.render(
102 102 r,
103 103 defaults=errors.value,
104 104 errors=errors.error_dict or {},
105 105 prefix_error=False,
106 106 encoding="UTF-8")
107 107
108 108 except Exception:
109 109 log.error(traceback.format_exc())
110 msg = _('error occured during creation of repository %s') \
110 msg = _('error occurred during creation of repository %s') \
111 111 % form_result.get('repo_name')
112 112 h.flash(msg, category='error')
113 113 if request.POST.get('user_created'):
114 114 return redirect(url('home'))
115 115 return redirect(url('repos'))
116 116
117 117 @HasPermissionAllDecorator('hg.admin')
118 118 def new(self, format='html'):
119 119 """GET /repos/new: Form to create a new item"""
120 120 new_repo = request.GET.get('repo', '')
121 121 c.new_repo = h.repo_name_slug(new_repo)
122 122
123 123 return render('admin/repos/repo_add.html')
124 124
125 125 @HasPermissionAllDecorator('hg.admin')
126 126 def update(self, repo_name):
127 127 """PUT /repos/repo_name: Update an existing item"""
128 128 # Forms posted to this method should contain a hidden field:
129 129 # <input type="hidden" name="_method" value="PUT" />
130 130 # Or using helpers:
131 131 # h.form(url('repo', repo_name=ID),
132 132 # method='put')
133 133 # url('repo', repo_name=ID)
134 134 repo_model = RepoModel()
135 135 changed_name = repo_name
136 136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
137 137
138 138 try:
139 139 form_result = _form.to_python(dict(request.POST))
140 140 repo_model.update(repo_name, form_result)
141 141 invalidate_cache('get_repo_cached_%s' % repo_name)
142 142 h.flash(_('Repository %s updated successfully' % repo_name),
143 143 category='success')
144 144 changed_name = form_result['repo_name']
145 145 action_logger(self.rhodecode_user, 'admin_updated_repo',
146 146 changed_name, '', self.sa)
147 147
148 148 except formencode.Invalid, errors:
149 149 c.repo_info = repo_model.get_by_repo_name(repo_name)
150 150 if c.repo_info.stats:
151 151 last_rev = c.repo_info.stats.stat_on_revision
152 152 else:
153 153 last_rev = 0
154 154 c.stats_revision = last_rev
155 155 r = ScmModel().get(repo_name)
156 156 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
157 157
158 158 if last_rev == 0:
159 159 c.stats_percentage = 0
160 160 else:
161 161 c.stats_percentage = '%.2f' % ((float((last_rev)) /
162 162 c.repo_last_rev) * 100)
163 163
164 164 c.users_array = repo_model.get_users_js()
165 165 errors.value.update({'user':c.repo_info.user.username})
166 166 return htmlfill.render(
167 167 render('admin/repos/repo_edit.html'),
168 168 defaults=errors.value,
169 169 errors=errors.error_dict or {},
170 170 prefix_error=False,
171 171 encoding="UTF-8")
172 172
173 173 except Exception:
174 174 log.error(traceback.format_exc())
175 175 h.flash(_('error occurred during update of repository %s') \
176 176 % repo_name, category='error')
177 177
178 178 return redirect(url('edit_repo', repo_name=changed_name))
179 179
180 180 @HasPermissionAllDecorator('hg.admin')
181 181 def delete(self, repo_name):
182 182 """DELETE /repos/repo_name: Delete an existing item"""
183 183 # Forms posted to this method should contain a hidden field:
184 184 # <input type="hidden" name="_method" value="DELETE" />
185 185 # Or using helpers:
186 186 # h.form(url('repo', repo_name=ID),
187 187 # method='delete')
188 188 # url('repo', repo_name=ID)
189 189
190 190 repo_model = RepoModel()
191 191 repo = repo_model.get_by_repo_name(repo_name)
192 192 if not repo:
193 193 h.flash(_('%s repository is not mapped to db perhaps'
194 194 ' it was moved or renamed from the filesystem'
195 195 ' please run the application again'
196 196 ' in order to rescan repositories') % repo_name,
197 197 category='error')
198 198
199 199 return redirect(url('repos'))
200 200 try:
201 201 action_logger(self.rhodecode_user, 'admin_deleted_repo',
202 202 repo_name, '', self.sa)
203 203 repo_model.delete(repo)
204 204 invalidate_cache('get_repo_cached_%s' % repo_name)
205 205 h.flash(_('deleted repository %s') % repo_name, category='success')
206 206
207 207 except Exception, e:
208 208 log.error(traceback.format_exc())
209 h.flash(_('An error occured during deletion of %s') % repo_name,
209 h.flash(_('An error occurred during deletion of %s') % repo_name,
210 210 category='error')
211 211
212 212 return redirect(url('repos'))
213 213
214 214 @HasPermissionAllDecorator('hg.admin')
215 215 def delete_perm_user(self, repo_name):
216 216 """
217 217 DELETE an existing repository permission user
218 218 :param repo_name:
219 219 """
220 220
221 221 try:
222 222 repo_model = RepoModel()
223 223 repo_model.delete_perm_user(request.POST, repo_name)
224 224 except Exception, e:
225 h.flash(_('An error occured during deletion of repository user'),
225 h.flash(_('An error occurred during deletion of repository user'),
226 226 category='error')
227 227 raise HTTPInternalServerError()
228 228
229 229 @HasPermissionAllDecorator('hg.admin')
230 230 def repo_stats(self, repo_name):
231 231 """
232 232 DELETE an existing repository statistics
233 233 :param repo_name:
234 234 """
235 235
236 236 try:
237 237 repo_model = RepoModel()
238 238 repo_model.delete_stats(repo_name)
239 239 except Exception, e:
240 h.flash(_('An error occured during deletion of repository stats'),
240 h.flash(_('An error occurred during deletion of repository stats'),
241 241 category='error')
242 242 return redirect(url('edit_repo', repo_name=repo_name))
243 243
244 244 @HasPermissionAllDecorator('hg.admin')
245 245 def repo_cache(self, repo_name):
246 246 """
247 INVALIDATE exisitings repository cache
247 INVALIDATE existing repository cache
248 248 :param repo_name:
249 249 """
250 250
251 251 try:
252 252 ScmModel().mark_for_invalidation(repo_name)
253 253 except Exception, e:
254 254 h.flash(_('An error occurred during cache invalidation'),
255 255 category='error')
256 256 return redirect(url('edit_repo', repo_name=repo_name))
257 257
258 258 @HasPermissionAllDecorator('hg.admin')
259 259 def show(self, repo_name, format='html'):
260 260 """GET /repos/repo_name: Show a specific item"""
261 261 # url('repo', repo_name=ID)
262 262
263 263 @HasPermissionAllDecorator('hg.admin')
264 264 def edit(self, repo_name, format='html'):
265 265 """GET /repos/repo_name/edit: Form to edit an existing item"""
266 266 # url('edit_repo', repo_name=ID)
267 267 repo_model = RepoModel()
268 268 r = ScmModel().get(repo_name)
269 269 c.repo_info = repo_model.get_by_repo_name(repo_name)
270 270
271 271 if c.repo_info is None:
272 272 h.flash(_('%s repository is not mapped to db perhaps'
273 273 ' it was created or renamed from the filesystem'
274 274 ' please run the application again'
275 275 ' in order to rescan repositories') % repo_name,
276 276 category='error')
277 277
278 278 return redirect(url('repos'))
279 279
280 280 if c.repo_info.stats:
281 281 last_rev = c.repo_info.stats.stat_on_revision
282 282 else:
283 283 last_rev = 0
284 284 c.stats_revision = last_rev
285 285
286 286 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
287 287
288 288 if last_rev == 0:
289 289 c.stats_percentage = 0
290 290 else:
291 291 c.stats_percentage = '%.2f' % ((float((last_rev)) /
292 292 c.repo_last_rev) * 100)
293 293
294 294 defaults = c.repo_info.get_dict()
295 295 if c.repo_info.user:
296 296 defaults.update({'user':c.repo_info.user.username})
297 297 else:
298 298 replacement_user = self.sa.query(User)\
299 299 .filter(User.admin == True).first().username
300 300 defaults.update({'user':replacement_user})
301 301
302 302 c.users_array = repo_model.get_users_js()
303 303
304 304 for p in c.repo_info.repo_to_perm:
305 305 defaults.update({'perm_%s' % p.user.username:
306 306 p.permission.permission_name})
307 307
308 308 return htmlfill.render(
309 309 render('admin/repos/repo_edit.html'),
310 310 defaults=defaults,
311 311 encoding="UTF-8",
312 312 force_defaults=False
313 313 )
@@ -1,339 +1,340 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 package.rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5 6 settings controller for rhodecode admin
6 7
7 8 :created_on: Jul 14, 2010
8 9 :author: marcink
9 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 11 :license: GPLv3, see COPYING for more details.
11 12 """
12 13 # This program is free software; you can redistribute it and/or
13 14 # modify it under the terms of the GNU General Public License
14 15 # as published by the Free Software Foundation; version 2
15 16 # of the License or (at your opinion) any later version of the license.
16 17 #
17 18 # This program is distributed in the hope that it will be useful,
18 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 21 # GNU General Public License for more details.
21 22 #
22 23 # You should have received a copy of the GNU General Public License
23 24 # along with this program; if not, write to the Free Software
24 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 26 # MA 02110-1301, USA.
26 27
27 28 from formencode import htmlfill
28 29 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
29 30 config
30 31 from pylons.controllers.util import abort, redirect
31 32 from pylons.i18n.translation import _
32 33 from rhodecode.lib import helpers as h
33 34 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
34 35 HasPermissionAnyDecorator, NotAnonymous
35 36 from rhodecode.lib.base import BaseController, render
36 37 from rhodecode.lib.celerylib import tasks, run_task
37 38 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
38 39 set_rhodecode_config
39 40 from rhodecode.model.db import RhodeCodeUi, Repository
40 41 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
41 42 ApplicationUiSettingsForm
42 43 from rhodecode.model.scm import ScmModel
43 44 from rhodecode.model.settings import SettingsModel
44 45 from rhodecode.model.user import UserModel
45 46 from sqlalchemy import func
46 47 import formencode
47 48 import logging
48 49 import traceback
49 50
50 51 log = logging.getLogger(__name__)
51 52
52 53
53 54 class SettingsController(BaseController):
54 55 """REST Controller styled on the Atom Publishing Protocol"""
55 56 # To properly map this controller, ensure your config/routing.py
56 57 # file has a resource setup:
57 58 # map.resource('setting', 'settings', controller='admin/settings',
58 59 # path_prefix='/admin', name_prefix='admin_')
59 60
60 61
61 62 @LoginRequired()
62 63 def __before__(self):
63 64 c.admin_user = session.get('admin_user')
64 65 c.admin_username = session.get('admin_username')
65 66 super(SettingsController, self).__before__()
66 67
67 68
68 69 @HasPermissionAllDecorator('hg.admin')
69 70 def index(self, format='html'):
70 71 """GET /admin/settings: All items in the collection"""
71 72 # url('admin_settings')
72 73
73 74 defaults = SettingsModel().get_app_settings()
74 75 defaults.update(self.get_hg_ui_settings())
75 76 return htmlfill.render(
76 77 render('admin/settings/settings.html'),
77 78 defaults=defaults,
78 79 encoding="UTF-8",
79 80 force_defaults=False
80 81 )
81 82
82 83 @HasPermissionAllDecorator('hg.admin')
83 84 def create(self):
84 85 """POST /admin/settings: Create a new item"""
85 86 # url('admin_settings')
86 87
87 88 @HasPermissionAllDecorator('hg.admin')
88 89 def new(self, format='html'):
89 90 """GET /admin/settings/new: Form to create a new item"""
90 91 # url('admin_new_setting')
91 92
92 93 @HasPermissionAllDecorator('hg.admin')
93 94 def update(self, setting_id):
94 95 """PUT /admin/settings/setting_id: Update an existing item"""
95 96 # Forms posted to this method should contain a hidden field:
96 97 # <input type="hidden" name="_method" value="PUT" />
97 98 # Or using helpers:
98 99 # h.form(url('admin_setting', setting_id=ID),
99 100 # method='put')
100 101 # url('admin_setting', setting_id=ID)
101 102 if setting_id == 'mapping':
102 103 rm_obsolete = request.POST.get('destroy', False)
103 104 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
104 105
105 106 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
106 107 for repo_name in initial.keys():
107 108 invalidate_cache('get_repo_cached_%s' % repo_name)
108 109
109 110 repo2db_mapper(initial, rm_obsolete)
110 111
111 112 h.flash(_('Repositories successfully rescanned'), category='success')
112 113
113 114 if setting_id == 'whoosh':
114 115 repo_location = self.get_hg_ui_settings()['paths_root_path']
115 116 full_index = request.POST.get('full_index', False)
116 117 task = run_task(tasks.whoosh_index, repo_location, full_index)
117 118
118 119 h.flash(_('Whoosh reindex task scheduled'), category='success')
119 120 if setting_id == 'global':
120 121
121 122 application_form = ApplicationSettingsForm()()
122 123 try:
123 124 form_result = application_form.to_python(dict(request.POST))
124 125 settings_model = SettingsModel()
125 126 try:
126 127 hgsettings1 = settings_model.get('title')
127 128 hgsettings1.app_settings_value = form_result['rhodecode_title']
128 129
129 130 hgsettings2 = settings_model.get('realm')
130 131 hgsettings2.app_settings_value = form_result['rhodecode_realm']
131 132
132 133
133 134 self.sa.add(hgsettings1)
134 135 self.sa.add(hgsettings2)
135 136 self.sa.commit()
136 137 set_rhodecode_config(config)
137 138 h.flash(_('Updated application settings'),
138 139 category='success')
139 140
140 141 except:
141 142 log.error(traceback.format_exc())
142 143 h.flash(_('error occurred during updating application settings'),
143 144 category='error')
144 145
145 146 self.sa.rollback()
146 147
147 148
148 149 except formencode.Invalid, errors:
149 150 return htmlfill.render(
150 151 render('admin/settings/settings.html'),
151 152 defaults=errors.value,
152 153 errors=errors.error_dict or {},
153 154 prefix_error=False,
154 155 encoding="UTF-8")
155 156
156 157 if setting_id == 'mercurial':
157 158 application_form = ApplicationUiSettingsForm()()
158 159 try:
159 160 form_result = application_form.to_python(dict(request.POST))
160 161
161 162 try:
162 163
163 164 hgsettings1 = self.sa.query(RhodeCodeUi)\
164 165 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
165 166 hgsettings1.ui_value = form_result['web_push_ssl']
166 167
167 168 hgsettings2 = self.sa.query(RhodeCodeUi)\
168 169 .filter(RhodeCodeUi.ui_key == '/').one()
169 170 hgsettings2.ui_value = form_result['paths_root_path']
170 171
171 172
172 173 #HOOKS
173 174 hgsettings3 = self.sa.query(RhodeCodeUi)\
174 175 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
175 176 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
176 177
177 178 hgsettings4 = self.sa.query(RhodeCodeUi)\
178 179 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
179 180 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
180 181
181 182 hgsettings5 = self.sa.query(RhodeCodeUi)\
182 183 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
183 184 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
184 185
185 186 hgsettings6 = self.sa.query(RhodeCodeUi)\
186 187 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
187 188 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
188 189
189 190
190 191 self.sa.add(hgsettings1)
191 192 self.sa.add(hgsettings2)
192 193 self.sa.add(hgsettings3)
193 194 self.sa.add(hgsettings4)
194 195 self.sa.add(hgsettings5)
195 196 self.sa.add(hgsettings6)
196 197 self.sa.commit()
197 198
198 199 h.flash(_('Updated mercurial settings'),
199 200 category='success')
200 201
201 202 except:
202 203 log.error(traceback.format_exc())
203 204 h.flash(_('error occurred during updating application settings'),
204 205 category='error')
205 206
206 207 self.sa.rollback()
207 208
208 209
209 210 except formencode.Invalid, errors:
210 211 return htmlfill.render(
211 212 render('admin/settings/settings.html'),
212 213 defaults=errors.value,
213 214 errors=errors.error_dict or {},
214 215 prefix_error=False,
215 216 encoding="UTF-8")
216 217
217 218
218 219
219 220 return redirect(url('admin_settings'))
220 221
221 222 @HasPermissionAllDecorator('hg.admin')
222 223 def delete(self, setting_id):
223 224 """DELETE /admin/settings/setting_id: Delete an existing item"""
224 225 # Forms posted to this method should contain a hidden field:
225 226 # <input type="hidden" name="_method" value="DELETE" />
226 227 # Or using helpers:
227 228 # h.form(url('admin_setting', setting_id=ID),
228 229 # method='delete')
229 230 # url('admin_setting', setting_id=ID)
230 231
231 232 @HasPermissionAllDecorator('hg.admin')
232 233 def show(self, setting_id, format='html'):
233 234 """GET /admin/settings/setting_id: Show a specific item"""
234 235 # url('admin_setting', setting_id=ID)
235 236
236 237 @HasPermissionAllDecorator('hg.admin')
237 238 def edit(self, setting_id, format='html'):
238 239 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
239 240 # url('admin_edit_setting', setting_id=ID)
240 241
241 242 @NotAnonymous()
242 243 def my_account(self):
243 244 """
244 245 GET /_admin/my_account Displays info about my account
245 246 """
246 247 # url('admin_settings_my_account')
247 248
248 249 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
249 250 all_repos = self.sa.query(Repository)\
250 251 .filter(Repository.user_id == c.user.user_id)\
251 252 .order_by(func.lower(Repository.repo_name))\
252 253 .all()
253 254
254 255 c.user_repos = ScmModel().get_repos(all_repos)
255 256
256 257 if c.user.username == 'default':
257 258 h.flash(_("You can't edit this user since it's"
258 259 " crucial for entire application"), category='warning')
259 260 return redirect(url('users'))
260 261
261 262 defaults = c.user.get_dict()
262 263 return htmlfill.render(
263 264 render('admin/users/user_edit_my_account.html'),
264 265 defaults=defaults,
265 266 encoding="UTF-8",
266 267 force_defaults=False
267 268 )
268 269
269 270 def my_account_update(self):
270 271 """PUT /_admin/my_account_update: Update an existing item"""
271 272 # Forms posted to this method should contain a hidden field:
272 273 # <input type="hidden" name="_method" value="PUT" />
273 274 # Or using helpers:
274 275 # h.form(url('admin_settings_my_account_update'),
275 276 # method='put')
276 277 # url('admin_settings_my_account_update', id=ID)
277 278 user_model = UserModel()
278 279 uid = c.rhodecode_user.user_id
279 280 _form = UserForm(edit=True, old_data={'user_id':uid,
280 281 'email':c.rhodecode_user.email})()
281 282 form_result = {}
282 283 try:
283 284 form_result = _form.to_python(dict(request.POST))
284 285 user_model.update_my_account(uid, form_result)
285 286 h.flash(_('Your account was updated successfully'),
286 287 category='success')
287 288
288 289 except formencode.Invalid, errors:
289 290 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
290 291 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
291 292 all_repos = self.sa.query(Repository)\
292 293 .filter(Repository.user_id == c.user.user_id)\
293 294 .order_by(func.lower(Repository.repo_name))\
294 295 .all()
295 296 c.user_repos = ScmModel().get_repos(all_repos)
296 297
297 298 return htmlfill.render(
298 299 render('admin/users/user_edit_my_account.html'),
299 300 defaults=errors.value,
300 301 errors=errors.error_dict or {},
301 302 prefix_error=False,
302 303 encoding="UTF-8")
303 304 except Exception:
304 305 log.error(traceback.format_exc())
305 306 h.flash(_('error occurred during update of user %s') \
306 307 % form_result.get('username'), category='error')
307 308
308 309 return redirect(url('my_account'))
309 310
310 311 @NotAnonymous()
311 312 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
312 313 def create_repository(self):
313 314 """GET /_admin/create_repository: Form to create a new item"""
314 315 new_repo = request.GET.get('repo', '')
315 316 c.new_repo = h.repo_name_slug(new_repo)
316 317
317 318 return render('admin/repos/repo_add_create_repository.html')
318 319
319 320 def get_hg_ui_settings(self):
320 321 ret = self.sa.query(RhodeCodeUi).all()
321 322
322 323 if not ret:
323 324 raise Exception('Could not get application ui settings !')
324 325 settings = {}
325 326 for each in ret:
326 327 k = each.ui_key
327 328 v = each.ui_value
328 329 if k == '/':
329 330 k = 'root_path'
330 331
331 332 if k.find('.') != -1:
332 333 k = k.replace('.', '_')
333 334
334 335 if each.ui_section == 'hooks':
335 336 v = each.ui_active
336 337
337 338 settings[each.ui_section + '_' + k] = v
338 339
339 340 return settings
@@ -1,172 +1,172 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.users
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Users crud controller for pylons
7 7
8 8 :created_on: Apr 4, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import logging
29 29 import traceback
30 30 import formencode
31 31
32 32 from formencode import htmlfill
33 33 from pylons import request, session, tmpl_context as c, url
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36
37 37 from rhodecode.lib.exceptions import *
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
40 40 from rhodecode.lib.base import BaseController, render
41 41
42 42 from rhodecode.model.db import User
43 43 from rhodecode.model.forms import UserForm
44 44 from rhodecode.model.user import UserModel
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48 class UsersController(BaseController):
49 49 """REST Controller styled on the Atom Publishing Protocol"""
50 50 # To properly map this controller, ensure your config/routing.py
51 51 # file has a resource setup:
52 52 # map.resource('user', 'users')
53 53
54 54 @LoginRequired()
55 55 @HasPermissionAllDecorator('hg.admin')
56 56 def __before__(self):
57 57 c.admin_user = session.get('admin_user')
58 58 c.admin_username = session.get('admin_username')
59 59 super(UsersController, self).__before__()
60 60
61 61
62 62 def index(self, format='html'):
63 63 """GET /users: All items in the collection"""
64 64 # url('users')
65 65
66 66 c.users_list = self.sa.query(User).all()
67 67 return render('admin/users/users.html')
68 68
69 69 def create(self):
70 70 """POST /users: Create a new item"""
71 71 # url('users')
72 72
73 73 user_model = UserModel()
74 74 login_form = UserForm()()
75 75 try:
76 76 form_result = login_form.to_python(dict(request.POST))
77 77 user_model.create(form_result)
78 78 h.flash(_('created user %s') % form_result['username'],
79 79 category='success')
80 80 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
81 81 except formencode.Invalid, errors:
82 82 return htmlfill.render(
83 83 render('admin/users/user_add.html'),
84 84 defaults=errors.value,
85 85 errors=errors.error_dict or {},
86 86 prefix_error=False,
87 87 encoding="UTF-8")
88 88 except Exception:
89 89 log.error(traceback.format_exc())
90 h.flash(_('error occured during creation of user %s') \
90 h.flash(_('error occurred during creation of user %s') \
91 91 % request.POST.get('username'), category='error')
92 92 return redirect(url('users'))
93 93
94 94 def new(self, format='html'):
95 95 """GET /users/new: Form to create a new item"""
96 96 # url('new_user')
97 97 return render('admin/users/user_add.html')
98 98
99 99 def update(self, id):
100 100 """PUT /users/id: Update an existing item"""
101 101 # Forms posted to this method should contain a hidden field:
102 102 # <input type="hidden" name="_method" value="PUT" />
103 103 # Or using helpers:
104 104 # h.form(url('user', id=ID),
105 105 # method='put')
106 106 # url('user', id=ID)
107 107 user_model = UserModel()
108 108 c.user = user_model.get(id)
109 109
110 110 _form = UserForm(edit=True, old_data={'user_id':id,
111 111 'email':c.user.email})()
112 112 form_result = {}
113 113 try:
114 114 form_result = _form.to_python(dict(request.POST))
115 115 user_model.update(id, form_result)
116 116 h.flash(_('User updated succesfully'), category='success')
117 117
118 118 except formencode.Invalid, errors:
119 119 return htmlfill.render(
120 120 render('admin/users/user_edit.html'),
121 121 defaults=errors.value,
122 122 errors=errors.error_dict or {},
123 123 prefix_error=False,
124 124 encoding="UTF-8")
125 125 except Exception:
126 126 log.error(traceback.format_exc())
127 127 h.flash(_('error occurred during update of user %s') \
128 128 % form_result.get('username'), category='error')
129 129
130 130 return redirect(url('users'))
131 131
132 132 def delete(self, id):
133 133 """DELETE /users/id: Delete an existing item"""
134 134 # Forms posted to this method should contain a hidden field:
135 135 # <input type="hidden" name="_method" value="DELETE" />
136 136 # Or using helpers:
137 137 # h.form(url('user', id=ID),
138 138 # method='delete')
139 139 # url('user', id=ID)
140 140 user_model = UserModel()
141 141 try:
142 142 user_model.delete(id)
143 143 h.flash(_('sucessfully deleted user'), category='success')
144 144 except (UserOwnsReposException, DefaultUserException), e:
145 145 h.flash(str(e), category='warning')
146 146 except Exception:
147 h.flash(_('An error occured during deletion of user'),
147 h.flash(_('An error occurred during deletion of user'),
148 148 category='error')
149 149 return redirect(url('users'))
150 150
151 151 def show(self, id, format='html'):
152 152 """GET /users/id: Show a specific item"""
153 153 # url('user', id=ID)
154 154
155 155
156 156 def edit(self, id, format='html'):
157 157 """GET /users/id/edit: Form to edit an existing item"""
158 158 # url('edit_user', id=ID)
159 159 c.user = self.sa.query(User).get(id)
160 160 if not c.user:
161 161 return redirect(url('users'))
162 162 if c.user.username == 'default':
163 163 h.flash(_("You can't edit this user"), category='warning')
164 164 return redirect(url('users'))
165 165
166 166 defaults = c.user.get_dict()
167 167 return htmlfill.render(
168 168 render('admin/users/user_edit.html'),
169 169 defaults=defaults,
170 170 encoding="UTF-8",
171 171 force_defaults=False
172 172 )
@@ -1,47 +1,54 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # branches controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.branches
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 branches controller for rhodecode
7
8 :created_on: Apr 21, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 13 # This program is free software; you can redistribute it and/or
7 14 # modify it under the terms of the GNU General Public License
8 15 # as published by the Free Software Foundation; version 2
9 16 # of the License or (at your opinion) any later version of the license.
10 17 #
11 18 # This program is distributed in the hope that it will be useful,
12 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 21 # GNU General Public License for more details.
15 22 #
16 23 # You should have received a copy of the GNU General Public License
17 24 # along with this program; if not, write to the Free Software
18 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 26 # MA 02110-1301, USA.
20 """
21 Created on April 21, 2010
22 branches controller for pylons
23 @author: marcink
24 """
27
28 import logging
29
25 30 from pylons import tmpl_context as c
31
26 32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 33 from rhodecode.lib.base import BaseController, render
28 34 from rhodecode.lib.utils import OrderedDict
29 35 from rhodecode.model.scm import ScmModel
30 import logging
36
31 37 log = logging.getLogger(__name__)
32 38
33 39 class BranchesController(BaseController):
34 40
35 41 @LoginRequired()
36 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', 'repository.admin')
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 'repository.admin')
37 44 def __before__(self):
38 45 super(BranchesController, self).__before__()
39 46
40 47 def index(self):
41 48 hg_model = ScmModel()
42 49 c.repo_info = hg_model.get_repo(c.repo_name)
43 50 c.repo_branches = OrderedDict()
44 51 for name, hash_ in c.repo_info.branches.items():
45 52 c.repo_branches[name] = c.repo_info.get_changeset(hash_)
46 53
47 54 return render('branches/branches.html')
@@ -1,99 +1,106 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # changelog controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.changelog
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 changelog controller for rhodecode
7
8 :created_on: Apr 21, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 13 # This program is free software; you can redistribute it and/or
7 14 # modify it under the terms of the GNU General Public License
8 15 # as published by the Free Software Foundation; version 2
9 16 # of the License or (at your opinion) any later version of the license.
10 17 #
11 18 # This program is distributed in the hope that it will be useful,
12 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 21 # GNU General Public License for more details.
15 22 #
16 23 # You should have received a copy of the GNU General Public License
17 24 # along with this program; if not, write to the Free Software
18 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 26 # MA 02110-1301, USA.
20 """
21 Created on April 21, 2010
22 changelog controller for pylons
23 @author: marcink
24 """
27
28 import logging
25 29
26 30 try:
27 31 import json
28 32 except ImportError:
29 33 #python 2.5 compatibility
30 34 import simplejson as json
35
31 36 from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev
32 37 from pylons import request, session, tmpl_context as c
38
33 39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 40 from rhodecode.lib.base import BaseController, render
35 41 from rhodecode.model.scm import ScmModel
42
36 43 from webhelpers.paginate import Page
37 import logging
44
38 45 log = logging.getLogger(__name__)
39 46
40 47 class ChangelogController(BaseController):
41 48
42 49 @LoginRequired()
43 50 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 51 'repository.admin')
45 52 def __before__(self):
46 53 super(ChangelogController, self).__before__()
47 54
48 55 def index(self):
49 56 limit = 100
50 57 default = 20
51 58 if request.params.get('size'):
52 59 try:
53 60 int_size = int(request.params.get('size'))
54 61 except ValueError:
55 62 int_size = default
56 63 int_size = int_size if int_size <= limit else limit
57 64 c.size = int_size
58 65 session['changelog_size'] = c.size
59 66 session.save()
60 67 else:
61 68 c.size = int(session.get('changelog_size', default))
62 69
63 70 changesets = ScmModel().get_repo(c.repo_name)
64 71
65 72 p = int(request.params.get('page', 1))
66 73 c.total_cs = len(changesets)
67 74 c.pagination = Page(changesets, page=p, item_count=c.total_cs,
68 75 items_per_page=c.size)
69 76
70 77 self._graph(changesets, c.size, p)
71 78
72 79 return render('changelog/changelog.html')
73 80
74 81
75 82 def _graph(self, repo, size, p):
76 83 revcount = size
77 84 if not repo.revisions or repo.alias == 'git':
78 85 c.jsdata = json.dumps([])
79 86 return
80 87
81 88 max_rev = repo.revisions[-1]
82 89
83 90 offset = 1 if p == 1 else ((p - 1) * revcount + 1)
84 91
85 92 rev_start = repo.revisions[(-1 * offset)]
86 93
87 94 revcount = min(max_rev, revcount)
88 95 rev_end = max(0, rev_start - revcount)
89 96 dag = graph_rev(repo.repo, rev_start, rev_end)
90 97
91 98 c.dag = tree = list(colored(dag))
92 99 data = []
93 100 for (id, type, ctx, vtx, edges) in tree:
94 101 if type != CHANGESET:
95 102 continue
96 103 data.append(('', vtx, edges))
97 104
98 105 c.jsdata = json.dumps(data)
99 106
@@ -1,110 +1,110 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 package.rhodecode.controllers.error
4 ~~~~~~~~~~~~~~
3 rhodecode.controllers.error
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 RhodeCode error controller
7 7
8 8 :created_on: Dec 8, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27 import os
28 28 import cgi
29 29 import logging
30 30 import paste.fileapp
31 31
32 32 from pylons import tmpl_context as c, request
33 33 from pylons.i18n.translation import _
34 34 from pylons.middleware import media_path
35 35
36 36 from rhodecode.lib.base import BaseController, render
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40 class ErrorController(BaseController):
41 41 """Generates error documents as and when they are required.
42 42
43 43 The ErrorDocuments middleware forwards to ErrorController when error
44 44 related status codes are returned from the application.
45 45
46 46 This behavior can be altered by changing the parameters to the
47 47 ErrorDocuments middleware in your config/middleware.py file.
48 48 """
49 49
50 50 def __before__(self):
51 51 pass#disable all base actions since we don't need them here
52 52
53 53 def document(self):
54 54 resp = request.environ.get('pylons.original_response')
55 55
56 56 log.debug('### %s ###', resp.status)
57 57
58 58 e = request.environ
59 59 c.serv_p = r'%(protocol)s://%(host)s/' % {
60 60 'protocol': e.get('wsgi.url_scheme'),
61 61 'host':e.get('HTTP_HOST'),
62 62 }
63 63
64 64
65 65 c.error_message = cgi.escape(request.GET.get('code', str(resp.status)))
66 66 c.error_explanation = self.get_error_explanation(resp.status_int)
67 67
68 68 #redirect to when error with given seconds
69 69 c.redirect_time = 0
70 70 c.redirect_module = _('Home page')# name to what your going to be redirected
71 71 c.url_redirect = "/"
72 72
73 73 return render('/errors/error_document.html')
74 74
75 75
76 76 def img(self, id):
77 77 """Serve Pylons' stock images"""
78 78 return self._serve_file(os.path.join(media_path, 'img', id))
79 79
80 80 def style(self, id):
81 81 """Serve Pylons' stock stylesheets"""
82 82 return self._serve_file(os.path.join(media_path, 'style', id))
83 83
84 84 def _serve_file(self, path):
85 85 """Call Paste's FileApp (a WSGI application) to serve the file
86 86 at the specified path
87 87 """
88 88 fapp = paste.fileapp.FileApp(path)
89 89 return fapp(request.environ, self.start_response)
90 90
91 91 def get_error_explanation(self, code):
92 92 ''' get the error explanations of int codes
93 93 [400, 401, 403, 404, 500]'''
94 94 try:
95 95 code = int(code)
96 96 except:
97 97 code = 500
98 98
99 99 if code == 400:
100 100 return _('The request could not be understood by the server due to malformed syntax.')
101 101 if code == 401:
102 102 return _('Unauthorized access to resource')
103 103 if code == 403:
104 104 return _("You don't have permission to view this page")
105 105 if code == 404:
106 106 return _('The resource could not be found')
107 107 if code == 500:
108 108 return _('The server encountered an unexpected condition which prevented it from fulfilling the request.')
109 109
110 110
@@ -1,80 +1,90 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # feed controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.feed
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 Feed controller for rhodecode
7
8 :created_on: Apr 23, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 13 # This program is free software; you can redistribute it and/or
7 14 # modify it under the terms of the GNU General Public License
8 15 # as published by the Free Software Foundation; version 2
9 16 # of the License or (at your opinion) any later version of the license.
10 17 #
11 18 # This program is distributed in the hope that it will be useful,
12 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 21 # GNU General Public License for more details.
15 22 #
16 23 # You should have received a copy of the GNU General Public License
17 24 # along with this program; if not, write to the Free Software
18 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 26 # MA 02110-1301, USA.
20 """
21 Created on April 23, 2010
22 feed controller for pylons
23 @author: marcink
24 """
25 from pylons import tmpl_context as c, url, response
26 from rhodecode.lib.base import BaseController, render
27
28 import logging
29
30 from pylons import url, response
31
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 from rhodecode.lib.base import BaseController
27 34 from rhodecode.model.scm import ScmModel
35
28 36 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
29 import logging
37
30 38 log = logging.getLogger(__name__)
31 39
32 40 class FeedController(BaseController):
33 41
34 #secure it or not ?
42 @LoginRequired()
43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 'repository.admin')
35 45 def __before__(self):
36 46 super(FeedController, self).__before__()
37 47 #common values for feeds
38 48 self.description = 'Changes on %s repository'
39 49 self.title = "%s feed"
40 50 self.language = 'en-us'
41 51 self.ttl = "5"
42 52 self.feed_nr = 10
43 53
44 54 def atom(self, repo_name):
45 55 """Produce an atom-1.0 feed via feedgenerator module"""
46 56 feed = Atom1Feed(title=self.title % repo_name,
47 57 link=url('summary_home', repo_name=repo_name, qualified=True),
48 58 description=self.description % repo_name,
49 59 language=self.language,
50 60 ttl=self.ttl)
51 61
52 62 changesets = ScmModel().get_repo(repo_name)
53 63
54 64 for cs in changesets[:self.feed_nr]:
55 65 feed.add_item(title=cs.message,
56 66 link=url('changeset_home', repo_name=repo_name,
57 67 revision=cs.raw_id, qualified=True),
58 68 description=str(cs.date))
59 69
60 70 response.content_type = feed.mime_type
61 71 return feed.writeString('utf-8')
62 72
63 73
64 74 def rss(self, repo_name):
65 75 """Produce an rss2 feed via feedgenerator module"""
66 76 feed = Rss201rev2Feed(title=self.title % repo_name,
67 77 link=url('summary_home', repo_name=repo_name, qualified=True),
68 78 description=self.description % repo_name,
69 79 language=self.language,
70 80 ttl=self.ttl)
71 81
72 82 changesets = ScmModel().get_repo(repo_name)
73 83 for cs in changesets[:self.feed_nr]:
74 84 feed.add_item(title=cs.message,
75 85 link=url('changeset_home', repo_name=repo_name,
76 86 revision=cs.raw_id, qualified=True),
77 87 description=str(cs.date))
78 88
79 89 response.content_type = feed.mime_type
80 90 return feed.writeString('utf-8')
@@ -1,58 +1,66 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # hg controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.home
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Home controller for Rhodecode
7
8 :created_on: Feb 18, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 13 # This program is free software; you can redistribute it and/or
7 14 # modify it under the terms of the GNU General Public License
8 15 # as published by the Free Software Foundation; version 2
9 16 # of the License or (at your opinion) any later version of the license.
10 17 #
11 18 # This program is distributed in the hope that it will be useful,
12 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 21 # GNU General Public License for more details.
15 22 #
16 23 # You should have received a copy of the GNU General Public License
17 24 # along with this program; if not, write to the Free Software
18 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 26 # MA 02110-1301, USA.
20 """
21 Created on February 18, 2010
22 hg controller for pylons
23 @author: marcink
24 """
27
28 import logging
25 29 from operator import itemgetter
30
26 31 from pylons import tmpl_context as c, request
32
27 33 from rhodecode.lib.auth import LoginRequired
28 34 from rhodecode.lib.base import BaseController, render
29 35 from rhodecode.model.scm import ScmModel
30 import logging
36
31 37 log = logging.getLogger(__name__)
32 38
33 39 class HomeController(BaseController):
34 40
35 41 @LoginRequired()
36 42 def __before__(self):
37 43 super(HomeController, self).__before__()
38 44
39 45 def index(self):
40 46 sortables = ['name', 'description', 'last_change', 'tip', 'contact']
41 47 current_sort = request.GET.get('sort', 'name')
42 48 current_sort_slug = current_sort.replace('-', '')
43 49
44 50 if current_sort_slug not in sortables:
45 51 c.sort_by = 'name'
46 52 current_sort_slug = c.sort_by
47 53 else:
48 54 c.sort_by = current_sort
49 55 c.sort_slug = current_sort_slug
50 56 cached_repo_list = ScmModel().get_repos()
51 57
52 58 sort_key = current_sort_slug + '_sort'
53 59 if c.sort_by.startswith('-'):
54 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=True)
60 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
61 reverse=True)
55 62 else:
56 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=False)
63 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
64 reverse=False)
57 65
58 66 return render('/index.html')
@@ -1,93 +1,97 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # journal controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.journal
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Journal controller for pylons
7
8 :created_on: Nov 21, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 13 # This program is free software; you can redistribute it and/or
7 14 # modify it under the terms of the GNU General Public License
8 15 # as published by the Free Software Foundation; version 2
9 16 # of the License or (at your opinion) any later version of the license.
10 17 #
11 18 # This program is distributed in the hope that it will be useful,
12 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 21 # GNU General Public License for more details.
15 22 #
16 23 # You should have received a copy of the GNU General Public License
17 24 # along with this program; if not, write to the Free Software
18 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 26 # MA 02110-1301, USA.
20 """
21 Created on November 21, 2010
22 journal controller for pylons
23 @author: marcink
24 """
27
28 import logging
29 from sqlalchemy import or_
25 30
26 31 from pylons import request, response, session, tmpl_context as c, url
27 from pylons.controllers.util import abort, redirect
32
28 33 from rhodecode.lib.auth import LoginRequired, NotAnonymous
29 34 from rhodecode.lib.base import BaseController, render
30 35 from rhodecode.lib.helpers import get_token
31 36 from rhodecode.model.db import UserLog, UserFollowing
32 37 from rhodecode.model.scm import ScmModel
33 from sqlalchemy import or_
34 import logging
35 from paste.httpexceptions import HTTPInternalServerError, HTTPNotFound
38
39 from paste.httpexceptions import HTTPInternalServerError
36 40
37 41 log = logging.getLogger(__name__)
38 42
39 43 class JournalController(BaseController):
40 44
41 45
42 46 @LoginRequired()
43 47 @NotAnonymous()
44 48 def __before__(self):
45 49 super(JournalController, self).__before__()
46 50
47 51 def index(self):
48 52 # Return a rendered template
49 53
50 54 c.following = self.sa.query(UserFollowing)\
51 55 .filter(UserFollowing.user_id == c.rhodecode_user.user_id).all()
52 56
53 57 repo_ids = [x.follows_repository.repo_id for x in c.following
54 58 if x.follows_repository is not None]
55 59 user_ids = [x.follows_user.user_id for x in c.following
56 60 if x.follows_user is not None]
57 61
58 62 c.journal = self.sa.query(UserLog)\
59 63 .filter(or_(
60 64 UserLog.repository_id.in_(repo_ids),
61 65 UserLog.user_id.in_(user_ids),
62 66 ))\
63 67 .order_by(UserLog.action_date.desc())\
64 68 .limit(20)\
65 69 .all()
66 70 return render('/journal.html')
67 71
68 72 def toggle_following(self):
69 73
70 74 if request.POST.get('auth_token') == get_token():
71 75 scm_model = ScmModel()
72 76
73 77 user_id = request.POST.get('follows_user_id')
74 78 if user_id:
75 79 try:
76 80 scm_model.toggle_following_user(user_id,
77 81 c.rhodecode_user.user_id)
78 82 return 'ok'
79 83 except:
80 84 raise HTTPInternalServerError()
81 85
82 86 repo_id = request.POST.get('follows_repo_id')
83 87 if repo_id:
84 88 try:
85 89 scm_model.toggle_following_repo(repo_id,
86 90 c.rhodecode_user.user_id)
87 91 return 'ok'
88 92 except:
89 93 raise HTTPInternalServerError()
90 94
91 95
92 96
93 97 raise HTTPInternalServerError()
@@ -1,146 +1,152 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # login controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.login
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Login controller for rhodeocode
7
8 :created_on: Apr 22, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 13 # This program is free software; you can redistribute it and/or
7 14 # modify it under the terms of the GNU General Public License
8 15 # as published by the Free Software Foundation; version 2
9 16 # of the License or (at your opinion) any later version of the license.
10 17 #
11 18 # This program is distributed in the hope that it will be useful,
12 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 21 # GNU General Public License for more details.
15 22 #
16 23 # You should have received a copy of the GNU General Public License
17 24 # along with this program; if not, write to the Free Software
18 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 26 # MA 02110-1301, USA.
20 27
21 """
22 Created on April 22, 2010
23 login controller for pylons
24 @author: marcink
25 """
28 import logging
29 import formencode
30
26 31 from formencode import htmlfill
32
33 from pylons.i18n.translation import _
34 from pylons.controllers.util import abort, redirect
27 35 from pylons import request, response, session, tmpl_context as c, url
28 from pylons.controllers.util import abort, redirect
36
37 import rhodecode.lib.helpers as h
29 38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
30 39 from rhodecode.lib.base import BaseController, render
31 import rhodecode.lib.helpers as h
32 from pylons.i18n.translation import _
33 40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
34 41 from rhodecode.model.user import UserModel
35 import formencode
36 import logging
42
37 43
38 44 log = logging.getLogger(__name__)
39 45
40 46 class LoginController(BaseController):
41 47
42 48 def __before__(self):
43 49 super(LoginController, self).__before__()
44 50
45 51 def index(self):
46 52 #redirect if already logged in
47 53 c.came_from = request.GET.get('came_from', None)
48 54
49 55 if c.rhodecode_user.is_authenticated \
50 56 and c.rhodecode_user.username != 'default':
51 57
52 58 return redirect(url('home'))
53 59
54 60 if request.POST:
55 61 #import Login Form validator class
56 62 login_form = LoginForm()
57 63 try:
58 64 c.form_result = login_form.to_python(dict(request.POST))
59 65 username = c.form_result['username']
60 66 user = UserModel().get_by_username(username, case_insensitive=True)
61 67 auth_user = AuthUser()
62 68 auth_user.username = user.username
63 69 auth_user.is_authenticated = True
64 70 auth_user.is_admin = user.admin
65 71 auth_user.user_id = user.user_id
66 72 auth_user.name = user.name
67 73 auth_user.lastname = user.lastname
68 74 session['rhodecode_user'] = auth_user
69 75 session.save()
70 76 log.info('user %s is now authenticated', username)
71 77
72 78 user.update_lastlogin()
73 79
74 80 if c.came_from:
75 81 return redirect(c.came_from)
76 82 else:
77 83 return redirect(url('home'))
78 84
79 85 except formencode.Invalid, errors:
80 86 return htmlfill.render(
81 87 render('/login.html'),
82 88 defaults=errors.value,
83 89 errors=errors.error_dict or {},
84 90 prefix_error=False,
85 91 encoding="UTF-8")
86 92
87 93 return render('/login.html')
88 94
89 95 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
90 96 'hg.register.manual_activate')
91 97 def register(self):
92 98 user_model = UserModel()
93 99 c.auto_active = False
94 100 for perm in user_model.get_by_username('default', cache=False).user_perms:
95 101 if perm.permission.permission_name == 'hg.register.auto_activate':
96 102 c.auto_active = True
97 103 break
98 104
99 105 if request.POST:
100 106
101 107 register_form = RegisterForm()()
102 108 try:
103 109 form_result = register_form.to_python(dict(request.POST))
104 110 form_result['active'] = c.auto_active
105 111 user_model.create_registration(form_result)
106 112 h.flash(_('You have successfully registered into rhodecode'),
107 113 category='success')
108 114 return redirect(url('login_home'))
109 115
110 116 except formencode.Invalid, errors:
111 117 return htmlfill.render(
112 118 render('/register.html'),
113 119 defaults=errors.value,
114 120 errors=errors.error_dict or {},
115 121 prefix_error=False,
116 122 encoding="UTF-8")
117 123
118 124 return render('/register.html')
119 125
120 126 def password_reset(self):
121 127 user_model = UserModel()
122 128 if request.POST:
123 129
124 130 password_reset_form = PasswordResetForm()()
125 131 try:
126 132 form_result = password_reset_form.to_python(dict(request.POST))
127 133 user_model.reset_password(form_result)
128 134 h.flash(_('Your new password was sent'),
129 135 category='success')
130 136 return redirect(url('login_home'))
131 137
132 138 except formencode.Invalid, errors:
133 139 return htmlfill.render(
134 140 render('/password_reset.html'),
135 141 defaults=errors.value,
136 142 errors=errors.error_dict or {},
137 143 prefix_error=False,
138 144 encoding="UTF-8")
139 145
140 146 return render('/password_reset.html')
141 147
142 148 def logout(self):
143 149 session['rhodecode_user'] = AuthUser()
144 150 session.save()
145 151 log.info('Logging out and setting user as Empty')
146 152 redirect(url('home'))
@@ -1,113 +1,120 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # search controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.search
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Search controller for rhodecode
7
8 :created_on: Aug 7, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 13 # This program is free software; you can redistribute it and/or
7 14 # modify it under the terms of the GNU General Public License
8 15 # as published by the Free Software Foundation; version 2
9 16 # of the License or (at your opinion) any later version of the license.
10 17 #
11 18 # This program is distributed in the hope that it will be useful,
12 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 21 # GNU General Public License for more details.
15 22 #
16 23 # You should have received a copy of the GNU General Public License
17 24 # along with this program; if not, write to the Free Software
18 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 26 # MA 02110-1301, USA.
20 """
21 Created on Aug 7, 2010
22 search controller for pylons
23 @author: marcink
24 """
27 import logging
28 import traceback
29
30 from pylons.i18n.translation import _
25 31 from pylons import request, response, config, session, tmpl_context as c, url
26 32 from pylons.controllers.util import abort, redirect
33
27 34 from rhodecode.lib.auth import LoginRequired
28 35 from rhodecode.lib.base import BaseController, render
29 36 from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
37
30 38 from webhelpers.paginate import Page
31 39 from webhelpers.util import update_params
32 from pylons.i18n.translation import _
40
33 41 from whoosh.index import open_dir, EmptyIndexError
34 42 from whoosh.qparser import QueryParser, QueryParserError
35 43 from whoosh.query import Phrase
36 import logging
37 import traceback
38 44
39 45 log = logging.getLogger(__name__)
40 46
41 47 class SearchController(BaseController):
42 48
43 49 @LoginRequired()
44 50 def __before__(self):
45 51 super(SearchController, self).__before__()
46 52
47 53 def index(self, search_repo=None):
48 54 c.repo_name = search_repo
49 55 c.formated_results = []
50 56 c.runtime = ''
51 57 c.cur_query = request.GET.get('q', None)
52 58 c.cur_type = request.GET.get('type', 'source')
53 59 c.cur_search = search_type = {'content':'content',
54 60 'commit':'content',
55 61 'path':'path',
56 62 'repository':'repository'}\
57 63 .get(c.cur_type, 'content')
58 64
59 65
60 66 if c.cur_query:
61 67 cur_query = c.cur_query.lower()
62 68
63 69 if c.cur_query:
64 70 p = int(request.params.get('page', 1))
65 71 highlight_items = set()
66 72 try:
67 73 idx = open_dir(config['app_conf']['index_dir']
68 74 , indexname=IDX_NAME)
69 75 searcher = idx.searcher()
70 76
71 77 qp = QueryParser(search_type, schema=SCHEMA)
72 78 if c.repo_name:
73 79 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
74 80 try:
75 81 query = qp.parse(unicode(cur_query))
76 82
77 83 if isinstance(query, Phrase):
78 84 highlight_items.update(query.words)
79 85 else:
80 86 for i in query.all_terms():
81 87 if i[0] == 'content':
82 88 highlight_items.add(i[1])
83 89
84 90 matcher = query.matcher(searcher)
85 91
86 92 log.debug(query)
87 93 log.debug(highlight_items)
88 94 results = searcher.search(query)
89 95 res_ln = len(results)
90 96 c.runtime = '%s results (%.3f seconds)' \
91 97 % (res_ln, results.runtime)
92 98
93 99 def url_generator(**kw):
94 100 return update_params("?q=%s&type=%s" \
95 101 % (c.cur_query, c.cur_search), **kw)
96 102
97 103 c.formated_results = Page(
98 104 ResultWrapper(search_type, searcher, matcher,
99 105 highlight_items),
100 106 page=p, item_count=res_ln,
101 107 items_per_page=10, url=url_generator)
102 108
103 109
104 110 except QueryParserError:
105 111 c.runtime = _('Invalid search query. Try quoting it.')
106 112 searcher.close()
107 113 except (EmptyIndexError, IOError):
108 114 log.error(traceback.format_exc())
109 115 log.error('Empty Index data')
110 c.runtime = _('There is no index to search in. Please run whoosh indexer')
116 c.runtime = _('There is no index to search in. '
117 'Please run whoosh indexer')
111 118
112 119 # Return a rendered template
113 120 return render('/search/search.html')
@@ -1,178 +1,184 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # settings controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Settings controller for rhodecode
7
8 :created_on: Jun 30, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 13 # This program is free software; you can redistribute it and/or
7 14 # modify it under the terms of the GNU General Public License
8 15 # as published by the Free Software Foundation; version 2
9 16 # of the License or (at your opinion) any later version of the license.
10 17 #
11 18 # This program is distributed in the hope that it will be useful,
12 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 21 # GNU General Public License for more details.
15 22 #
16 23 # You should have received a copy of the GNU General Public License
17 24 # along with this program; if not, write to the Free Software
18 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 26 # MA 02110-1301, USA.
20 """
21 Created on June 30, 2010
22 settings controller for pylons
23 @author: marcink
24 """
27
28 import logging
29 import traceback
30
31 import formencode
25 32 from formencode import htmlfill
33
26 34 from pylons import tmpl_context as c, request, url
27 35 from pylons.controllers.util import redirect
28 36 from pylons.i18n.translation import _
37
38 import rhodecode.lib.helpers as h
29 39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 40 from rhodecode.lib.base import BaseController, render
31 41 from rhodecode.lib.utils import invalidate_cache, action_logger
32 42 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
33 43 from rhodecode.model.repo import RepoModel
34 import formencode
35 import logging
36 import rhodecode.lib.helpers as h
37 import traceback
38 44
39 45 log = logging.getLogger(__name__)
40 46
41 47 class SettingsController(BaseController):
42 48
43 49 @LoginRequired()
44 50 @HasRepoPermissionAllDecorator('repository.admin')
45 51 def __before__(self):
46 52 super(SettingsController, self).__before__()
47 53
48 54 def index(self, repo_name):
49 55 repo_model = RepoModel()
50 56 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
51 57 if not repo:
52 58 h.flash(_('%s repository is not mapped to db perhaps'
53 59 ' it was created or renamed from the filesystem'
54 60 ' please run the application again'
55 61 ' in order to rescan repositories') % repo_name,
56 62 category='error')
57 63
58 64 return redirect(url('home'))
59 65 defaults = c.repo_info.get_dict()
60 66 defaults.update({'user':c.repo_info.user.username})
61 67 c.users_array = repo_model.get_users_js()
62 68
63 69 for p in c.repo_info.repo_to_perm:
64 70 defaults.update({'perm_%s' % p.user.username:
65 71 p.permission.permission_name})
66 72
67 73 return htmlfill.render(
68 74 render('settings/repo_settings.html'),
69 75 defaults=defaults,
70 76 encoding="UTF-8",
71 77 force_defaults=False
72 78 )
73 79
74 80 def update(self, repo_name):
75 81 repo_model = RepoModel()
76 82 changed_name = repo_name
77 83 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
78 84 try:
79 85 form_result = _form.to_python(dict(request.POST))
80 86 repo_model.update(repo_name, form_result)
81 87 invalidate_cache('get_repo_cached_%s' % repo_name)
82 88 h.flash(_('Repository %s updated successfully' % repo_name),
83 89 category='success')
84 90 changed_name = form_result['repo_name']
85 91 action_logger(self.rhodecode_user, 'user_updated_repo',
86 92 changed_name, '', self.sa)
87 93 except formencode.Invalid, errors:
88 94 c.repo_info = repo_model.get_by_repo_name(repo_name)
89 95 c.users_array = repo_model.get_users_js()
90 96 errors.value.update({'user':c.repo_info.user.username})
91 97 return htmlfill.render(
92 98 render('settings/repo_settings.html'),
93 99 defaults=errors.value,
94 100 errors=errors.error_dict or {},
95 101 prefix_error=False,
96 102 encoding="UTF-8")
97 103 except Exception:
98 104 log.error(traceback.format_exc())
99 105 h.flash(_('error occurred during update of repository %s') \
100 106 % repo_name, category='error')
101 107
102 108 return redirect(url('repo_settings_home', repo_name=changed_name))
103 109
104 110
105 111
106 112 def delete(self, repo_name):
107 113 """DELETE /repos/repo_name: Delete an existing item"""
108 114 # Forms posted to this method should contain a hidden field:
109 115 # <input type="hidden" name="_method" value="DELETE" />
110 116 # Or using helpers:
111 117 # h.form(url('repo_settings_delete', repo_name=ID),
112 118 # method='delete')
113 119 # url('repo_settings_delete', repo_name=ID)
114 120
115 121 repo_model = RepoModel()
116 122 repo = repo_model.get_by_repo_name(repo_name)
117 123 if not repo:
118 124 h.flash(_('%s repository is not mapped to db perhaps'
119 125 ' it was moved or renamed from the filesystem'
120 126 ' please run the application again'
121 127 ' in order to rescan repositories') % repo_name,
122 128 category='error')
123 129
124 130 return redirect(url('home'))
125 131 try:
126 132 action_logger(self.rhodecode_user, 'user_deleted_repo',
127 133 repo_name, '', self.sa)
128 134 repo_model.delete(repo)
129 135 invalidate_cache('get_repo_cached_%s' % repo_name)
130 136 h.flash(_('deleted repository %s') % repo_name, category='success')
131 137 except Exception:
132 138 h.flash(_('An error occurred during deletion of %s') % repo_name,
133 139 category='error')
134 140
135 141 return redirect(url('home'))
136 142
137 143 def fork(self, repo_name):
138 144 repo_model = RepoModel()
139 145 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
140 146 if not repo:
141 147 h.flash(_('%s repository is not mapped to db perhaps'
142 148 ' it was created or renamed from the filesystem'
143 149 ' please run the application again'
144 150 ' in order to rescan repositories') % repo_name,
145 151 category='error')
146 152
147 153 return redirect(url('home'))
148 154
149 155 return render('settings/repo_fork.html')
150 156
151 157
152 158
153 159 def fork_create(self, repo_name):
154 160 repo_model = RepoModel()
155 161 c.repo_info = repo_model.get_by_repo_name(repo_name)
156 162 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
157 163 form_result = {}
158 164 try:
159 165 form_result = _form.to_python(dict(request.POST))
160 166 form_result.update({'repo_name':repo_name})
161 167 repo_model.create_fork(form_result, c.rhodecode_user)
162 168 h.flash(_('forked %s repository as %s') \
163 169 % (repo_name, form_result['fork_name']),
164 170 category='success')
165 171 action_logger(self.rhodecode_user,
166 172 'user_forked_repo:%s' % form_result['fork_name'],
167 173 repo_name, '', self.sa)
168 174 except formencode.Invalid, errors:
169 175 c.new_repo = errors.value['fork_name']
170 176 r = render('settings/repo_fork.html')
171 177
172 178 return htmlfill.render(
173 179 r,
174 180 defaults=errors.value,
175 181 errors=errors.error_dict or {},
176 182 prefix_error=False,
177 183 encoding="UTF-8")
178 184 return redirect(url('home'))
@@ -1,49 +1,56 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # shortlog controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.shortlog
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 Shortlog controller for rhodecode
7
8 :created_on: Apr 18, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 13 # This program is free software; you can redistribute it and/or
7 14 # modify it under the terms of the GNU General Public License
8 15 # as published by the Free Software Foundation; version 2
9 16 # of the License or (at your opinion) any later version of the license.
10 17 #
11 18 # This program is distributed in the hope that it will be useful,
12 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 21 # GNU General Public License for more details.
15 22 #
16 23 # You should have received a copy of the GNU General Public License
17 24 # along with this program; if not, write to the Free Software
18 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 26 # MA 02110-1301, USA.
20 """
21 Created on April 18, 2010
22 shortlog controller for pylons
23 @author: marcink
24 """
27
28 import logging
29
25 30 from pylons import tmpl_context as c, request
31
32 from webhelpers.paginate import Page
33
26 34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 35 from rhodecode.lib.base import BaseController, render
28 36 from rhodecode.model.scm import ScmModel
29 from webhelpers.paginate import Page
30 import logging
37
31 38 log = logging.getLogger(__name__)
32 39
33 40 class ShortlogController(BaseController):
34 41
35 42 @LoginRequired()
36 43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
37 44 'repository.admin')
38 45 def __before__(self):
39 46 super(ShortlogController, self).__before__()
40 47
41 48 def index(self):
42 49 p = int(request.params.get('page', 1))
43 50 repo = ScmModel().get_repo(c.repo_name)
44 51 c.repo_changesets = Page(repo, page=p, items_per_page=20)
45 52 c.shortlog_data = render('shortlog/shortlog_data.html')
46 53 if request.params.get('partial'):
47 54 return c.shortlog_data
48 55 r = render('shortlog/shortlog.html')
49 56 return r
@@ -1,144 +1,144 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 package.rhodecode.controllers.summary
4 ~~~~~~~~~~~~~~
3 rhodecode.controllers.summary
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Summary controller for Rhodecode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import calendar
29 29 import logging
30 30 from time import mktime
31 31 from datetime import datetime, timedelta
32 32
33 33 from vcs.exceptions import ChangesetError
34 34
35 35 from pylons import tmpl_context as c, request, url
36 36 from pylons.i18n.translation import _
37 37
38 38 from rhodecode.model.scm import ScmModel
39 39 from rhodecode.model.db import Statistics
40 40
41 41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 42 from rhodecode.lib.base import BaseController, render
43 43 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
44 44
45 45 from rhodecode.lib.celerylib import run_task
46 46 from rhodecode.lib.celerylib.tasks import get_commits_stats
47 47
48 48 from webhelpers.paginate import Page
49 49
50 50 try:
51 51 import json
52 52 except ImportError:
53 53 #python 2.5 compatibility
54 54 import simplejson as json
55 55 log = logging.getLogger(__name__)
56 56
57 57 class SummaryController(BaseController):
58 58
59 59 @LoginRequired()
60 60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 61 'repository.admin')
62 62 def __before__(self):
63 63 super(SummaryController, self).__before__()
64 64
65 65 def index(self):
66 66 scm_model = ScmModel()
67 67 c.repo_info = scm_model.get_repo(c.repo_name)
68 68 c.following = scm_model.is_following_repo(c.repo_name,
69 69 c.rhodecode_user.user_id)
70 70 def url_generator(**kw):
71 71 return url('shortlog_home', repo_name=c.repo_name, **kw)
72 72
73 73 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
74 74 url=url_generator)
75 75
76 76 e = request.environ
77 77
78 78 if self.rhodecode_user.username == 'default':
79 79 password = ':default'
80 80 else:
81 81 password = ''
82 82
83 83 uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
84 84 'protocol': e.get('wsgi.url_scheme'),
85 85 'user':str(c.rhodecode_user.username),
86 86 'password':password,
87 87 'host':e.get('HTTP_HOST'),
88 88 'prefix':e.get('SCRIPT_NAME'),
89 89 'repo_name':c.repo_name, }
90 90 c.clone_repo_url = uri
91 91 c.repo_tags = OrderedDict()
92 92 for name, hash in c.repo_info.tags.items()[:10]:
93 93 try:
94 94 c.repo_tags[name] = c.repo_info.get_changeset(hash)
95 95 except ChangesetError:
96 96 c.repo_tags[name] = EmptyChangeset(hash)
97 97
98 98 c.repo_branches = OrderedDict()
99 99 for name, hash in c.repo_info.branches.items()[:10]:
100 100 try:
101 101 c.repo_branches[name] = c.repo_info.get_changeset(hash)
102 102 except ChangesetError:
103 103 c.repo_branches[name] = EmptyChangeset(hash)
104 104
105 105 td = datetime.today() + timedelta(days=1)
106 106 y, m, d = td.year, td.month, td.day
107 107
108 108 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
109 109 d, 0, 0, 0, 0, 0, 0,))
110 110 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
111 111 d, 0, 0, 0, 0, 0, 0,))
112 112
113 113 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
114 114 if c.repo_info.dbrepo.enable_statistics:
115 115 c.no_data_msg = _('No data loaded yet')
116 116 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
117 117 else:
118 118 c.no_data_msg = _('Statistics update are disabled for this repository')
119 119 c.ts_min = ts_min_m
120 120 c.ts_max = ts_max_y
121 121
122 122 stats = self.sa.query(Statistics)\
123 123 .filter(Statistics.repository == c.repo_info.dbrepo)\
124 124 .scalar()
125 125
126 126
127 127 if stats and stats.languages:
128 128 c.no_data = False is c.repo_info.dbrepo.enable_statistics
129 129 lang_stats = json.loads(stats.languages)
130 130 c.commit_data = stats.commit_activity
131 131 c.overview_data = stats.commit_activity_combined
132 132 c.trending_languages = json.dumps(OrderedDict(
133 133 sorted(lang_stats.items(), reverse=True,
134 134 key=lambda k: k[1])[:10]
135 135 )
136 136 )
137 137 else:
138 138 c.commit_data = json.dumps({})
139 139 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
140 140 c.trending_languages = json.dumps({})
141 141 c.no_data = True
142 142
143 143 return render('summary/summary.html')
144 144
@@ -1,47 +1,53 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # tags controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.controllers.tags
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
6 Tags controller for rhodecode
7
8 :created_on: Apr 21, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 13 # This program is free software; you can redistribute it and/or
7 14 # modify it under the terms of the GNU General Public License
8 15 # as published by the Free Software Foundation; version 2
9 16 # of the License or (at your opinion) any later version of the license.
10 17 #
11 18 # This program is distributed in the hope that it will be useful,
12 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 21 # GNU General Public License for more details.
15 22 #
16 23 # You should have received a copy of the GNU General Public License
17 24 # along with this program; if not, write to the Free Software
18 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 26 # MA 02110-1301, USA.
20 """
21 Created on April 21, 2010
22 tags controller for pylons
23 @author: marcink
24 """
27 import logging
28
25 29 from pylons import tmpl_context as c
30
26 31 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 32 from rhodecode.lib.base import BaseController, render
28 33 from rhodecode.lib.utils import OrderedDict
29 34 from rhodecode.model.scm import ScmModel
30 import logging
35
31 36 log = logging.getLogger(__name__)
32 37
33 38 class TagsController(BaseController):
34 39
35 40 @LoginRequired()
36 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', 'repository.admin')
41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
42 'repository.admin')
37 43 def __before__(self):
38 44 super(TagsController, self).__before__()
39 45
40 46 def index(self):
41 47 hg_model = ScmModel()
42 48 c.repo_info = hg_model.get_repo(c.repo_name)
43 49 c.repo_tags = OrderedDict()
44 50 for name, hash_ in c.repo_info.tags.items():
45 51 c.repo_tags[name] = c.repo_info.get_changeset(hash_)
46 52
47 53 return render('tags/tags.html')
@@ -1,546 +1,546 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):
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 + 1]))))
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):
255 255 i, t = it
256 256 t = '<div id="#S-%s">%s</div>' % (cnt + 1, 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 = ' '
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 revs_limit = 5
427 revs_limit = 1000
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 uniq_id = revs[0]
435 435 html_tmpl = ('<span> %s '
436 436 '<a class="show_more" id="_%s" href="#">%s</a> '
437 437 '%s</span>')
438 438 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
439 439 % (len(revs) - revs_limit),
440 440 _('revisions'))
441 441
442 442 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
443 443 cs_links += html_tmpl % (uniq_id, ', '.join([link(rev,
444 444 url('changeset_home',
445 445 repo_name=user_log.repository.repo_name,
446 446 revision=rev)) for rev in revs[:revs_limit] ]))
447 447
448 448 return cs_links
449 449 return ''
450 450
451 451 def get_fork_name():
452 452 if action == 'user_forked_repo':
453 453 from rhodecode.model.scm import ScmModel
454 454 repo_name = action_params
455 455 repo = ScmModel().get(repo_name)
456 456 if repo is None:
457 457 return repo_name
458 458 return link_to(action_params, url('summary_home',
459 459 repo_name=repo.name,),
460 460 title=repo.dbrepo.description)
461 461 return ''
462 462 map = {'user_deleted_repo':_('User [deleted] repository'),
463 463 'user_created_repo':_('User [created] repository'),
464 464 'user_forked_repo':_('User [forked] repository as: %s') % get_fork_name(),
465 465 'user_updated_repo':_('User [updated] repository'),
466 466 'admin_deleted_repo':_('Admin [delete] repository'),
467 467 'admin_created_repo':_('Admin [created] repository'),
468 468 'admin_forked_repo':_('Admin [forked] repository'),
469 469 'admin_updated_repo':_('Admin [updated] repository'),
470 470 'push':_('[Pushed] %s') % get_cs_links(),
471 471 'pull':_('[Pulled]'),
472 472 'started_following_repo':_('User [started following] repository'),
473 473 'stopped_following_repo':_('User [stopped following] repository'),
474 474 }
475 475
476 476 action_str = map.get(action, action)
477 477 return literal(action_str.replace('[', '<span class="journal_highlight">')\
478 478 .replace(']', '</span>'))
479 479
480 480 def action_parser_icon(user_log):
481 481 action = user_log.action
482 482 action_params = None
483 483 x = action.split(':')
484 484
485 485 if len(x) > 1:
486 486 action, action_params = x
487 487
488 488 tmpl = """<img src="/images/icons/%s" alt="%s"/>"""
489 489 map = {'user_deleted_repo':'database_delete.png',
490 490 'user_created_repo':'database_add.png',
491 491 'user_forked_repo':'arrow_divide.png',
492 492 'user_updated_repo':'database_edit.png',
493 493 'admin_deleted_repo':'database_delete.png',
494 494 'admin_created_repo':'database_ddd.png',
495 495 'admin_forked_repo':'arrow_divide.png',
496 496 'admin_updated_repo':'database_edit.png',
497 497 'push':'script_add.png',
498 498 'pull':'down_16.png',
499 499 'started_following_repo':'heart_add.png',
500 500 'stopped_following_repo':'heart_delete.png',
501 501 }
502 502 return literal(tmpl % (map.get(action, action), action))
503 503
504 504
505 505 #==============================================================================
506 506 # PERMS
507 507 #==============================================================================
508 508 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
509 509 HasRepoPermissionAny, HasRepoPermissionAll
510 510
511 511 #==============================================================================
512 512 # GRAVATAR URL
513 513 #==============================================================================
514 514 import hashlib
515 515 import urllib
516 516 from pylons import request
517 517
518 518 def gravatar_url(email_address, size=30):
519 519 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
520 520 default = 'identicon'
521 521 baseurl_nossl = "http://www.gravatar.com/avatar/"
522 522 baseurl_ssl = "https://secure.gravatar.com/avatar/"
523 523 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
524 524
525 525
526 526 # construct the url
527 527 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
528 528 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
529 529
530 530 return gravatar_url
531 531
532 532 def safe_unicode(str):
533 533 """safe unicode function. In case of UnicodeDecode error we try to return
534 534 unicode with errors replace, if this failes we return unicode with
535 535 string_escape decoding """
536 536
537 537 try:
538 538 u_str = unicode(str)
539 539 except UnicodeDecodeError:
540 540 try:
541 541 u_str = unicode(str, 'utf-8', 'replace')
542 542 except UnicodeDecodeError:
543 543 #incase we have a decode error just represent as byte string
544 544 u_str = unicode(str(str).encode('string_escape'))
545 545
546 546 return u_str
@@ -1,614 +1,614 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Utilities library for RhodeCode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import os
29 29 import logging
30 30 import datetime
31 31 import traceback
32 32
33 33 from UserDict import DictMixin
34 34
35 35 from mercurial import ui, config, hg
36 36 from mercurial.error import RepoError
37 37
38 38 import paste
39 39 import beaker
40 40 from paste.script.command import Command, BadCommand
41 41
42 42 from vcs.backends.base import BaseChangeset
43 43 from vcs.utils.lazy import LazyProperty
44 44
45 45 from rhodecode.model import meta
46 46 from rhodecode.model.caching_query import FromCache
47 47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog
48 48 from rhodecode.model.repo import RepoModel
49 49 from rhodecode.model.user import UserModel
50 50
51 51 log = logging.getLogger(__name__)
52 52
53 53
54 54 def get_repo_slug(request):
55 55 return request.environ['pylons.routes_dict'].get('repo_name')
56 56
57 57 def action_logger(user, action, repo, ipaddr='', sa=None):
58 58 """
59 59 Action logger for various actions made by users
60 60
61 61 :param user: user that made this action, can be a unique username string or
62 62 object containing user_id attribute
63 63 :param action: action to log, should be on of predefined unique actions for
64 64 easy translations
65 65 :param repo: string name of repository or object containing repo_id,
66 66 that action was made on
67 67 :param ipaddr: optional ip address from what the action was made
68 68 :param sa: optional sqlalchemy session
69 69
70 70 """
71 71
72 72 if not sa:
73 73 sa = meta.Session()
74 74
75 75 try:
76 76 um = UserModel()
77 77 if hasattr(user, 'user_id'):
78 78 user_obj = user
79 79 elif isinstance(user, basestring):
80 80 user_obj = um.get_by_username(user, cache=False)
81 81 else:
82 82 raise Exception('You have to provide user object or username')
83 83
84 84
85 85 rm = RepoModel()
86 86 if hasattr(repo, 'repo_id'):
87 87 repo_obj = rm.get(repo.repo_id, cache=False)
88 88 repo_name = repo_obj.repo_name
89 89 elif isinstance(repo, basestring):
90 90 repo_name = repo.lstrip('/')
91 91 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
92 92 else:
93 93 raise Exception('You have to provide repository to action logger')
94 94
95 95
96 96 user_log = UserLog()
97 97 user_log.user_id = user_obj.user_id
98 98 user_log.action = action
99 99
100 100 user_log.repository_id = repo_obj.repo_id
101 101 user_log.repository_name = repo_name
102 102
103 103 user_log.action_date = datetime.datetime.now()
104 104 user_log.user_ip = ipaddr
105 105 sa.add(user_log)
106 106 sa.commit()
107 107
108 108 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
109 109 except:
110 110 log.error(traceback.format_exc())
111 111 sa.rollback()
112 112
113 113 def get_repos(path, recursive=False, initial=False):
114 114 """
115 115 Scans given path for repos and return (name,(type,path)) tuple
116
116 117 :param prefix:
117 118 :param path:
118 119 :param recursive:
119 120 :param initial:
120 121 """
121 122 from vcs.utils.helpers import get_scm
122 123 from vcs.exceptions import VCSError
123 124
124 125 try:
125 126 scm = get_scm(path)
126 127 except:
127 128 pass
128 129 else:
129 130 raise Exception('The given path %s should not be a repository got %s',
130 131 path, scm)
131 132
132 133 for dirpath in os.listdir(path):
133 134 try:
134 135 yield dirpath, get_scm(os.path.join(path, dirpath))
135 136 except VCSError:
136 137 pass
137 138
138 139 def check_repo_fast(repo_name, base_path):
139 140 """
140 Check given path for existance of directory
141 Check given path for existence of directory
141 142 :param repo_name:
142 143 :param base_path:
143 144
144 145 :return False: if this directory is present
145 146 """
146 147 if os.path.isdir(os.path.join(base_path, repo_name)):return False
147 148 return True
148 149
149 150 def check_repo(repo_name, base_path, verify=True):
150 151
151 152 repo_path = os.path.join(base_path, repo_name)
152 153
153 154 try:
154 155 if not check_repo_fast(repo_name, base_path):
155 156 return False
156 157 r = hg.repository(ui.ui(), repo_path)
157 158 if verify:
158 159 hg.verify(r)
159 160 #here we hnow that repo exists it was verified
160 161 log.info('%s repo is already created', repo_name)
161 162 return False
162 163 except RepoError:
163 164 #it means that there is no valid repo there...
164 165 log.info('%s repo is free for creation', repo_name)
165 166 return True
166 167
167 168 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
168 169 while True:
169 170 ok = raw_input(prompt)
170 171 if ok in ('y', 'ye', 'yes'): return True
171 172 if ok in ('n', 'no', 'nop', 'nope'): return False
172 173 retries = retries - 1
173 174 if retries < 0: raise IOError
174 175 print complaint
175 176
176 177 #propagated from mercurial documentation
177 178 ui_sections = ['alias', 'auth',
178 179 'decode/encode', 'defaults',
179 180 'diff', 'email',
180 181 'extensions', 'format',
181 182 'merge-patterns', 'merge-tools',
182 183 'hooks', 'http_proxy',
183 184 'smtp', 'patch',
184 185 'paths', 'profiling',
185 186 'server', 'trusted',
186 187 'ui', 'web', ]
187 188
188 189 def make_ui(read_from='file', path=None, checkpaths=True):
189 190 """
190 191 A function that will read python rc files or database
191 192 and make an mercurial ui object from read options
192 193
193 194 :param path: path to mercurial config file
194 195 :param checkpaths: check the path
195 196 :param read_from: read from 'file' or 'db'
196 197 """
197 198
198 199 baseui = ui.ui()
199 200
200 201 #clean the baseui object
201 202 baseui._ocfg = config.config()
202 203 baseui._ucfg = config.config()
203 204 baseui._tcfg = config.config()
204 205
205 206 if read_from == 'file':
206 207 if not os.path.isfile(path):
207 208 log.warning('Unable to read config file %s' % path)
208 209 return False
209 210 log.debug('reading hgrc from %s', path)
210 211 cfg = config.config()
211 212 cfg.read(path)
212 213 for section in ui_sections:
213 214 for k, v in cfg.items(section):
214 215 log.debug('settings ui from file[%s]%s:%s', section, k, v)
215 216 baseui.setconfig(section, k, v)
216 217
217 218
218 219 elif read_from == 'db':
219 220 sa = meta.Session()
220 221 ret = sa.query(RhodeCodeUi)\
221 222 .options(FromCache("sql_cache_short",
222 223 "get_hg_ui_settings")).all()
223 224
224 225 hg_ui = ret
225 226 for ui_ in hg_ui:
226 227 if ui_.ui_active:
227 228 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
228 229 ui_.ui_key, ui_.ui_value)
229 230 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
230 231
231 232 meta.Session.remove()
232 233 return baseui
233 234
234 235
235 236 def set_rhodecode_config(config):
236 """
237 Updates pylons config with new settings from database
237 """Updates pylons config with new settings from database
238
238 239 :param config:
239 240 """
240 241 from rhodecode.model.settings import SettingsModel
241 242 hgsettings = SettingsModel().get_app_settings()
242 243
243 244 for k, v in hgsettings.items():
244 245 config[k] = v
245 246
246 247 def invalidate_cache(cache_key, *args):
247 """
248 Puts cache invalidation task into db for
248 """Puts cache invalidation task into db for
249 249 further global cache invalidation
250 250 """
251
251 252 from rhodecode.model.scm import ScmModel
252 253
253 254 if cache_key.startswith('get_repo_cached_'):
254 255 name = cache_key.split('get_repo_cached_')[-1]
255 256 ScmModel().mark_for_invalidation(name)
256 257
257 258 class EmptyChangeset(BaseChangeset):
258 259 """
259 260 An dummy empty changeset. It's possible to pass hash when creating
260 261 an EmptyChangeset
261 262 """
262 263
263 264 def __init__(self, cs='0' * 40):
264 265 self._empty_cs = cs
265 266 self.revision = -1
266 267 self.message = ''
267 268 self.author = ''
268 269 self.date = ''
269 270
270 271 @LazyProperty
271 272 def raw_id(self):
272 """
273 Returns raw string identifying this changeset, useful for web
273 """Returns raw string identifying this changeset, useful for web
274 274 representation.
275 275 """
276
276 277 return self._empty_cs
277 278
278 279 @LazyProperty
279 280 def short_id(self):
280 281 return self.raw_id[:12]
281 282
282 283 def get_file_changeset(self, path):
283 284 return self
284 285
285 286 def get_file_content(self, path):
286 287 return u''
287 288
288 289 def get_file_size(self, path):
289 290 return 0
290 291
291 292 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
292 """
293 maps all found repositories into db
293 """maps all found repositories into db
294 294 """
295 295
296 296 sa = meta.Session()
297 297 rm = RepoModel()
298 298 user = sa.query(User).filter(User.admin == True).first()
299 299
300 300 for name, repo in initial_repo_list.items():
301 301 if not rm.get_by_repo_name(name, cache=False):
302 302 log.info('repository %s not found creating default', name)
303 303
304 304 form_data = {
305 305 'repo_name':name,
306 306 'repo_type':repo.alias,
307 307 'description':repo.description \
308 308 if repo.description != 'unknown' else \
309 309 '%s repository' % name,
310 310 'private':False
311 311 }
312 312 rm.create(form_data, user, just_db=True)
313 313
314 314 if remove_obsolete:
315 315 #remove from database those repositories that are not in the filesystem
316 316 for repo in sa.query(Repository).all():
317 317 if repo.repo_name not in initial_repo_list.keys():
318 318 sa.delete(repo)
319 319 sa.commit()
320 320
321 321 class OrderedDict(dict, DictMixin):
322 322
323 323 def __init__(self, *args, **kwds):
324 324 if len(args) > 1:
325 325 raise TypeError('expected at most 1 arguments, got %d' % len(args))
326 326 try:
327 327 self.__end
328 328 except AttributeError:
329 329 self.clear()
330 330 self.update(*args, **kwds)
331 331
332 332 def clear(self):
333 333 self.__end = end = []
334 334 end += [None, end, end] # sentinel node for doubly linked list
335 335 self.__map = {} # key --> [key, prev, next]
336 336 dict.clear(self)
337 337
338 338 def __setitem__(self, key, value):
339 339 if key not in self:
340 340 end = self.__end
341 341 curr = end[1]
342 342 curr[2] = end[1] = self.__map[key] = [key, curr, end]
343 343 dict.__setitem__(self, key, value)
344 344
345 345 def __delitem__(self, key):
346 346 dict.__delitem__(self, key)
347 347 key, prev, next = self.__map.pop(key)
348 348 prev[2] = next
349 349 next[1] = prev
350 350
351 351 def __iter__(self):
352 352 end = self.__end
353 353 curr = end[2]
354 354 while curr is not end:
355 355 yield curr[0]
356 356 curr = curr[2]
357 357
358 358 def __reversed__(self):
359 359 end = self.__end
360 360 curr = end[1]
361 361 while curr is not end:
362 362 yield curr[0]
363 363 curr = curr[1]
364 364
365 365 def popitem(self, last=True):
366 366 if not self:
367 367 raise KeyError('dictionary is empty')
368 368 if last:
369 369 key = reversed(self).next()
370 370 else:
371 371 key = iter(self).next()
372 372 value = self.pop(key)
373 373 return key, value
374 374
375 375 def __reduce__(self):
376 376 items = [[k, self[k]] for k in self]
377 377 tmp = self.__map, self.__end
378 378 del self.__map, self.__end
379 379 inst_dict = vars(self).copy()
380 380 self.__map, self.__end = tmp
381 381 if inst_dict:
382 382 return (self.__class__, (items,), inst_dict)
383 383 return self.__class__, (items,)
384 384
385 385 def keys(self):
386 386 return list(self)
387 387
388 388 setdefault = DictMixin.setdefault
389 389 update = DictMixin.update
390 390 pop = DictMixin.pop
391 391 values = DictMixin.values
392 392 items = DictMixin.items
393 393 iterkeys = DictMixin.iterkeys
394 394 itervalues = DictMixin.itervalues
395 395 iteritems = DictMixin.iteritems
396 396
397 397 def __repr__(self):
398 398 if not self:
399 399 return '%s()' % (self.__class__.__name__,)
400 400 return '%s(%r)' % (self.__class__.__name__, self.items())
401 401
402 402 def copy(self):
403 403 return self.__class__(self)
404 404
405 405 @classmethod
406 406 def fromkeys(cls, iterable, value=None):
407 407 d = cls()
408 408 for key in iterable:
409 409 d[key] = value
410 410 return d
411 411
412 412 def __eq__(self, other):
413 413 if isinstance(other, OrderedDict):
414 414 return len(self) == len(other) and self.items() == other.items()
415 415 return dict.__eq__(self, other)
416 416
417 417 def __ne__(self, other):
418 418 return not self == other
419 419
420 420
421 421 #set cache regions for beaker so celery can utilise it
422 422 def add_cache(settings):
423 423 cache_settings = {'regions':None}
424 424 for key in settings.keys():
425 425 for prefix in ['beaker.cache.', 'cache.']:
426 426 if key.startswith(prefix):
427 427 name = key.split(prefix)[1].strip()
428 428 cache_settings[name] = settings[key].strip()
429 429 if cache_settings['regions']:
430 430 for region in cache_settings['regions'].split(','):
431 431 region = region.strip()
432 432 region_settings = {}
433 433 for key, value in cache_settings.items():
434 434 if key.startswith(region):
435 435 region_settings[key.split('.')[1]] = value
436 436 region_settings['expire'] = int(region_settings.get('expire',
437 437 60))
438 438 region_settings.setdefault('lock_dir',
439 439 cache_settings.get('lock_dir'))
440 440 if 'type' not in region_settings:
441 441 region_settings['type'] = cache_settings.get('type',
442 442 'memory')
443 443 beaker.cache.cache_regions[region] = region_settings
444 444
445 445 def get_current_revision():
446 """
447 Returns tuple of (number, id) from repository containing this package
446 """Returns tuple of (number, id) from repository containing this package
448 447 or None if repository could not be found.
449 448 """
449
450 450 try:
451 451 from vcs import get_repo
452 452 from vcs.utils.helpers import get_scm
453 453 from vcs.exceptions import RepositoryError, VCSError
454 454 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
455 455 scm = get_scm(repopath)[0]
456 456 repo = get_repo(path=repopath, alias=scm)
457 457 tip = repo.get_changeset()
458 458 return (tip.revision, tip.short_id)
459 459 except (ImportError, RepositoryError, VCSError), err:
460 460 logging.debug("Cannot retrieve rhodecode's revision. Original error "
461 461 "was: %s" % err)
462 462 return None
463 463
464 464 #===============================================================================
465 465 # TEST FUNCTIONS AND CREATORS
466 466 #===============================================================================
467 467 def create_test_index(repo_location, full_index):
468 468 """Makes default test index
469 469 :param repo_location:
470 470 :param full_index:
471 471 """
472 472 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
473 473 from rhodecode.lib.pidlock import DaemonLock, LockHeld
474 474 import shutil
475 475
476 476 index_location = os.path.join(repo_location, 'index')
477 477 if os.path.exists(index_location):
478 478 shutil.rmtree(index_location)
479 479
480 480 try:
481 481 l = DaemonLock()
482 482 WhooshIndexingDaemon(index_location=index_location,
483 483 repo_location=repo_location)\
484 484 .run(full_index=full_index)
485 485 l.release()
486 486 except LockHeld:
487 487 pass
488 488
489 489 def create_test_env(repos_test_path, config):
490 490 """Makes a fresh database and
491 491 install test repository into tmp dir
492 492 """
493 493 from rhodecode.lib.db_manage import DbManage
494 494 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
495 495 HG_FORK, GIT_FORK, TESTS_TMP_PATH
496 496 import tarfile
497 497 import shutil
498 498 from os.path import dirname as dn, join as jn, abspath
499 499
500 500 log = logging.getLogger('TestEnvCreator')
501 501 # create logger
502 502 log.setLevel(logging.DEBUG)
503 503 log.propagate = True
504 504 # create console handler and set level to debug
505 505 ch = logging.StreamHandler()
506 506 ch.setLevel(logging.DEBUG)
507 507
508 508 # create formatter
509 509 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
510 510
511 511 # add formatter to ch
512 512 ch.setFormatter(formatter)
513 513
514 514 # add ch to logger
515 515 log.addHandler(ch)
516 516
517 517 #PART ONE create db
518 518 dbconf = config['sqlalchemy.db1.url']
519 519 log.debug('making test db %s', dbconf)
520 520
521 521 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
522 522 tests=True)
523 523 dbmanage.create_tables(override=True)
524 524 dbmanage.config_prompt(repos_test_path)
525 525 dbmanage.create_default_user()
526 526 dbmanage.admin_prompt()
527 527 dbmanage.create_permissions()
528 528 dbmanage.populate_default_permissions()
529 529
530 530 #PART TWO make test repo
531 531 log.debug('making test vcs repositories')
532 532
533 533 #remove old one from previos tests
534 534 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
535 535
536 536 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
537 537 log.debug('removing %s', r)
538 538 shutil.rmtree(jn(TESTS_TMP_PATH, r))
539 539
540 540 #CREATE DEFAULT HG REPOSITORY
541 541 cur_dir = dn(dn(abspath(__file__)))
542 542 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
543 543 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
544 544 tar.close()
545 545
546 546
547 547 #==============================================================================
548 548 # PASTER COMMANDS
549 549 #==============================================================================
550 550
551 551 class BasePasterCommand(Command):
552 552 """
553 553 Abstract Base Class for paster commands.
554 554
555 555 The celery commands are somewhat aggressive about loading
556 556 celery.conf, and since our module sets the `CELERY_LOADER`
557 557 environment variable to our loader, we have to bootstrap a bit and
558 558 make sure we've had a chance to load the pylons config off of the
559 559 command line, otherwise everything fails.
560 560 """
561 561 min_args = 1
562 562 min_args_error = "Please provide a paster config file as an argument."
563 563 takes_config_file = 1
564 564 requires_config_file = True
565 565
566 566 def notify_msg(self, msg, log=False):
567 567 """Make a notification to user, additionally if logger is passed
568 568 it logs this action using given logger
569 569
570 570 :param msg: message that will be printed to user
571 571 :param log: logging instance, to use to additionally log this message
572 572
573 573 """
574 574 print msg
575 575 if log and isinstance(log, logging):
576 576 log(msg)
577 577
578 578
579 579 def run(self, args):
580 580 """
581 581 Overrides Command.run
582 582
583 583 Checks for a config file argument and loads it.
584 584 """
585 585 if len(args) < self.min_args:
586 586 raise BadCommand(
587 587 self.min_args_error % {'min_args': self.min_args,
588 588 'actual_args': len(args)})
589 589
590 590 # Decrement because we're going to lob off the first argument.
591 591 # @@ This is hacky
592 592 self.min_args -= 1
593 593 self.bootstrap_config(args[0])
594 594 self.update_parser()
595 595 return super(BasePasterCommand, self).run(args[1:])
596 596
597 597 def update_parser(self):
598 598 """
599 599 Abstract method. Allows for the class's parser to be updated
600 600 before the superclass's `run` method is called. Necessary to
601 601 allow options/arguments to be passed through to the underlying
602 602 celery command.
603 603 """
604 604 raise NotImplementedError("Abstract Method.")
605 605
606 606 def bootstrap_config(self, conf):
607 607 """
608 608 Loads the pylons configuration.
609 609 """
610 610 from pylons import config as pylonsconfig
611 611
612 612 path_to_ini_file = os.path.realpath(conf)
613 613 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
614 614 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
General Comments 0
You need to be logged in to leave comments. Login now