##// END OF EJS Templates
refactor codes and setup for python 2.5...
marcink -
r564:ba7e24cd default
parent child Browse files
Show More
@@ -1,94 +1,87 b''
1 --------------------------------------------------------------
2 Pylons based repository management for mercurial (and soon git)
3 --------------------------------------------------------------
4
5 Fully customizable, with authentication, permissions. Based on vcs library.
1 ------------------------------------------------
2 Pylons based repository management for mercurial
3 ------------------------------------------------
6 4
7 5 **Overview**
8 6
9 - has it's own middleware to handle mercurial protocol request each request can
10 be logged and authenticated + threaded performance unlikely to hgweb
11 - full permissions per project read/write/admin access even on mercurial request
12 - mako templates let's you customize look and feel of application.
13 - diffs annotations and source code all colored by pygments.
14 - mercurial branch graph and yui-flot powered graphs with zooming and statistics
15 - admin interface for performing user/permission managements as well as repository
16 management.
17 - server side forks, it's possible to fork a project and hack it free without
7 - Has it's own middleware to handle mercurial protocol request. Each request can
8 be logged and authenticated. Runs on threads unlikely to hgweb You can make
9 multiple pulls/pushes simultaneous
10 - Full permissions and authentication per project private/read/write/admin.
11 One account for web interface and mercurial push/pull/clone.
12 - Mako templates let's you customize look and feel of application.
13 - Beautiful diffs, annotations and source codes all colored by pygments.
14 - Mercurial branch graph and yui-flot powered graphs with zooming and statistics
15 - Admin interface with user/permission management. User activity journal logs
16 pulls, pushes, forks,registrations. Possible to disable built in hooks
17 - Server side forks, it's possible to fork a project and hack it free without
18 18 breaking the main.
19 - full text search of source codes with indexing daemons using whoosh
19 - Full text search on source codes, search on file names. All powered by whoosh
20 and build in indexing daemons
20 21 (no external search servers required all in one application)
21 - async tasks for speed and performance using celery (works without them too)
22 - Additional settings for mercurial web, (hooks editable from admin
23 panel !) also manage paths, archive, remote messages
24 - backup scripts can do backup of whole app and send it over scp to desired location
25 - setup project descriptions and info inside built in db for easy, non
22 - Rss / atom feeds, gravatar support, download sources as zip/tarballs
23 - Async tasks for speed and performance using celery (works without them too)
24 - Backup scripts can do backup of whole app and send it over scp to desired
25 location
26 - Setup project descriptions and info inside built in db for easy, non
26 27 file-system operations
27 - added cache with invalidation on push/repo management for high performance and
28 - Added cache with invalidation on push/repo management for high performance and
28 29 always up to date data.
29 - rss / atom feeds, gravatar support
30 - based on pylons 1.0 / sqlalchemy 0.6
30 - Based on pylons 1.0 / sqlalchemy 0.6 / sqlite
31 31
32 32 **Incoming**
33 33
34 34 - code review based on hg-review (when it's stable)
35 35 - git support (when vcs can handle it - almost there !)
36 36 - commit based wikis
37 37 - clonning from remote repositories into rhodecode (git/mercurial)
38 38 - other cools stuff that i can figure out (or You can help me figure out)
39
40 .. note::
41 This software is still in beta mode.
42 I don't guarantee that it'll work correctly.
43 39
40 ------------
41 Installation
42 ------------
44 43
45 -------------
46 Installation
47 -------------
48
49 quick setup
44 **quick setup**
50 45
51 - pip install -E rhodecode-venv http://bitbucket.org/marcinkuzminski/rhodecode/get/tip.zip
46 - pip install -E rhodecode-venv rhodecode
52 47 - activate virtualenv
53 48 - run `paster make-config RhodeCode production.ini`
54 49 - run `paster setup-app production.ini`
55 50 - run `paster runserver production.ini`
56 51
57 52 You're ready to go.
58 53
59
60 MORE DETAILED INSTRUCTIONS
54 **MORE DETAILED INSTRUCTIONS**
61 55
62 56 - I highly recommend to install new virtualenv for rhodecode see
63 57 http://pypi.python.org/pypi/virtualenv for more details.
64 58 - Create new virtualenv using `virtualenv --no-site-packages /var/www/rhodecode-venv`
65 59 this will install new virtual env into /var/www/rhodecode-venv.
66 60 Activate the virtualenv by running
67 61 `source activate /var/www/rhodecode-venv/bin/activate`
68 62 - Make a folder for rhodecode somewhere on the filesystem for example /var/www/rhodecode
69 - Run easy_install http://bitbucket.org/marcinkuzminski/rhodecode/get/tip.zip.
63 - Run easy_install rhodecode
70 64 - Run `paster make-config RhodeCode production.inii` in order to install
71 65 the application config. You can play with the app settings later
72 66 - Run `paster setup-app production.ini` it should create all needed tables
73 67 and an admin account make sure You specify correct path to repositories.
74 68 - Remember that the given path for mercurial repositories must be write
75 69 accessible for the application
76 70 - Run paster serve production.ini - or you can use sample init.d scripts.
77 71 the app should be available at the 127.0.0.1:5000
78 72 - Use admin account you created to login.
79 73 - Default permissions on each repository is read, and owner is admin. So remember
80 74 to update these.
81 75 - In order to use full power of async tasks, You must install message broker
82 76 preferably rabbitmq and start celeryd daemon together with rhodecode.
83 77 The app should gain a lot of speed and become much more responsible.
84 78 For installation instructions You can visit:
85 79 http://ask.github.com/celery/getting-started/index.html.
86 - All needed configs are inside rhodecode ie. celeryconfig.py , production.ini
87 You can configure the email, ports, loggers, workers from there.
80 - All needed configs are inside rhodecode sources ie. celeryconfig.py,
81 development.ini, production.ini You can configure the email, ports, loggers,
82 workers from there.
88 83 - For full text search You can either put crontab entry for
89 84 `python /var/www/rhodecode/rhodecode/lib/indexers/daemon.py incremental <path_to_repos>`
90 85 or run indexer from admin panel. This will scann the repos given in the
91 86 application setup or given path for daemon.py and each scann in incremental
92 mode will scann only changed files,
93 Hg Update hook must be activated to index the content it's enabled by default
94 after setup No newline at end of file
87 mode will scann only changed files. No newline at end of file
@@ -1,163 +1,163 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # permissions controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 27, 2010
22 22 permissions controller for pylons
23 23 @author: marcink
24 24 """
25 25
26 26 from formencode import htmlfill
27 27 from pylons import request, session, tmpl_context as c, url
28 28 from pylons.controllers.util import abort, redirect
29 29 from pylons.i18n.translation import _
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 32 from rhodecode.lib.base import BaseController, render
33 33 from rhodecode.model.db import User, UserLog
34 34 from rhodecode.model.forms import UserForm, DefaultPermissionsForm
35 35 from rhodecode.model.permission_model import PermissionModel
36 36 from rhodecode.model.user_model import UserModel
37 37 import formencode
38 38 import logging
39 39 import traceback
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43 class PermissionsController(BaseController):
44 44 """REST Controller styled on the Atom Publishing Protocol"""
45 45 # To properly map this controller, ensure your config/routing.py
46 46 # file has a resource setup:
47 47 # map.resource('permission', 'permissions')
48 48
49 49 @LoginRequired()
50 50 @HasPermissionAllDecorator('hg.admin')
51 51 def __before__(self):
52 52 c.admin_user = session.get('admin_user')
53 53 c.admin_username = session.get('admin_username')
54 54 super(PermissionsController, self).__before__()
55 55
56 56 self.perms_choices = [('repository.none', _('None'),),
57 57 ('repository.read', _('Read'),),
58 58 ('repository.write', _('Write'),),
59 59 ('repository.admin', _('Admin'),)]
60 60 self.register_choices = [
61 61 ('hg.register.none', 'disabled'),
62 62 ('hg.register.manual_activate',
63 63 _('allowed with manual account activation')),
64 64 ('hg.register.auto_activate',
65 65 _('allowed with automatic account activation')), ]
66 66
67 67 self.create_choices = [('hg.create.none', _('Disabled')),
68 68 ('hg.create.repository', _('Enabled'))]
69 69
70 70
71 71 def index(self, format='html'):
72 72 """GET /permissions: All items in the collection"""
73 73 # url('permissions')
74 74
75 75 def create(self):
76 76 """POST /permissions: Create a new item"""
77 77 # url('permissions')
78 78
79 79 def new(self, format='html'):
80 80 """GET /permissions/new: Form to create a new item"""
81 81 # url('new_permission')
82 82
83 83 def update(self, id):
84 84 """PUT /permissions/id: Update an existing item"""
85 85 # Forms posted to this method should contain a hidden field:
86 86 # <input type="hidden" name="_method" value="PUT" />
87 87 # Or using helpers:
88 88 # h.form(url('permission', id=ID),
89 89 # method='put')
90 90 # url('permission', id=ID)
91 91
92 92 permission_model = PermissionModel()
93 93
94 94 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
95 95 [x[0] for x in self.register_choices],
96 96 [x[0] for x in self.create_choices])()
97 97
98 98 try:
99 99 form_result = _form.to_python(dict(request.POST))
100 100 form_result.update({'perm_user_name':id})
101 101 permission_model.update(form_result)
102 102 h.flash(_('Default permissions updated succesfully'),
103 103 category='success')
104 104
105 except formencode.Invalid as errors:
105 except formencode.Invalid, errors:
106 106 c.perms_choices = self.perms_choices
107 107 c.register_choices = self.register_choices
108 108 c.create_choices = self.create_choices
109 109
110 110 return htmlfill.render(
111 111 render('admin/permissions/permissions.html'),
112 112 defaults=errors.value,
113 113 errors=errors.error_dict or {},
114 114 prefix_error=False,
115 115 encoding="UTF-8")
116 116 except Exception:
117 117 log.error(traceback.format_exc())
118 118 h.flash(_('error occured during update of permissions'),
119 119 category='error')
120 120
121 121 return redirect(url('edit_permission', id=id))
122 122
123 123
124 124
125 125 def delete(self, id):
126 126 """DELETE /permissions/id: Delete an existing item"""
127 127 # Forms posted to this method should contain a hidden field:
128 128 # <input type="hidden" name="_method" value="DELETE" />
129 129 # Or using helpers:
130 130 # h.form(url('permission', id=ID),
131 131 # method='delete')
132 132 # url('permission', id=ID)
133 133
134 134 def show(self, id, format='html'):
135 135 """GET /permissions/id: Show a specific item"""
136 136 # url('permission', id=ID)
137 137
138 138 def edit(self, id, format='html'):
139 139 """GET /permissions/id/edit: Form to edit an existing item"""
140 140 #url('edit_permission', id=ID)
141 141 c.perms_choices = self.perms_choices
142 142 c.register_choices = self.register_choices
143 143 c.create_choices = self.create_choices
144 144
145 145 if id == 'default':
146 146 defaults = {'_method':'put'}
147 147 for p in UserModel().get_default().user_perms:
148 148 if p.permission.permission_name.startswith('repository.'):
149 149 defaults['default_perm'] = p.permission.permission_name
150 150
151 151 if p.permission.permission_name.startswith('hg.register.'):
152 152 defaults['default_register'] = p.permission.permission_name
153 153
154 154 if p.permission.permission_name.startswith('hg.create.'):
155 155 defaults['default_create'] = p.permission.permission_name
156 156
157 157 return htmlfill.render(
158 158 render('admin/permissions/permissions.html'),
159 159 defaults=defaults,
160 160 encoding="UTF-8",
161 161 force_defaults=True,)
162 162 else:
163 163 return redirect(url('admin_home'))
@@ -1,245 +1,245 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # repos controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 7, 2010
22 22 admin controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from operator import itemgetter
27 27 from paste.httpexceptions import HTTPInternalServerError
28 28 from pylons import request, response, session, tmpl_context as c, url
29 29 from pylons.controllers.util import abort, redirect
30 30 from pylons.i18n.translation import _
31 31 from rhodecode.lib import helpers as h
32 32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
33 33 HasPermissionAnyDecorator
34 34 from rhodecode.lib.base import BaseController, render
35 35 from rhodecode.lib.utils import invalidate_cache, action_logger
36 36 from rhodecode.model.db import User
37 37 from rhodecode.model.forms import RepoForm
38 38 from rhodecode.model.hg_model import HgModel
39 39 from rhodecode.model.repo_model import RepoModel
40 40 import formencode
41 41 import logging
42 42 import traceback
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 class ReposController(BaseController):
47 47 """REST Controller styled on the Atom Publishing Protocol"""
48 48 # To properly map this controller, ensure your config/routing.py
49 49 # file has a resource setup:
50 50 # map.resource('repo', 'repos')
51 51
52 52 @LoginRequired()
53 53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
54 54 def __before__(self):
55 55 c.admin_user = session.get('admin_user')
56 56 c.admin_username = session.get('admin_username')
57 57 super(ReposController, self).__before__()
58 58
59 59 @HasPermissionAllDecorator('hg.admin')
60 60 def index(self, format='html'):
61 61 """GET /repos: All items in the collection"""
62 62 # url('repos')
63 63 cached_repo_list = HgModel().get_repos()
64 64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
65 65 return render('admin/repos/repos.html')
66 66
67 67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
68 68 def create(self):
69 69 """POST /repos: Create a new item"""
70 70 # url('repos')
71 71 repo_model = RepoModel()
72 72 _form = RepoForm()()
73 73 form_result = {}
74 74 try:
75 75 form_result = _form.to_python(dict(request.POST))
76 76 repo_model.create(form_result, c.rhodecode_user)
77 77 invalidate_cache('cached_repo_list')
78 78 h.flash(_('created repository %s') % form_result['repo_name'],
79 79 category='success')
80 80
81 81 if request.POST.get('user_created'):
82 action_logger(self.rhodecode_user, 'user_created_repo',
82 action_logger(self.rhodecode_user, 'user_created_repo',
83 83 form_result['repo_name'], '', self.sa)
84 84 else:
85 action_logger(self.rhodecode_user, 'admin_created_repo',
85 action_logger(self.rhodecode_user, 'admin_created_repo',
86 86 form_result['repo_name'], '', self.sa)
87 87
88 except formencode.Invalid as errors:
88 except formencode.Invalid, errors:
89 89 c.new_repo = errors.value['repo_name']
90 90
91 91 if request.POST.get('user_created'):
92 92 r = render('admin/repos/repo_add_create_repository.html')
93 93 else:
94 94 r = render('admin/repos/repo_add.html')
95 95
96 96 return htmlfill.render(
97 97 r,
98 98 defaults=errors.value,
99 99 errors=errors.error_dict or {},
100 100 prefix_error=False,
101 101 encoding="UTF-8")
102 102
103 103 except Exception:
104 104 log.error(traceback.format_exc())
105 105 msg = _('error occured during creation of repository %s') \
106 106 % form_result.get('repo_name')
107 107 h.flash(msg, category='error')
108 108 if request.POST.get('user_created'):
109 109 return redirect(url('hg_home'))
110 110 return redirect(url('repos'))
111 111
112 112 @HasPermissionAllDecorator('hg.admin')
113 113 def new(self, format='html'):
114 114 """GET /repos/new: Form to create a new item"""
115 115 new_repo = request.GET.get('repo', '')
116 116 c.new_repo = h.repo_name_slug(new_repo)
117 117
118 118 return render('admin/repos/repo_add.html')
119 119
120 120 @HasPermissionAllDecorator('hg.admin')
121 121 def update(self, repo_name):
122 122 """PUT /repos/repo_name: Update an existing item"""
123 123 # Forms posted to this method should contain a hidden field:
124 124 # <input type="hidden" name="_method" value="PUT" />
125 125 # Or using helpers:
126 126 # h.form(url('repo', repo_name=ID),
127 127 # method='put')
128 128 # url('repo', repo_name=ID)
129 129 repo_model = RepoModel()
130 130 changed_name = repo_name
131 131 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
132 132
133 133 try:
134 134 form_result = _form.to_python(dict(request.POST))
135 135 repo_model.update(repo_name, form_result)
136 136 invalidate_cache('cached_repo_list')
137 137 h.flash(_('Repository %s updated succesfully' % repo_name),
138 138 category='success')
139 139 changed_name = form_result['repo_name']
140 except formencode.Invalid as errors:
140 except formencode.Invalid, errors:
141 141 c.repo_info = repo_model.get(repo_name)
142 142 c.users_array = repo_model.get_users_js()
143 143 errors.value.update({'user':c.repo_info.user.username})
144 144 return htmlfill.render(
145 145 render('admin/repos/repo_edit.html'),
146 146 defaults=errors.value,
147 147 errors=errors.error_dict or {},
148 148 prefix_error=False,
149 149 encoding="UTF-8")
150 150
151 151 except Exception:
152 152 log.error(traceback.format_exc())
153 153 h.flash(_('error occured during update of repository %s') \
154 154 % repo_name, category='error')
155 155
156 156 return redirect(url('edit_repo', repo_name=changed_name))
157 157
158 158 @HasPermissionAllDecorator('hg.admin')
159 159 def delete(self, repo_name):
160 160 """DELETE /repos/repo_name: Delete an existing item"""
161 161 # Forms posted to this method should contain a hidden field:
162 162 # <input type="hidden" name="_method" value="DELETE" />
163 163 # Or using helpers:
164 164 # h.form(url('repo', repo_name=ID),
165 165 # method='delete')
166 166 # url('repo', repo_name=ID)
167 167
168 168 repo_model = RepoModel()
169 169 repo = repo_model.get(repo_name)
170 170 if not repo:
171 171 h.flash(_('%s repository is not mapped to db perhaps'
172 172 ' it was moved or renamed from the filesystem'
173 173 ' please run the application again'
174 174 ' in order to rescan repositories') % repo_name,
175 175 category='error')
176 176
177 177 return redirect(url('repos'))
178 178 try:
179 action_logger(self.rhodecode_user, 'admin_deleted_repo',
179 action_logger(self.rhodecode_user, 'admin_deleted_repo',
180 180 repo_name, '', self.sa)
181 181 repo_model.delete(repo)
182 182 invalidate_cache('cached_repo_list')
183 183 h.flash(_('deleted repository %s') % repo_name, category='success')
184 184
185 185 except Exception, e:
186 186 log.error(traceback.format_exc())
187 187 h.flash(_('An error occured during deletion of %s') % repo_name,
188 188 category='error')
189 189
190 190 return redirect(url('repos'))
191 191
192 192 @HasPermissionAllDecorator('hg.admin')
193 193 def delete_perm_user(self, repo_name):
194 194 """
195 195 DELETE an existing repository permission user
196 196 @param repo_name:
197 197 """
198 198
199 199 try:
200 200 repo_model = RepoModel()
201 201 repo_model.delete_perm_user(request.POST, repo_name)
202 except Exception as e:
202 except Exception, e:
203 203 h.flash(_('An error occured during deletion of repository user'),
204 204 category='error')
205 205 raise HTTPInternalServerError()
206 206
207 207 @HasPermissionAllDecorator('hg.admin')
208 208 def show(self, repo_name, format='html'):
209 209 """GET /repos/repo_name: Show a specific item"""
210 210 # url('repo', repo_name=ID)
211 211
212 212 @HasPermissionAllDecorator('hg.admin')
213 213 def edit(self, repo_name, format='html'):
214 214 """GET /repos/repo_name/edit: Form to edit an existing item"""
215 215 # url('edit_repo', repo_name=ID)
216 216 repo_model = RepoModel()
217 217 c.repo_info = repo = repo_model.get(repo_name)
218 218 if not repo:
219 219 h.flash(_('%s repository is not mapped to db perhaps'
220 220 ' it was created or renamed from the filesystem'
221 221 ' please run the application again'
222 222 ' in order to rescan repositories') % repo_name,
223 223 category='error')
224 224
225 225 return redirect(url('repos'))
226 226 defaults = c.repo_info.__dict__
227 227 if c.repo_info.user:
228 228 defaults.update({'user':c.repo_info.user.username})
229 229 else:
230 230 replacement_user = self.sa.query(User)\
231 231 .filter(User.admin == True).first().username
232 232 defaults.update({'user':replacement_user})
233 233
234 234 c.users_array = repo_model.get_users_js()
235 235
236 236 for p in c.repo_info.repo_to_perm:
237 237 defaults.update({'perm_%s' % p.user.username:
238 238 p.permission.permission_name})
239 239
240 240 return htmlfill.render(
241 241 render('admin/repos/repo_edit.html'),
242 242 defaults=defaults,
243 243 encoding="UTF-8",
244 244 force_defaults=False
245 245 )
@@ -1,298 +1,298 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # settings controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on July 14, 2010
22 22 settings controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
27 27 config
28 28 from pylons.controllers.util import abort, redirect
29 29 from pylons.i18n.translation import _
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 32 HasPermissionAnyDecorator
33 33 from rhodecode.lib.base import BaseController, render
34 34 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
35 35 set_rhodecode_config, get_hg_settings, get_hg_ui_settings, make_ui
36 36 from rhodecode.model.db import User, UserLog, RhodeCodeSettings, RhodeCodeUi
37 37 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
38 38 ApplicationUiSettingsForm
39 39 from rhodecode.model.hg_model import HgModel
40 40 from rhodecode.model.user_model import UserModel
41 41 from rhodecode.lib.celerylib import tasks, run_task
42 42 import formencode
43 43 import logging
44 44 import traceback
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 class SettingsController(BaseController):
50 50 """REST Controller styled on the Atom Publishing Protocol"""
51 51 # To properly map this controller, ensure your config/routing.py
52 52 # file has a resource setup:
53 53 # map.resource('setting', 'settings', controller='admin/settings',
54 54 # path_prefix='/admin', name_prefix='admin_')
55 55
56 56
57 57 @LoginRequired()
58 58 def __before__(self):
59 59 c.admin_user = session.get('admin_user')
60 60 c.admin_username = session.get('admin_username')
61 61 super(SettingsController, self).__before__()
62 62
63 63
64 64 @HasPermissionAllDecorator('hg.admin')
65 65 def index(self, format='html'):
66 66 """GET /admin/settings: All items in the collection"""
67 67 # url('admin_settings')
68 68
69 69 defaults = get_hg_settings()
70 70 defaults.update(get_hg_ui_settings())
71 71 return htmlfill.render(
72 72 render('admin/settings/settings.html'),
73 73 defaults=defaults,
74 74 encoding="UTF-8",
75 75 force_defaults=False
76 76 )
77 77
78 78 @HasPermissionAllDecorator('hg.admin')
79 79 def create(self):
80 80 """POST /admin/settings: Create a new item"""
81 81 # url('admin_settings')
82 82
83 83 @HasPermissionAllDecorator('hg.admin')
84 84 def new(self, format='html'):
85 85 """GET /admin/settings/new: Form to create a new item"""
86 86 # url('admin_new_setting')
87 87
88 88 @HasPermissionAllDecorator('hg.admin')
89 89 def update(self, setting_id):
90 90 """PUT /admin/settings/setting_id: Update an existing item"""
91 91 # Forms posted to this method should contain a hidden field:
92 92 # <input type="hidden" name="_method" value="PUT" />
93 93 # Or using helpers:
94 94 # h.form(url('admin_setting', setting_id=ID),
95 95 # method='put')
96 96 # url('admin_setting', setting_id=ID)
97 97 if setting_id == 'mapping':
98 98 rm_obsolete = request.POST.get('destroy', False)
99 99 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
100 100
101 101 initial = HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
102 102 repo2db_mapper(initial, rm_obsolete)
103 103 invalidate_cache('cached_repo_list')
104 104 h.flash(_('Repositories successfully rescanned'), category='success')
105 105
106 106 if setting_id == 'whoosh':
107 107 repo_location = get_hg_ui_settings()['paths_root_path']
108 108 full_index = request.POST.get('full_index', False)
109 109 task = run_task(tasks.whoosh_index, repo_location, full_index)
110 110
111 111 h.flash(_('Whoosh reindex task scheduled'), category='success')
112 112 if setting_id == 'global':
113 113
114 114 application_form = ApplicationSettingsForm()()
115 115 try:
116 116 form_result = application_form.to_python(dict(request.POST))
117 117
118 118 try:
119 119 hgsettings1 = self.sa.query(RhodeCodeSettings)\
120 120 .filter(RhodeCodeSettings.app_settings_name == 'title').one()
121 121 hgsettings1.app_settings_value = form_result['rhodecode_title']
122 122
123 123 hgsettings2 = self.sa.query(RhodeCodeSettings)\
124 124 .filter(RhodeCodeSettings.app_settings_name == 'realm').one()
125 125 hgsettings2.app_settings_value = form_result['rhodecode_realm']
126 126
127 127
128 128 self.sa.add(hgsettings1)
129 129 self.sa.add(hgsettings2)
130 130 self.sa.commit()
131 131 set_rhodecode_config(config)
132 132 h.flash(_('Updated application settings'),
133 133 category='success')
134 134
135 135 except:
136 136 log.error(traceback.format_exc())
137 137 h.flash(_('error occurred during updating application settings'),
138 138 category='error')
139 139
140 140 self.sa.rollback()
141 141
142 142
143 except formencode.Invalid as errors:
143 except formencode.Invalid, errors:
144 144 return htmlfill.render(
145 145 render('admin/settings/settings.html'),
146 146 defaults=errors.value,
147 147 errors=errors.error_dict or {},
148 148 prefix_error=False,
149 149 encoding="UTF-8")
150 150
151 151 if setting_id == 'mercurial':
152 152 application_form = ApplicationUiSettingsForm()()
153 153 try:
154 154 form_result = application_form.to_python(dict(request.POST))
155 155
156 156 try:
157 157
158 158 hgsettings1 = self.sa.query(RhodeCodeUi)\
159 159 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
160 160 hgsettings1.ui_value = form_result['web_push_ssl']
161 161
162 162 hgsettings2 = self.sa.query(RhodeCodeUi)\
163 163 .filter(RhodeCodeUi.ui_key == '/').one()
164 164 hgsettings2.ui_value = form_result['paths_root_path']
165 165
166 166
167 167 #HOOKS
168 168 hgsettings3 = self.sa.query(RhodeCodeUi)\
169 169 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
170 170 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
171 171
172 172 hgsettings4 = self.sa.query(RhodeCodeUi)\
173 173 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
174 174 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
175 175
176 176
177 177
178 178
179 179 self.sa.add(hgsettings1)
180 180 self.sa.add(hgsettings2)
181 181 self.sa.add(hgsettings3)
182 182 self.sa.add(hgsettings4)
183 183 self.sa.commit()
184 184
185 185 h.flash(_('Updated mercurial settings'),
186 186 category='success')
187 187
188 188 except:
189 189 log.error(traceback.format_exc())
190 190 h.flash(_('error occurred during updating application settings'),
191 191 category='error')
192 192
193 193 self.sa.rollback()
194 194
195 195
196 except formencode.Invalid as errors:
196 except formencode.Invalid, errors:
197 197 return htmlfill.render(
198 198 render('admin/settings/settings.html'),
199 199 defaults=errors.value,
200 200 errors=errors.error_dict or {},
201 201 prefix_error=False,
202 202 encoding="UTF-8")
203 203
204 204
205 205
206 206 return redirect(url('admin_settings'))
207 207
208 208 @HasPermissionAllDecorator('hg.admin')
209 209 def delete(self, setting_id):
210 210 """DELETE /admin/settings/setting_id: Delete an existing item"""
211 211 # Forms posted to this method should contain a hidden field:
212 212 # <input type="hidden" name="_method" value="DELETE" />
213 213 # Or using helpers:
214 214 # h.form(url('admin_setting', setting_id=ID),
215 215 # method='delete')
216 216 # url('admin_setting', setting_id=ID)
217 217
218 218 @HasPermissionAllDecorator('hg.admin')
219 219 def show(self, setting_id, format='html'):
220 220 """GET /admin/settings/setting_id: Show a specific item"""
221 221 # url('admin_setting', setting_id=ID)
222 222
223 223 @HasPermissionAllDecorator('hg.admin')
224 224 def edit(self, setting_id, format='html'):
225 225 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
226 226 # url('admin_edit_setting', setting_id=ID)
227 227
228 228
229 229 def my_account(self):
230 230 """
231 231 GET /_admin/my_account Displays info about my account
232 232 """
233 233 # url('admin_settings_my_account')
234 234 c.user = self.sa.query(User).get(c.rhodecode_user.user_id)
235 235 c.user_repos = []
236 236 for repo in c.cached_repo_list.values():
237 237 if repo.dbrepo.user.username == c.user.username:
238 238 c.user_repos.append(repo)
239 239
240 240 if c.user.username == 'default':
241 241 h.flash(_("You can't edit this user since it's"
242 242 " crucial for entire application"), category='warning')
243 243 return redirect(url('users'))
244 244
245 245 defaults = c.user.__dict__
246 246 return htmlfill.render(
247 247 render('admin/users/user_edit_my_account.html'),
248 248 defaults=defaults,
249 249 encoding="UTF-8",
250 250 force_defaults=False
251 251 )
252 252
253 253 def my_account_update(self):
254 254 """PUT /_admin/my_account_update: Update an existing item"""
255 255 # Forms posted to this method should contain a hidden field:
256 256 # <input type="hidden" name="_method" value="PUT" />
257 257 # Or using helpers:
258 258 # h.form(url('admin_settings_my_account_update'),
259 259 # method='put')
260 260 # url('admin_settings_my_account_update', id=ID)
261 261 user_model = UserModel()
262 262 uid = c.rhodecode_user.user_id
263 263 _form = UserForm(edit=True, old_data={'user_id':uid,
264 264 'email':c.rhodecode_user.email})()
265 265 form_result = {}
266 266 try:
267 267 form_result = _form.to_python(dict(request.POST))
268 268 user_model.update_my_account(uid, form_result)
269 269 h.flash(_('Your account was updated succesfully'),
270 270 category='success')
271 271
272 except formencode.Invalid as errors:
272 except formencode.Invalid, errors:
273 273 c.user = self.sa.query(User).get(c.rhodecode_user.user_id)
274 274 c.user_repos = []
275 275 for repo in c.cached_repo_list.values():
276 276 if repo.dbrepo.user.username == c.user.username:
277 277 c.user_repos.append(repo)
278 278 return htmlfill.render(
279 279 render('admin/users/user_edit_my_account.html'),
280 280 defaults=errors.value,
281 281 errors=errors.error_dict or {},
282 282 prefix_error=False,
283 283 encoding="UTF-8")
284 284 except Exception:
285 285 log.error(traceback.format_exc())
286 286 h.flash(_('error occured during update of user %s') \
287 287 % form_result.get('username'), category='error')
288 288
289 289 return redirect(url('my_account'))
290 290
291 291 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
292 292 def create_repository(self):
293 293 """GET /_admin/create_repository: Form to create a new item"""
294 294 new_repo = request.GET.get('repo', '')
295 295 c.new_repo = h.repo_name_slug(new_repo)
296 296
297 297 return render('admin/repos/repo_add_create_repository.html')
298 298
@@ -1,168 +1,168 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # users controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 from rhodecode.lib.utils import action_logger
21 21 """
22 22 Created on April 4, 2010
23 23 users controller for pylons
24 24 @author: marcink
25 25 """
26 26
27 27 from formencode import htmlfill
28 28 from pylons import request, session, tmpl_context as c, url
29 29 from pylons.controllers.util import abort, redirect
30 30 from pylons.i18n.translation import _
31 31 from rhodecode.lib import helpers as h
32 32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
33 33 from rhodecode.lib.base import BaseController, render
34 34 from rhodecode.model.db import User, UserLog
35 35 from rhodecode.model.forms import UserForm
36 36 from rhodecode.model.user_model import UserModel, DefaultUserException
37 37 import formencode
38 38 import logging
39 39 import traceback
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43 class UsersController(BaseController):
44 44 """REST Controller styled on the Atom Publishing Protocol"""
45 45 # To properly map this controller, ensure your config/routing.py
46 46 # file has a resource setup:
47 47 # map.resource('user', 'users')
48 48
49 49 @LoginRequired()
50 50 @HasPermissionAllDecorator('hg.admin')
51 51 def __before__(self):
52 52 c.admin_user = session.get('admin_user')
53 53 c.admin_username = session.get('admin_username')
54 54 super(UsersController, self).__before__()
55 55
56 56
57 57 def index(self, format='html'):
58 58 """GET /users: All items in the collection"""
59 59 # url('users')
60 60
61 61 c.users_list = self.sa.query(User).all()
62 62 return render('admin/users/users.html')
63 63
64 64 def create(self):
65 65 """POST /users: Create a new item"""
66 66 # url('users')
67 67
68 68 user_model = UserModel()
69 69 login_form = UserForm()()
70 70 try:
71 71 form_result = login_form.to_python(dict(request.POST))
72 72 user_model.create(form_result)
73 73 h.flash(_('created user %s') % form_result['username'],
74 74 category='success')
75 75 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
76 except formencode.Invalid as errors:
76 except formencode.Invalid, errors:
77 77 return htmlfill.render(
78 78 render('admin/users/user_add.html'),
79 79 defaults=errors.value,
80 80 errors=errors.error_dict or {},
81 81 prefix_error=False,
82 82 encoding="UTF-8")
83 83 except Exception:
84 84 log.error(traceback.format_exc())
85 85 h.flash(_('error occured during creation of user %s') \
86 86 % request.POST.get('username'), category='error')
87 87 return redirect(url('users'))
88 88
89 89 def new(self, format='html'):
90 90 """GET /users/new: Form to create a new item"""
91 91 # url('new_user')
92 92 return render('admin/users/user_add.html')
93 93
94 94 def update(self, id):
95 95 """PUT /users/id: Update an existing item"""
96 96 # Forms posted to this method should contain a hidden field:
97 97 # <input type="hidden" name="_method" value="PUT" />
98 98 # Or using helpers:
99 99 # h.form(url('user', id=ID),
100 100 # method='put')
101 101 # url('user', id=ID)
102 102 user_model = UserModel()
103 103 c.user = user_model.get_user(id)
104 104
105 105 _form = UserForm(edit=True, old_data={'user_id':id,
106 106 'email':c.user.email})()
107 107 form_result = {}
108 108 try:
109 109 form_result = _form.to_python(dict(request.POST))
110 110 user_model.update(id, form_result)
111 111 h.flash(_('User updated succesfully'), category='success')
112 112
113 except formencode.Invalid as errors:
113 except formencode.Invalid, errors:
114 114 return htmlfill.render(
115 115 render('admin/users/user_edit.html'),
116 116 defaults=errors.value,
117 117 errors=errors.error_dict or {},
118 118 prefix_error=False,
119 119 encoding="UTF-8")
120 120 except Exception:
121 121 log.error(traceback.format_exc())
122 122 h.flash(_('error occured during update of user %s') \
123 123 % form_result.get('username'), category='error')
124 124
125 125 return redirect(url('users'))
126 126
127 127 def delete(self, id):
128 128 """DELETE /users/id: Delete an existing item"""
129 129 # Forms posted to this method should contain a hidden field:
130 130 # <input type="hidden" name="_method" value="DELETE" />
131 131 # Or using helpers:
132 132 # h.form(url('user', id=ID),
133 133 # method='delete')
134 134 # url('user', id=ID)
135 135 user_model = UserModel()
136 136 try:
137 137 user_model.delete(id)
138 138 h.flash(_('sucessfully deleted user'), category='success')
139 except DefaultUserException as e:
139 except DefaultUserException, e:
140 140 h.flash(str(e), category='warning')
141 141 except Exception:
142 142 h.flash(_('An error occured during deletion of user'),
143 143 category='error')
144 144 return redirect(url('users'))
145 145
146 146 def show(self, id, format='html'):
147 147 """GET /users/id: Show a specific item"""
148 148 # url('user', id=ID)
149 149
150 150
151 151 def edit(self, id, format='html'):
152 152 """GET /users/id/edit: Form to edit an existing item"""
153 153 # url('edit_user', id=ID)
154 154 c.user = self.sa.query(User).get(id)
155 155 if not c.user:
156 156 return redirect(url('users'))
157 157 if c.user.username == 'default':
158 158 h.flash(_("You can't edit this user since it's"
159 159 " crucial for entire application"), category='warning')
160 160 return redirect(url('users'))
161 161
162 162 defaults = c.user.__dict__
163 163 return htmlfill.render(
164 164 render('admin/users/user_edit.html'),
165 165 defaults=defaults,
166 166 encoding="UTF-8",
167 167 force_defaults=False
168 168 )
@@ -1,144 +1,144 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # login controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20
21 21 """
22 22 Created on April 22, 2010
23 23 login controller for pylons
24 24 @author: marcink
25 25 """
26 26 from formencode import htmlfill
27 27 from pylons import request, response, session, tmpl_context as c, url
28 28 from pylons.controllers.util import abort, redirect
29 29 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
30 30 from rhodecode.lib.base import BaseController, render
31 31 import rhodecode.lib.helpers as h
32 32 from pylons.i18n.translation import _
33 33 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
34 34 from rhodecode.model.user_model import UserModel
35 35 import formencode
36 36 import logging
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40 class LoginController(BaseController):
41 41
42 42 def __before__(self):
43 43 super(LoginController, self).__before__()
44 44
45 45 def index(self):
46 46 #redirect if already logged in
47 47 c.came_from = request.GET.get('came_from', None)
48 48
49 49 if c.rhodecode_user.is_authenticated:
50 50 return redirect(url('hg_home'))
51 51
52 52 if request.POST:
53 53 #import Login Form validator class
54 54 login_form = LoginForm()
55 55 try:
56 56 c.form_result = login_form.to_python(dict(request.POST))
57 57 username = c.form_result['username']
58 58 user = UserModel().get_user_by_name(username)
59 59 auth_user = AuthUser()
60 60 auth_user.username = user.username
61 61 auth_user.is_authenticated = True
62 62 auth_user.is_admin = user.admin
63 63 auth_user.user_id = user.user_id
64 64 auth_user.name = user.name
65 65 auth_user.lastname = user.lastname
66 66 session['rhodecode_user'] = auth_user
67 67 session.save()
68 68 log.info('user %s is now authenticated', username)
69 69
70 70 user.update_lastlogin()
71 71
72 72 if c.came_from:
73 73 return redirect(c.came_from)
74 74 else:
75 75 return redirect(url('hg_home'))
76 76
77 except formencode.Invalid as errors:
77 except formencode.Invalid, errors:
78 78 return htmlfill.render(
79 79 render('/login.html'),
80 80 defaults=errors.value,
81 81 errors=errors.error_dict or {},
82 82 prefix_error=False,
83 83 encoding="UTF-8")
84 84
85 85 return render('/login.html')
86 86
87 87 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
88 88 'hg.register.manual_activate')
89 89 def register(self):
90 90 user_model = UserModel()
91 91 c.auto_active = False
92 92 for perm in user_model.get_default().user_perms:
93 93 if perm.permission.permission_name == 'hg.register.auto_activate':
94 94 c.auto_active = True
95 95 break
96 96
97 97 if request.POST:
98 98
99 99 register_form = RegisterForm()()
100 100 try:
101 101 form_result = register_form.to_python(dict(request.POST))
102 102 form_result['active'] = c.auto_active
103 103 user_model.create_registration(form_result)
104 104 h.flash(_('You have successfully registered into rhodecode'),
105 105 category='success')
106 106 return redirect(url('login_home'))
107 107
108 except formencode.Invalid as errors:
108 except formencode.Invalid, errors:
109 109 return htmlfill.render(
110 110 render('/register.html'),
111 111 defaults=errors.value,
112 112 errors=errors.error_dict or {},
113 113 prefix_error=False,
114 114 encoding="UTF-8")
115 115
116 116 return render('/register.html')
117 117
118 118 def password_reset(self):
119 119 user_model = UserModel()
120 120 if request.POST:
121 121
122 122 password_reset_form = PasswordResetForm()()
123 123 try:
124 124 form_result = password_reset_form.to_python(dict(request.POST))
125 125 user_model.reset_password(form_result)
126 126 h.flash(_('Your new password was sent'),
127 127 category='success')
128 128 return redirect(url('login_home'))
129 129
130 except formencode.Invalid as errors:
130 except formencode.Invalid, errors:
131 131 return htmlfill.render(
132 132 render('/password_reset.html'),
133 133 defaults=errors.value,
134 134 errors=errors.error_dict or {},
135 135 prefix_error=False,
136 136 encoding="UTF-8")
137 137
138 138 return render('/password_reset.html')
139 139
140 140 def logout(self):
141 141 session['rhodecode_user'] = AuthUser()
142 142 session.save()
143 143 log.info('Logging out and setting user as Empty')
144 144 redirect(url('hg_home'))
@@ -1,175 +1,175 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # settings controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on June 30, 2010
22 22 settings controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from pylons import tmpl_context as c, request, url
27 27 from pylons.controllers.util import redirect
28 28 from pylons.i18n.translation import _
29 29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 30 from rhodecode.lib.base import BaseController, render
31 31 from rhodecode.lib.utils import invalidate_cache, action_logger
32 32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
33 33 from rhodecode.model.repo_model import RepoModel
34 34 import formencode
35 35 import logging
36 36 import rhodecode.lib.helpers as h
37 37 import traceback
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41 class SettingsController(BaseController):
42 42
43 43 @LoginRequired()
44 44 @HasRepoPermissionAllDecorator('repository.admin')
45 45 def __before__(self):
46 46 super(SettingsController, self).__before__()
47 47
48 48 def index(self, repo_name):
49 49 repo_model = RepoModel()
50 50 c.repo_info = repo = repo_model.get(repo_name)
51 51 if not repo:
52 52 h.flash(_('%s repository is not mapped to db perhaps'
53 53 ' it was created or renamed from the filesystem'
54 54 ' please run the application again'
55 55 ' in order to rescan repositories') % repo_name,
56 56 category='error')
57 57
58 58 return redirect(url('hg_home'))
59 59 defaults = c.repo_info.__dict__
60 60 defaults.update({'user':c.repo_info.user.username})
61 61 c.users_array = repo_model.get_users_js()
62 62
63 63 for p in c.repo_info.repo_to_perm:
64 64 defaults.update({'perm_%s' % p.user.username:
65 65 p.permission.permission_name})
66 66
67 67 return htmlfill.render(
68 68 render('settings/repo_settings.html'),
69 69 defaults=defaults,
70 70 encoding="UTF-8",
71 71 force_defaults=False
72 72 )
73 73
74 74 def update(self, repo_name):
75 75 repo_model = RepoModel()
76 76 changed_name = repo_name
77 77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
78 78 try:
79 79 form_result = _form.to_python(dict(request.POST))
80 80 repo_model.update(repo_name, form_result)
81 81 invalidate_cache('cached_repo_list')
82 82 h.flash(_('Repository %s updated successfully' % repo_name),
83 83 category='success')
84 84 changed_name = form_result['repo_name']
85 except formencode.Invalid as errors:
85 except formencode.Invalid, errors:
86 86 c.repo_info = repo_model.get(repo_name)
87 87 c.users_array = repo_model.get_users_js()
88 88 errors.value.update({'user':c.repo_info.user.username})
89 89 return htmlfill.render(
90 90 render('settings/repo_settings.html'),
91 91 defaults=errors.value,
92 92 errors=errors.error_dict or {},
93 93 prefix_error=False,
94 94 encoding="UTF-8")
95 95 except Exception:
96 96 log.error(traceback.format_exc())
97 97 h.flash(_('error occured during update of repository %s') \
98 98 % repo_name, category='error')
99 99
100 100 return redirect(url('repo_settings_home', repo_name=changed_name))
101 101
102 102
103 103
104 104 def delete(self, repo_name):
105 105 """DELETE /repos/repo_name: Delete an existing item"""
106 106 # Forms posted to this method should contain a hidden field:
107 107 # <input type="hidden" name="_method" value="DELETE" />
108 108 # Or using helpers:
109 109 # h.form(url('repo_settings_delete', repo_name=ID),
110 110 # method='delete')
111 111 # url('repo_settings_delete', repo_name=ID)
112 112
113 113 repo_model = RepoModel()
114 114 repo = repo_model.get(repo_name)
115 115 if not repo:
116 116 h.flash(_('%s repository is not mapped to db perhaps'
117 117 ' it was moved or renamed from the filesystem'
118 118 ' please run the application again'
119 119 ' in order to rescan repositories') % repo_name,
120 120 category='error')
121 121
122 122 return redirect(url('hg_home'))
123 123 try:
124 action_logger(self.rhodecode_user, 'user_deleted_repo',
124 action_logger(self.rhodecode_user, 'user_deleted_repo',
125 125 repo_name, '', self.sa)
126 126 repo_model.delete(repo)
127 127 invalidate_cache('cached_repo_list')
128 128 h.flash(_('deleted repository %s') % repo_name, category='success')
129 129 except Exception:
130 130 h.flash(_('An error occurred during deletion of %s') % repo_name,
131 131 category='error')
132 132
133 133 return redirect(url('hg_home'))
134 134
135 135 def fork(self, repo_name):
136 136 repo_model = RepoModel()
137 137 c.repo_info = repo = repo_model.get(repo_name)
138 138 if not repo:
139 139 h.flash(_('%s repository is not mapped to db perhaps'
140 140 ' it was created or renamed from the filesystem'
141 141 ' please run the application again'
142 142 ' in order to rescan repositories') % repo_name,
143 143 category='error')
144 144
145 145 return redirect(url('hg_home'))
146 146
147 147 return render('settings/repo_fork.html')
148 148
149 149
150 150
151 151 def fork_create(self, repo_name):
152 152 repo_model = RepoModel()
153 153 c.repo_info = repo_model.get(repo_name)
154 154 _form = RepoForkForm()()
155 155 form_result = {}
156 156 try:
157 157 form_result = _form.to_python(dict(request.POST))
158 158 form_result.update({'repo_name':repo_name})
159 159 repo_model.create_fork(form_result, c.rhodecode_user)
160 160 h.flash(_('fork %s repository as %s task added') \
161 161 % (repo_name, form_result['fork_name']),
162 162 category='success')
163 163 action_logger(self.rhodecode_user, 'user_forked_repo',
164 164 repo_name, '', self.sa)
165 except formencode.Invalid as errors:
165 except formencode.Invalid, errors:
166 166 c.new_repo = errors.value['fork_name']
167 167 r = render('settings/repo_fork.html')
168 168
169 169 return htmlfill.render(
170 170 r,
171 171 defaults=errors.value,
172 172 errors=errors.error_dict or {},
173 173 prefix_error=False,
174 174 encoding="UTF-8")
175 175 return redirect(url('hg_home'))
@@ -1,486 +1,486 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # authentication and permission libraries
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 4, 2010
22 22
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region
26 26 from pylons import config, session, url, request
27 27 from pylons.controllers.util import abort, redirect
28 28 from rhodecode.lib.utils import get_repo_slug
29 29 from rhodecode.model import meta
30 30 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
31 31 UserToPerm
32 32 from sqlalchemy.exc import OperationalError
33 33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
34 34 import bcrypt
35 35 from decorator import decorator
36 36 import logging
37 37 import random
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41 class PasswordGenerator(object):
42 42 """This is a simple class for generating password from
43 43 different sets of characters
44 44 usage:
45 45 passwd_gen = PasswordGenerator()
46 46 #print 8-letter password containing only big and small letters of alphabet
47 47 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
48 48 """
49 49 ALPHABETS_NUM = r'''1234567890'''#[0]
50 50 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
51 51 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
52 52 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
53 53 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
54 54 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
55 55 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
56 56 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
57 57 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
58 58
59 59 def __init__(self, passwd=''):
60 60 self.passwd = passwd
61 61
62 62 def gen_password(self, len, type):
63 63 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
64 64 return self.passwd
65 65
66 66
67 67 def get_crypt_password(password):
68 68 """Cryptographic function used for password hashing based on sha1
69 69 @param password: password to hash
70 70 """
71 71 return bcrypt.hashpw(password, bcrypt.gensalt(10))
72 72
73 73 def check_password(password, hashed):
74 74 return bcrypt.hashpw(password, hashed) == hashed
75 75
76 76 @cache_region('super_short_term', 'cached_user')
77 77 def get_user_cached(username):
78 78 sa = meta.Session
79 79 try:
80 80 user = sa.query(User).filter(User.username == username).one()
81 81 finally:
82 82 meta.Session.remove()
83 83 return user
84 84
85 85 def authfunc(environ, username, password):
86 86 try:
87 87 user = get_user_cached(username)
88 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
88 except (NoResultFound, MultipleResultsFound, OperationalError), e:
89 89 log.error(e)
90 90 user = None
91 91
92 92 if user:
93 93 if user.active:
94 94 if user.username == username and check_password(password, user.password):
95 95 log.info('user %s authenticated correctly', username)
96 96 return True
97 97 else:
98 98 log.error('user %s is disabled', username)
99 99
100 100 return False
101 101
102 102 class AuthUser(object):
103 103 """
104 104 A simple object that handles a mercurial username for authentication
105 105 """
106 106 def __init__(self):
107 107 self.username = 'None'
108 108 self.name = ''
109 109 self.lastname = ''
110 110 self.email = ''
111 111 self.user_id = None
112 112 self.is_authenticated = False
113 113 self.is_admin = False
114 114 self.permissions = {}
115 115
116 116
117 117 def set_available_permissions(config):
118 118 """
119 119 This function will propagate pylons globals with all available defined
120 120 permission given in db. We don't wannt to check each time from db for new
121 121 permissions since adding a new permission also requires application restart
122 122 ie. to decorate new views with the newly created permission
123 123 @param config:
124 124 """
125 125 log.info('getting information about all available permissions')
126 126 try:
127 127 sa = meta.Session
128 128 all_perms = sa.query(Permission).all()
129 129 finally:
130 130 meta.Session.remove()
131 131
132 132 config['available_permissions'] = [x.permission_name for x in all_perms]
133 133
134 134 def set_base_path(config):
135 135 config['base_path'] = config['pylons.app_globals'].base_path
136 136
137 137 def fill_data(user):
138 138 """
139 139 Fills user data with those from database and log out user if not present
140 140 in database
141 141 @param user:
142 142 """
143 143 sa = meta.Session
144 144 dbuser = sa.query(User).get(user.user_id)
145 145 if dbuser:
146 146 user.username = dbuser.username
147 147 user.is_admin = dbuser.admin
148 148 user.name = dbuser.name
149 149 user.lastname = dbuser.lastname
150 150 user.email = dbuser.email
151 151 else:
152 152 user.is_authenticated = False
153 153 meta.Session.remove()
154 154 return user
155 155
156 156 def fill_perms(user):
157 157 """
158 158 Fills user permission attribute with permissions taken from database
159 159 @param user:
160 160 """
161 161
162 162 sa = meta.Session
163 163 user.permissions['repositories'] = {}
164 164 user.permissions['global'] = set()
165 165
166 166 #===========================================================================
167 167 # fetch default permissions
168 168 #===========================================================================
169 169 default_perms = sa.query(RepoToPerm, Repository, Permission)\
170 170 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
171 171 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
172 172 .filter(RepoToPerm.user == sa.query(User).filter(User.username ==
173 173 'default').scalar()).all()
174 174
175 175 if user.is_admin:
176 176 #=======================================================================
177 177 # #admin have all default rights set to admin
178 178 #=======================================================================
179 179 user.permissions['global'].add('hg.admin')
180 180
181 181 for perm in default_perms:
182 182 p = 'repository.admin'
183 183 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
184 184
185 185 else:
186 186 #=======================================================================
187 187 # set default permissions
188 188 #=======================================================================
189 189
190 190 #default global
191 191 default_global_perms = sa.query(UserToPerm)\
192 192 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
193 193 'default').one())
194 194
195 195 for perm in default_global_perms:
196 196 user.permissions['global'].add(perm.permission.permission_name)
197 197
198 198 #default repositories
199 199 for perm in default_perms:
200 200 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
201 201 #disable defaults for private repos,
202 202 p = 'repository.none'
203 203 elif perm.Repository.user_id == user.user_id:
204 204 #set admin if owner
205 205 p = 'repository.admin'
206 206 else:
207 207 p = perm.Permission.permission_name
208 208
209 209 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
210 210
211 211 #=======================================================================
212 212 # #overwrite default with user permissions if any
213 213 #=======================================================================
214 214 user_perms = sa.query(RepoToPerm, Permission, Repository)\
215 215 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
216 216 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
217 217 .filter(RepoToPerm.user_id == user.user_id).all()
218 218
219 219 for perm in user_perms:
220 220 if perm.Repository.user_id == user.user_id:#set admin if owner
221 221 p = 'repository.admin'
222 222 else:
223 223 p = perm.Permission.permission_name
224 224 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
225 225 meta.Session.remove()
226 226 return user
227 227
228 228 def get_user(session):
229 229 """
230 230 Gets user from session, and wraps permissions into user
231 231 @param session:
232 232 """
233 233 user = session.get('rhodecode_user', AuthUser())
234 234 if user.is_authenticated:
235 235 user = fill_data(user)
236 236 user = fill_perms(user)
237 237 session['rhodecode_user'] = user
238 238 session.save()
239 239 return user
240 240
241 241 #===============================================================================
242 242 # CHECK DECORATORS
243 243 #===============================================================================
244 244 class LoginRequired(object):
245 245 """Must be logged in to execute this function else redirect to login page"""
246 246
247 247 def __call__(self, func):
248 248 return decorator(self.__wrapper, func)
249 249
250 250 def __wrapper(self, func, *fargs, **fkwargs):
251 251 user = session.get('rhodecode_user', AuthUser())
252 252 log.debug('Checking login required for user:%s', user.username)
253 253 if user.is_authenticated:
254 254 log.debug('user %s is authenticated', user.username)
255 255 return func(*fargs, **fkwargs)
256 256 else:
257 257 log.warn('user %s not authenticated', user.username)
258 258
259 259 p = ''
260 260 if request.environ.get('SCRIPT_NAME') != '/':
261 261 p += request.environ.get('SCRIPT_NAME')
262 262
263 263 p += request.environ.get('PATH_INFO')
264 264 if request.environ.get('QUERY_STRING'):
265 265 p += '?' + request.environ.get('QUERY_STRING')
266 266
267 267 log.debug('redirecting to login page with %s', p)
268 268 return redirect(url('login_home', came_from=p))
269 269
270 270 class PermsDecorator(object):
271 271 """Base class for decorators"""
272 272
273 273 def __init__(self, *required_perms):
274 274 available_perms = config['available_permissions']
275 275 for perm in required_perms:
276 276 if perm not in available_perms:
277 277 raise Exception("'%s' permission is not defined" % perm)
278 278 self.required_perms = set(required_perms)
279 279 self.user_perms = None
280 280
281 281 def __call__(self, func):
282 282 return decorator(self.__wrapper, func)
283 283
284 284
285 285 def __wrapper(self, func, *fargs, **fkwargs):
286 286 # _wrapper.__name__ = func.__name__
287 287 # _wrapper.__dict__.update(func.__dict__)
288 288 # _wrapper.__doc__ = func.__doc__
289 289
290 290 self.user_perms = session.get('rhodecode_user', AuthUser()).permissions
291 291 log.debug('checking %s permissions %s for %s',
292 292 self.__class__.__name__, self.required_perms, func.__name__)
293 293
294 294 if self.check_permissions():
295 295 log.debug('Permission granted for %s', func.__name__)
296 296
297 297 return func(*fargs, **fkwargs)
298 298
299 299 else:
300 300 log.warning('Permission denied for %s', func.__name__)
301 301 #redirect with forbidden ret code
302 302 return abort(403)
303 303
304 304
305 305
306 306 def check_permissions(self):
307 307 """Dummy function for overriding"""
308 308 raise Exception('You have to write this function in child class')
309 309
310 310 class HasPermissionAllDecorator(PermsDecorator):
311 311 """Checks for access permission for all given predicates. All of them
312 312 have to be meet in order to fulfill the request
313 313 """
314 314
315 315 def check_permissions(self):
316 316 if self.required_perms.issubset(self.user_perms.get('global')):
317 317 return True
318 318 return False
319 319
320 320
321 321 class HasPermissionAnyDecorator(PermsDecorator):
322 322 """Checks for access permission for any of given predicates. In order to
323 323 fulfill the request any of predicates must be meet
324 324 """
325 325
326 326 def check_permissions(self):
327 327 if self.required_perms.intersection(self.user_perms.get('global')):
328 328 return True
329 329 return False
330 330
331 331 class HasRepoPermissionAllDecorator(PermsDecorator):
332 332 """Checks for access permission for all given predicates for specific
333 333 repository. All of them have to be meet in order to fulfill the request
334 334 """
335 335
336 336 def check_permissions(self):
337 337 repo_name = get_repo_slug(request)
338 338 try:
339 339 user_perms = set([self.user_perms['repositories'][repo_name]])
340 340 except KeyError:
341 341 return False
342 342 if self.required_perms.issubset(user_perms):
343 343 return True
344 344 return False
345 345
346 346
347 347 class HasRepoPermissionAnyDecorator(PermsDecorator):
348 348 """Checks for access permission for any of given predicates for specific
349 349 repository. In order to fulfill the request any of predicates must be meet
350 350 """
351 351
352 352 def check_permissions(self):
353 353 repo_name = get_repo_slug(request)
354 354
355 355 try:
356 356 user_perms = set([self.user_perms['repositories'][repo_name]])
357 357 except KeyError:
358 358 return False
359 359 if self.required_perms.intersection(user_perms):
360 360 return True
361 361 return False
362 362 #===============================================================================
363 363 # CHECK FUNCTIONS
364 364 #===============================================================================
365 365
366 366 class PermsFunction(object):
367 367 """Base function for other check functions"""
368 368
369 369 def __init__(self, *perms):
370 370 available_perms = config['available_permissions']
371 371
372 372 for perm in perms:
373 373 if perm not in available_perms:
374 374 raise Exception("'%s' permission in not defined" % perm)
375 375 self.required_perms = set(perms)
376 376 self.user_perms = None
377 377 self.granted_for = ''
378 378 self.repo_name = None
379 379
380 380 def __call__(self, check_Location=''):
381 381 user = session.get('rhodecode_user', False)
382 382 if not user:
383 383 return False
384 384 self.user_perms = user.permissions
385 385 self.granted_for = user.username
386 386 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
387 387
388 388 if self.check_permissions():
389 389 log.debug('Permission granted for %s @%s', self.granted_for,
390 390 check_Location)
391 391 return True
392 392
393 393 else:
394 394 log.warning('Permission denied for %s @%s', self.granted_for,
395 395 check_Location)
396 396 return False
397 397
398 398 def check_permissions(self):
399 399 """Dummy function for overriding"""
400 400 raise Exception('You have to write this function in child class')
401 401
402 402 class HasPermissionAll(PermsFunction):
403 403 def check_permissions(self):
404 404 if self.required_perms.issubset(self.user_perms.get('global')):
405 405 return True
406 406 return False
407 407
408 408 class HasPermissionAny(PermsFunction):
409 409 def check_permissions(self):
410 410 if self.required_perms.intersection(self.user_perms.get('global')):
411 411 return True
412 412 return False
413 413
414 414 class HasRepoPermissionAll(PermsFunction):
415 415
416 416 def __call__(self, repo_name=None, check_Location=''):
417 417 self.repo_name = repo_name
418 418 return super(HasRepoPermissionAll, self).__call__(check_Location)
419 419
420 420 def check_permissions(self):
421 421 if not self.repo_name:
422 422 self.repo_name = get_repo_slug(request)
423 423
424 424 try:
425 425 self.user_perms = set([self.user_perms['repositories']\
426 426 [self.repo_name]])
427 427 except KeyError:
428 428 return False
429 429 self.granted_for = self.repo_name
430 430 if self.required_perms.issubset(self.user_perms):
431 431 return True
432 432 return False
433 433
434 434 class HasRepoPermissionAny(PermsFunction):
435 435
436 436 def __call__(self, repo_name=None, check_Location=''):
437 437 self.repo_name = repo_name
438 438 return super(HasRepoPermissionAny, self).__call__(check_Location)
439 439
440 440 def check_permissions(self):
441 441 if not self.repo_name:
442 442 self.repo_name = get_repo_slug(request)
443 443
444 444 try:
445 445 self.user_perms = set([self.user_perms['repositories']\
446 446 [self.repo_name]])
447 447 except KeyError:
448 448 return False
449 449 self.granted_for = self.repo_name
450 450 if self.required_perms.intersection(self.user_perms):
451 451 return True
452 452 return False
453 453
454 454 #===============================================================================
455 455 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
456 456 #===============================================================================
457 457
458 458 class HasPermissionAnyMiddleware(object):
459 459 def __init__(self, *perms):
460 460 self.required_perms = set(perms)
461 461
462 462 def __call__(self, user, repo_name):
463 463 usr = AuthUser()
464 464 usr.user_id = user.user_id
465 465 usr.username = user.username
466 466 usr.is_admin = user.admin
467 467
468 468 try:
469 469 self.user_perms = set([fill_perms(usr)\
470 470 .permissions['repositories'][repo_name]])
471 471 except:
472 472 self.user_perms = set()
473 473 self.granted_for = ''
474 474 self.username = user.username
475 475 self.repo_name = repo_name
476 476 return self.check_permissions()
477 477
478 478 def check_permissions(self):
479 479 log.debug('checking mercurial protocol '
480 480 'permissions for user:%s repository:%s',
481 481 self.username, self.repo_name)
482 482 if self.required_perms.intersection(self.user_perms):
483 483 log.debug('permission granted')
484 484 return True
485 485 log.debug('permission denied')
486 486 return False
@@ -1,322 +1,327 b''
1 1 from celery.decorators import task
2 2
3 3 from operator import itemgetter
4 4 from pylons.i18n.translation import _
5 5 from rhodecode.lib.celerylib import run_task, locked_task
6 6 from rhodecode.lib.helpers import person
7 7 from rhodecode.lib.smtp_mailer import SmtpMailer
8 8 from rhodecode.lib.utils import OrderedDict
9 9 from time import mktime
10 10 from vcs.backends.hg import MercurialRepository
11 import json
12 11 import traceback
13 12
14 13 try:
14 import json
15 except ImportError:
16 #python 2.5 compatibility
17 import simplejson as json
18
19 try:
15 20 from celeryconfig import PYLONS_CONFIG as config
16 21 celery_on = True
17 22 except ImportError:
18 23 #if celeryconfig is not present let's just load our pylons
19 24 #config instead
20 25 from pylons import config
21 26 celery_on = False
22 27
23 28
24 29 __all__ = ['whoosh_index', 'get_commits_stats',
25 30 'reset_user_password', 'send_email']
26 31
27 32 def get_session():
28 33 if celery_on:
29 34 from sqlalchemy import engine_from_config
30 35 from sqlalchemy.orm import sessionmaker, scoped_session
31 36 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
32 37 sa = scoped_session(sessionmaker(bind=engine))
33 38 else:
34 39 #If we don't use celery reuse our current application Session
35 40 from rhodecode.model.meta import Session
36 41 sa = Session
37 42
38 43 return sa
39 44
40 45 def get_hg_settings():
41 46 from rhodecode.model.db import RhodeCodeSettings
42 47 sa = get_session()
43 48 ret = sa.query(RhodeCodeSettings).all()
44 49
45 50 if not ret:
46 51 raise Exception('Could not get application settings !')
47 52 settings = {}
48 53 for each in ret:
49 54 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
50 55
51 56 return settings
52 57
53 58 def get_hg_ui_settings():
54 59 from rhodecode.model.db import RhodeCodeUi
55 60 sa = get_session()
56 61 ret = sa.query(RhodeCodeUi).all()
57 62
58 63 if not ret:
59 64 raise Exception('Could not get application ui settings !')
60 65 settings = {}
61 66 for each in ret:
62 67 k = each.ui_key
63 68 v = each.ui_value
64 69 if k == '/':
65 70 k = 'root_path'
66 71
67 72 if k.find('.') != -1:
68 73 k = k.replace('.', '_')
69 74
70 75 if each.ui_section == 'hooks':
71 76 v = each.ui_active
72 77
73 78 settings[each.ui_section + '_' + k] = v
74 79
75 80 return settings
76 81
77 82 @task
78 83 @locked_task
79 84 def whoosh_index(repo_location, full_index):
80 85 log = whoosh_index.get_logger()
81 86 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
82 87 WhooshIndexingDaemon(repo_location=repo_location).run(full_index=full_index)
83 88
84 89 @task
85 90 @locked_task
86 91 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
87 92 from rhodecode.model.db import Statistics, Repository
88 93 log = get_commits_stats.get_logger()
89 94 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
90 95
91 96 commits_by_day_author_aggregate = {}
92 97 commits_by_day_aggregate = {}
93 98 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
94 99 repo = MercurialRepository(repos_path + repo_name)
95 100
96 101 skip_date_limit = True
97 102 parse_limit = 350 #limit for single task changeset parsing optimal for
98 103 last_rev = 0
99 104 last_cs = None
100 105 timegetter = itemgetter('time')
101 106
102 107 sa = get_session()
103 108
104 109 dbrepo = sa.query(Repository)\
105 110 .filter(Repository.repo_name == repo_name).scalar()
106 111 cur_stats = sa.query(Statistics)\
107 112 .filter(Statistics.repository == dbrepo).scalar()
108 113 if cur_stats:
109 114 last_rev = cur_stats.stat_on_revision
110 115 if not repo.revisions:
111 116 return True
112 117
113 118 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
114 119 #pass silently without any work if we're not on first revision or current
115 120 #state of parsing revision(from db marker) is the last revision
116 121 return True
117 122
118 123 if cur_stats:
119 124 commits_by_day_aggregate = OrderedDict(
120 125 json.loads(
121 126 cur_stats.commit_activity_combined))
122 127 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
123 128
124 129 log.debug('starting parsing %s', parse_limit)
125 130 for cnt, rev in enumerate(repo.revisions[last_rev:]):
126 131 last_cs = cs = repo.get_changeset(rev)
127 132 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
128 133 cs.date.timetuple()[2])
129 134 timetupple = [int(x) for x in k.split('-')]
130 135 timetupple.extend([0 for _ in xrange(6)])
131 136 k = mktime(timetupple)
132 137 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
133 138 try:
134 139 l = [timegetter(x) for x in commits_by_day_author_aggregate\
135 140 [author_key_cleaner(cs.author)]['data']]
136 141 time_pos = l.index(k)
137 142 except ValueError:
138 143 time_pos = False
139 144
140 145 if time_pos >= 0 and time_pos is not False:
141 146
142 147 datadict = commits_by_day_author_aggregate\
143 148 [author_key_cleaner(cs.author)]['data'][time_pos]
144 149
145 150 datadict["commits"] += 1
146 151 datadict["added"] += len(cs.added)
147 152 datadict["changed"] += len(cs.changed)
148 153 datadict["removed"] += len(cs.removed)
149 154
150 155 else:
151 156 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
152 157
153 158 datadict = {"time":k,
154 159 "commits":1,
155 160 "added":len(cs.added),
156 161 "changed":len(cs.changed),
157 162 "removed":len(cs.removed),
158 163 }
159 164 commits_by_day_author_aggregate\
160 165 [author_key_cleaner(cs.author)]['data'].append(datadict)
161 166
162 167 else:
163 168 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
164 169 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
165 170 "label":author_key_cleaner(cs.author),
166 171 "data":[{"time":k,
167 172 "commits":1,
168 173 "added":len(cs.added),
169 174 "changed":len(cs.changed),
170 175 "removed":len(cs.removed),
171 176 }],
172 177 "schema":["commits"],
173 178 }
174 179
175 180 #gather all data by day
176 181 if commits_by_day_aggregate.has_key(k):
177 182 commits_by_day_aggregate[k] += 1
178 183 else:
179 184 commits_by_day_aggregate[k] = 1
180 185
181 186 if cnt >= parse_limit:
182 187 #don't fetch to much data since we can freeze application
183 188 break
184 189
185 190 overview_data = []
186 191 for k, v in commits_by_day_aggregate.items():
187 192 overview_data.append([k, v])
188 193 overview_data = sorted(overview_data, key=itemgetter(0))
189 194
190 195 if not commits_by_day_author_aggregate:
191 196 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
192 197 "label":author_key_cleaner(repo.contact),
193 198 "data":[0, 1],
194 199 "schema":["commits"],
195 200 }
196 201
197 202 stats = cur_stats if cur_stats else Statistics()
198 203 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
199 204 stats.commit_activity_combined = json.dumps(overview_data)
200 205
201 206 log.debug('last revison %s', last_rev)
202 207 leftovers = len(repo.revisions[last_rev:])
203 208 log.debug('revisions to parse %s', leftovers)
204 209
205 210 if last_rev == 0 or leftovers < parse_limit:
206 211 stats.languages = json.dumps(__get_codes_stats(repo_name))
207 212
208 213 stats.repository = dbrepo
209 214 stats.stat_on_revision = last_cs.revision
210 215
211 216 try:
212 217 sa.add(stats)
213 218 sa.commit()
214 219 except:
215 220 log.error(traceback.format_exc())
216 221 sa.rollback()
217 222 return False
218 223 if len(repo.revisions) > 1:
219 224 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
220 225
221 226 return True
222 227
223 228 @task
224 229 def reset_user_password(user_email):
225 230 log = reset_user_password.get_logger()
226 231 from rhodecode.lib import auth
227 232 from rhodecode.model.db import User
228 233
229 234 try:
230 235 try:
231 236 sa = get_session()
232 237 user = sa.query(User).filter(User.email == user_email).scalar()
233 238 new_passwd = auth.PasswordGenerator().gen_password(8,
234 239 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
235 240 if user:
236 241 user.password = auth.get_crypt_password(new_passwd)
237 242 sa.add(user)
238 243 sa.commit()
239 244 log.info('change password for %s', user_email)
240 245 if new_passwd is None:
241 246 raise Exception('unable to generate new password')
242 247
243 248 except:
244 249 log.error(traceback.format_exc())
245 250 sa.rollback()
246 251
247 252 run_task(send_email, user_email,
248 253 "Your new rhodecode password",
249 254 'Your new rhodecode password:%s' % (new_passwd))
250 255 log.info('send new password mail to %s', user_email)
251 256
252 257
253 258 except:
254 259 log.error('Failed to update user password')
255 260 log.error(traceback.format_exc())
256 261 return True
257 262
258 263 @task
259 264 def send_email(recipients, subject, body):
260 265 log = send_email.get_logger()
261 266 email_config = dict(config.items('DEFAULT'))
262 267 mail_from = email_config.get('app_email_from')
263 268 user = email_config.get('smtp_username')
264 269 passwd = email_config.get('smtp_password')
265 270 mail_server = email_config.get('smtp_server')
266 271 mail_port = email_config.get('smtp_port')
267 272 tls = email_config.get('smtp_use_tls')
268 273 ssl = False
269 274
270 275 try:
271 276 m = SmtpMailer(mail_from, user, passwd, mail_server,
272 277 mail_port, ssl, tls)
273 278 m.send(recipients, subject, body)
274 279 except:
275 280 log.error('Mail sending failed')
276 281 log.error(traceback.format_exc())
277 282 return False
278 283 return True
279 284
280 285 @task
281 286 def create_repo_fork(form_data, cur_user):
282 287 import os
283 288 from rhodecode.model.repo_model import RepoModel
284 289 sa = get_session()
285 290 rm = RepoModel(sa)
286 291
287 292 rm.create(form_data, cur_user, just_db=True, fork=True)
288 293
289 294 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
290 295 repo_path = os.path.join(repos_path, form_data['repo_name'])
291 296 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
292 297
293 298 MercurialRepository(str(repo_fork_path), True, clone_url=str(repo_path))
294 299
295 300
296 301 def __get_codes_stats(repo_name):
297 302 LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
298 303 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el', 'erl',
299 304 'h', 'java', 'js', 'jsp', 'jspx', 'lisp',
300 305 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
301 306 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh',
302 307 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
303 308 'yaws']
304 309 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
305 310 repo = MercurialRepository(repos_path + repo_name)
306 311
307 312 code_stats = {}
308 313 for topnode, dirs, files in repo.walk('/', 'tip'):
309 314 for f in files:
310 315 k = f.mimetype
311 316 if f.extension in LANGUAGES_EXTENSIONS:
312 317 if code_stats.has_key(k):
313 318 code_stats[k] += 1
314 319 else:
315 320 code_stats[k] = 1
316 321
317 322 return code_stats or {}
318 323
319 324
320 325
321 326
322 327
@@ -1,74 +1,78 b''
1 1 from rhodecode import get_version
2 2 import sys
3 py_version = sys.version_info
3 4
4 5 requirements = [
5 6 "Pylons>=1.0.0",
6 7 "SQLAlchemy>=0.6",
7 8 "babel",
8 9 "Mako>=0.3.2",
9 10 "vcs>=0.1.7",
10 11 "pygments>=1.3.0",
11 12 "mercurial>=1.6",
12 "pysqlite",
13 13 "whoosh==1.0.0",
14 14 "py-bcrypt",
15 15 "celery",
16 16 ]
17 17
18 if sys.version_info < (2, 6):
19 requirements.append("simplejson")
20 requirements.append("pysqlite")
21
18 22 #additional files from project that goes somewhere in the filesystem
19 23 #relative to sys.prefix
20 24 data_files = []
21 25
22 26 #additional files that goes into package itself
23 27 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
24 28
25 29 #long description
26 30 try:
27 31 readme_file = 'README.rst'
28 32 long_description = open(readme_file).read()
29 33 except IOError, err:
30 34 sys.stderr.write("[ERROR] Cannot find file specified as "
31 35 "long_description (%s)\n" % readme_file)
32 36 sys.exit(1)
33 37
34 38
35 39 try:
36 40 from setuptools import setup, find_packages
37 41 except ImportError:
38 42 from ez_setup import use_setuptools
39 43 use_setuptools()
40 44 from setuptools import setup, find_packages
41 45 #packages
42 46 packages = find_packages(exclude=['ez_setup'])
43 47
44 48 setup(
45 49 name='RhodeCode',
46 50 version=get_version(),
47 51 description='Mercurial repository serving and browsing app',
48 52 long_description=long_description,
49 53 keywords='mercurial web hgwebdir replacement serving hgweb rhodecode',
50 54 license='BSD',
51 55 author='Marcin Kuzminski',
52 56 author_email='marcin@python-works.com',
53 57 url='http://hg.python-works.com',
54 58 install_requires=requirements,
55 59 setup_requires=["PasteScript>=1.6.3"],
56 60 data_files=data_files,
57 61 packages=packages,
58 62 include_package_data=True,
59 63 test_suite='nose.collector',
60 64 package_data=package_data,
61 65 message_extractors={'rhodecode': [
62 66 ('**.py', 'python', None),
63 67 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
64 68 ('public/**', 'ignore', None)]},
65 69 zip_safe=False,
66 70 paster_plugins=['PasteScript', 'Pylons'],
67 71 entry_points="""
68 72 [paste.app_factory]
69 73 main = rhodecode.config.middleware:make_app
70 74
71 75 [paste.app_install]
72 76 main = pylons.util:PylonsInstaller
73 77 """,
74 78 )
General Comments 0
You need to be logged in to leave comments. Login now