##// END OF EJS Templates
fixed copyright year to 2011
marcink -
r902:07a6e8c6 beta
parent child Browse files
Show More
@@ -1,52 +1,52
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.__init__
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 RhodeCode, a web based repository management based on pylons
7 7 versioning implementation: http://semver.org/
8 8
9 9 :created_on: Apr 9, 2010
10 10 :author: marcink
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software; you can redistribute it and/or
15 15 # modify it under the terms of the GNU General Public License
16 16 # as published by the Free Software Foundation; version 2
17 17 # of the License or (at your opinion) any later version of the license.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program; if not, write to the Free Software
26 26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 27 # MA 02110-1301, USA.
28 28
29 29
30 30 VERSION = (1, 2, 0, 'beta')
31 31 __version__ = '.'.join((str(each) for each in VERSION[:4]))
32 32 __dbversion__ = 3 #defines current db version for migrations
33 33
34 34 try:
35 35 from rhodecode.lib.utils import get_current_revision
36 36 _rev = get_current_revision()
37 37 except ImportError:
38 38 #this is needed when doing some setup.py operations
39 39 _rev = False
40 40
41 41 if len(VERSION) > 3 and _rev:
42 42 __version__ += ' [rev:%s]' % _rev[0]
43 43
44 44 def get_version():
45 45 """Returns shorter version (digit parts only) as string."""
46 46
47 47 return '.'.join((str(each) for each in VERSION[:3]))
48 48
49 49 BACKENDS = {
50 50 'hg': 'Mercurial repository',
51 51 #'git': 'Git repository',
52 52 }
@@ -1,53 +1,53
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.admin
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Controller for Admin panel of Rhodecode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 from pylons import request, tmpl_context as c
30 30 from rhodecode.lib.base import BaseController, render
31 31 from rhodecode.model.db import UserLog
32 32 from webhelpers.paginate import Page
33 33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37 class AdminController(BaseController):
38 38
39 39 @LoginRequired()
40 40 def __before__(self):
41 41 super(AdminController, self).__before__()
42 42
43 43 @HasPermissionAllDecorator('hg.admin')
44 44 def index(self):
45 45
46 46 users_log = self.sa.query(UserLog).order_by(UserLog.action_date.desc())
47 47 p = int(request.params.get('page', 1))
48 48 c.users_log = Page(users_log, page=p, items_per_page=10)
49 49 c.log_data = render('admin/admin_log.html')
50 50 if request.params.get('partial'):
51 51 return c.log_data
52 52 return render('admin/admin.html')
53 53
@@ -1,105 +1,105
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.ldap_settings
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 ldap controller for RhodeCode
7 7
8 8 :created_on: Nov 26, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 logging
28 28 import formencode
29 29 import traceback
30 30
31 31 from formencode import htmlfill
32 32
33 33 from pylons import request, response, 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.base import BaseController, render
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
40 40 from rhodecode.lib.auth_ldap import LdapImportError
41 41 from rhodecode.model.settings import SettingsModel
42 42 from rhodecode.model.forms import LdapSettingsForm
43 43 from sqlalchemy.exc import DatabaseError
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48
49 49 class LdapSettingsController(BaseController):
50 50
51 51 @LoginRequired()
52 52 @HasPermissionAllDecorator('hg.admin')
53 53 def __before__(self):
54 54 c.admin_user = session.get('admin_user')
55 55 c.admin_username = session.get('admin_username')
56 56 super(LdapSettingsController, self).__before__()
57 57
58 58 def index(self):
59 59 defaults = SettingsModel().get_ldap_settings()
60 60
61 61 return htmlfill.render(
62 62 render('admin/ldap/ldap.html'),
63 63 defaults=defaults,
64 64 encoding="UTF-8",
65 65 force_defaults=True,)
66 66
67 67 def ldap_settings(self):
68 68 """POST ldap create and store ldap settings"""
69 69
70 70 settings_model = SettingsModel()
71 71 _form = LdapSettingsForm()()
72 72
73 73 try:
74 74 form_result = _form.to_python(dict(request.POST))
75 75 try:
76 76
77 77 for k, v in form_result.items():
78 78 if k.startswith('ldap_'):
79 79 setting = settings_model.get(k)
80 80 setting.app_settings_value = v
81 81 self.sa.add(setting)
82 82
83 83 self.sa.commit()
84 84 h.flash(_('Ldap settings updated successfully'),
85 85 category='success')
86 86 except (DatabaseError,):
87 87 raise
88 88 except LdapImportError:
89 89 h.flash(_('Unable to activate ldap. The "python-ldap" library '
90 90 'is missing.'), category='warning')
91 91
92 92 except formencode.Invalid, errors:
93 93
94 94 return htmlfill.render(
95 95 render('admin/ldap/ldap.html'),
96 96 defaults=errors.value,
97 97 errors=errors.error_dict or {},
98 98 prefix_error=False,
99 99 encoding="UTF-8")
100 100 except Exception:
101 101 log.error(traceback.format_exc())
102 102 h.flash(_('error occurred during update of ldap settings'),
103 103 category='error')
104 104
105 105 return redirect(url('ldap_home'))
@@ -1,171 +1,171
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 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 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
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 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 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 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 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 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 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,351 +1,351
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.settings
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 settings controller for rhodecode admin
7 7
8 8 :created_on: Jul 14, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 operator import itemgetter
33 33 from formencode import htmlfill
34 34 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
35 35 config
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, NotAnonymous
42 42 from rhodecode.lib.base import BaseController, render
43 43 from rhodecode.lib.celerylib import tasks, run_task
44 44 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
45 45 set_rhodecode_config
46 46 from rhodecode.model.db import RhodeCodeUi, Repository
47 47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 48 ApplicationUiSettingsForm
49 49 from rhodecode.model.scm import ScmModel
50 50 from rhodecode.model.settings import SettingsModel
51 51 from rhodecode.model.user import UserModel
52 52
53 53 from sqlalchemy import func
54 54
55 55
56 56 log = logging.getLogger(__name__)
57 57
58 58
59 59 class SettingsController(BaseController):
60 60 """REST Controller styled on the Atom Publishing Protocol"""
61 61 # To properly map this controller, ensure your config/routing.py
62 62 # file has a resource setup:
63 63 # map.resource('setting', 'settings', controller='admin/settings',
64 64 # path_prefix='/admin', name_prefix='admin_')
65 65
66 66
67 67 @LoginRequired()
68 68 def __before__(self):
69 69 c.admin_user = session.get('admin_user')
70 70 c.admin_username = session.get('admin_username')
71 71 super(SettingsController, self).__before__()
72 72
73 73
74 74 @HasPermissionAllDecorator('hg.admin')
75 75 def index(self, format='html'):
76 76 """GET /admin/settings: All items in the collection"""
77 77 # url('admin_settings')
78 78
79 79 defaults = SettingsModel().get_app_settings()
80 80 defaults.update(self.get_hg_ui_settings())
81 81 return htmlfill.render(
82 82 render('admin/settings/settings.html'),
83 83 defaults=defaults,
84 84 encoding="UTF-8",
85 85 force_defaults=False
86 86 )
87 87
88 88 @HasPermissionAllDecorator('hg.admin')
89 89 def create(self):
90 90 """POST /admin/settings: Create a new item"""
91 91 # url('admin_settings')
92 92
93 93 @HasPermissionAllDecorator('hg.admin')
94 94 def new(self, format='html'):
95 95 """GET /admin/settings/new: Form to create a new item"""
96 96 # url('admin_new_setting')
97 97
98 98 @HasPermissionAllDecorator('hg.admin')
99 99 def update(self, setting_id):
100 100 """PUT /admin/settings/setting_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('admin_setting', setting_id=ID),
105 105 # method='put')
106 106 # url('admin_setting', setting_id=ID)
107 107 if setting_id == 'mapping':
108 108 rm_obsolete = request.POST.get('destroy', False)
109 109 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
110 110
111 111 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
112 112 for repo_name in initial.keys():
113 113 invalidate_cache('get_repo_cached_%s' % repo_name)
114 114
115 115 repo2db_mapper(initial, rm_obsolete)
116 116
117 117 h.flash(_('Repositories successfully rescanned'), category='success')
118 118
119 119 if setting_id == 'whoosh':
120 120 repo_location = self.get_hg_ui_settings()['paths_root_path']
121 121 full_index = request.POST.get('full_index', False)
122 122 task = run_task(tasks.whoosh_index, repo_location, full_index)
123 123
124 124 h.flash(_('Whoosh reindex task scheduled'), category='success')
125 125 if setting_id == 'global':
126 126
127 127 application_form = ApplicationSettingsForm()()
128 128 try:
129 129 form_result = application_form.to_python(dict(request.POST))
130 130 settings_model = SettingsModel()
131 131
132 132 try:
133 133 hgsettings1 = settings_model.get('title')
134 134 hgsettings1.app_settings_value = form_result['rhodecode_title']
135 135
136 136 hgsettings2 = settings_model.get('realm')
137 137 hgsettings2.app_settings_value = form_result['rhodecode_realm']
138 138
139 139 hgsettings3 = settings_model.get('ga_code')
140 140 hgsettings3.app_settings_value = form_result['rhodecode_ga_code']
141 141
142 142
143 143
144 144 self.sa.add(hgsettings1)
145 145 self.sa.add(hgsettings2)
146 146 self.sa.add(hgsettings3)
147 147 self.sa.commit()
148 148 set_rhodecode_config(config)
149 149 h.flash(_('Updated application settings'),
150 150 category='success')
151 151
152 152 except:
153 153 log.error(traceback.format_exc())
154 154 h.flash(_('error occurred during updating application settings'),
155 155 category='error')
156 156
157 157 self.sa.rollback()
158 158
159 159
160 160 except formencode.Invalid, errors:
161 161 return htmlfill.render(
162 162 render('admin/settings/settings.html'),
163 163 defaults=errors.value,
164 164 errors=errors.error_dict or {},
165 165 prefix_error=False,
166 166 encoding="UTF-8")
167 167
168 168 if setting_id == 'mercurial':
169 169 application_form = ApplicationUiSettingsForm()()
170 170 try:
171 171 form_result = application_form.to_python(dict(request.POST))
172 172
173 173 try:
174 174
175 175 hgsettings1 = self.sa.query(RhodeCodeUi)\
176 176 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
177 177 hgsettings1.ui_value = form_result['web_push_ssl']
178 178
179 179 hgsettings2 = self.sa.query(RhodeCodeUi)\
180 180 .filter(RhodeCodeUi.ui_key == '/').one()
181 181 hgsettings2.ui_value = form_result['paths_root_path']
182 182
183 183
184 184 #HOOKS
185 185 hgsettings3 = self.sa.query(RhodeCodeUi)\
186 186 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
187 187 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
188 188
189 189 hgsettings4 = self.sa.query(RhodeCodeUi)\
190 190 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
191 191 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
192 192
193 193 hgsettings5 = self.sa.query(RhodeCodeUi)\
194 194 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
195 195 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
196 196
197 197 hgsettings6 = self.sa.query(RhodeCodeUi)\
198 198 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
199 199 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
200 200
201 201
202 202 self.sa.add(hgsettings1)
203 203 self.sa.add(hgsettings2)
204 204 self.sa.add(hgsettings3)
205 205 self.sa.add(hgsettings4)
206 206 self.sa.add(hgsettings5)
207 207 self.sa.add(hgsettings6)
208 208 self.sa.commit()
209 209
210 210 h.flash(_('Updated mercurial settings'),
211 211 category='success')
212 212
213 213 except:
214 214 log.error(traceback.format_exc())
215 215 h.flash(_('error occurred during updating application settings'),
216 216 category='error')
217 217
218 218 self.sa.rollback()
219 219
220 220
221 221 except formencode.Invalid, errors:
222 222 return htmlfill.render(
223 223 render('admin/settings/settings.html'),
224 224 defaults=errors.value,
225 225 errors=errors.error_dict or {},
226 226 prefix_error=False,
227 227 encoding="UTF-8")
228 228
229 229
230 230
231 231 return redirect(url('admin_settings'))
232 232
233 233 @HasPermissionAllDecorator('hg.admin')
234 234 def delete(self, setting_id):
235 235 """DELETE /admin/settings/setting_id: Delete an existing item"""
236 236 # Forms posted to this method should contain a hidden field:
237 237 # <input type="hidden" name="_method" value="DELETE" />
238 238 # Or using helpers:
239 239 # h.form(url('admin_setting', setting_id=ID),
240 240 # method='delete')
241 241 # url('admin_setting', setting_id=ID)
242 242
243 243 @HasPermissionAllDecorator('hg.admin')
244 244 def show(self, setting_id, format='html'):
245 245 """GET /admin/settings/setting_id: Show a specific item"""
246 246 # url('admin_setting', setting_id=ID)
247 247
248 248 @HasPermissionAllDecorator('hg.admin')
249 249 def edit(self, setting_id, format='html'):
250 250 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
251 251 # url('admin_edit_setting', setting_id=ID)
252 252
253 253 @NotAnonymous()
254 254 def my_account(self):
255 255 """
256 256 GET /_admin/my_account Displays info about my account
257 257 """
258 258 # url('admin_settings_my_account')
259 259
260 260 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
261 261 all_repos = self.sa.query(Repository)\
262 262 .filter(Repository.user_id == c.user.user_id)\
263 263 .order_by(func.lower(Repository.repo_name))\
264 264 .all()
265 265
266 266 c.user_repos = ScmModel().get_repos(all_repos)
267 267
268 268 if c.user.username == 'default':
269 269 h.flash(_("You can't edit this user since it's"
270 270 " crucial for entire application"), category='warning')
271 271 return redirect(url('users'))
272 272
273 273 defaults = c.user.get_dict()
274 274 return htmlfill.render(
275 275 render('admin/users/user_edit_my_account.html'),
276 276 defaults=defaults,
277 277 encoding="UTF-8",
278 278 force_defaults=False
279 279 )
280 280
281 281 def my_account_update(self):
282 282 """PUT /_admin/my_account_update: Update an existing item"""
283 283 # Forms posted to this method should contain a hidden field:
284 284 # <input type="hidden" name="_method" value="PUT" />
285 285 # Or using helpers:
286 286 # h.form(url('admin_settings_my_account_update'),
287 287 # method='put')
288 288 # url('admin_settings_my_account_update', id=ID)
289 289 user_model = UserModel()
290 290 uid = c.rhodecode_user.user_id
291 291 _form = UserForm(edit=True, old_data={'user_id':uid,
292 292 'email':c.rhodecode_user.email})()
293 293 form_result = {}
294 294 try:
295 295 form_result = _form.to_python(dict(request.POST))
296 296 user_model.update_my_account(uid, form_result)
297 297 h.flash(_('Your account was updated successfully'),
298 298 category='success')
299 299
300 300 except formencode.Invalid, errors:
301 301 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
302 302 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
303 303 all_repos = self.sa.query(Repository)\
304 304 .filter(Repository.user_id == c.user.user_id)\
305 305 .order_by(func.lower(Repository.repo_name))\
306 306 .all()
307 307 c.user_repos = ScmModel().get_repos(all_repos)
308 308
309 309 return htmlfill.render(
310 310 render('admin/users/user_edit_my_account.html'),
311 311 defaults=errors.value,
312 312 errors=errors.error_dict or {},
313 313 prefix_error=False,
314 314 encoding="UTF-8")
315 315 except Exception:
316 316 log.error(traceback.format_exc())
317 317 h.flash(_('error occurred during update of user %s') \
318 318 % form_result.get('username'), category='error')
319 319
320 320 return redirect(url('my_account'))
321 321
322 322 @NotAnonymous()
323 323 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
324 324 def create_repository(self):
325 325 """GET /_admin/create_repository: Form to create a new item"""
326 326 new_repo = request.GET.get('repo', '')
327 327 c.new_repo = h.repo_name_slug(new_repo)
328 328
329 329 return render('admin/repos/repo_add_create_repository.html')
330 330
331 331 def get_hg_ui_settings(self):
332 332 ret = self.sa.query(RhodeCodeUi).all()
333 333
334 334 if not ret:
335 335 raise Exception('Could not get application ui settings !')
336 336 settings = {}
337 337 for each in ret:
338 338 k = each.ui_key
339 339 v = each.ui_value
340 340 if k == '/':
341 341 k = 'root_path'
342 342
343 343 if k.find('.') != -1:
344 344 k = k.replace('.', '_')
345 345
346 346 if each.ui_section == 'hooks':
347 347 v = each.ui_active
348 348
349 349 settings[each.ui_section + '_' + k] = v
350 350
351 351 return settings
@@ -1,176 +1,176
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 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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, config
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 DefaultUserException, UserOwnsReposException
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 40 fill_perms
41 41 from rhodecode.lib.base import BaseController, render
42 42
43 43 from rhodecode.model.db import User
44 44 from rhodecode.model.forms import UserForm
45 45 from rhodecode.model.user import UserModel
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49 class UsersController(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('user', 'users')
54 54
55 55 @LoginRequired()
56 56 @HasPermissionAllDecorator('hg.admin')
57 57 def __before__(self):
58 58 c.admin_user = session.get('admin_user')
59 59 c.admin_username = session.get('admin_username')
60 60 super(UsersController, self).__before__()
61 61 c.available_permissions = config['available_permissions']
62 62
63 63 def index(self, format='html'):
64 64 """GET /users: All items in the collection"""
65 65 # url('users')
66 66
67 67 c.users_list = self.sa.query(User).all()
68 68 return render('admin/users/users.html')
69 69
70 70 def create(self):
71 71 """POST /users: Create a new item"""
72 72 # url('users')
73 73
74 74 user_model = UserModel()
75 75 login_form = UserForm()()
76 76 try:
77 77 form_result = login_form.to_python(dict(request.POST))
78 78 user_model.create(form_result)
79 79 h.flash(_('created user %s') % form_result['username'],
80 80 category='success')
81 81 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
82 82 except formencode.Invalid, errors:
83 83 return htmlfill.render(
84 84 render('admin/users/user_add.html'),
85 85 defaults=errors.value,
86 86 errors=errors.error_dict or {},
87 87 prefix_error=False,
88 88 encoding="UTF-8")
89 89 except Exception:
90 90 log.error(traceback.format_exc())
91 91 h.flash(_('error occurred during creation of user %s') \
92 92 % request.POST.get('username'), category='error')
93 93 return redirect(url('users'))
94 94
95 95 def new(self, format='html'):
96 96 """GET /users/new: Form to create a new item"""
97 97 # url('new_user')
98 98 return render('admin/users/user_add.html')
99 99
100 100 def update(self, id):
101 101 """PUT /users/id: Update an existing item"""
102 102 # Forms posted to this method should contain a hidden field:
103 103 # <input type="hidden" name="_method" value="PUT" />
104 104 # Or using helpers:
105 105 # h.form(url('user', id=ID),
106 106 # method='put')
107 107 # url('user', id=ID)
108 108 user_model = UserModel()
109 109 c.user = user_model.get(id)
110 110
111 111 _form = UserForm(edit=True, old_data={'user_id':id,
112 112 'email':c.user.email})()
113 113 form_result = {}
114 114 try:
115 115 form_result = _form.to_python(dict(request.POST))
116 116 user_model.update(id, form_result)
117 117 h.flash(_('User updated succesfully'), category='success')
118 118
119 119 except formencode.Invalid, errors:
120 120 return htmlfill.render(
121 121 render('admin/users/user_edit.html'),
122 122 defaults=errors.value,
123 123 errors=errors.error_dict or {},
124 124 prefix_error=False,
125 125 encoding="UTF-8")
126 126 except Exception:
127 127 log.error(traceback.format_exc())
128 128 h.flash(_('error occurred during update of user %s') \
129 129 % form_result.get('username'), category='error')
130 130
131 131 return redirect(url('users'))
132 132
133 133 def delete(self, id):
134 134 """DELETE /users/id: Delete an existing item"""
135 135 # Forms posted to this method should contain a hidden field:
136 136 # <input type="hidden" name="_method" value="DELETE" />
137 137 # Or using helpers:
138 138 # h.form(url('user', id=ID),
139 139 # method='delete')
140 140 # url('user', id=ID)
141 141 user_model = UserModel()
142 142 try:
143 143 user_model.delete(id)
144 144 h.flash(_('successfully deleted user'), category='success')
145 145 except (UserOwnsReposException, DefaultUserException), e:
146 146 h.flash(str(e), category='warning')
147 147 except Exception:
148 148 h.flash(_('An error occurred during deletion of user'),
149 149 category='error')
150 150 return redirect(url('users'))
151 151
152 152 def show(self, id, format='html'):
153 153 """GET /users/id: Show a specific item"""
154 154 # url('user', id=ID)
155 155
156 156
157 157 def edit(self, id, format='html'):
158 158 """GET /users/id/edit: Form to edit an existing item"""
159 159 # url('edit_user', id=ID)
160 160 c.user = self.sa.query(User).get(id)
161 161 if not c.user:
162 162 return redirect(url('users'))
163 163 if c.user.username == 'default':
164 164 h.flash(_("You can't edit this user"), category='warning')
165 165 return redirect(url('users'))
166 166 c.user.permissions = {}
167 167 c.granted_permissions = fill_perms(c.user).permissions['global']
168 168
169 169 defaults = c.user.get_dict()
170 170
171 171 return htmlfill.render(
172 172 render('admin/users/user_edit.html'),
173 173 defaults=defaults,
174 174 encoding="UTF-8",
175 175 force_defaults=False
176 176 )
@@ -1,54 +1,54
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.branches
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 branches controller for rhodecode
7 7
8 8 :created_on: Apr 21, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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
30 30 from pylons import tmpl_context as c
31 31
32 32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 33 from rhodecode.lib.base import BaseController, render
34 34 from rhodecode.lib.utils import OrderedDict
35 35 from rhodecode.model.scm import ScmModel
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39 class BranchesController(BaseController):
40 40
41 41 @LoginRequired()
42 42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 43 'repository.admin')
44 44 def __before__(self):
45 45 super(BranchesController, self).__before__()
46 46
47 47 def index(self):
48 48 hg_model = ScmModel()
49 49 c.repo_info = hg_model.get_repo(c.repo_name)
50 50 c.repo_branches = OrderedDict()
51 51 for name, hash_ in c.repo_info.branches.items():
52 52 c.repo_branches[name] = c.repo_info.get_changeset(hash_)
53 53
54 54 return render('branches/branches.html')
@@ -1,106 +1,106
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.changelog
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 changelog controller for rhodecode
7 7
8 8 :created_on: Apr 21, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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
30 30 try:
31 31 import json
32 32 except ImportError:
33 33 #python 2.5 compatibility
34 34 import simplejson as json
35 35
36 36 from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev
37 37 from pylons import request, session, tmpl_context as c
38 38
39 39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
40 40 from rhodecode.lib.base import BaseController, render
41 41 from rhodecode.model.scm import ScmModel
42 42
43 43 from webhelpers.paginate import Page
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47 class ChangelogController(BaseController):
48 48
49 49 @LoginRequired()
50 50 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
51 51 'repository.admin')
52 52 def __before__(self):
53 53 super(ChangelogController, self).__before__()
54 54
55 55 def index(self):
56 56 limit = 100
57 57 default = 20
58 58 if request.params.get('size'):
59 59 try:
60 60 int_size = int(request.params.get('size'))
61 61 except ValueError:
62 62 int_size = default
63 63 int_size = int_size if int_size <= limit else limit
64 64 c.size = int_size
65 65 session['changelog_size'] = c.size
66 66 session.save()
67 67 else:
68 68 c.size = int(session.get('changelog_size', default))
69 69
70 70 changesets = ScmModel().get_repo(c.repo_name)
71 71
72 72 p = int(request.params.get('page', 1))
73 73 c.total_cs = len(changesets)
74 74 c.pagination = Page(changesets, page=p, item_count=c.total_cs,
75 75 items_per_page=c.size)
76 76
77 77 self._graph(changesets, c.size, p)
78 78
79 79 return render('changelog/changelog.html')
80 80
81 81
82 82 def _graph(self, repo, size, p):
83 83 revcount = size
84 84 if not repo.revisions or repo.alias == 'git':
85 85 c.jsdata = json.dumps([])
86 86 return
87 87
88 88 max_rev = repo.revisions[-1]
89 89
90 90 offset = 1 if p == 1 else ((p - 1) * revcount + 1)
91 91
92 92 rev_start = repo.revisions[(-1 * offset)]
93 93
94 94 revcount = min(max_rev, revcount)
95 95 rev_end = max(0, rev_start - revcount)
96 96 dag = graph_rev(repo.repo, rev_start, rev_end)
97 97
98 98 c.dag = tree = list(colored(dag))
99 99 data = []
100 100 for (id, type, ctx, vtx, edges) in tree:
101 101 if type != CHANGESET:
102 102 continue
103 103 data.append(('', vtx, edges))
104 104
105 105 c.jsdata = json.dumps(data)
106 106
@@ -1,192 +1,192
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.changeset
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 changeset controller for pylons
7 7
8 8 :created_on: Apr 25, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 logging
28 28 import traceback
29 29
30 30 from pylons import tmpl_context as c, url, request, response
31 31 from pylons.i18n.translation import _
32 32 from pylons.controllers.util import redirect
33 33
34 34 import rhodecode.lib.helpers as h
35 35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 36 from rhodecode.lib.base import BaseController, render
37 37 from rhodecode.lib.utils import EmptyChangeset
38 38 from rhodecode.model.scm import ScmModel
39 39
40 40 from vcs.exceptions import RepositoryError, ChangesetError
41 41 from vcs.nodes import FileNode
42 42 from vcs.utils import diffs as differ
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 class ChangesetController(BaseController):
47 47
48 48 @LoginRequired()
49 49 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
50 50 'repository.admin')
51 51 def __before__(self):
52 52 super(ChangesetController, self).__before__()
53 53
54 54 def index(self, revision):
55 55 hg_model = ScmModel()
56 56
57 57 def wrap_to_table(str):
58 58
59 59 return '''<table class="code-difftable">
60 60 <tr class="line">
61 61 <td class="lineno new"></td>
62 62 <td class="code"><pre>%s</pre></td>
63 63 </tr>
64 64 </table>''' % str
65 65
66 66 try:
67 67 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
68 68 except RepositoryError, e:
69 69 log.error(traceback.format_exc())
70 70 h.flash(str(e), category='warning')
71 71 return redirect(url('home'))
72 72 else:
73 73 try:
74 74 c.changeset_old = c.changeset.parents[0]
75 75 except IndexError:
76 76 c.changeset_old = None
77 77 c.changes = []
78 78
79 79 #===================================================================
80 80 # ADDED FILES
81 81 #===================================================================
82 82 c.sum_added = 0
83 83 for node in c.changeset.added:
84 84
85 85 filenode_old = FileNode(node.path, '', EmptyChangeset())
86 86 if filenode_old.is_binary or node.is_binary:
87 87 diff = wrap_to_table(_('binary file'))
88 88 else:
89 89 c.sum_added += node.size
90 90 if c.sum_added < self.cut_off_limit:
91 91 f_udiff = differ.get_udiff(filenode_old, node)
92 92 diff = differ.DiffProcessor(f_udiff).as_html()
93 93
94 94 else:
95 95 diff = wrap_to_table(_('Changeset is to big and was cut'
96 96 ' off, see raw changeset instead'))
97 97
98 98 cs1 = None
99 99 cs2 = node.last_changeset.raw_id
100 100 c.changes.append(('added', node, diff, cs1, cs2))
101 101
102 102 #===================================================================
103 103 # CHANGED FILES
104 104 #===================================================================
105 105 c.sum_removed = 0
106 106 for node in c.changeset.changed:
107 107 try:
108 108 filenode_old = c.changeset_old.get_node(node.path)
109 109 except ChangesetError:
110 110 filenode_old = FileNode(node.path, '', EmptyChangeset())
111 111
112 112 if filenode_old.is_binary or node.is_binary:
113 113 diff = wrap_to_table(_('binary file'))
114 114 else:
115 115
116 116 if c.sum_removed < self.cut_off_limit:
117 117 f_udiff = differ.get_udiff(filenode_old, node)
118 118 diff = differ.DiffProcessor(f_udiff).as_html()
119 119 if diff:
120 120 c.sum_removed += len(diff)
121 121 else:
122 122 diff = wrap_to_table(_('Changeset is to big and was cut'
123 123 ' off, see raw changeset instead'))
124 124
125 125
126 126 cs1 = filenode_old.last_changeset.raw_id
127 127 cs2 = node.last_changeset.raw_id
128 128 c.changes.append(('changed', node, diff, cs1, cs2))
129 129
130 130 #===================================================================
131 131 # REMOVED FILES
132 132 #===================================================================
133 133 for node in c.changeset.removed:
134 134 c.changes.append(('removed', node, None, None, None))
135 135
136 136 return render('changeset/changeset.html')
137 137
138 138 def raw_changeset(self, revision):
139 139
140 140 hg_model = ScmModel()
141 141 method = request.GET.get('diff', 'show')
142 142 try:
143 143 r = hg_model.get_repo(c.repo_name)
144 144 c.scm_type = r.alias
145 145 c.changeset = r.get_changeset(revision)
146 146 except RepositoryError:
147 147 log.error(traceback.format_exc())
148 148 return redirect(url('home'))
149 149 else:
150 150 try:
151 151 c.changeset_old = c.changeset.parents[0]
152 152 except IndexError:
153 153 c.changeset_old = None
154 154 c.changes = []
155 155
156 156 for node in c.changeset.added:
157 157 filenode_old = FileNode(node.path, '')
158 158 if filenode_old.is_binary or node.is_binary:
159 159 diff = _('binary file') + '\n'
160 160 else:
161 161 f_udiff = differ.get_udiff(filenode_old, node)
162 162 diff = differ.DiffProcessor(f_udiff).raw_diff()
163 163
164 164 cs1 = None
165 165 cs2 = node.last_changeset.raw_id
166 166 c.changes.append(('added', node, diff, cs1, cs2))
167 167
168 168 for node in c.changeset.changed:
169 169 filenode_old = c.changeset_old.get_node(node.path)
170 170 if filenode_old.is_binary or node.is_binary:
171 171 diff = _('binary file')
172 172 else:
173 173 f_udiff = differ.get_udiff(filenode_old, node)
174 174 diff = differ.DiffProcessor(f_udiff).raw_diff()
175 175
176 176 cs1 = filenode_old.last_changeset.raw_id
177 177 cs2 = node.last_changeset.raw_id
178 178 c.changes.append(('changed', node, diff, cs1, cs2))
179 179
180 180 response.content_type = 'text/plain'
181 181
182 182 if method == 'download':
183 183 response.content_disposition = 'attachment; filename=%s.patch' % revision
184 184
185 185 parent = True if len(c.changeset.parents) > 0 else False
186 186 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0].raw_id if parent else ''
187 187
188 188 c.diffs = ''
189 189 for x in c.changes:
190 190 c.diffs += x[2]
191 191
192 192 return render('changeset/raw_changeset.html')
@@ -1,110 +1,110
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.error
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 RhodeCode error controller
7 7
8 8 :created_on: Dec 8, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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,90 +1,90
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.feed
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Feed controller for rhodecode
7 7
8 8 :created_on: Apr 23, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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
30 30 from pylons import url, response
31 31
32 32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 33 from rhodecode.lib.base import BaseController
34 34 from rhodecode.model.scm import ScmModel
35 35
36 36 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40 class FeedController(BaseController):
41 41
42 42 @LoginRequired()
43 43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 44 'repository.admin')
45 45 def __before__(self):
46 46 super(FeedController, self).__before__()
47 47 #common values for feeds
48 48 self.description = 'Changes on %s repository'
49 49 self.title = "%s feed"
50 50 self.language = 'en-us'
51 51 self.ttl = "5"
52 52 self.feed_nr = 10
53 53
54 54 def atom(self, repo_name):
55 55 """Produce an atom-1.0 feed via feedgenerator module"""
56 56 feed = Atom1Feed(title=self.title % repo_name,
57 57 link=url('summary_home', repo_name=repo_name, qualified=True),
58 58 description=self.description % repo_name,
59 59 language=self.language,
60 60 ttl=self.ttl)
61 61
62 62 changesets = ScmModel().get_repo(repo_name)
63 63
64 64 for cs in changesets[:self.feed_nr]:
65 65 feed.add_item(title=cs.message,
66 66 link=url('changeset_home', repo_name=repo_name,
67 67 revision=cs.raw_id, qualified=True),
68 68 description=str(cs.date))
69 69
70 70 response.content_type = feed.mime_type
71 71 return feed.writeString('utf-8')
72 72
73 73
74 74 def rss(self, repo_name):
75 75 """Produce an rss2 feed via feedgenerator module"""
76 76 feed = Rss201rev2Feed(title=self.title % repo_name,
77 77 link=url('summary_home', repo_name=repo_name, qualified=True),
78 78 description=self.description % repo_name,
79 79 language=self.language,
80 80 ttl=self.ttl)
81 81
82 82 changesets = ScmModel().get_repo(repo_name)
83 83 for cs in changesets[:self.feed_nr]:
84 84 feed.add_item(title=cs.message,
85 85 link=url('changeset_home', repo_name=repo_name,
86 86 revision=cs.raw_id, qualified=True),
87 87 description=str(cs.date))
88 88
89 89 response.content_type = feed.mime_type
90 90 return feed.writeString('utf-8')
@@ -1,268 +1,268
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.files
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Files controller for RhodeCode
7 7
8 8 :created_on: Apr 21, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 tempfile
28 28 import logging
29 29 import rhodecode.lib.helpers as h
30 30
31 31 from mercurial import archival
32 32
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.i18n.translation import _
35 35 from pylons.controllers.util import redirect
36 36
37 37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 38 from rhodecode.lib.base import BaseController, render
39 39 from rhodecode.lib.utils import EmptyChangeset
40 40 from rhodecode.model.scm import ScmModel
41 41
42 42 from vcs.exceptions import RepositoryError, ChangesetError, ChangesetDoesNotExistError
43 43 from vcs.nodes import FileNode
44 44 from vcs.utils import diffs as differ
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48 class FilesController(BaseController):
49 49
50 50 @LoginRequired()
51 51 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
52 52 'repository.admin')
53 53 def __before__(self):
54 54 super(FilesController, self).__before__()
55 55 c.cut_off_limit = self.cut_off_limit
56 56
57 57 def index(self, repo_name, revision, f_path):
58 58 hg_model = ScmModel()
59 59 c.repo = hg_model.get_repo(c.repo_name)
60 60
61 61 try:
62 62 #reditect to given revision from form
63 63 post_revision = request.POST.get('at_rev', None)
64 64 if post_revision:
65 65 post_revision = c.repo.get_changeset(post_revision).raw_id
66 66 redirect(url('files_home', repo_name=c.repo_name,
67 67 revision=post_revision, f_path=f_path))
68 68
69 69 c.branch = request.GET.get('branch', None)
70 70
71 71 c.f_path = f_path
72 72
73 73 c.changeset = c.repo.get_changeset(revision)
74 74 cur_rev = c.changeset.revision
75 75
76 76 #prev link
77 77 try:
78 78 prev_rev = c.repo.get_changeset(cur_rev).prev(c.branch).raw_id
79 79 c.url_prev = url('files_home', repo_name=c.repo_name,
80 80 revision=prev_rev, f_path=f_path)
81 81 if c.branch:
82 82 c.url_prev += '?branch=%s' % c.branch
83 83 except ChangesetDoesNotExistError:
84 84 c.url_prev = '#'
85 85
86 86 #next link
87 87 try:
88 88 next_rev = c.repo.get_changeset(cur_rev).next(c.branch).raw_id
89 89 c.url_next = url('files_home', repo_name=c.repo_name,
90 90 revision=next_rev, f_path=f_path)
91 91 if c.branch:
92 92 c.url_next += '?branch=%s' % c.branch
93 93 except ChangesetDoesNotExistError:
94 94 c.url_next = '#'
95 95
96 96 #files
97 97 try:
98 98 c.files_list = c.changeset.get_node(f_path)
99 99 c.file_history = self._get_history(c.repo, c.files_list, f_path)
100 100 except RepositoryError, e:
101 101 h.flash(str(e), category='warning')
102 102 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
103 103
104 104 except RepositoryError, e:
105 105 h.flash(str(e), category='warning')
106 106 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
107 107
108 108
109 109
110 110 return render('files/files.html')
111 111
112 112 def rawfile(self, repo_name, revision, f_path):
113 113 hg_model = ScmModel()
114 114 c.repo = hg_model.get_repo(c.repo_name)
115 115 file_node = c.repo.get_changeset(revision).get_node(f_path)
116 116 response.content_type = file_node.mimetype
117 117 response.content_disposition = 'attachment; filename=%s' \
118 118 % f_path.split('/')[-1]
119 119 return file_node.content
120 120
121 121 def raw(self, repo_name, revision, f_path):
122 122 hg_model = ScmModel()
123 123 c.repo = hg_model.get_repo(c.repo_name)
124 124 file_node = c.repo.get_changeset(revision).get_node(f_path)
125 125 response.content_type = 'text/plain'
126 126
127 127 return file_node.content
128 128
129 129 def annotate(self, repo_name, revision, f_path):
130 130 hg_model = ScmModel()
131 131 c.repo = hg_model.get_repo(c.repo_name)
132 132
133 133 try:
134 134 c.cs = c.repo.get_changeset(revision)
135 135 c.file = c.cs.get_node(f_path)
136 136 except RepositoryError, e:
137 137 h.flash(str(e), category='warning')
138 138 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
139 139
140 140 c.file_history = self._get_history(c.repo, c.file, f_path)
141 141
142 142 c.f_path = f_path
143 143
144 144 return render('files/files_annotate.html')
145 145
146 146 def archivefile(self, repo_name, fname):
147 147 info = fname.split('.')
148 148 revision, fileformat = info[0], '.' + '.'.join(info[1:])
149 149 archive_specs = {
150 150 '.tar.bz2': ('application/x-tar', 'tbz2'),
151 151 '.tar.gz': ('application/x-tar', 'tgz'),
152 152 '.zip': ('application/zip', 'zip'),
153 153 }
154 154 if not archive_specs.has_key(fileformat):
155 155 return _('Unknown archive type %s') % fileformat
156 156
157 157 repo = ScmModel().get_repo(repo_name)
158 158
159 159 try:
160 160 repo.get_changeset(revision)
161 161 except ChangesetDoesNotExistError:
162 162 return _('Unknown revision %s') % revision
163 163
164 164 archive = tempfile.TemporaryFile()
165 165 localrepo = repo.repo
166 166 fname = '%s-%s%s' % (repo_name, revision, fileformat)
167 167 archival.archive(localrepo, archive, revision, archive_specs[fileformat][1],
168 168 prefix='%s-%s' % (repo_name, revision))
169 169 response.content_type = archive_specs[fileformat][0]
170 170 response.content_disposition = 'attachment; filename=%s' % fname
171 171 archive.seek(0)
172 172
173 173 def read_in_chunks(file_object, chunk_size=1024 * 40):
174 174 """Lazy function (generator) to read a file piece by piece.
175 175 Default chunk size: 40k."""
176 176 while True:
177 177 data = file_object.read(chunk_size)
178 178 if not data:
179 179 break
180 180 yield data
181 181
182 182 return read_in_chunks(archive)
183 183
184 184 def diff(self, repo_name, f_path):
185 185 hg_model = ScmModel()
186 186 diff1 = request.GET.get('diff1')
187 187 diff2 = request.GET.get('diff2')
188 188 c.action = request.GET.get('diff')
189 189 c.no_changes = diff1 == diff2
190 190 c.f_path = f_path
191 191 c.repo = hg_model.get_repo(c.repo_name)
192 192
193 193 try:
194 194 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
195 195 c.changeset_1 = c.repo.get_changeset(diff1)
196 196 node1 = c.changeset_1.get_node(f_path)
197 197 else:
198 198 c.changeset_1 = EmptyChangeset()
199 199 node1 = FileNode('.', '', changeset=c.changeset_1)
200 200
201 201 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
202 202 c.changeset_2 = c.repo.get_changeset(diff2)
203 203 node2 = c.changeset_2.get_node(f_path)
204 204 else:
205 205 c.changeset_2 = EmptyChangeset()
206 206 node2 = FileNode('.', '', changeset=c.changeset_2)
207 207 except RepositoryError:
208 208 return redirect(url('files_home',
209 209 repo_name=c.repo_name, f_path=f_path))
210 210
211 211 f_udiff = differ.get_udiff(node1, node2)
212 212 diff = differ.DiffProcessor(f_udiff)
213 213
214 214 if c.action == 'download':
215 215 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
216 216 response.content_type = 'text/plain'
217 217 response.content_disposition = 'attachment; filename=%s' \
218 218 % diff_name
219 219 return diff.raw_diff()
220 220
221 221 elif c.action == 'raw':
222 222 response.content_type = 'text/plain'
223 223 return diff.raw_diff()
224 224
225 225 elif c.action == 'diff':
226 226 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
227 227 c.cur_diff = _('Diff is to big to display')
228 228 else:
229 229 c.cur_diff = diff.as_html()
230 230 else:
231 231 #default option
232 232 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
233 233 c.cur_diff = _('Diff is to big to display')
234 234 else:
235 235 c.cur_diff = diff.as_html()
236 236
237 237 if not c.cur_diff: c.no_changes = True
238 238 return render('files/file_diff.html')
239 239
240 240 def _get_history(self, repo, node, f_path):
241 241 from vcs.nodes import NodeKind
242 242 if not node.kind is NodeKind.FILE:
243 243 return []
244 244 changesets = node.history
245 245 hist_l = []
246 246
247 247 changesets_group = ([], _("Changesets"))
248 248 branches_group = ([], _("Branches"))
249 249 tags_group = ([], _("Tags"))
250 250
251 251 for chs in changesets:
252 252 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
253 253 changesets_group[0].append((chs.raw_id, n_desc,))
254 254
255 255 hist_l.append(changesets_group)
256 256
257 257 for name, chs in c.repository_branches.items():
258 258 #chs = chs.split(':')[-1]
259 259 branches_group[0].append((chs, name),)
260 260 hist_l.append(branches_group)
261 261
262 262 for name, chs in c.repository_tags.items():
263 263 #chs = chs.split(':')[-1]
264 264 tags_group[0].append((chs, name),)
265 265 hist_l.append(tags_group)
266 266
267 267 return hist_l
268 268
@@ -1,66 +1,66
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.home
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Home controller for Rhodecode
7 7
8 8 :created_on: Feb 18, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 from operator import itemgetter
30 30
31 31 from pylons import tmpl_context as c, request
32 32
33 33 from rhodecode.lib.auth import LoginRequired
34 34 from rhodecode.lib.base import BaseController, render
35 35 from rhodecode.model.scm import ScmModel
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39 class HomeController(BaseController):
40 40
41 41 @LoginRequired()
42 42 def __before__(self):
43 43 super(HomeController, self).__before__()
44 44
45 45 def index(self):
46 46 sortables = ['name', 'description', 'last_change', 'tip', 'contact']
47 47 current_sort = request.GET.get('sort', 'name')
48 48 current_sort_slug = current_sort.replace('-', '')
49 49
50 50 if current_sort_slug not in sortables:
51 51 c.sort_by = 'name'
52 52 current_sort_slug = c.sort_by
53 53 else:
54 54 c.sort_by = current_sort
55 55 c.sort_slug = current_sort_slug
56 56 cached_repo_list = ScmModel().get_repos()
57 57
58 58 sort_key = current_sort_slug + '_sort'
59 59 if c.sort_by.startswith('-'):
60 60 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
61 61 reverse=True)
62 62 else:
63 63 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
64 64 reverse=False)
65 65
66 66 return render('/index.html')
@@ -1,98 +1,98
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.journal
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Journal controller for pylons
7 7
8 8 :created_on: Nov 21, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 from sqlalchemy import or_
30 30
31 31 from pylons import request, response, session, tmpl_context as c, url
32 32
33 33 from rhodecode.lib.auth import LoginRequired, NotAnonymous
34 34 from rhodecode.lib.base import BaseController, render
35 35 from rhodecode.lib.helpers import get_token
36 36 from rhodecode.model.db import UserLog, UserFollowing
37 37 from rhodecode.model.scm import ScmModel
38 38
39 39 from paste.httpexceptions import HTTPInternalServerError
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43 class JournalController(BaseController):
44 44
45 45
46 46 @LoginRequired()
47 47 @NotAnonymous()
48 48 def __before__(self):
49 49 super(JournalController, self).__before__()
50 50
51 51 def index(self):
52 52 # Return a rendered template
53 53
54 54 c.following = self.sa.query(UserFollowing)\
55 55 .filter(UserFollowing.user_id == c.rhodecode_user.user_id).all()
56 56
57 57 repo_ids = [x.follows_repository.repo_id for x in c.following
58 58 if x.follows_repository is not None]
59 59 user_ids = [x.follows_user.user_id for x in c.following
60 60 if x.follows_user is not None]
61 61
62 62 c.journal = self.sa.query(UserLog)\
63 63 .filter(or_(
64 64 UserLog.repository_id.in_(repo_ids),
65 65 UserLog.user_id.in_(user_ids),
66 66 ))\
67 67 .order_by(UserLog.action_date.desc())\
68 68 .limit(20)\
69 69 .all()
70 70 return render('/journal.html')
71 71
72 72 def toggle_following(self):
73 73 cur_token = request.POST.get('auth_token')
74 74 token = get_token()
75 75 if cur_token == token:
76 76 scm_model = ScmModel()
77 77
78 78 user_id = request.POST.get('follows_user_id')
79 79 if user_id:
80 80 try:
81 81 scm_model.toggle_following_user(user_id,
82 82 c.rhodecode_user.user_id)
83 83 return 'ok'
84 84 except:
85 85 raise HTTPInternalServerError()
86 86
87 87 repo_id = request.POST.get('follows_repo_id')
88 88 if repo_id:
89 89 try:
90 90 scm_model.toggle_following_repo(repo_id,
91 91 c.rhodecode_user.user_id)
92 92 return 'ok'
93 93 except:
94 94 raise HTTPInternalServerError()
95 95
96 96
97 97 log.debug('token mismatch %s vs %s', cur_token, token)
98 98 raise HTTPInternalServerError()
@@ -1,152 +1,152
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.login
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Login controller for rhodeocode
7 7
8 8 :created_on: Apr 22, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 formencode
30 30
31 31 from formencode import htmlfill
32 32
33 33 from pylons.i18n.translation import _
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons import request, response, session, tmpl_context as c, url
36 36
37 37 import rhodecode.lib.helpers as h
38 38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
39 39 from rhodecode.lib.base import BaseController, render
40 40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
41 41 from rhodecode.model.user import UserModel
42 42
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 class LoginController(BaseController):
47 47
48 48 def __before__(self):
49 49 super(LoginController, self).__before__()
50 50
51 51 def index(self):
52 52 #redirect if already logged in
53 53 c.came_from = request.GET.get('came_from', None)
54 54
55 55 if c.rhodecode_user.is_authenticated \
56 56 and c.rhodecode_user.username != 'default':
57 57
58 58 return redirect(url('home'))
59 59
60 60 if request.POST:
61 61 #import Login Form validator class
62 62 login_form = LoginForm()
63 63 try:
64 64 c.form_result = login_form.to_python(dict(request.POST))
65 65 username = c.form_result['username']
66 66 user = UserModel().get_by_username(username, case_insensitive=True)
67 67 auth_user = AuthUser()
68 68 auth_user.username = user.username
69 69 auth_user.is_authenticated = True
70 70 auth_user.is_admin = user.admin
71 71 auth_user.user_id = user.user_id
72 72 auth_user.name = user.name
73 73 auth_user.lastname = user.lastname
74 74 session['rhodecode_user'] = auth_user
75 75 session.save()
76 76 log.info('user %s is now authenticated', username)
77 77
78 78 user.update_lastlogin()
79 79
80 80 if c.came_from:
81 81 return redirect(c.came_from)
82 82 else:
83 83 return redirect(url('home'))
84 84
85 85 except formencode.Invalid, errors:
86 86 return htmlfill.render(
87 87 render('/login.html'),
88 88 defaults=errors.value,
89 89 errors=errors.error_dict or {},
90 90 prefix_error=False,
91 91 encoding="UTF-8")
92 92
93 93 return render('/login.html')
94 94
95 95 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
96 96 'hg.register.manual_activate')
97 97 def register(self):
98 98 user_model = UserModel()
99 99 c.auto_active = False
100 100 for perm in user_model.get_by_username('default', cache=False).user_perms:
101 101 if perm.permission.permission_name == 'hg.register.auto_activate':
102 102 c.auto_active = True
103 103 break
104 104
105 105 if request.POST:
106 106
107 107 register_form = RegisterForm()()
108 108 try:
109 109 form_result = register_form.to_python(dict(request.POST))
110 110 form_result['active'] = c.auto_active
111 111 user_model.create_registration(form_result)
112 112 h.flash(_('You have successfully registered into rhodecode'),
113 113 category='success')
114 114 return redirect(url('login_home'))
115 115
116 116 except formencode.Invalid, errors:
117 117 return htmlfill.render(
118 118 render('/register.html'),
119 119 defaults=errors.value,
120 120 errors=errors.error_dict or {},
121 121 prefix_error=False,
122 122 encoding="UTF-8")
123 123
124 124 return render('/register.html')
125 125
126 126 def password_reset(self):
127 127 user_model = UserModel()
128 128 if request.POST:
129 129
130 130 password_reset_form = PasswordResetForm()()
131 131 try:
132 132 form_result = password_reset_form.to_python(dict(request.POST))
133 133 user_model.reset_password(form_result)
134 134 h.flash(_('Your new password was sent'),
135 135 category='success')
136 136 return redirect(url('login_home'))
137 137
138 138 except formencode.Invalid, errors:
139 139 return htmlfill.render(
140 140 render('/password_reset.html'),
141 141 defaults=errors.value,
142 142 errors=errors.error_dict or {},
143 143 prefix_error=False,
144 144 encoding="UTF-8")
145 145
146 146 return render('/password_reset.html')
147 147
148 148 def logout(self):
149 149 session['rhodecode_user'] = AuthUser()
150 150 session.save()
151 151 log.info('Logging out and setting user as Empty')
152 152 redirect(url('home'))
@@ -1,120 +1,120
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.search
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Search controller for rhodecode
7 7
8 8 :created_on: Aug 7, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 logging
28 28 import traceback
29 29
30 30 from pylons.i18n.translation import _
31 31 from pylons import request, response, config, session, tmpl_context as c, url
32 32 from pylons.controllers.util import abort, redirect
33 33
34 34 from rhodecode.lib.auth import LoginRequired
35 35 from rhodecode.lib.base import BaseController, render
36 36 from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
37 37
38 38 from webhelpers.paginate import Page
39 39 from webhelpers.util import update_params
40 40
41 41 from whoosh.index import open_dir, EmptyIndexError
42 42 from whoosh.qparser import QueryParser, QueryParserError
43 43 from whoosh.query import Phrase
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47 class SearchController(BaseController):
48 48
49 49 @LoginRequired()
50 50 def __before__(self):
51 51 super(SearchController, self).__before__()
52 52
53 53 def index(self, search_repo=None):
54 54 c.repo_name = search_repo
55 55 c.formated_results = []
56 56 c.runtime = ''
57 57 c.cur_query = request.GET.get('q', None)
58 58 c.cur_type = request.GET.get('type', 'source')
59 59 c.cur_search = search_type = {'content':'content',
60 60 'commit':'content',
61 61 'path':'path',
62 62 'repository':'repository'}\
63 63 .get(c.cur_type, 'content')
64 64
65 65
66 66 if c.cur_query:
67 67 cur_query = c.cur_query.lower()
68 68
69 69 if c.cur_query:
70 70 p = int(request.params.get('page', 1))
71 71 highlight_items = set()
72 72 try:
73 73 idx = open_dir(config['app_conf']['index_dir']
74 74 , indexname=IDX_NAME)
75 75 searcher = idx.searcher()
76 76
77 77 qp = QueryParser(search_type, schema=SCHEMA)
78 78 if c.repo_name:
79 79 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
80 80 try:
81 81 query = qp.parse(unicode(cur_query))
82 82
83 83 if isinstance(query, Phrase):
84 84 highlight_items.update(query.words)
85 85 else:
86 86 for i in query.all_terms():
87 87 if i[0] == 'content':
88 88 highlight_items.add(i[1])
89 89
90 90 matcher = query.matcher(searcher)
91 91
92 92 log.debug(query)
93 93 log.debug(highlight_items)
94 94 results = searcher.search(query)
95 95 res_ln = len(results)
96 96 c.runtime = '%s results (%.3f seconds)' \
97 97 % (res_ln, results.runtime)
98 98
99 99 def url_generator(**kw):
100 100 return update_params("?q=%s&type=%s" \
101 101 % (c.cur_query, c.cur_search), **kw)
102 102
103 103 c.formated_results = Page(
104 104 ResultWrapper(search_type, searcher, matcher,
105 105 highlight_items),
106 106 page=p, item_count=res_ln,
107 107 items_per_page=10, url=url_generator)
108 108
109 109
110 110 except QueryParserError:
111 111 c.runtime = _('Invalid search query. Try quoting it.')
112 112 searcher.close()
113 113 except (EmptyIndexError, IOError):
114 114 log.error(traceback.format_exc())
115 115 log.error('Empty Index data')
116 116 c.runtime = _('There is no index to search in. '
117 117 'Please run whoosh indexer')
118 118
119 119 # Return a rendered template
120 120 return render('/search/search.html')
@@ -1,184 +1,184
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.settings
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Settings controller for rhodecode
7 7
8 8 :created_on: Jun 30, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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
31 31 import formencode
32 32 from formencode import htmlfill
33 33
34 34 from pylons import tmpl_context as c, request, url
35 35 from pylons.controllers.util import redirect
36 36 from pylons.i18n.translation import _
37 37
38 38 import rhodecode.lib.helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
40 40 from rhodecode.lib.base import BaseController, render
41 41 from rhodecode.lib.utils import invalidate_cache, action_logger
42 42 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
43 43 from rhodecode.model.repo import RepoModel
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47 class SettingsController(BaseController):
48 48
49 49 @LoginRequired()
50 50 @HasRepoPermissionAllDecorator('repository.admin')
51 51 def __before__(self):
52 52 super(SettingsController, self).__before__()
53 53
54 54 def index(self, repo_name):
55 55 repo_model = RepoModel()
56 56 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
57 57 if not repo:
58 58 h.flash(_('%s repository is not mapped to db perhaps'
59 59 ' it was created or renamed from the file system'
60 60 ' please run the application again'
61 61 ' in order to rescan repositories') % repo_name,
62 62 category='error')
63 63
64 64 return redirect(url('home'))
65 65 defaults = c.repo_info.get_dict()
66 66 defaults.update({'user':c.repo_info.user.username})
67 67 c.users_array = repo_model.get_users_js()
68 68
69 69 for p in c.repo_info.repo_to_perm:
70 70 defaults.update({'perm_%s' % p.user.username:
71 71 p.permission.permission_name})
72 72
73 73 return htmlfill.render(
74 74 render('settings/repo_settings.html'),
75 75 defaults=defaults,
76 76 encoding="UTF-8",
77 77 force_defaults=False
78 78 )
79 79
80 80 def update(self, repo_name):
81 81 repo_model = RepoModel()
82 82 changed_name = repo_name
83 83 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
84 84 try:
85 85 form_result = _form.to_python(dict(request.POST))
86 86 repo_model.update(repo_name, form_result)
87 87 invalidate_cache('get_repo_cached_%s' % repo_name)
88 88 h.flash(_('Repository %s updated successfully' % repo_name),
89 89 category='success')
90 90 changed_name = form_result['repo_name']
91 91 action_logger(self.rhodecode_user, 'user_updated_repo',
92 92 changed_name, '', self.sa)
93 93 except formencode.Invalid, errors:
94 94 c.repo_info = repo_model.get_by_repo_name(repo_name)
95 95 c.users_array = repo_model.get_users_js()
96 96 errors.value.update({'user':c.repo_info.user.username})
97 97 return htmlfill.render(
98 98 render('settings/repo_settings.html'),
99 99 defaults=errors.value,
100 100 errors=errors.error_dict or {},
101 101 prefix_error=False,
102 102 encoding="UTF-8")
103 103 except Exception:
104 104 log.error(traceback.format_exc())
105 105 h.flash(_('error occurred during update of repository %s') \
106 106 % repo_name, category='error')
107 107
108 108 return redirect(url('repo_settings_home', repo_name=changed_name))
109 109
110 110
111 111
112 112 def delete(self, repo_name):
113 113 """DELETE /repos/repo_name: Delete an existing item"""
114 114 # Forms posted to this method should contain a hidden field:
115 115 # <input type="hidden" name="_method" value="DELETE" />
116 116 # Or using helpers:
117 117 # h.form(url('repo_settings_delete', repo_name=ID),
118 118 # method='delete')
119 119 # url('repo_settings_delete', repo_name=ID)
120 120
121 121 repo_model = RepoModel()
122 122 repo = repo_model.get_by_repo_name(repo_name)
123 123 if not repo:
124 124 h.flash(_('%s repository is not mapped to db perhaps'
125 125 ' it was moved or renamed from the filesystem'
126 126 ' please run the application again'
127 127 ' in order to rescan repositories') % repo_name,
128 128 category='error')
129 129
130 130 return redirect(url('home'))
131 131 try:
132 132 action_logger(self.rhodecode_user, 'user_deleted_repo',
133 133 repo_name, '', self.sa)
134 134 repo_model.delete(repo)
135 135 invalidate_cache('get_repo_cached_%s' % repo_name)
136 136 h.flash(_('deleted repository %s') % repo_name, category='success')
137 137 except Exception:
138 138 h.flash(_('An error occurred during deletion of %s') % repo_name,
139 139 category='error')
140 140
141 141 return redirect(url('home'))
142 142
143 143 def fork(self, repo_name):
144 144 repo_model = RepoModel()
145 145 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
146 146 if not repo:
147 147 h.flash(_('%s repository is not mapped to db perhaps'
148 148 ' it was created or renamed from the file system'
149 149 ' please run the application again'
150 150 ' in order to rescan repositories') % repo_name,
151 151 category='error')
152 152
153 153 return redirect(url('home'))
154 154
155 155 return render('settings/repo_fork.html')
156 156
157 157
158 158
159 159 def fork_create(self, repo_name):
160 160 repo_model = RepoModel()
161 161 c.repo_info = repo_model.get_by_repo_name(repo_name)
162 162 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
163 163 form_result = {}
164 164 try:
165 165 form_result = _form.to_python(dict(request.POST))
166 166 form_result.update({'repo_name':repo_name})
167 167 repo_model.create_fork(form_result, c.rhodecode_user)
168 168 h.flash(_('forked %s repository as %s') \
169 169 % (repo_name, form_result['fork_name']),
170 170 category='success')
171 171 action_logger(self.rhodecode_user,
172 172 'user_forked_repo:%s' % form_result['fork_name'],
173 173 repo_name, '', self.sa)
174 174 except formencode.Invalid, errors:
175 175 c.new_repo = errors.value['fork_name']
176 176 r = render('settings/repo_fork.html')
177 177
178 178 return htmlfill.render(
179 179 r,
180 180 defaults=errors.value,
181 181 errors=errors.error_dict or {},
182 182 prefix_error=False,
183 183 encoding="UTF-8")
184 184 return redirect(url('home'))
@@ -1,56 +1,56
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.shortlog
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Shortlog controller for rhodecode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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
30 30 from pylons import tmpl_context as c, request
31 31
32 32 from webhelpers.paginate import Page
33 33
34 34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 35 from rhodecode.lib.base import BaseController, render
36 36 from rhodecode.model.scm import ScmModel
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40 class ShortlogController(BaseController):
41 41
42 42 @LoginRequired()
43 43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 44 'repository.admin')
45 45 def __before__(self):
46 46 super(ShortlogController, self).__before__()
47 47
48 48 def index(self):
49 49 p = int(request.params.get('page', 1))
50 50 repo = ScmModel().get_repo(c.repo_name)
51 51 c.repo_changesets = Page(repo, page=p, items_per_page=20)
52 52 c.shortlog_data = render('shortlog/shortlog_data.html')
53 53 if request.params.get('partial'):
54 54 return c.shortlog_data
55 55 r = render('shortlog/shortlog.html')
56 56 return r
@@ -1,144 +1,144
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.summary
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Summary controller for Rhodecode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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,53 +1,53
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.tags
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Tags controller for rhodecode
7 7
8 8 :created_on: Apr 21, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 logging
28 28
29 29 from pylons import tmpl_context as c
30 30
31 31 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
32 32 from rhodecode.lib.base import BaseController, render
33 33 from rhodecode.lib.utils import OrderedDict
34 34 from rhodecode.model.scm import ScmModel
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38 class TagsController(BaseController):
39 39
40 40 @LoginRequired()
41 41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
42 42 'repository.admin')
43 43 def __before__(self):
44 44 super(TagsController, self).__before__()
45 45
46 46 def index(self):
47 47 hg_model = ScmModel()
48 48 c.repo_info = hg_model.get_repo(c.repo_name)
49 49 c.repo_tags = OrderedDict()
50 50 for name, hash_ in c.repo_info.tags.items():
51 51 c.repo_tags[name] = c.repo_info.get_changeset(hash_)
52 52
53 53 return render('tags/tags.html')
@@ -1,104 +1,104
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # ldap authentication lib
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 Nov 17, 2010
22 22
23 23 @author: marcink
24 24 """
25 25
26 26 from rhodecode.lib.exceptions import *
27 27 import logging
28 28
29 29 log = logging.getLogger(__name__)
30 30
31 31 try:
32 32 import ldap
33 33 except ImportError:
34 34 pass
35 35
36 36 class AuthLdap(object):
37 37
38 38 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
39 39 use_ldaps=False, ldap_version=3):
40 40 self.ldap_version = ldap_version
41 41 if use_ldaps:
42 42 port = port or 689
43 43 self.LDAP_USE_LDAPS = use_ldaps
44 44 self.LDAP_SERVER_ADDRESS = server
45 45 self.LDAP_SERVER_PORT = port
46 46
47 47 #USE FOR READ ONLY BIND TO LDAP SERVER
48 48 self.LDAP_BIND_DN = bind_dn
49 49 self.LDAP_BIND_PASS = bind_pass
50 50
51 51 ldap_server_type = 'ldap'
52 52 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
53 53 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
54 54 self.LDAP_SERVER_ADDRESS,
55 55 self.LDAP_SERVER_PORT)
56 56
57 57 self.BASE_DN = base_dn
58 58
59 59 def authenticate_ldap(self, username, password):
60 60 """Authenticate a user via LDAP and return his/her LDAP properties.
61 61
62 62 Raises AuthenticationError if the credentials are rejected, or
63 63 EnvironmentError if the LDAP server can't be reached.
64 64
65 65 :param username: username
66 66 :param password: password
67 67 """
68 68
69 69 from rhodecode.lib.helpers import chop_at
70 70
71 71 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
72 72
73 73 if "," in username:
74 74 raise LdapUsernameError("invalid character in username: ,")
75 75 try:
76 76 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
77 77 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
78 78 server = ldap.initialize(self.LDAP_SERVER)
79 79 if self.ldap_version == 2:
80 80 server.protocol = ldap.VERSION2
81 81 else:
82 82 server.protocol = ldap.VERSION3
83 83
84 84 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
85 85 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
86 86
87 87 dn = self.BASE_DN % {'user':uid}
88 88 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
89 89 server.simple_bind_s(dn, password)
90 90
91 91 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
92 92 if not properties:
93 93 raise ldap.NO_SUCH_OBJECT()
94 94 except ldap.NO_SUCH_OBJECT, e:
95 95 log.debug("LDAP says no such user '%s' (%s)", uid, username)
96 96 raise LdapUsernameError()
97 97 except ldap.INVALID_CREDENTIALS, e:
98 98 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
99 99 raise LdapPasswordError()
100 100 except ldap.SERVER_DOWN, e:
101 101 raise LdapConnectionError("LDAP can't access authentication server")
102 102
103 103 return properties[0]
104 104
@@ -1,108 +1,108
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # mercurial repository backup manager
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 Feb 28, 2010
23 23 Mercurial repositories backup manager
24 24 @author: marcink
25 25 """
26 26
27 27
28 28 import logging
29 29 import tarfile
30 30 import os
31 31 import datetime
32 32 import sys
33 33 import subprocess
34 34 logging.basicConfig(level=logging.DEBUG,
35 35 format="%(asctime)s %(levelname)-5.5s %(message)s")
36 36
37 37 class BackupManager(object):
38 38 def __init__(self, repos_location, rsa_key, backup_server):
39 39 today = datetime.datetime.now().weekday() + 1
40 40 self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
41 41
42 42 self.id_rsa_path = self.get_id_rsa(rsa_key)
43 43 self.repos_path = self.get_repos_path(repos_location)
44 44 self.backup_server = backup_server
45 45
46 46 self.backup_file_path = '/tmp'
47 47
48 48 logging.info('starting backup for %s', self.repos_path)
49 49 logging.info('backup target %s', self.backup_file_path)
50 50
51 51
52 52 def get_id_rsa(self, rsa_key):
53 53 if not os.path.isfile(rsa_key):
54 54 logging.error('Could not load id_rsa key file in %s', rsa_key)
55 55 sys.exit()
56 56 return rsa_key
57 57
58 58 def get_repos_path(self, path):
59 59 if not os.path.isdir(path):
60 60 logging.error('Wrong location for repositories in %s', path)
61 61 sys.exit()
62 62 return path
63 63
64 64 def backup_repos(self):
65 65 bckp_file = os.path.join(self.backup_file_path, self.backup_file_name)
66 66 tar = tarfile.open(bckp_file, "w:gz")
67 67
68 68 for dir_name in os.listdir(self.repos_path):
69 69 logging.info('backing up %s', dir_name)
70 70 tar.add(os.path.join(self.repos_path, dir_name), dir_name)
71 71 tar.close()
72 72 logging.info('finished backup of mercurial repositories')
73 73
74 74
75 75
76 76 def transfer_files(self):
77 77 params = {
78 78 'id_rsa_key': self.id_rsa_path,
79 79 'backup_file':os.path.join(self.backup_file_path,
80 80 self.backup_file_name),
81 81 'backup_server':self.backup_server
82 82 }
83 83 cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params,
84 84 '%(backup_file)s' % params,
85 85 '%(backup_server)s' % params]
86 86
87 87 subprocess.call(cmd)
88 88 logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4])
89 89
90 90
91 91 def rm_file(self):
92 92 logging.info('Removing file %s', self.backup_file_name)
93 93 os.remove(os.path.join(self.backup_file_path, self.backup_file_name))
94 94
95 95
96 96
97 97 if __name__ == "__main__":
98 98
99 99 repo_location = '/home/repo_path'
100 100 backup_server = 'root@192.168.1.100:/backups/mercurial'
101 101 rsa_key = '/home/id_rsa'
102 102
103 103 B_MANAGER = BackupManager(repo_location, rsa_key, backup_server)
104 104 B_MANAGER.backup_repos()
105 105 B_MANAGER.transfer_files()
106 106 B_MANAGER.rm_file()
107 107
108 108
@@ -1,106 +1,106
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 package.rhodecode.lib.celerylib.__init__
4 4 ~~~~~~~~~~~~~~
5 5
6 6 celery libs for RhodeCode
7 7
8 8 :created_on: Nov 27, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 sys
30 30 import socket
31 31 import traceback
32 32 import logging
33 33
34 34 from hashlib import md5
35 35 from decorator import decorator
36 36 from vcs.utils.lazy import LazyProperty
37 37
38 38 from rhodecode.lib.pidlock import DaemonLock, LockHeld
39 39
40 40 from pylons import config
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44 def str2bool(v):
45 45 return v.lower() in ["yes", "true", "t", "1"] if v else None
46 46
47 47 try:
48 48 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
49 49 except KeyError:
50 50 CELERY_ON = False
51 51
52 52 class ResultWrapper(object):
53 53 def __init__(self, task):
54 54 self.task = task
55 55
56 56 @LazyProperty
57 57 def result(self):
58 58 return self.task
59 59
60 60 def run_task(task, *args, **kwargs):
61 61 if CELERY_ON:
62 62 try:
63 63 t = task.delay(*args, **kwargs)
64 64 log.info('running task %s:%s', t.task_id, task)
65 65 return t
66 66 except socket.error, e:
67 67 if e.errno == 111:
68 68 log.debug('Unable to connect to celeryd. Sync execution')
69 69 else:
70 70 log.error(traceback.format_exc())
71 71 except KeyError, e:
72 72 log.debug('Unable to connect to celeryd. Sync execution')
73 73 except Exception, e:
74 74 log.error(traceback.format_exc())
75 75
76 76 log.debug('executing task %s in sync mode', task)
77 77 return ResultWrapper(task(*args, **kwargs))
78 78
79 79
80 80 def locked_task(func):
81 81 def __wrapper(func, *fargs, **fkwargs):
82 82 params = list(fargs)
83 83 params.extend(['%s-%s' % ar for ar in fkwargs.items()])
84 84
85 85 lockkey = 'task_%s' % \
86 86 md5(str(func.__name__) + '-' + \
87 87 '-'.join(map(str, params))).hexdigest()
88 88 log.info('running task with lockkey %s', lockkey)
89 89 try:
90 90 l = DaemonLock(lockkey)
91 91 ret = func(*fargs, **fkwargs)
92 92 l.release()
93 93 return ret
94 94 except LockHeld:
95 95 log.info('LockHeld')
96 96 return 'Task with key %s already running' % lockkey
97 97
98 98 return decorator(__wrapper, func)
99 99
100 100
101 101
102 102
103 103
104 104
105 105
106 106
@@ -1,494 +1,494
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.db_manage
4 4 ~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Database creation, and setup module for RhodeCode. Used for creation
7 7 of database as well as for migration operations
8 8
9 9 :created_on: Apr 10, 2010
10 10 :author: marcink
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software; you can redistribute it and/or
15 15 # modify it under the terms of the GNU General Public License
16 16 # as published by the Free Software Foundation; version 2
17 17 # of the License or (at your opinion) any later version of the license.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program; if not, write to the Free Software
26 26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 27 # MA 02110-1301, USA.
28 28
29 29 import os
30 30 import sys
31 31 import uuid
32 32 import logging
33 33 from os.path import dirname as dn, join as jn
34 34
35 35 from rhodecode import __dbversion__
36 36 from rhodecode.model import meta
37 37
38 38 from rhodecode.lib.auth import get_crypt_password
39 39 from rhodecode.lib.utils import ask_ok
40 40 from rhodecode.model import init_model
41 41 from rhodecode.model.db import User, Permission, RhodeCodeUi, RhodeCodeSettings, \
42 42 UserToPerm, DbMigrateVersion
43 43
44 44 from sqlalchemy.engine import create_engine
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48 class DbManage(object):
49 49 def __init__(self, log_sql, dbconf, root, tests=False):
50 50 self.dbname = dbconf.split('/')[-1]
51 51 self.tests = tests
52 52 self.root = root
53 53 self.dburi = dbconf
54 54 engine = create_engine(self.dburi, echo=log_sql)
55 55 init_model(engine)
56 56 self.sa = meta.Session()
57 57 self.db_exists = False
58 58
59 59 def check_for_db(self, override):
60 60 db_path = jn(self.root, self.dbname)
61 61 if self.dburi.startswith('sqlite'):
62 62 log.info('checking for existing db in %s', db_path)
63 63 if os.path.isfile(db_path):
64 64
65 65 self.db_exists = True
66 66 if not override:
67 67 raise Exception('database already exists')
68 68
69 69 def create_tables(self, override=False):
70 70 """Create a auth database
71 71 """
72 72
73 73 self.check_for_db(override)
74 74 if self.db_exists:
75 75 log.info("database exist and it's going to be destroyed")
76 76 if self.tests:
77 77 destroy = True
78 78 else:
79 79 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
80 80 if not destroy:
81 81 sys.exit()
82 82 if self.db_exists and destroy:
83 83 os.remove(jn(self.root, self.dbname))
84 84 checkfirst = not override
85 85 meta.Base.metadata.create_all(checkfirst=checkfirst)
86 86 log.info('Created tables for %s', self.dbname)
87 87
88 88
89 89
90 90 def set_db_version(self):
91 91 try:
92 92 ver = DbMigrateVersion()
93 93 ver.version = __dbversion__
94 94 ver.repository_id = 'rhodecode_db_migrations'
95 95 ver.repository_path = 'versions'
96 96 self.sa.add(ver)
97 97 self.sa.commit()
98 98 except:
99 99 self.sa.rollback()
100 100 raise
101 101 log.info('db version set to: %s', __dbversion__)
102 102
103 103
104 104 def upgrade(self):
105 105 """Upgrades given database schema to given revision following
106 106 all needed steps, to perform the upgrade
107 107
108 108 """
109 109
110 110 from rhodecode.lib.dbmigrate.migrate.versioning import api
111 111 from rhodecode.lib.dbmigrate.migrate.exceptions import \
112 112 DatabaseNotControlledError
113 113
114 114 upgrade = ask_ok('You are about to perform database upgrade, make '
115 115 'sure You backed up your database before. '
116 116 'Continue ? [y/n]')
117 117 if not upgrade:
118 118 sys.exit('Nothing done')
119 119
120 120 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
121 121 'rhodecode/lib/dbmigrate')
122 122 db_uri = self.dburi
123 123
124 124 try:
125 125 curr_version = api.db_version(db_uri, repository_path)
126 126 msg = ('Found current database under version'
127 127 ' control with version %s' % curr_version)
128 128
129 129 except (RuntimeError, DatabaseNotControlledError), e:
130 130 curr_version = 1
131 131 msg = ('Current database is not under version control. Setting'
132 132 ' as version %s' % curr_version)
133 133 api.version_control(db_uri, repository_path, curr_version)
134 134
135 135 print (msg)
136 136
137 137 if curr_version == __dbversion__:
138 138 sys.exit('This database is already at the newest version')
139 139
140 140 #======================================================================
141 141 # UPGRADE STEPS
142 142 #======================================================================
143 143 class UpgradeSteps(object):
144 144 """Those steps follow schema versions so for example schema
145 145 for example schema with seq 002 == step_2 and so on.
146 146 """
147 147
148 148 def __init__(self, klass):
149 149 self.klass = klass
150 150
151 151 def step_0(self):
152 152 #step 0 is the schema upgrade, and than follow proper upgrades
153 153 print ('attempting to do database upgrade to version %s' \
154 154 % __dbversion__)
155 155 api.upgrade(db_uri, repository_path, __dbversion__)
156 156 print ('Schema upgrade completed')
157 157
158 158 def step_1(self):
159 159 pass
160 160
161 161 def step_2(self):
162 162 print ('Patching repo paths for newer version of RhodeCode')
163 163 self.klass.fix_repo_paths()
164 164
165 165 print ('Patching default user of RhodeCode')
166 166 self.klass.fix_default_user()
167 167
168 168 log.info('Changing ui settings')
169 169 self.klass.create_ui_settings()
170 170
171 171 def step_3(self):
172 172 print ('Adding additional settings into RhodeCode db')
173 173 self.klass.fix_settings()
174 174
175 175 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
176 176
177 177 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
178 178 for step in upgrade_steps:
179 179 print ('performing upgrade step %s' % step)
180 180 callable = getattr(UpgradeSteps(self), 'step_%s' % step)()
181 181
182 182
183 183
184 184 def fix_repo_paths(self):
185 185 """Fixes a old rhodecode version path into new one without a '*'
186 186 """
187 187
188 188 paths = self.sa.query(RhodeCodeUi)\
189 189 .filter(RhodeCodeUi.ui_key == '/')\
190 190 .scalar()
191 191
192 192 paths.ui_value = paths.ui_value.replace('*', '')
193 193
194 194 try:
195 195 self.sa.add(paths)
196 196 self.sa.commit()
197 197 except:
198 198 self.sa.rollback()
199 199 raise
200 200
201 201 def fix_default_user(self):
202 202 """Fixes a old default user with some 'nicer' default values,
203 203 used mostly for anonymous access
204 204 """
205 205 def_user = self.sa.query(User)\
206 206 .filter(User.username == 'default')\
207 207 .one()
208 208
209 209 def_user.name = 'Anonymous'
210 210 def_user.lastname = 'User'
211 211 def_user.email = 'anonymous@rhodecode.org'
212 212
213 213 try:
214 214 self.sa.add(def_user)
215 215 self.sa.commit()
216 216 except:
217 217 self.sa.rollback()
218 218 raise
219 219
220 220 def fix_settings(self):
221 221 """Fixes rhodecode settings adds ga_code key for google analytics
222 222 """
223 223
224 224 hgsettings3 = RhodeCodeSettings('ga_code', '')
225 225 try:
226 226 self.sa.add(hgsettings3)
227 227 self.sa.commit()
228 228 except:
229 229 self.sa.rollback()
230 230 raise
231 231
232 232 def admin_prompt(self, second=False):
233 233 if not self.tests:
234 234 import getpass
235 235
236 236
237 237 def get_password():
238 238 password = getpass.getpass('Specify admin password (min 6 chars):')
239 239 confirm = getpass.getpass('Confirm password:')
240 240
241 241 if password != confirm:
242 242 log.error('passwords mismatch')
243 243 return False
244 244 if len(password) < 6:
245 245 log.error('password is to short use at least 6 characters')
246 246 return False
247 247
248 248 return password
249 249
250 250 username = raw_input('Specify admin username:')
251 251
252 252 password = get_password()
253 253 if not password:
254 254 #second try
255 255 password = get_password()
256 256 if not password:
257 257 sys.exit()
258 258
259 259 email = raw_input('Specify admin email:')
260 260 self.create_user(username, password, email, True)
261 261 else:
262 262 log.info('creating admin and regular test users')
263 263 self.create_user('test_admin', 'test12', 'test_admin@mail.com', True)
264 264 self.create_user('test_regular', 'test12', 'test_regular@mail.com', False)
265 265 self.create_user('test_regular2', 'test12', 'test_regular2@mail.com', False)
266 266
267 267 def create_ui_settings(self):
268 268 """Creates ui settings, fills out hooks
269 269 and disables dotencode
270 270
271 271 """
272 272 #HOOKS
273 273 hooks1_key = 'changegroup.update'
274 274 hooks1_ = self.sa.query(RhodeCodeUi)\
275 275 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
276 276
277 277 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
278 278 hooks1.ui_section = 'hooks'
279 279 hooks1.ui_key = hooks1_key
280 280 hooks1.ui_value = 'hg update >&2'
281 281 hooks1.ui_active = False
282 282
283 283 hooks2_key = 'changegroup.repo_size'
284 284 hooks2_ = self.sa.query(RhodeCodeUi)\
285 285 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
286 286
287 287 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
288 288 hooks2.ui_section = 'hooks'
289 289 hooks2.ui_key = hooks2_key
290 290 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
291 291
292 292 hooks3 = RhodeCodeUi()
293 293 hooks3.ui_section = 'hooks'
294 294 hooks3.ui_key = 'pretxnchangegroup.push_logger'
295 295 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
296 296
297 297 hooks4 = RhodeCodeUi()
298 298 hooks4.ui_section = 'hooks'
299 299 hooks4.ui_key = 'preoutgoing.pull_logger'
300 300 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
301 301
302 302 #For mercurial 1.7 set backward comapatibility with format
303 303 dotencode_disable = RhodeCodeUi()
304 304 dotencode_disable.ui_section = 'format'
305 305 dotencode_disable.ui_key = 'dotencode'
306 306 dotencode_disable.ui_value = 'false'
307 307
308 308 try:
309 309 self.sa.add(hooks1)
310 310 self.sa.add(hooks2)
311 311 self.sa.add(hooks3)
312 312 self.sa.add(hooks4)
313 313 self.sa.add(dotencode_disable)
314 314 self.sa.commit()
315 315 except:
316 316 self.sa.rollback()
317 317 raise
318 318
319 319
320 320 def create_ldap_options(self):
321 321 """Creates ldap settings"""
322 322
323 323 try:
324 324 for k in ['ldap_active', 'ldap_host', 'ldap_port', 'ldap_ldaps',
325 325 'ldap_dn_user', 'ldap_dn_pass', 'ldap_base_dn']:
326 326
327 327 setting = RhodeCodeSettings(k, '')
328 328 self.sa.add(setting)
329 329 self.sa.commit()
330 330 except:
331 331 self.sa.rollback()
332 332 raise
333 333
334 334 def config_prompt(self, test_repo_path=''):
335 335 log.info('Setting up repositories config')
336 336
337 337 if not self.tests and not test_repo_path:
338 338 path = raw_input('Specify valid full path to your repositories'
339 339 ' you can change this later in application settings:')
340 340 else:
341 341 path = test_repo_path
342 342
343 343 if not os.path.isdir(path):
344 344 log.error('You entered wrong path: %s', path)
345 345 sys.exit()
346 346
347 347 self.create_ui_settings()
348 348
349 349 #HG UI OPTIONS
350 350 web1 = RhodeCodeUi()
351 351 web1.ui_section = 'web'
352 352 web1.ui_key = 'push_ssl'
353 353 web1.ui_value = 'false'
354 354
355 355 web2 = RhodeCodeUi()
356 356 web2.ui_section = 'web'
357 357 web2.ui_key = 'allow_archive'
358 358 web2.ui_value = 'gz zip bz2'
359 359
360 360 web3 = RhodeCodeUi()
361 361 web3.ui_section = 'web'
362 362 web3.ui_key = 'allow_push'
363 363 web3.ui_value = '*'
364 364
365 365 web4 = RhodeCodeUi()
366 366 web4.ui_section = 'web'
367 367 web4.ui_key = 'baseurl'
368 368 web4.ui_value = '/'
369 369
370 370 paths = RhodeCodeUi()
371 371 paths.ui_section = 'paths'
372 372 paths.ui_key = '/'
373 373 paths.ui_value = path
374 374
375 375
376 376 hgsettings1 = RhodeCodeSettings('realm', 'RhodeCode authentication')
377 377 hgsettings2 = RhodeCodeSettings('title', 'RhodeCode')
378 378 hgsettings3 = RhodeCodeSettings('ga_code', '')
379 379
380 380
381 381 try:
382 382 self.sa.add(web1)
383 383 self.sa.add(web2)
384 384 self.sa.add(web3)
385 385 self.sa.add(web4)
386 386 self.sa.add(paths)
387 387 self.sa.add(hgsettings1)
388 388 self.sa.add(hgsettings2)
389 389 self.sa.add(hgsettings3)
390 390
391 391 self.sa.commit()
392 392 except:
393 393 self.sa.rollback()
394 394 raise
395 395
396 396 self.create_ldap_options()
397 397
398 398 log.info('created ui config')
399 399
400 400 def create_user(self, username, password, email='', admin=False):
401 401 log.info('creating administrator user %s', username)
402 402 new_user = User()
403 403 new_user.username = username
404 404 new_user.password = get_crypt_password(password)
405 405 new_user.name = 'RhodeCode'
406 406 new_user.lastname = 'Admin'
407 407 new_user.email = email
408 408 new_user.admin = admin
409 409 new_user.active = True
410 410
411 411 try:
412 412 self.sa.add(new_user)
413 413 self.sa.commit()
414 414 except:
415 415 self.sa.rollback()
416 416 raise
417 417
418 418 def create_default_user(self):
419 419 log.info('creating default user')
420 420 #create default user for handling default permissions.
421 421 def_user = User()
422 422 def_user.username = 'default'
423 423 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
424 424 def_user.name = 'Anonymous'
425 425 def_user.lastname = 'User'
426 426 def_user.email = 'anonymous@rhodecode.org'
427 427 def_user.admin = False
428 428 def_user.active = False
429 429 try:
430 430 self.sa.add(def_user)
431 431 self.sa.commit()
432 432 except:
433 433 self.sa.rollback()
434 434 raise
435 435
436 436 def create_permissions(self):
437 437 #module.(access|create|change|delete)_[name]
438 438 #module.(read|write|owner)
439 439 perms = [('repository.none', 'Repository no access'),
440 440 ('repository.read', 'Repository read access'),
441 441 ('repository.write', 'Repository write access'),
442 442 ('repository.admin', 'Repository admin access'),
443 443 ('hg.admin', 'Hg Administrator'),
444 444 ('hg.create.repository', 'Repository create'),
445 445 ('hg.create.none', 'Repository creation disabled'),
446 446 ('hg.register.none', 'Register disabled'),
447 447 ('hg.register.manual_activate', 'Register new user with rhodecode without manual activation'),
448 448 ('hg.register.auto_activate', 'Register new user with rhodecode without auto activation'),
449 449 ]
450 450
451 451 for p in perms:
452 452 new_perm = Permission()
453 453 new_perm.permission_name = p[0]
454 454 new_perm.permission_longname = p[1]
455 455 try:
456 456 self.sa.add(new_perm)
457 457 self.sa.commit()
458 458 except:
459 459 self.sa.rollback()
460 460 raise
461 461
462 462 def populate_default_permissions(self):
463 463 log.info('creating default user permissions')
464 464
465 465 default_user = self.sa.query(User)\
466 466 .filter(User.username == 'default').scalar()
467 467
468 468 reg_perm = UserToPerm()
469 469 reg_perm.user = default_user
470 470 reg_perm.permission = self.sa.query(Permission)\
471 471 .filter(Permission.permission_name == 'hg.register.manual_activate')\
472 472 .scalar()
473 473
474 474 create_repo_perm = UserToPerm()
475 475 create_repo_perm.user = default_user
476 476 create_repo_perm.permission = self.sa.query(Permission)\
477 477 .filter(Permission.permission_name == 'hg.create.repository')\
478 478 .scalar()
479 479
480 480 default_repo_perm = UserToPerm()
481 481 default_repo_perm.user = default_user
482 482 default_repo_perm.permission = self.sa.query(Permission)\
483 483 .filter(Permission.permission_name == 'repository.read')\
484 484 .scalar()
485 485
486 486 try:
487 487 self.sa.add(reg_perm)
488 488 self.sa.add(create_repo_perm)
489 489 self.sa.add(default_repo_perm)
490 490 self.sa.commit()
491 491 except:
492 492 self.sa.rollback()
493 493 raise
494 494
@@ -1,69 +1,69
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.dbmigrate.__init__
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Database migration modules
7 7
8 8 :created_on: Dec 11, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 from sqlalchemy import engine_from_config
30 30
31 31
32 32 from rhodecode.lib.utils import BasePasterCommand, Command, add_cache
33 33 from rhodecode.lib.db_manage import DbManage
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37 class UpgradeDb(BasePasterCommand):
38 38 """Command used for paster to upgrade our database to newer version
39 39 """
40 40
41 41 max_args = 1
42 42 min_args = 1
43 43
44 44 usage = "CONFIG_FILE"
45 45 summary = "Upgrades current db to newer version given configuration file"
46 46 group_name = "RhodeCode"
47 47
48 48 parser = Command.standard_parser(verbose=True)
49 49
50 50 def command(self):
51 51 from pylons import config
52 52
53 53 add_cache(config)
54 54
55 55 db_uri = config['sqlalchemy.db1.url']
56 56
57 57 dbmanage = DbManage(log_sql=True, dbconf=db_uri,
58 58 root=config['here'], tests=False)
59 59
60 60 dbmanage.upgrade()
61 61
62 62
63 63
64 64 def update_parser(self):
65 65 self.parser.add_option('--sql',
66 66 action='store_true',
67 67 dest='just_sql',
68 68 help="Prints upgrade sql for further investigation",
69 69 default=False)
@@ -1,26 +1,26
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.dbmigrate.versions.__init__
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Package containing new versions of database models
7 7
8 8 :created_on: Dec 11, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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.
@@ -1,32 +1,32
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Custom Exceptions modules
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 Nov 17, 2010
22 22 Custom Exceptions modules
23 23 @author: marcink
24 24 """
25 25
26 26 class LdapUsernameError(Exception):pass
27 27 class LdapPasswordError(Exception):pass
28 28 class LdapConnectionError(Exception):pass
29 29 class LdapImportError(Exception):pass
30 30
31 31 class DefaultUserException(Exception):pass
32 32 class UserOwnsReposException(Exception):pass
@@ -1,105 +1,105
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # custom hooks for application
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 Aug 6, 2010
22 22
23 23 @author: marcink
24 24 """
25 25 from mercurial.cmdutil import revrange
26 26 from mercurial.node import nullrev
27 27 from rhodecode.lib import helpers as h
28 28 from rhodecode.lib.utils import action_logger
29 29 import os
30 30 import sys
31 31
32 32 def repo_size(ui, repo, hooktype=None, **kwargs):
33 33
34 34 if hooktype != 'changegroup':
35 35 return False
36 36 size_hg, size_root = 0, 0
37 37 for path, dirs, files in os.walk(repo.root):
38 38 if path.find('.hg') != -1:
39 39 for f in files:
40 40 try:
41 41 size_hg += os.path.getsize(os.path.join(path, f))
42 42 except OSError:
43 43 pass
44 44 else:
45 45 for f in files:
46 46 try:
47 47 size_root += os.path.getsize(os.path.join(path, f))
48 48 except OSError:
49 49 pass
50 50
51 51 size_hg_f = h.format_byte_size(size_hg)
52 52 size_root_f = h.format_byte_size(size_root)
53 53 size_total_f = h.format_byte_size(size_root + size_hg)
54 54 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
55 55 % (size_hg_f, size_root_f, size_total_f))
56 56
57 57 def log_pull_action(ui, repo, **kwargs):
58 58 """
59 59 Logs user last pull action
60 60 :param ui:
61 61 :param repo:
62 62 """
63 63
64 64 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
65 65 username = extra_params['username']
66 66 repository = extra_params['repository']
67 67 action = 'pull'
68 68
69 69 action_logger(username, action, repository, extra_params['ip'])
70 70
71 71 return 0
72 72
73 73 def log_push_action(ui, repo, **kwargs):
74 74 """
75 75 Maps user last push action to new changeset id, from mercurial
76 76 :param ui:
77 77 :param repo:
78 78 """
79 79
80 80 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
81 81 username = extra_params['username']
82 82 repository = extra_params['repository']
83 83 action = 'push:%s'
84 84 node = kwargs['node']
85 85
86 86 def get_revs(repo, rev_opt):
87 87 if rev_opt:
88 88 revs = revrange(repo, rev_opt)
89 89
90 90 if len(revs) == 0:
91 91 return (nullrev, nullrev)
92 92 return (max(revs), min(revs))
93 93 else:
94 94 return (len(repo) - 1, 0)
95 95
96 96 stop, start = get_revs(repo, [node + ':'])
97 97
98 98 revs = (str(repo[r]) for r in xrange(start, stop + 1))
99 99
100 100 action = action % ','.join(revs)
101 101
102 102 action_logger(username, action, repository, extra_params['ip'])
103 103
104 104 return 0
105 105
@@ -1,236 +1,236
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.indexers.daemon
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 A deamon will read from task table and run tasks
7 7
8 8 :created_on: Jan 26, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 sys
29 29 import os
30 30 import traceback
31 31 from os.path import dirname as dn
32 32 from os.path import join as jn
33 33
34 34 #to get the rhodecode import
35 35 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
36 36 sys.path.append(project_path)
37 37
38 38
39 39 from rhodecode.model.scm import ScmModel
40 40 from rhodecode.lib.helpers import safe_unicode
41 41 from whoosh.index import create_in, open_dir
42 42 from shutil import rmtree
43 43 from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
44 44
45 45 from time import mktime
46 46 from vcs.exceptions import ChangesetError, RepositoryError
47 47
48 48 import logging
49 49
50 50 log = logging.getLogger('whooshIndexer')
51 51 # create logger
52 52 log.setLevel(logging.DEBUG)
53 53 log.propagate = False
54 54 # create console handler and set level to debug
55 55 ch = logging.StreamHandler()
56 56 ch.setLevel(logging.DEBUG)
57 57
58 58 # create formatter
59 59 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
60 60
61 61 # add formatter to ch
62 62 ch.setFormatter(formatter)
63 63
64 64 # add ch to logger
65 65 log.addHandler(ch)
66 66
67 67 class WhooshIndexingDaemon(object):
68 68 """
69 69 Deamon for atomic jobs
70 70 """
71 71
72 72 def __init__(self, indexname='HG_INDEX', index_location=None,
73 73 repo_location=None, sa=None, repo_list=None):
74 74 self.indexname = indexname
75 75
76 76 self.index_location = index_location
77 77 if not index_location:
78 78 raise Exception('You have to provide index location')
79 79
80 80 self.repo_location = repo_location
81 81 if not repo_location:
82 82 raise Exception('You have to provide repositories location')
83 83
84 84 self.repo_paths = ScmModel(sa).repo_scan(self.repo_location, None)
85 85
86 86 if repo_list:
87 87 filtered_repo_paths = {}
88 88 for repo_name, repo in self.repo_paths.items():
89 89 if repo_name in repo_list:
90 90 filtered_repo_paths[repo.name] = repo
91 91
92 92 self.repo_paths = filtered_repo_paths
93 93
94 94
95 95 self.initial = False
96 96 if not os.path.isdir(self.index_location):
97 97 os.makedirs(self.index_location)
98 98 log.info('Cannot run incremental index since it does not'
99 99 ' yet exist running full build')
100 100 self.initial = True
101 101
102 102 def get_paths(self, repo):
103 103 """recursive walk in root dir and return a set of all path in that dir
104 104 based on repository walk function
105 105 """
106 106 index_paths_ = set()
107 107 try:
108 108 for topnode, dirs, files in repo.walk('/', 'tip'):
109 109 for f in files:
110 110 index_paths_.add(jn(repo.path, f.path))
111 111 for dir in dirs:
112 112 for f in files:
113 113 index_paths_.add(jn(repo.path, f.path))
114 114
115 115 except RepositoryError, e:
116 116 log.debug(traceback.format_exc())
117 117 pass
118 118 return index_paths_
119 119
120 120 def get_node(self, repo, path):
121 121 n_path = path[len(repo.path) + 1:]
122 122 node = repo.get_changeset().get_node(n_path)
123 123 return node
124 124
125 125 def get_node_mtime(self, node):
126 126 return mktime(node.last_changeset.date.timetuple())
127 127
128 128 def add_doc(self, writer, path, repo):
129 129 """Adding doc to writer this function itself fetches data from
130 130 the instance of vcs backend"""
131 131 node = self.get_node(repo, path)
132 132
133 133 #we just index the content of chosen files, and skip binary files
134 134 if node.extension in INDEX_EXTENSIONS and not node.is_binary:
135 135
136 136 u_content = node.content
137 137 if not isinstance(u_content, unicode):
138 138 log.warning(' >> %s Could not get this content as unicode '
139 139 'replacing with empty content', path)
140 140 u_content = u''
141 141 else:
142 142 log.debug(' >> %s [WITH CONTENT]' % path)
143 143
144 144 else:
145 145 log.debug(' >> %s' % path)
146 146 #just index file name without it's content
147 147 u_content = u''
148 148
149 149 writer.add_document(owner=unicode(repo.contact),
150 150 repository=safe_unicode(repo.name),
151 151 path=safe_unicode(path),
152 152 content=u_content,
153 153 modtime=self.get_node_mtime(node),
154 154 extension=node.extension)
155 155
156 156
157 157 def build_index(self):
158 158 if os.path.exists(self.index_location):
159 159 log.debug('removing previous index')
160 160 rmtree(self.index_location)
161 161
162 162 if not os.path.exists(self.index_location):
163 163 os.mkdir(self.index_location)
164 164
165 165 idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
166 166 writer = idx.writer()
167 167
168 168 for repo in self.repo_paths.values():
169 169 log.debug('building index @ %s' % repo.path)
170 170
171 171 for idx_path in self.get_paths(repo):
172 172 self.add_doc(writer, idx_path, repo)
173 173
174 174 log.debug('>> COMMITING CHANGES <<')
175 175 writer.commit(merge=True)
176 176 log.debug('>>> FINISHED BUILDING INDEX <<<')
177 177
178 178
179 179 def update_index(self):
180 180 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
181 181
182 182 idx = open_dir(self.index_location, indexname=self.indexname)
183 183 # The set of all paths in the index
184 184 indexed_paths = set()
185 185 # The set of all paths we need to re-index
186 186 to_index = set()
187 187
188 188 reader = idx.reader()
189 189 writer = idx.writer()
190 190
191 191 # Loop over the stored fields in the index
192 192 for fields in reader.all_stored_fields():
193 193 indexed_path = fields['path']
194 194 indexed_paths.add(indexed_path)
195 195
196 196 repo = self.repo_paths[fields['repository']]
197 197
198 198 try:
199 199 node = self.get_node(repo, indexed_path)
200 200 except ChangesetError:
201 201 # This file was deleted since it was indexed
202 202 log.debug('removing from index %s' % indexed_path)
203 203 writer.delete_by_term('path', indexed_path)
204 204
205 205 else:
206 206 # Check if this file was changed since it was indexed
207 207 indexed_time = fields['modtime']
208 208 mtime = self.get_node_mtime(node)
209 209 if mtime > indexed_time:
210 210 # The file has changed, delete it and add it to the list of
211 211 # files to reindex
212 212 log.debug('adding to reindex list %s' % indexed_path)
213 213 writer.delete_by_term('path', indexed_path)
214 214 to_index.add(indexed_path)
215 215
216 216 # Loop over the files in the filesystem
217 217 # Assume we have a function that gathers the filenames of the
218 218 # documents to be indexed
219 219 for repo in self.repo_paths.values():
220 220 for path in self.get_paths(repo):
221 221 if path in to_index or path not in indexed_paths:
222 222 # This is either a file that's changed, or a new file
223 223 # that wasn't indexed before. So index it!
224 224 self.add_doc(writer, path, repo)
225 225 log.debug('re indexing %s' % path)
226 226
227 227 log.debug('>> COMMITING CHANGES <<')
228 228 writer.commit(merge=True)
229 229 log.debug('>>> FINISHED REBUILDING INDEX <<<')
230 230
231 231 def run(self, full_index=False):
232 232 """Run daemon"""
233 233 if full_index or self.initial:
234 234 self.build_index()
235 235 else:
236 236 self.update_index()
@@ -1,47 +1,47
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # middleware to handle https correctly
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 May 23, 2010
23 23
24 24 @author: marcink
25 25 """
26 26
27 27 class HttpsFixup(object):
28 28 def __init__(self, app):
29 29 self.application = app
30 30
31 31 def __call__(self, environ, start_response):
32 32 self.__fixup(environ)
33 33 return self.application(environ, start_response)
34 34
35 35
36 36 def __fixup(self, environ):
37 37 """Function to fixup the environ as needed. In order to use this
38 38 middleware you should set this header inside your
39 39 proxy ie. nginx, apache etc.
40 40 """
41 41 proto = environ.get('HTTP_X_URL_SCHEME')
42 42
43 43 if proto == 'https':
44 44 environ['wsgi.url_scheme'] = proto
45 45 else:
46 46 environ['wsgi.url_scheme'] = 'http'
47 47 return None
@@ -1,222 +1,222
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # middleware to handle git api calls
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 2010-04-28
22 22
23 23 @author: marcink
24 24 SimpleGit middleware for handling git protocol request (push/clone etc.)
25 25 It's implemented with basic auth function
26 26 """
27 27
28 28 from dulwich import server as dulserver
29 29
30 30 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
31 31
32 32 def handle(self):
33 33 write = lambda x: self.proto.write_sideband(1, x)
34 34
35 35 graph_walker = dulserver.ProtocolGraphWalker(self, self.repo.object_store,
36 36 self.repo.get_peeled)
37 37 objects_iter = self.repo.fetch_objects(
38 38 graph_walker.determine_wants, graph_walker, self.progress,
39 39 get_tagged=self.get_tagged)
40 40
41 41 # Do they want any objects?
42 42 if len(objects_iter) == 0:
43 43 return
44 44
45 45 self.progress("counting objects: %d, done.\n" % len(objects_iter))
46 46 dulserver.write_pack_data(dulserver.ProtocolFile(None, write), objects_iter,
47 47 len(objects_iter))
48 48 messages = []
49 49 messages.append('thank you for using rhodecode')
50 50
51 51 for msg in messages:
52 52 self.progress(msg + "\n")
53 53 # we are done
54 54 self.proto.write("0000")
55 55
56 56 dulserver.DEFAULT_HANDLERS = {
57 57 'git-upload-pack': SimpleGitUploadPackHandler,
58 58 'git-receive-pack': dulserver.ReceivePackHandler,
59 59 }
60 60
61 61 from dulwich.repo import Repo
62 62 from dulwich.web import HTTPGitApplication
63 63 from paste.auth.basic import AuthBasicAuthenticator
64 64 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
65 65 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
66 66 from rhodecode.lib.utils import invalidate_cache, check_repo_fast
67 67 from rhodecode.model.user import UserModel
68 68 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
69 69 import logging
70 70 import os
71 71 import traceback
72 72
73 73 log = logging.getLogger(__name__)
74 74
75 75 def is_git(environ):
76 76 """
77 77 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
78 78 then have git client version given.
79 79
80 80 :param environ:
81 81 """
82 82 http_user_agent = environ.get('HTTP_USER_AGENT')
83 83 if http_user_agent and http_user_agent.startswith('git'):
84 84 return True
85 85 return False
86 86
87 87 class SimpleGit(object):
88 88
89 89 def __init__(self, application, config):
90 90 self.application = application
91 91 self.config = config
92 92 #authenticate this git request using
93 93 self.authenticate = AuthBasicAuthenticator('', authfunc)
94 94 self.ipaddr = '0.0.0.0'
95 95 self.repository = None
96 96 self.username = None
97 97 self.action = None
98 98
99 99 def __call__(self, environ, start_response):
100 100 if not is_git(environ):
101 101 return self.application(environ, start_response)
102 102
103 103 proxy_key = 'HTTP_X_REAL_IP'
104 104 def_key = 'REMOTE_ADDR'
105 105 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
106 106 # skip passing error to error controller
107 107 environ['pylons.status_code_redirect'] = True
108 108 #===================================================================
109 109 # AUTHENTICATE THIS GIT REQUEST
110 110 #===================================================================
111 111 username = REMOTE_USER(environ)
112 112 if not username:
113 113 self.authenticate.realm = self.config['rhodecode_realm']
114 114 result = self.authenticate(environ)
115 115 if isinstance(result, str):
116 116 AUTH_TYPE.update(environ, 'basic')
117 117 REMOTE_USER.update(environ, result)
118 118 else:
119 119 return result.wsgi_application(environ, start_response)
120 120
121 121 #=======================================================================
122 122 # GET REPOSITORY
123 123 #=======================================================================
124 124 try:
125 125 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
126 126 if repo_name.endswith('/'):
127 127 repo_name = repo_name.rstrip('/')
128 128 self.repository = repo_name
129 129 except:
130 130 log.error(traceback.format_exc())
131 131 return HTTPInternalServerError()(environ, start_response)
132 132
133 133 #===================================================================
134 134 # CHECK PERMISSIONS FOR THIS REQUEST
135 135 #===================================================================
136 136 self.action = self.__get_action(environ)
137 137 if self.action:
138 138 username = self.__get_environ_user(environ)
139 139 try:
140 140 user = self.__get_user(username)
141 141 self.username = user.username
142 142 except:
143 143 log.error(traceback.format_exc())
144 144 return HTTPInternalServerError()(environ, start_response)
145 145
146 146 #check permissions for this repository
147 147 if self.action == 'push':
148 148 if not HasPermissionAnyMiddleware('repository.write',
149 149 'repository.admin')\
150 150 (user, repo_name):
151 151 return HTTPForbidden()(environ, start_response)
152 152
153 153 else:
154 154 #any other action need at least read permission
155 155 if not HasPermissionAnyMiddleware('repository.read',
156 156 'repository.write',
157 157 'repository.admin')\
158 158 (user, repo_name):
159 159 return HTTPForbidden()(environ, start_response)
160 160
161 161 self.extras = {'ip':self.ipaddr,
162 162 'username':self.username,
163 163 'action':self.action,
164 164 'repository':self.repository}
165 165
166 166 #===================================================================
167 167 # GIT REQUEST HANDLING
168 168 #===================================================================
169 169 self.basepath = self.config['base_path']
170 170 self.repo_path = os.path.join(self.basepath, self.repo_name)
171 171 #quick check if that dir exists...
172 172 if check_repo_fast(self.repo_name, self.basepath):
173 173 return HTTPNotFound()(environ, start_response)
174 174 try:
175 175 app = self.__make_app()
176 176 except:
177 177 log.error(traceback.format_exc())
178 178 return HTTPInternalServerError()(environ, start_response)
179 179
180 180 #invalidate cache on push
181 181 if self.action == 'push':
182 182 self.__invalidate_cache(self.repo_name)
183 183 messages = []
184 184 messages.append('thank you for using rhodecode')
185 185 return app(environ, start_response)
186 186 else:
187 187 return app(environ, start_response)
188 188
189 189
190 190 def __make_app(self):
191 191 backend = dulserver.DictBackend({'/' + self.repo_name: Repo(self.repo_path)})
192 192 gitserve = HTTPGitApplication(backend)
193 193
194 194 return gitserve
195 195
196 196 def __get_environ_user(self, environ):
197 197 return environ.get('REMOTE_USER')
198 198
199 199 def __get_user(self, username):
200 200 return UserModel().get_by_username(username, cache=True)
201 201
202 202 def __get_action(self, environ):
203 203 """
204 204 Maps git request commands into a pull or push command.
205 205 :param environ:
206 206 """
207 207 service = environ['QUERY_STRING'].split('=')
208 208 if len(service) > 1:
209 209 service_cmd = service[1]
210 210 mapping = {'git-receive-pack': 'push',
211 211 'git-upload-pack': 'pull',
212 212 }
213 213
214 214 return mapping.get(service_cmd, service_cmd if service_cmd else 'other')
215 215 else:
216 216 return 'other'
217 217
218 218 def __invalidate_cache(self, repo_name):
219 219 """we know that some change was made to repositories and we should
220 220 invalidate the cache to see the changes right away but only for
221 221 push requests"""
222 222 invalidate_cache('get_repo_cached_%s' % repo_name)
@@ -1,230 +1,230
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # middleware to handle mercurial api calls
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 2010-04-28
22 22
23 23 @author: marcink
24 24 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
25 25 It's implemented with basic auth function
26 26 """
27 27 from mercurial.error import RepoError
28 28 from mercurial.hgweb import hgweb
29 29 from mercurial.hgweb.request import wsgiapplication
30 30 from paste.auth.basic import AuthBasicAuthenticator
31 31 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
32 32 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
33 33 from rhodecode.lib.utils import make_ui, invalidate_cache, \
34 34 check_repo_fast, ui_sections
35 35 from rhodecode.model.user import UserModel
36 36 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
37 37 import logging
38 38 import os
39 39 import traceback
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43 def is_mercurial(environ):
44 44 """
45 45 Returns True if request's target is mercurial server - header
46 46 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
47 47 """
48 48 http_accept = environ.get('HTTP_ACCEPT')
49 49 if http_accept and http_accept.startswith('application/mercurial'):
50 50 return True
51 51 return False
52 52
53 53 class SimpleHg(object):
54 54
55 55 def __init__(self, application, config):
56 56 self.application = application
57 57 self.config = config
58 58 #authenticate this mercurial request using authfunc
59 59 self.authenticate = AuthBasicAuthenticator('', authfunc)
60 60 self.ipaddr = '0.0.0.0'
61 61 self.repository = None
62 62 self.username = None
63 63 self.action = None
64 64
65 65 def __call__(self, environ, start_response):
66 66 if not is_mercurial(environ):
67 67 return self.application(environ, start_response)
68 68
69 69 proxy_key = 'HTTP_X_REAL_IP'
70 70 def_key = 'REMOTE_ADDR'
71 71 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
72 72 # skip passing error to error controller
73 73 environ['pylons.status_code_redirect'] = True
74 74 #===================================================================
75 75 # AUTHENTICATE THIS MERCURIAL REQUEST
76 76 #===================================================================
77 77 username = REMOTE_USER(environ)
78 78
79 79 if not username:
80 80 self.authenticate.realm = self.config['rhodecode_realm']
81 81 result = self.authenticate(environ)
82 82 if isinstance(result, str):
83 83 AUTH_TYPE.update(environ, 'basic')
84 84 REMOTE_USER.update(environ, result)
85 85 else:
86 86 return result.wsgi_application(environ, start_response)
87 87
88 88 #=======================================================================
89 89 # GET REPOSITORY
90 90 #=======================================================================
91 91 try:
92 92 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
93 93 if repo_name.endswith('/'):
94 94 repo_name = repo_name.rstrip('/')
95 95 self.repository = repo_name
96 96 except:
97 97 log.error(traceback.format_exc())
98 98 return HTTPInternalServerError()(environ, start_response)
99 99
100 100 #===================================================================
101 101 # CHECK PERMISSIONS FOR THIS REQUEST
102 102 #===================================================================
103 103 self.action = self.__get_action(environ)
104 104 if self.action:
105 105 username = self.__get_environ_user(environ)
106 106 try:
107 107 user = self.__get_user(username)
108 108 self.username = user.username
109 109 except:
110 110 log.error(traceback.format_exc())
111 111 return HTTPInternalServerError()(environ, start_response)
112 112
113 113 #check permissions for this repository
114 114 if self.action == 'push':
115 115 if not HasPermissionAnyMiddleware('repository.write',
116 116 'repository.admin')\
117 117 (user, repo_name):
118 118 return HTTPForbidden()(environ, start_response)
119 119
120 120 else:
121 121 #any other action need at least read permission
122 122 if not HasPermissionAnyMiddleware('repository.read',
123 123 'repository.write',
124 124 'repository.admin')\
125 125 (user, repo_name):
126 126 return HTTPForbidden()(environ, start_response)
127 127
128 128 self.extras = {'ip':self.ipaddr,
129 129 'username':self.username,
130 130 'action':self.action,
131 131 'repository':self.repository}
132 132
133 133 #===================================================================
134 134 # MERCURIAL REQUEST HANDLING
135 135 #===================================================================
136 136 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
137 137 self.baseui = make_ui('db')
138 138 self.basepath = self.config['base_path']
139 139 self.repo_path = os.path.join(self.basepath, repo_name)
140 140
141 141 #quick check if that dir exists...
142 142 if check_repo_fast(repo_name, self.basepath):
143 143 return HTTPNotFound()(environ, start_response)
144 144 try:
145 145 app = wsgiapplication(self.__make_app)
146 146 except RepoError, e:
147 147 if str(e).find('not found') != -1:
148 148 return HTTPNotFound()(environ, start_response)
149 149 except Exception:
150 150 log.error(traceback.format_exc())
151 151 return HTTPInternalServerError()(environ, start_response)
152 152
153 153 #invalidate cache on push
154 154 if self.action == 'push':
155 155 self.__invalidate_cache(repo_name)
156 156
157 157 return app(environ, start_response)
158 158
159 159
160 160 def __make_app(self):
161 161 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
162 162 return self.__load_web_settings(hgserve, self.extras)
163 163
164 164 def __get_environ_user(self, environ):
165 165 return environ.get('REMOTE_USER')
166 166
167 167 def __get_user(self, username):
168 168 return UserModel().get_by_username(username, cache=True)
169 169
170 170 def __get_action(self, environ):
171 171 """
172 172 Maps mercurial request commands into a clone,pull or push command.
173 173 This should always return a valid command string
174 174 :param environ:
175 175 """
176 176 mapping = {'changegroup': 'pull',
177 177 'changegroupsubset': 'pull',
178 178 'stream_out': 'pull',
179 179 'listkeys': 'pull',
180 180 'unbundle': 'push',
181 181 'pushkey': 'push', }
182 182 for qry in environ['QUERY_STRING'].split('&'):
183 183 if qry.startswith('cmd'):
184 184 cmd = qry.split('=')[-1]
185 185 if mapping.has_key(cmd):
186 186 return mapping[cmd]
187 187 else:
188 188 return cmd
189 189
190 190 def __invalidate_cache(self, repo_name):
191 191 """we know that some change was made to repositories and we should
192 192 invalidate the cache to see the changes right away but only for
193 193 push requests"""
194 194 invalidate_cache('get_repo_cached_%s' % repo_name)
195 195
196 196
197 197 def __load_web_settings(self, hgserve, extras={}):
198 198 #set the global ui for hgserve instance passed
199 199 hgserve.repo.ui = self.baseui
200 200
201 201 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
202 202
203 203 #inject some additional parameters that will be available in ui
204 204 #for hooks
205 205 for k, v in extras.items():
206 206 hgserve.repo.ui.setconfig('rhodecode_extras', k, v)
207 207
208 208 repoui = make_ui('file', hgrc, False)
209 209
210 210 if repoui:
211 211 #overwrite our ui instance with the section from hgrc file
212 212 for section in ui_sections:
213 213 for k, v in repoui.configitems(section):
214 214 hgserve.repo.ui.setconfig(section, k, v)
215 215
216 216 return hgserve
217 217
218 218
219 219
220 220
221 221
222 222
223 223
224 224
225 225
226 226
227 227
228 228
229 229
230 230
@@ -1,650 +1,650
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 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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, Group
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):
114 114 """
115 115 Scans given path for repos and return (name,(type,path)) tuple
116 116
117 117 :param path: path to scann for repositories
118 118 :param recursive: recursive search and return names with subdirs in front
119 119 """
120 120 from vcs.utils.helpers import get_scm
121 121 from vcs.exceptions import VCSError
122 122
123 123 if path.endswith('/'):
124 124 #add ending slash for better results
125 125 path = path[:-1]
126 126
127 127 def _get_repos(p):
128 128 for dirpath in os.listdir(p):
129 129 if os.path.isfile(os.path.join(p, dirpath)):
130 130 continue
131 131 cur_path = os.path.join(p, dirpath)
132 132 try:
133 133 scm_info = get_scm(cur_path)
134 134 yield scm_info[1].split(path)[-1].lstrip('/'), scm_info
135 135 except VCSError:
136 136 if not recursive:
137 137 continue
138 138 #check if this dir containts other repos for recursive scan
139 139 rec_path = os.path.join(p, dirpath)
140 140 if os.path.isdir(rec_path):
141 141 for inner_scm in _get_repos(rec_path):
142 142 yield inner_scm
143 143
144 144 return _get_repos(path)
145 145
146 146 def check_repo_fast(repo_name, base_path):
147 147 """
148 148 Check given path for existence of directory
149 149 :param repo_name:
150 150 :param base_path:
151 151
152 152 :return False: if this directory is present
153 153 """
154 154 if os.path.isdir(os.path.join(base_path, repo_name)):return False
155 155 return True
156 156
157 157 def check_repo(repo_name, base_path, verify=True):
158 158
159 159 repo_path = os.path.join(base_path, repo_name)
160 160
161 161 try:
162 162 if not check_repo_fast(repo_name, base_path):
163 163 return False
164 164 r = hg.repository(ui.ui(), repo_path)
165 165 if verify:
166 166 hg.verify(r)
167 167 #here we hnow that repo exists it was verified
168 168 log.info('%s repo is already created', repo_name)
169 169 return False
170 170 except RepoError:
171 171 #it means that there is no valid repo there...
172 172 log.info('%s repo is free for creation', repo_name)
173 173 return True
174 174
175 175 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
176 176 while True:
177 177 ok = raw_input(prompt)
178 178 if ok in ('y', 'ye', 'yes'): return True
179 179 if ok in ('n', 'no', 'nop', 'nope'): return False
180 180 retries = retries - 1
181 181 if retries < 0: raise IOError
182 182 print complaint
183 183
184 184 #propagated from mercurial documentation
185 185 ui_sections = ['alias', 'auth',
186 186 'decode/encode', 'defaults',
187 187 'diff', 'email',
188 188 'extensions', 'format',
189 189 'merge-patterns', 'merge-tools',
190 190 'hooks', 'http_proxy',
191 191 'smtp', 'patch',
192 192 'paths', 'profiling',
193 193 'server', 'trusted',
194 194 'ui', 'web', ]
195 195
196 196 def make_ui(read_from='file', path=None, checkpaths=True):
197 197 """
198 198 A function that will read python rc files or database
199 199 and make an mercurial ui object from read options
200 200
201 201 :param path: path to mercurial config file
202 202 :param checkpaths: check the path
203 203 :param read_from: read from 'file' or 'db'
204 204 """
205 205
206 206 baseui = ui.ui()
207 207
208 208 #clean the baseui object
209 209 baseui._ocfg = config.config()
210 210 baseui._ucfg = config.config()
211 211 baseui._tcfg = config.config()
212 212
213 213 if read_from == 'file':
214 214 if not os.path.isfile(path):
215 215 log.warning('Unable to read config file %s' % path)
216 216 return False
217 217 log.debug('reading hgrc from %s', path)
218 218 cfg = config.config()
219 219 cfg.read(path)
220 220 for section in ui_sections:
221 221 for k, v in cfg.items(section):
222 222 log.debug('settings ui from file[%s]%s:%s', section, k, v)
223 223 baseui.setconfig(section, k, v)
224 224
225 225
226 226 elif read_from == 'db':
227 227 sa = meta.Session()
228 228 ret = sa.query(RhodeCodeUi)\
229 229 .options(FromCache("sql_cache_short",
230 230 "get_hg_ui_settings")).all()
231 231
232 232 hg_ui = ret
233 233 for ui_ in hg_ui:
234 234 if ui_.ui_active:
235 235 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
236 236 ui_.ui_key, ui_.ui_value)
237 237 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
238 238
239 239 meta.Session.remove()
240 240 return baseui
241 241
242 242
243 243 def set_rhodecode_config(config):
244 244 """Updates pylons config with new settings from database
245 245
246 246 :param config:
247 247 """
248 248 from rhodecode.model.settings import SettingsModel
249 249 hgsettings = SettingsModel().get_app_settings()
250 250
251 251 for k, v in hgsettings.items():
252 252 config[k] = v
253 253
254 254 def invalidate_cache(cache_key, *args):
255 255 """Puts cache invalidation task into db for
256 256 further global cache invalidation
257 257 """
258 258
259 259 from rhodecode.model.scm import ScmModel
260 260
261 261 if cache_key.startswith('get_repo_cached_'):
262 262 name = cache_key.split('get_repo_cached_')[-1]
263 263 ScmModel().mark_for_invalidation(name)
264 264
265 265 class EmptyChangeset(BaseChangeset):
266 266 """
267 267 An dummy empty changeset. It's possible to pass hash when creating
268 268 an EmptyChangeset
269 269 """
270 270
271 271 def __init__(self, cs='0' * 40):
272 272 self._empty_cs = cs
273 273 self.revision = -1
274 274 self.message = ''
275 275 self.author = ''
276 276 self.date = ''
277 277
278 278 @LazyProperty
279 279 def raw_id(self):
280 280 """Returns raw string identifying this changeset, useful for web
281 281 representation.
282 282 """
283 283
284 284 return self._empty_cs
285 285
286 286 @LazyProperty
287 287 def short_id(self):
288 288 return self.raw_id[:12]
289 289
290 290 def get_file_changeset(self, path):
291 291 return self
292 292
293 293 def get_file_content(self, path):
294 294 return u''
295 295
296 296 def get_file_size(self, path):
297 297 return 0
298 298
299 299 def map_groups(groups):
300 300 """Checks for groups existence, and creates groups structures.
301 301 It returns last group in structure
302 302
303 303 :param groups: list of groups structure
304 304 """
305 305 sa = meta.Session()
306 306
307 307 parent = None
308 308 group = None
309 309 for lvl, group_name in enumerate(groups[:-1]):
310 310 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
311 311
312 312 if group is None:
313 313 group = Group(group_name, parent)
314 314 sa.add(group)
315 315 sa.commit()
316 316
317 317 parent = group
318 318
319 319 return group
320 320
321 321 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
322 322 """maps all repos given in initial_repo_list, non existing repositories
323 323 are created, if remove_obsolete is True it also check for db entries
324 324 that are not in initial_repo_list and removes them.
325 325
326 326 :param initial_repo_list: list of repositories found by scanning methods
327 327 :param remove_obsolete: check for obsolete entries in database
328 328 """
329 329
330 330 sa = meta.Session()
331 331 rm = RepoModel()
332 332 user = sa.query(User).filter(User.admin == True).first()
333 333
334 334 for name, repo in initial_repo_list.items():
335 335 group = map_groups(name.split('/'))
336 336 if not rm.get_by_repo_name(name, cache=False):
337 337 log.info('repository %s not found creating default', name)
338 338
339 339 form_data = {
340 340 'repo_name':name,
341 341 'repo_type':repo.alias,
342 342 'description':repo.description \
343 343 if repo.description != 'unknown' else \
344 344 '%s repository' % name,
345 345 'private':False,
346 346 'group_id':getattr(group, 'group_id', None)
347 347 }
348 348 rm.create(form_data, user, just_db=True)
349 349
350 350 if remove_obsolete:
351 351 #remove from database those repositories that are not in the filesystem
352 352 for repo in sa.query(Repository).all():
353 353 if repo.repo_name not in initial_repo_list.keys():
354 354 sa.delete(repo)
355 355 sa.commit()
356 356
357 357 class OrderedDict(dict, DictMixin):
358 358
359 359 def __init__(self, *args, **kwds):
360 360 if len(args) > 1:
361 361 raise TypeError('expected at most 1 arguments, got %d' % len(args))
362 362 try:
363 363 self.__end
364 364 except AttributeError:
365 365 self.clear()
366 366 self.update(*args, **kwds)
367 367
368 368 def clear(self):
369 369 self.__end = end = []
370 370 end += [None, end, end] # sentinel node for doubly linked list
371 371 self.__map = {} # key --> [key, prev, next]
372 372 dict.clear(self)
373 373
374 374 def __setitem__(self, key, value):
375 375 if key not in self:
376 376 end = self.__end
377 377 curr = end[1]
378 378 curr[2] = end[1] = self.__map[key] = [key, curr, end]
379 379 dict.__setitem__(self, key, value)
380 380
381 381 def __delitem__(self, key):
382 382 dict.__delitem__(self, key)
383 383 key, prev, next = self.__map.pop(key)
384 384 prev[2] = next
385 385 next[1] = prev
386 386
387 387 def __iter__(self):
388 388 end = self.__end
389 389 curr = end[2]
390 390 while curr is not end:
391 391 yield curr[0]
392 392 curr = curr[2]
393 393
394 394 def __reversed__(self):
395 395 end = self.__end
396 396 curr = end[1]
397 397 while curr is not end:
398 398 yield curr[0]
399 399 curr = curr[1]
400 400
401 401 def popitem(self, last=True):
402 402 if not self:
403 403 raise KeyError('dictionary is empty')
404 404 if last:
405 405 key = reversed(self).next()
406 406 else:
407 407 key = iter(self).next()
408 408 value = self.pop(key)
409 409 return key, value
410 410
411 411 def __reduce__(self):
412 412 items = [[k, self[k]] for k in self]
413 413 tmp = self.__map, self.__end
414 414 del self.__map, self.__end
415 415 inst_dict = vars(self).copy()
416 416 self.__map, self.__end = tmp
417 417 if inst_dict:
418 418 return (self.__class__, (items,), inst_dict)
419 419 return self.__class__, (items,)
420 420
421 421 def keys(self):
422 422 return list(self)
423 423
424 424 setdefault = DictMixin.setdefault
425 425 update = DictMixin.update
426 426 pop = DictMixin.pop
427 427 values = DictMixin.values
428 428 items = DictMixin.items
429 429 iterkeys = DictMixin.iterkeys
430 430 itervalues = DictMixin.itervalues
431 431 iteritems = DictMixin.iteritems
432 432
433 433 def __repr__(self):
434 434 if not self:
435 435 return '%s()' % (self.__class__.__name__,)
436 436 return '%s(%r)' % (self.__class__.__name__, self.items())
437 437
438 438 def copy(self):
439 439 return self.__class__(self)
440 440
441 441 @classmethod
442 442 def fromkeys(cls, iterable, value=None):
443 443 d = cls()
444 444 for key in iterable:
445 445 d[key] = value
446 446 return d
447 447
448 448 def __eq__(self, other):
449 449 if isinstance(other, OrderedDict):
450 450 return len(self) == len(other) and self.items() == other.items()
451 451 return dict.__eq__(self, other)
452 452
453 453 def __ne__(self, other):
454 454 return not self == other
455 455
456 456
457 457 #set cache regions for beaker so celery can utilise it
458 458 def add_cache(settings):
459 459 cache_settings = {'regions':None}
460 460 for key in settings.keys():
461 461 for prefix in ['beaker.cache.', 'cache.']:
462 462 if key.startswith(prefix):
463 463 name = key.split(prefix)[1].strip()
464 464 cache_settings[name] = settings[key].strip()
465 465 if cache_settings['regions']:
466 466 for region in cache_settings['regions'].split(','):
467 467 region = region.strip()
468 468 region_settings = {}
469 469 for key, value in cache_settings.items():
470 470 if key.startswith(region):
471 471 region_settings[key.split('.')[1]] = value
472 472 region_settings['expire'] = int(region_settings.get('expire',
473 473 60))
474 474 region_settings.setdefault('lock_dir',
475 475 cache_settings.get('lock_dir'))
476 476 if 'type' not in region_settings:
477 477 region_settings['type'] = cache_settings.get('type',
478 478 'memory')
479 479 beaker.cache.cache_regions[region] = region_settings
480 480
481 481 def get_current_revision():
482 482 """Returns tuple of (number, id) from repository containing this package
483 483 or None if repository could not be found.
484 484 """
485 485
486 486 try:
487 487 from vcs import get_repo
488 488 from vcs.utils.helpers import get_scm
489 489 from vcs.exceptions import RepositoryError, VCSError
490 490 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
491 491 scm = get_scm(repopath)[0]
492 492 repo = get_repo(path=repopath, alias=scm)
493 493 tip = repo.get_changeset()
494 494 return (tip.revision, tip.short_id)
495 495 except (ImportError, RepositoryError, VCSError), err:
496 496 logging.debug("Cannot retrieve rhodecode's revision. Original error "
497 497 "was: %s" % err)
498 498 return None
499 499
500 500 #===============================================================================
501 501 # TEST FUNCTIONS AND CREATORS
502 502 #===============================================================================
503 503 def create_test_index(repo_location, full_index):
504 504 """Makes default test index
505 505 :param repo_location:
506 506 :param full_index:
507 507 """
508 508 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
509 509 from rhodecode.lib.pidlock import DaemonLock, LockHeld
510 510 import shutil
511 511
512 512 index_location = os.path.join(repo_location, 'index')
513 513 if os.path.exists(index_location):
514 514 shutil.rmtree(index_location)
515 515
516 516 try:
517 517 l = DaemonLock()
518 518 WhooshIndexingDaemon(index_location=index_location,
519 519 repo_location=repo_location)\
520 520 .run(full_index=full_index)
521 521 l.release()
522 522 except LockHeld:
523 523 pass
524 524
525 525 def create_test_env(repos_test_path, config):
526 526 """Makes a fresh database and
527 527 install test repository into tmp dir
528 528 """
529 529 from rhodecode.lib.db_manage import DbManage
530 530 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
531 531 HG_FORK, GIT_FORK, TESTS_TMP_PATH
532 532 import tarfile
533 533 import shutil
534 534 from os.path import dirname as dn, join as jn, abspath
535 535
536 536 log = logging.getLogger('TestEnvCreator')
537 537 # create logger
538 538 log.setLevel(logging.DEBUG)
539 539 log.propagate = True
540 540 # create console handler and set level to debug
541 541 ch = logging.StreamHandler()
542 542 ch.setLevel(logging.DEBUG)
543 543
544 544 # create formatter
545 545 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
546 546
547 547 # add formatter to ch
548 548 ch.setFormatter(formatter)
549 549
550 550 # add ch to logger
551 551 log.addHandler(ch)
552 552
553 553 #PART ONE create db
554 554 dbconf = config['sqlalchemy.db1.url']
555 555 log.debug('making test db %s', dbconf)
556 556
557 557 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
558 558 tests=True)
559 559 dbmanage.create_tables(override=True)
560 560 dbmanage.config_prompt(repos_test_path)
561 561 dbmanage.create_default_user()
562 562 dbmanage.admin_prompt()
563 563 dbmanage.create_permissions()
564 564 dbmanage.populate_default_permissions()
565 565
566 566 #PART TWO make test repo
567 567 log.debug('making test vcs repositories')
568 568
569 569 #remove old one from previos tests
570 570 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
571 571
572 572 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
573 573 log.debug('removing %s', r)
574 574 shutil.rmtree(jn(TESTS_TMP_PATH, r))
575 575
576 576 #CREATE DEFAULT HG REPOSITORY
577 577 cur_dir = dn(dn(abspath(__file__)))
578 578 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
579 579 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
580 580 tar.close()
581 581
582 582
583 583 #==============================================================================
584 584 # PASTER COMMANDS
585 585 #==============================================================================
586 586
587 587 class BasePasterCommand(Command):
588 588 """
589 589 Abstract Base Class for paster commands.
590 590
591 591 The celery commands are somewhat aggressive about loading
592 592 celery.conf, and since our module sets the `CELERY_LOADER`
593 593 environment variable to our loader, we have to bootstrap a bit and
594 594 make sure we've had a chance to load the pylons config off of the
595 595 command line, otherwise everything fails.
596 596 """
597 597 min_args = 1
598 598 min_args_error = "Please provide a paster config file as an argument."
599 599 takes_config_file = 1
600 600 requires_config_file = True
601 601
602 602 def notify_msg(self, msg, log=False):
603 603 """Make a notification to user, additionally if logger is passed
604 604 it logs this action using given logger
605 605
606 606 :param msg: message that will be printed to user
607 607 :param log: logging instance, to use to additionally log this message
608 608
609 609 """
610 610 print msg
611 611 if log and isinstance(log, logging):
612 612 log(msg)
613 613
614 614
615 615 def run(self, args):
616 616 """
617 617 Overrides Command.run
618 618
619 619 Checks for a config file argument and loads it.
620 620 """
621 621 if len(args) < self.min_args:
622 622 raise BadCommand(
623 623 self.min_args_error % {'min_args': self.min_args,
624 624 'actual_args': len(args)})
625 625
626 626 # Decrement because we're going to lob off the first argument.
627 627 # @@ This is hacky
628 628 self.min_args -= 1
629 629 self.bootstrap_config(args[0])
630 630 self.update_parser()
631 631 return super(BasePasterCommand, self).run(args[1:])
632 632
633 633 def update_parser(self):
634 634 """
635 635 Abstract method. Allows for the class's parser to be updated
636 636 before the superclass's `run` method is called. Necessary to
637 637 allow options/arguments to be passed through to the underlying
638 638 celery command.
639 639 """
640 640 raise NotImplementedError("Abstract Method.")
641 641
642 642 def bootstrap_config(self, conf):
643 643 """
644 644 Loads the pylons configuration.
645 645 """
646 646 from pylons import config as pylonsconfig
647 647
648 648 path_to_ini_file = os.path.realpath(conf)
649 649 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
650 650 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
@@ -1,71 +1,71
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.__init__
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 The application's model objects
7 7
8 8 :created_on: Nov 25, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12
13 13
14 14 :example:
15 15
16 16 .. code-block:: python
17 17
18 18 from paste.deploy import appconfig
19 19 from pylons import config
20 20 from sqlalchemy import engine_from_config
21 21 from rhodecode.config.environment import load_environment
22 22
23 23 conf = appconfig('config:development.ini', relative_to = './../../')
24 24 load_environment(conf.global_conf, conf.local_conf)
25 25
26 26 engine = engine_from_config(config, 'sqlalchemy.')
27 27 init_model(engine)
28 28 # RUN YOUR CODE HERE
29 29
30 30 """
31 31 # This program is free software; you can redistribute it and/or
32 32 # modify it under the terms of the GNU General Public License
33 33 # as published by the Free Software Foundation; version 2
34 34 # of the License or (at your opinion) any later version of the license.
35 35 #
36 36 # This program is distributed in the hope that it will be useful,
37 37 # but WITHOUT ANY WARRANTY; without even the implied warranty of
38 38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 39 # GNU General Public License for more details.
40 40 #
41 41 # You should have received a copy of the GNU General Public License
42 42 # along with this program; if not, write to the Free Software
43 43 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
44 44 # MA 02110-1301, USA.
45 45
46 46 import logging
47 47 from rhodecode.model import meta
48 48 log = logging.getLogger(__name__)
49 49
50 50 def init_model(engine):
51 51 """Initializes db session, bind the engine with the metadata,
52 52 Call this before using any of the tables or classes in the model, preferably
53 53 once in application start
54 54
55 55 :param engine: engine to bind to
56 56 """
57 57 log.info("initializing db models for %s", engine)
58 58 meta.Base.metadata.bind = engine
59 59
60 60 class BaseModel(object):
61 61 """Base Model for all RhodeCode models, it adds sql alchemy session
62 62 into instance of model
63 63
64 64 :param sa: If passed it reuses this session instead of creating a new one
65 65 """
66 66
67 67 def __init__(self, sa=None):
68 68 if sa is not None:
69 69 self.sa = sa
70 70 else:
71 71 self.sa = meta.Session()
@@ -1,298 +1,298
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.db
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Database Models for RhodeCode
7 7
8 8 :created_on: Apr 08, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 logging
28 28 import datetime
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.exc import DatabaseError
32 32 from sqlalchemy.orm import relation, backref, class_mapper
33 33 from sqlalchemy.orm.session import Session
34 34
35 35 from rhodecode.model.meta import Base
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39 class BaseModel(object):
40 40
41 41 @classmethod
42 42 def _get_keys(cls):
43 43 """return column names for this model """
44 44 return class_mapper(cls).c.keys()
45 45
46 46 def get_dict(self):
47 47 """return dict with keys and values corresponding
48 48 to this model data """
49 49
50 50 d = {}
51 51 for k in self._get_keys():
52 52 d[k] = getattr(self, k)
53 53 return d
54 54
55 55 def get_appstruct(self):
56 56 """return list with keys and values tupples corresponding
57 57 to this model data """
58 58
59 59 l = []
60 60 for k in self._get_keys():
61 61 l.append((k, getattr(self, k),))
62 62 return l
63 63
64 64 def populate_obj(self, populate_dict):
65 65 """populate model with data from given populate_dict"""
66 66
67 67 for k in self._get_keys():
68 68 if k in populate_dict:
69 69 setattr(self, k, populate_dict[k])
70 70
71 71 class RhodeCodeSettings(Base, BaseModel):
72 72 __tablename__ = 'rhodecode_settings'
73 73 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
74 74 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
75 75 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 76 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
77 77
78 78 def __init__(self, k, v):
79 79 self.app_settings_name = k
80 80 self.app_settings_value = v
81 81
82 82 def __repr__(self):
83 83 return "<%s('%s:%s')>" % (self.__class__.__name__,
84 84 self.app_settings_name, self.app_settings_value)
85 85
86 86 class RhodeCodeUi(Base, BaseModel):
87 87 __tablename__ = 'rhodecode_ui'
88 88 __table_args__ = {'useexisting':True}
89 89 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
90 90 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
91 91 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 92 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
93 93 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
94 94
95 95
96 96 class User(Base, BaseModel):
97 97 __tablename__ = 'users'
98 98 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
99 99 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
100 100 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 101 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 102 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
103 103 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
104 104 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 105 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 106 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 107 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
108 108 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
109 109
110 110 user_log = relation('UserLog', cascade='all')
111 111 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
112 112
113 113 repositories = relation('Repository')
114 114 user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
115 115
116 116 @property
117 117 def full_contact(self):
118 118 return '%s %s <%s>' % (self.name, self.lastname, self.email)
119 119
120 120
121 121 @property
122 122 def is_admin(self):
123 123 return self.admin
124 124
125 125 def __repr__(self):
126 126 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
127 127 self.user_id, self.username)
128 128
129 129 def update_lastlogin(self):
130 130 """Update user lastlogin"""
131 131
132 132 try:
133 133 session = Session.object_session(self)
134 134 self.last_login = datetime.datetime.now()
135 135 session.add(self)
136 136 session.commit()
137 137 log.debug('updated user %s lastlogin', self.username)
138 138 except (DatabaseError,):
139 139 session.rollback()
140 140
141 141
142 142 class UserLog(Base, BaseModel):
143 143 __tablename__ = 'user_logs'
144 144 __table_args__ = {'useexisting':True}
145 145 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
146 146 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
147 147 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
148 148 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 149 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 150 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 151 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
152 152
153 153 user = relation('User')
154 154 repository = relation('Repository')
155 155
156 156 class Repository(Base, BaseModel):
157 157 __tablename__ = 'repositories'
158 158 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
159 159 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
160 160 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
161 161 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
162 162 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
163 163 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
164 164 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
165 165 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
166 166 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
167 167 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
168 168
169 169 user = relation('User')
170 170 fork = relation('Repository', remote_side=repo_id)
171 171 group = relation('Group')
172 172 repo_to_perm = relation('RepoToPerm', cascade='all')
173 173 stats = relation('Statistics', cascade='all', uselist=False)
174 174
175 175 repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
176 176
177 177 def __repr__(self):
178 178 return "<%s('%s:%s')>" % (self.__class__.__name__,
179 179 self.repo_id, self.repo_name)
180 180
181 181 class Group(Base, BaseModel):
182 182 __tablename__ = 'groups'
183 183 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
184 184
185 185 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
186 186 group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
187 187 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
188 188
189 189 parent_group = relation('Group', remote_side=group_id)
190 190
191 191
192 192 def __init__(self, group_name='', parent_group=None):
193 193 self.group_name = group_name
194 194 self.parent_group = parent_group
195 195
196 196 def __repr__(self):
197 197 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
198 198 self.group_name)
199 199
200 200 class Permission(Base, BaseModel):
201 201 __tablename__ = 'permissions'
202 202 __table_args__ = {'useexisting':True}
203 203 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
204 204 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
205 205 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206 206
207 207 def __repr__(self):
208 208 return "<%s('%s:%s')>" % (self.__class__.__name__,
209 209 self.permission_id, self.permission_name)
210 210
211 211 class RepoToPerm(Base, BaseModel):
212 212 __tablename__ = 'repo_to_perm'
213 213 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
214 214 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
215 215 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
216 216 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
217 217 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
218 218
219 219 user = relation('User')
220 220 permission = relation('Permission')
221 221 repository = relation('Repository')
222 222
223 223 class UserToPerm(Base, BaseModel):
224 224 __tablename__ = 'user_to_perm'
225 225 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
226 226 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
227 227 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
228 228 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
229 229
230 230 user = relation('User')
231 231 permission = relation('Permission')
232 232
233 233 class GroupToPerm(Base, BaseModel):
234 234 __tablename__ = 'group_to_perm'
235 235 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
236 236
237 237 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
238 238 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
239 239 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
240 240 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
241 241
242 242 user = relation('User')
243 243 permission = relation('Permission')
244 244 group = relation('Group')
245 245
246 246 class Statistics(Base, BaseModel):
247 247 __tablename__ = 'statistics'
248 248 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
249 249 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
250 250 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
251 251 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
252 252 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
253 253 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
254 254 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
255 255
256 256 repository = relation('Repository', single_parent=True)
257 257
258 258 class UserFollowing(Base, BaseModel):
259 259 __tablename__ = 'user_followings'
260 260 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
261 261 UniqueConstraint('user_id', 'follows_user_id')
262 262 , {'useexisting':True})
263 263
264 264 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
265 265 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
266 266 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
267 267 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
268 268
269 269 user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
270 270
271 271 follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
272 272 follows_repository = relation('Repository')
273 273
274 274 class CacheInvalidation(Base, BaseModel):
275 275 __tablename__ = 'cache_invalidation'
276 276 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
277 277 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
278 278 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
279 279 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
280 280 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
281 281
282 282
283 283 def __init__(self, cache_key, cache_args=''):
284 284 self.cache_key = cache_key
285 285 self.cache_args = cache_args
286 286 self.cache_active = False
287 287
288 288 def __repr__(self):
289 289 return "<%s('%s:%s')>" % (self.__class__.__name__,
290 290 self.cache_id, self.cache_key)
291 291
292 292 class DbMigrateVersion(Base, BaseModel):
293 293 __tablename__ = 'db_migrate_version'
294 294 __table_args__ = {'useexisting':True}
295 295 repository_id = Column('repository_id', String(250), primary_key=True)
296 296 repository_path = Column('repository_path', Text)
297 297 version = Column('version', Integer)
298 298
@@ -1,114 +1,114
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.permission
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 permissions model for RhodeCode
7 7
8 8 :created_on: Aug 20, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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
31 31 from sqlalchemy.exc import DatabaseError
32 32
33 33 from rhodecode.model import BaseModel
34 34 from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
35 35 from rhodecode.model.caching_query import FromCache
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39
40 40 class PermissionModel(BaseModel):
41 41 """Permissions model for RhodeCode
42 42 """
43 43
44 44 def get_permission(self, permission_id, cache=False):
45 45 """Get's permissions by id
46 46
47 47 :param permission_id: id of permission to get from database
48 48 :param cache: use Cache for this query
49 49 """
50 50 perm = self.sa.query(Permission)
51 51 if cache:
52 52 perm = perm.options(FromCache("sql_cache_short",
53 53 "get_permission_%s" % permission_id))
54 54 return perm.get(permission_id)
55 55
56 56 def get_permission_by_name(self, name, cache=False):
57 57 """Get's permissions by given name
58 58
59 59 :param name: name to fetch
60 60 :param cache: Use cache for this query
61 61 """
62 62 perm = self.sa.query(Permission)\
63 63 .filter(Permission.permission_name == name)
64 64 if cache:
65 65 perm = perm.options(FromCache("sql_cache_short",
66 66 "get_permission_%s" % name))
67 67 return perm.scalar()
68 68
69 69 def update(self, form_result):
70 70 perm_user = self.sa.query(User)\
71 71 .filter(User.username == form_result['perm_user_name']).scalar()
72 72 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
73 73 if len(u2p) != 3:
74 74 raise Exception('Defined: %s should be 3 permissions for default'
75 75 ' user. This should not happen please verify'
76 76 ' your database' % len(u2p))
77 77
78 78 try:
79 79 #stage 1 change defaults
80 80 for p in u2p:
81 81 if p.permission.permission_name.startswith('repository.'):
82 82 p.permission = self.get_permission_by_name(
83 83 form_result['default_perm'])
84 84 self.sa.add(p)
85 85
86 86 if p.permission.permission_name.startswith('hg.register.'):
87 87 p.permission = self.get_permission_by_name(
88 88 form_result['default_register'])
89 89 self.sa.add(p)
90 90
91 91 if p.permission.permission_name.startswith('hg.create.'):
92 92 p.permission = self.get_permission_by_name(
93 93 form_result['default_create'])
94 94 self.sa.add(p)
95 95
96 96 #stage 2 update all default permissions for repos if checked
97 97 if form_result['overwrite_default'] == True:
98 98 for r2p in self.sa.query(RepoToPerm)\
99 99 .filter(RepoToPerm.user == perm_user).all():
100 100 r2p.permission = self.get_permission_by_name(
101 101 form_result['default_perm'])
102 102 self.sa.add(r2p)
103 103
104 104 #stage 3 set anonymous access
105 105 if perm_user.username == 'default':
106 106 perm_user.active = bool(form_result['anonymous'])
107 107 self.sa.add(perm_user)
108 108
109 109
110 110 self.sa.commit()
111 111 except (DatabaseError,):
112 112 log.error(traceback.format_exc())
113 113 self.sa.rollback()
114 114 raise
@@ -1,263 +1,263
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.repo
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repository model for rhodecode
7 7
8 8 :created_on: Jun 5, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 shutil
29 29 import logging
30 30 import traceback
31 31 from datetime import datetime
32 32
33 33 from pylons import app_globals as g
34 34
35 35 from rhodecode.model import BaseModel
36 36 from rhodecode.model.caching_query import FromCache
37 37 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
38 38 Statistics
39 39 from rhodecode.model.user import UserModel
40 40
41 41 from vcs.backends import get_backend
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45 class RepoModel(BaseModel):
46 46
47 47 def get(self, repo_id, cache=False):
48 48 repo = self.sa.query(Repository)\
49 49 .filter(Repository.repo_id == repo_id)
50 50
51 51 if cache:
52 52 repo = repo.options(FromCache("sql_cache_short",
53 53 "get_repo_%s" % repo_id))
54 54 return repo.scalar()
55 55
56 56
57 57 def get_by_repo_name(self, repo_name, cache=False):
58 58 repo = self.sa.query(Repository)\
59 59 .filter(Repository.repo_name == repo_name)
60 60
61 61 if cache:
62 62 repo = repo.options(FromCache("sql_cache_short",
63 63 "get_repo_%s" % repo_name))
64 64 return repo.scalar()
65 65
66 66 def get_users_js(self):
67 67
68 68 users = self.sa.query(User).filter(User.active == True).all()
69 69 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
70 70 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
71 71 u.lastname, u.username)
72 72 for u in users])
73 73 return users_array
74 74
75 75
76 76 def update(self, repo_name, form_data):
77 77 try:
78 78 cur_repo = self.get_by_repo_name(repo_name, cache=False)
79 79 user_model = UserModel(self.sa)
80 80
81 81 #update permissions
82 82 for username, perm in form_data['perms_updates']:
83 83 r2p = self.sa.query(RepoToPerm)\
84 84 .filter(RepoToPerm.user == user_model.get_by_username(username))\
85 85 .filter(RepoToPerm.repository == cur_repo)\
86 86 .one()
87 87
88 88 r2p.permission = self.sa.query(Permission)\
89 89 .filter(Permission.permission_name == perm)\
90 90 .scalar()
91 91 self.sa.add(r2p)
92 92
93 93 #set new permissions
94 94 for username, perm in form_data['perms_new']:
95 95 r2p = RepoToPerm()
96 96 r2p.repository = cur_repo
97 97 r2p.user = user_model.get_by_username(username, cache=False)
98 98
99 99 r2p.permission = self.sa.query(Permission)\
100 100 .filter(Permission.permission_name == perm)\
101 101 .scalar()
102 102 self.sa.add(r2p)
103 103
104 104 #update current repo
105 105 for k, v in form_data.items():
106 106 if k == 'user':
107 107 cur_repo.user = user_model.get(v)
108 108 else:
109 109 setattr(cur_repo, k, v)
110 110
111 111 self.sa.add(cur_repo)
112 112
113 113 if repo_name != form_data['repo_name']:
114 114 #rename our data
115 115 self.__rename_repo(repo_name, form_data['repo_name'])
116 116
117 117 self.sa.commit()
118 118 except:
119 119 log.error(traceback.format_exc())
120 120 self.sa.rollback()
121 121 raise
122 122
123 123 def create(self, form_data, cur_user, just_db=False, fork=False):
124 124 try:
125 125 if fork:
126 126 #force str since hg doesn't go with unicode
127 127 repo_name = str(form_data['fork_name'])
128 128 org_name = str(form_data['repo_name'])
129 129
130 130 else:
131 131 org_name = repo_name = str(form_data['repo_name'])
132 132 new_repo = Repository()
133 133 new_repo.enable_statistics = True
134 134 for k, v in form_data.items():
135 135 if k == 'repo_name':
136 136 v = repo_name
137 137 setattr(new_repo, k, v)
138 138
139 139 if fork:
140 140 parent_repo = self.sa.query(Repository)\
141 141 .filter(Repository.repo_name == org_name).scalar()
142 142 new_repo.fork = parent_repo
143 143
144 144 new_repo.user_id = cur_user.user_id
145 145 self.sa.add(new_repo)
146 146
147 147 #create default permission
148 148 repo_to_perm = RepoToPerm()
149 149 default = 'repository.read'
150 150 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
151 151 if p.permission.permission_name.startswith('repository.'):
152 152 default = p.permission.permission_name
153 153 break
154 154
155 155 default_perm = 'repository.none' if form_data['private'] else default
156 156
157 157 repo_to_perm.permission_id = self.sa.query(Permission)\
158 158 .filter(Permission.permission_name == default_perm)\
159 159 .one().permission_id
160 160
161 161 repo_to_perm.repository_id = new_repo.repo_id
162 162 repo_to_perm.user_id = UserModel(self.sa)\
163 163 .get_by_username('default', cache=False).user_id
164 164
165 165 self.sa.add(repo_to_perm)
166 166 self.sa.commit()
167 167
168 168
169 169 #now automatically start following this repository as owner
170 170 from rhodecode.model.scm import ScmModel
171 171 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
172 172 cur_user.user_id)
173 173
174 174 if not just_db:
175 175 self.__create_repo(repo_name, form_data['repo_type'])
176 176 except:
177 177 log.error(traceback.format_exc())
178 178 self.sa.rollback()
179 179 raise
180 180
181 181 def create_fork(self, form_data, cur_user):
182 182 from rhodecode.lib.celerylib import tasks, run_task
183 183 run_task(tasks.create_repo_fork, form_data, cur_user)
184 184
185 185 def delete(self, repo):
186 186 try:
187 187 self.sa.delete(repo)
188 188 self.__delete_repo(repo)
189 189 self.sa.commit()
190 190 except:
191 191 log.error(traceback.format_exc())
192 192 self.sa.rollback()
193 193 raise
194 194
195 195 def delete_perm_user(self, form_data, repo_name):
196 196 try:
197 197 self.sa.query(RepoToPerm)\
198 198 .filter(RepoToPerm.repository \
199 199 == self.get_by_repo_name(repo_name))\
200 200 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
201 201 self.sa.commit()
202 202 except:
203 203 log.error(traceback.format_exc())
204 204 self.sa.rollback()
205 205 raise
206 206
207 207 def delete_stats(self, repo_name):
208 208 try:
209 209 self.sa.query(Statistics)\
210 210 .filter(Statistics.repository == \
211 211 self.get_by_repo_name(repo_name)).delete()
212 212 self.sa.commit()
213 213 except:
214 214 log.error(traceback.format_exc())
215 215 self.sa.rollback()
216 216 raise
217 217
218 218
219 219 def __create_repo(self, repo_name, alias):
220 220 """
221 221 makes repository on filesystem
222 222 :param repo_name:
223 223 :param alias:
224 224 """
225 225 from rhodecode.lib.utils import check_repo
226 226 repo_path = os.path.join(g.base_path, repo_name)
227 227 if check_repo(repo_name, g.base_path):
228 228 log.info('creating repo %s in %s', repo_name, repo_path)
229 229 backend = get_backend(alias)
230 230 backend(repo_path, create=True)
231 231
232 232 def __rename_repo(self, old, new):
233 233 """
234 234 renames repository on filesystem
235 235 :param old: old name
236 236 :param new: new name
237 237 """
238 238 log.info('renaming repo from %s to %s', old, new)
239 239
240 240 old_path = os.path.join(g.base_path, old)
241 241 new_path = os.path.join(g.base_path, new)
242 242 if os.path.isdir(new_path):
243 243 raise Exception('Was trying to rename to already existing dir %s',
244 244 new_path)
245 245 shutil.move(old_path, new_path)
246 246
247 247 def __delete_repo(self, repo):
248 248 """
249 249 removes repo from filesystem, the removal is acctually made by
250 250 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
251 251 repository is no longer valid for rhodecode, can be undeleted later on
252 252 by reverting the renames on this repository
253 253 :param repo: repo object
254 254 """
255 255 rm_path = os.path.join(g.base_path, repo.repo_name)
256 256 log.info("Removing %s", rm_path)
257 257 #disable hg/git
258 258 alias = repo.repo_type
259 259 shutil.move(os.path.join(rm_path, '.%s' % alias),
260 260 os.path.join(rm_path, 'rm__.%s' % alias))
261 261 #disable repo
262 262 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
263 263 % (datetime.today(), repo.repo_name)))
@@ -1,384 +1,384
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.scm
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Scm model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 time
29 29 import traceback
30 30 import logging
31 31
32 32 from vcs import get_backend
33 33 from vcs.utils.helpers import get_scm
34 34 from vcs.exceptions import RepositoryError, VCSError
35 35 from vcs.utils.lazy import LazyProperty
36 36
37 37 from mercurial import ui
38 38
39 39 from beaker.cache import cache_region, region_invalidate
40 40
41 41 from rhodecode import BACKENDS
42 42 from rhodecode.lib import helpers as h
43 43 from rhodecode.lib.auth import HasRepoPermissionAny
44 44 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, action_logger
45 45 from rhodecode.model import BaseModel
46 46 from rhodecode.model.user import UserModel
47 47
48 48 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
49 49 UserFollowing, UserLog
50 50 from rhodecode.model.caching_query import FromCache
51 51
52 52 from sqlalchemy.orm import joinedload
53 53 from sqlalchemy.orm.session import make_transient
54 54 from sqlalchemy.exc import DatabaseError
55 55
56 56 log = logging.getLogger(__name__)
57 57
58 58
59 59 class UserTemp(object):
60 60 def __init__(self, user_id):
61 61 self.user_id = user_id
62 62
63 63 def __repr__(self):
64 64 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
65 65
66 66 class RepoTemp(object):
67 67 def __init__(self, repo_id):
68 68 self.repo_id = repo_id
69 69
70 70 def __repr__(self):
71 71 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
72 72
73 73 class ScmModel(BaseModel):
74 74 """Generic Scm Model
75 75 """
76 76
77 77 @LazyProperty
78 78 def repos_path(self):
79 79 """Get's the repositories root path from database
80 80 """
81 81
82 82 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
83 83
84 84 return q.ui_value
85 85
86 86 def repo_scan(self, repos_path, baseui):
87 87 """Listing of repositories in given path. This path should not be a
88 88 repository itself. Return a dictionary of repository objects
89 89
90 90 :param repos_path: path to directory containing repositories
91 91 :param baseui: baseui instance to instantiate MercurialRepostitory with
92 92 """
93 93
94 94 log.info('scanning for repositories in %s', repos_path)
95 95
96 96 if not isinstance(baseui, ui.ui):
97 97 baseui = make_ui('db')
98 98 repos_list = {}
99 99
100 100 for name, path in get_filesystem_repos(repos_path, recursive=True):
101 101 try:
102 102 if repos_list.has_key(name):
103 103 raise RepositoryError('Duplicate repository name %s '
104 104 'found in %s' % (name, path))
105 105 else:
106 106
107 107 klass = get_backend(path[0])
108 108
109 109 if path[0] == 'hg' and path[0] in BACKENDS.keys():
110 110 repos_list[name] = klass(path[1], baseui=baseui)
111 111
112 112 if path[0] == 'git' and path[0] in BACKENDS.keys():
113 113 repos_list[name] = klass(path[1])
114 114 except OSError:
115 115 continue
116 116
117 117 return repos_list
118 118
119 119 def get_repos(self, all_repos=None):
120 120 """Get all repos from db and for each repo create it's backend instance.
121 121 and fill that backed with information from database
122 122
123 123 :param all_repos: give specific repositories list, good for filtering
124 124 """
125 125
126 126 if all_repos is None:
127 127 all_repos = self.sa.query(Repository)\
128 128 .order_by(Repository.repo_name).all()
129 129
130 130 #get the repositories that should be invalidated
131 131 invalidation_list = [str(x.cache_key) for x in \
132 132 self.sa.query(CacheInvalidation.cache_key)\
133 133 .filter(CacheInvalidation.cache_active == False)\
134 134 .all()]
135 135
136 136 for r in all_repos:
137 137
138 138 repo = self.get(r.repo_name, invalidation_list)
139 139
140 140 if repo is not None:
141 141 last_change = repo.last_change
142 142 tip = h.get_changeset_safe(repo, 'tip')
143 143
144 144 tmp_d = {}
145 145 tmp_d['name'] = r.repo_name
146 146 tmp_d['name_sort'] = tmp_d['name'].lower()
147 147 tmp_d['description'] = repo.dbrepo.description
148 148 tmp_d['description_sort'] = tmp_d['description']
149 149 tmp_d['last_change'] = last_change
150 150 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
151 151 tmp_d['tip'] = tip.raw_id
152 152 tmp_d['tip_sort'] = tip.revision
153 153 tmp_d['rev'] = tip.revision
154 154 tmp_d['contact'] = repo.dbrepo.user.full_contact
155 155 tmp_d['contact_sort'] = tmp_d['contact']
156 156 tmp_d['repo_archives'] = list(repo._get_archives())
157 157 tmp_d['last_msg'] = tip.message
158 158 tmp_d['repo'] = repo
159 159 yield tmp_d
160 160
161 161 def get_repo(self, repo_name):
162 162 return self.get(repo_name)
163 163
164 164 def get(self, repo_name, invalidation_list=None):
165 165 """Get's repository from given name, creates BackendInstance and
166 166 propagates it's data from database with all additional information
167 167
168 168 :param repo_name:
169 169 :param invalidation_list: if a invalidation list is given the get
170 170 method should not manually check if this repository needs
171 171 invalidation and just invalidate the repositories in list
172 172
173 173 """
174 174 if not HasRepoPermissionAny('repository.read', 'repository.write',
175 175 'repository.admin')(repo_name, 'get repo check'):
176 176 return
177 177
178 178 #======================================================================
179 179 # CACHE FUNCTION
180 180 #======================================================================
181 181 @cache_region('long_term')
182 182 def _get_repo(repo_name):
183 183
184 184 repo_path = os.path.join(self.repos_path, repo_name)
185 185
186 186 try:
187 187 alias = get_scm(repo_path)[0]
188 188
189 189 log.debug('Creating instance of %s repository', alias)
190 190 backend = get_backend(alias)
191 191 except VCSError:
192 192 log.error(traceback.format_exc())
193 193 return
194 194
195 195 if alias == 'hg':
196 196 from pylons import app_globals as g
197 197 repo = backend(repo_path, create=False, baseui=g.baseui)
198 198 #skip hidden web repository
199 199 if repo._get_hidden():
200 200 return
201 201 else:
202 202 repo = backend(repo_path, create=False)
203 203
204 204 dbrepo = self.sa.query(Repository)\
205 205 .options(joinedload(Repository.fork))\
206 206 .options(joinedload(Repository.user))\
207 207 .filter(Repository.repo_name == repo_name)\
208 208 .scalar()
209 209
210 210 make_transient(dbrepo)
211 211 if dbrepo.user:
212 212 make_transient(dbrepo.user)
213 213 if dbrepo.fork:
214 214 make_transient(dbrepo.fork)
215 215
216 216 repo.dbrepo = dbrepo
217 217 return repo
218 218
219 219 pre_invalidate = True
220 220 if invalidation_list is not None:
221 221 pre_invalidate = repo_name in invalidation_list
222 222
223 223 if pre_invalidate:
224 224 invalidate = self._should_invalidate(repo_name)
225 225
226 226 if invalidate:
227 227 log.info('invalidating cache for repository %s', repo_name)
228 228 region_invalidate(_get_repo, None, repo_name)
229 229 self._mark_invalidated(invalidate)
230 230
231 231 return _get_repo(repo_name)
232 232
233 233
234 234
235 235 def mark_for_invalidation(self, repo_name):
236 236 """Puts cache invalidation task into db for
237 237 further global cache invalidation
238 238
239 239 :param repo_name: this repo that should invalidation take place
240 240 """
241 241
242 242 log.debug('marking %s for invalidation', repo_name)
243 243 cache = self.sa.query(CacheInvalidation)\
244 244 .filter(CacheInvalidation.cache_key == repo_name).scalar()
245 245
246 246 if cache:
247 247 #mark this cache as inactive
248 248 cache.cache_active = False
249 249 else:
250 250 log.debug('cache key not found in invalidation db -> creating one')
251 251 cache = CacheInvalidation(repo_name)
252 252
253 253 try:
254 254 self.sa.add(cache)
255 255 self.sa.commit()
256 256 except (DatabaseError,):
257 257 log.error(traceback.format_exc())
258 258 self.sa.rollback()
259 259
260 260
261 261 def toggle_following_repo(self, follow_repo_id, user_id):
262 262
263 263 f = self.sa.query(UserFollowing)\
264 264 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
265 265 .filter(UserFollowing.user_id == user_id).scalar()
266 266
267 267 if f is not None:
268 268
269 269 try:
270 270 self.sa.delete(f)
271 271 self.sa.commit()
272 272 action_logger(UserTemp(user_id),
273 273 'stopped_following_repo',
274 274 RepoTemp(follow_repo_id))
275 275 return
276 276 except:
277 277 log.error(traceback.format_exc())
278 278 self.sa.rollback()
279 279 raise
280 280
281 281
282 282 try:
283 283 f = UserFollowing()
284 284 f.user_id = user_id
285 285 f.follows_repo_id = follow_repo_id
286 286 self.sa.add(f)
287 287 self.sa.commit()
288 288 action_logger(UserTemp(user_id),
289 289 'started_following_repo',
290 290 RepoTemp(follow_repo_id))
291 291 except:
292 292 log.error(traceback.format_exc())
293 293 self.sa.rollback()
294 294 raise
295 295
296 296 def toggle_following_user(self, follow_user_id , user_id):
297 297 f = self.sa.query(UserFollowing)\
298 298 .filter(UserFollowing.follows_user_id == follow_user_id)\
299 299 .filter(UserFollowing.user_id == user_id).scalar()
300 300
301 301 if f is not None:
302 302 try:
303 303 self.sa.delete(f)
304 304 self.sa.commit()
305 305 return
306 306 except:
307 307 log.error(traceback.format_exc())
308 308 self.sa.rollback()
309 309 raise
310 310
311 311 try:
312 312 f = UserFollowing()
313 313 f.user_id = user_id
314 314 f.follows_user_id = follow_user_id
315 315 self.sa.add(f)
316 316 self.sa.commit()
317 317 except:
318 318 log.error(traceback.format_exc())
319 319 self.sa.rollback()
320 320 raise
321 321
322 322 def is_following_repo(self, repo_name, user_id):
323 323 r = self.sa.query(Repository)\
324 324 .filter(Repository.repo_name == repo_name).scalar()
325 325
326 326 f = self.sa.query(UserFollowing)\
327 327 .filter(UserFollowing.follows_repository == r)\
328 328 .filter(UserFollowing.user_id == user_id).scalar()
329 329
330 330 return f is not None
331 331
332 332 def is_following_user(self, username, user_id):
333 333 u = UserModel(self.sa).get_by_username(username)
334 334
335 335 f = self.sa.query(UserFollowing)\
336 336 .filter(UserFollowing.follows_user == u)\
337 337 .filter(UserFollowing.user_id == user_id).scalar()
338 338
339 339 return f is not None
340 340
341 341 def get_followers(self, repo_id):
342 342 return self.sa.query(UserFollowing)\
343 343 .filter(UserFollowing.follows_repo_id == repo_id).count()
344 344
345 345 def get_forks(self, repo_id):
346 346 return self.sa.query(Repository)\
347 347 .filter(Repository.fork_id == repo_id).count()
348 348
349 349
350 350 def get_unread_journal(self):
351 351 return self.sa.query(UserLog).count()
352 352
353 353
354 354 def _should_invalidate(self, repo_name):
355 355 """Looks up database for invalidation signals for this repo_name
356 356
357 357 :param repo_name:
358 358 """
359 359
360 360 ret = self.sa.query(CacheInvalidation)\
361 361 .options(FromCache('sql_cache_short',
362 362 'get_invalidation_%s' % repo_name))\
363 363 .filter(CacheInvalidation.cache_key == repo_name)\
364 364 .filter(CacheInvalidation.cache_active == False)\
365 365 .scalar()
366 366
367 367 return ret
368 368
369 369 def _mark_invalidated(self, cache_key):
370 370 """ Marks all occurences of cache to invaldation as already invalidated
371 371
372 372 :param cache_key:
373 373 """
374 374
375 375 if cache_key:
376 376 log.debug('marking %s as already invalidated', cache_key)
377 377 try:
378 378 cache_key.cache_active = True
379 379 self.sa.add(cache_key)
380 380 self.sa.commit()
381 381 except (DatabaseError,):
382 382 log.error(traceback.format_exc())
383 383 self.sa.rollback()
384 384
@@ -1,93 +1,93
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for RhodeCode settings
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 Nov 17, 2010
22 22 Model for RhodeCode
23 23 :author: marcink
24 24 """
25 25
26 26 from rhodecode.lib import helpers as h
27 27 from rhodecode.model import BaseModel
28 28 from rhodecode.model.caching_query import FromCache
29 29 from rhodecode.model.db import RhodeCodeSettings
30 30 from sqlalchemy.orm import joinedload
31 31 import logging
32 32
33 33 log = logging.getLogger(__name__)
34 34
35 35 class SettingsModel(BaseModel):
36 36 """
37 37 Settings model
38 38 """
39 39
40 40 def get(self, settings_key, cache=False):
41 41 r = self.sa.query(RhodeCodeSettings)\
42 42 .filter(RhodeCodeSettings.app_settings_name == settings_key).scalar()
43 43 if cache:
44 44 r = r.options(FromCache("sql_cache_short",
45 45 "get_setting_%s" % settings_key))
46 46 return r
47 47
48 48 def get_app_settings(self, cache=False):
49 49 """Get's config from database, each config key is prefixed with
50 50 'rhodecode_' prefix, than global pylons config is updated with such
51 51 keys
52 52 """
53 53
54 54 ret = self.sa.query(RhodeCodeSettings)
55 55
56 56 if cache:
57 57 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
58 58
59 59 if not ret:
60 60 raise Exception('Could not get application settings !')
61 61 settings = {}
62 62 for each in ret:
63 63 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
64 64
65 65 return settings
66 66
67 67 def get_ldap_settings(self):
68 68 """
69 69 Returns ldap settings from database
70 70 :returns:
71 71 ldap_active
72 72 ldap_host
73 73 ldap_port
74 74 ldap_ldaps
75 75 ldap_dn_user
76 76 ldap_dn_pass
77 77 ldap_base_dn
78 78 """
79 79
80 80 r = self.sa.query(RhodeCodeSettings)\
81 81 .filter(RhodeCodeSettings.app_settings_name\
82 82 .startswith('ldap_'))\
83 83 .all()
84 84
85 85 fd = {}
86 86
87 87 for row in r:
88 88 v = row.app_settings_value
89 89 if v in ['0', '1']:
90 90 v = v == '1'
91 91 fd.update({row.app_settings_name:v})
92 92
93 93 return fd
@@ -1,223 +1,223
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 package.rhodecode.model.user
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 users model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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
31 31 from pylons.i18n.translation import _
32 32
33 33 from rhodecode.model import BaseModel
34 34 from rhodecode.model.caching_query import FromCache
35 35 from rhodecode.model.db import User
36 36
37 37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
38 38
39 39 from sqlalchemy.exc import DatabaseError
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43 class UserModel(BaseModel):
44 44
45 45 def get(self, user_id, cache=False):
46 46 user = self.sa.query(User)
47 47 if cache:
48 48 user = user.options(FromCache("sql_cache_short",
49 49 "get_user_%s" % user_id))
50 50 return user.get(user_id)
51 51
52 52
53 53 def get_by_username(self, username, cache=False, case_insensitive=False):
54 54
55 55 if case_insensitive:
56 56 user = self.sa.query(User).filter(User.username.ilike(username))
57 57 else:
58 58 user = self.sa.query(User)\
59 59 .filter(User.username == username)
60 60 if cache:
61 61 user = user.options(FromCache("sql_cache_short",
62 62 "get_user_%s" % username))
63 63 return user.scalar()
64 64
65 65 def create(self, form_data):
66 66 try:
67 67 new_user = User()
68 68 for k, v in form_data.items():
69 69 setattr(new_user, k, v)
70 70
71 71 self.sa.add(new_user)
72 72 self.sa.commit()
73 73 except:
74 74 log.error(traceback.format_exc())
75 75 self.sa.rollback()
76 76 raise
77 77
78 78 def create_ldap(self, username, password):
79 79 """
80 80 Checks if user is in database, if not creates this user marked
81 81 as ldap user
82 82 :param username:
83 83 :param password:
84 84 """
85 85 from rhodecode.lib.auth import get_crypt_password
86 86 log.debug('Checking for such ldap account in RhodeCode database')
87 87 if self.get_by_username(username, case_insensitive=True) is None:
88 88 try:
89 89 new_user = User()
90 90 new_user.username = username.lower()#add ldap account always lowercase
91 91 new_user.password = get_crypt_password(password)
92 92 new_user.email = '%s@ldap.server' % username
93 93 new_user.active = True
94 94 new_user.is_ldap = True
95 95 new_user.name = '%s@ldap' % username
96 96 new_user.lastname = ''
97 97
98 98
99 99 self.sa.add(new_user)
100 100 self.sa.commit()
101 101 return True
102 102 except (DatabaseError,):
103 103 log.error(traceback.format_exc())
104 104 self.sa.rollback()
105 105 raise
106 106 log.debug('this %s user exists skipping creation of ldap account',
107 107 username)
108 108 return False
109 109
110 110 def create_registration(self, form_data):
111 111 from rhodecode.lib.celerylib import tasks, run_task
112 112 try:
113 113 new_user = User()
114 114 for k, v in form_data.items():
115 115 if k != 'admin':
116 116 setattr(new_user, k, v)
117 117
118 118 self.sa.add(new_user)
119 119 self.sa.commit()
120 120 body = ('New user registration\n'
121 121 'username: %s\n'
122 122 'email: %s\n')
123 123 body = body % (form_data['username'], form_data['email'])
124 124
125 125 run_task(tasks.send_email, None,
126 126 _('[RhodeCode] New User registration'),
127 127 body)
128 128 except:
129 129 log.error(traceback.format_exc())
130 130 self.sa.rollback()
131 131 raise
132 132
133 133 def update(self, user_id, form_data):
134 134 try:
135 135 new_user = self.get(user_id, cache=False)
136 136 if new_user.username == 'default':
137 137 raise DefaultUserException(
138 138 _("You can't Edit this user since it's"
139 139 " crucial for entire application"))
140 140
141 141 for k, v in form_data.items():
142 142 if k == 'new_password' and v != '':
143 143 new_user.password = v
144 144 else:
145 145 setattr(new_user, k, v)
146 146
147 147 self.sa.add(new_user)
148 148 self.sa.commit()
149 149 except:
150 150 log.error(traceback.format_exc())
151 151 self.sa.rollback()
152 152 raise
153 153
154 154 def update_my_account(self, user_id, form_data):
155 155 try:
156 156 new_user = self.get(user_id, cache=False)
157 157 if new_user.username == 'default':
158 158 raise DefaultUserException(
159 159 _("You can't Edit this user since it's"
160 160 " crucial for entire application"))
161 161 for k, v in form_data.items():
162 162 if k == 'new_password' and v != '':
163 163 new_user.password = v
164 164 else:
165 165 if k not in ['admin', 'active']:
166 166 setattr(new_user, k, v)
167 167
168 168 self.sa.add(new_user)
169 169 self.sa.commit()
170 170 except:
171 171 log.error(traceback.format_exc())
172 172 self.sa.rollback()
173 173 raise
174 174
175 175 def delete(self, user_id):
176 176 try:
177 177 user = self.get(user_id, cache=False)
178 178 if user.username == 'default':
179 179 raise DefaultUserException(
180 180 _("You can't remove this user since it's"
181 181 " crucial for entire application"))
182 182 if user.repositories:
183 183 raise UserOwnsReposException(_('This user still owns %s '
184 184 'repositories and cannot be '
185 185 'removed. Switch owners or '
186 186 'remove those repositories') \
187 187 % user.repositories)
188 188 self.sa.delete(user)
189 189 self.sa.commit()
190 190 except:
191 191 log.error(traceback.format_exc())
192 192 self.sa.rollback()
193 193 raise
194 194
195 195 def reset_password(self, data):
196 196 from rhodecode.lib.celerylib import tasks, run_task
197 197 run_task(tasks.reset_user_password, data['email'])
198 198
199 199
200 200 def fill_data(self, user):
201 201 """
202 202 Fills user data with those from database and log out user if not
203 203 present in database
204 204 :param user:
205 205 """
206 206
207 207 if not hasattr(user, 'user_id') or user.user_id is None:
208 208 raise Exception('passed in user has to have the user_id attribute')
209 209
210 210
211 211 log.debug('filling auth user data')
212 212 try:
213 213 dbuser = self.get(user.user_id)
214 214 user.username = dbuser.username
215 215 user.is_admin = dbuser.admin
216 216 user.name = dbuser.name
217 217 user.lastname = dbuser.lastname
218 218 user.email = dbuser.email
219 219 except:
220 220 log.error(traceback.format_exc())
221 221 user.is_authenticated = False
222 222
223 223 return user
@@ -1,54 +1,54
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.websetup
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Weboperations and setup for rhodecode
7 7
8 8 :created_on: Dec 11, 2010
9 9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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
31 31 from rhodecode.config.environment import load_environment
32 32 from rhodecode.lib.db_manage import DbManage
33 33
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37 def setup_app(command, conf, vars):
38 38 """Place any commands to setup rhodecode here"""
39 39 dbconf = conf['sqlalchemy.db1.url']
40 40 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=conf['here'], tests=False)
41 41 dbmanage.create_tables(override=True)
42 42 dbmanage.set_db_version()
43 43 dbmanage.config_prompt(None)
44 44 dbmanage.create_default_user()
45 45 dbmanage.admin_prompt()
46 46 dbmanage.create_permissions()
47 47 dbmanage.populate_default_permissions()
48 48
49 49 load_environment(conf.global_conf, conf.local_conf, initial=True)
50 50
51 51
52 52
53 53
54 54
General Comments 0
You need to be logged in to leave comments. Login now