##// END OF EJS Templates
fixes for issue #149
marcink -
r1217:a3b2b4b4 default
parent child Browse files
Show More
@@ -1,53 +1,51 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.admin
3 rhodecode.controllers.admin.admin
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Controller for Admin panel of Rhodecode
6 Controller for Admin panel of Rhodecode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29 from pylons import request, tmpl_context as c
27 from pylons import request, tmpl_context as c
30 from rhodecode.lib.base import BaseController, render
28 from rhodecode.lib.base import BaseController, render
31 from rhodecode.model.db import UserLog
29 from rhodecode.model.db import UserLog
32 from webhelpers.paginate import Page
30 from webhelpers.paginate import Page
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
34
32
35 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
36
34
37 class AdminController(BaseController):
35 class AdminController(BaseController):
38
36
39 @LoginRequired()
37 @LoginRequired()
40 def __before__(self):
38 def __before__(self):
41 super(AdminController, self).__before__()
39 super(AdminController, self).__before__()
42
40
43 @HasPermissionAllDecorator('hg.admin')
41 @HasPermissionAllDecorator('hg.admin')
44 def index(self):
42 def index(self):
45
43
46 users_log = self.sa.query(UserLog).order_by(UserLog.action_date.desc())
44 users_log = self.sa.query(UserLog).order_by(UserLog.action_date.desc())
47 p = int(request.params.get('page', 1))
45 p = int(request.params.get('page', 1))
48 c.users_log = Page(users_log, page=p, items_per_page=10)
46 c.users_log = Page(users_log, page=p, items_per_page=10)
49 c.log_data = render('admin/admin_log.html')
47 c.log_data = render('admin/admin_log.html')
50 if request.params.get('partial'):
48 if request.params.get('partial'):
51 return c.log_data
49 return c.log_data
52 return render('admin/admin.html')
50 return render('admin/admin.html')
53
51
@@ -1,106 +1,104 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.ldap_settings
3 rhodecode.controllers.admin.ldap_settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 ldap controller for RhodeCode
6 ldap controller for RhodeCode
7
7
8 :created_on: Nov 26, 2010
8 :created_on: Nov 26, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27 import logging
25 import logging
28 import formencode
26 import formencode
29 import traceback
27 import traceback
30
28
31 from formencode import htmlfill
29 from formencode import htmlfill
32
30
33 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
36
34
37 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
40 from rhodecode.lib.auth_ldap import LdapImportError
38 from rhodecode.lib.auth_ldap import LdapImportError
41 from rhodecode.model.settings import SettingsModel
39 from rhodecode.model.settings import SettingsModel
42 from rhodecode.model.forms import LdapSettingsForm
40 from rhodecode.model.forms import LdapSettingsForm
43 from sqlalchemy.exc import DatabaseError
41 from sqlalchemy.exc import DatabaseError
44
42
45 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
46
44
47
45
48
46
49 class LdapSettingsController(BaseController):
47 class LdapSettingsController(BaseController):
50
48
51 @LoginRequired()
49 @LoginRequired()
52 @HasPermissionAllDecorator('hg.admin')
50 @HasPermissionAllDecorator('hg.admin')
53 def __before__(self):
51 def __before__(self):
54 c.admin_user = session.get('admin_user')
52 c.admin_user = session.get('admin_user')
55 c.admin_username = session.get('admin_username')
53 c.admin_username = session.get('admin_username')
56 super(LdapSettingsController, self).__before__()
54 super(LdapSettingsController, self).__before__()
57
55
58 def index(self):
56 def index(self):
59 defaults = SettingsModel().get_ldap_settings()
57 defaults = SettingsModel().get_ldap_settings()
60
58
61 return htmlfill.render(
59 return htmlfill.render(
62 render('admin/ldap/ldap.html'),
60 render('admin/ldap/ldap.html'),
63 defaults=defaults,
61 defaults=defaults,
64 encoding="UTF-8",
62 encoding="UTF-8",
65 force_defaults=True,)
63 force_defaults=True,)
66
64
67 def ldap_settings(self):
65 def ldap_settings(self):
68 """POST ldap create and store ldap settings"""
66 """POST ldap create and store ldap settings"""
69
67
70 settings_model = SettingsModel()
68 settings_model = SettingsModel()
71 post_data = dict(request.POST)
69 post_data = dict(request.POST)
72 _form = LdapSettingsForm(post_data.get('ldap_active'))()
70 _form = LdapSettingsForm(post_data.get('ldap_active'))()
73
71
74 try:
72 try:
75 form_result = _form.to_python(post_data)
73 form_result = _form.to_python(post_data)
76 try:
74 try:
77
75
78 for k, v in form_result.items():
76 for k, v in form_result.items():
79 if k.startswith('ldap_'):
77 if k.startswith('ldap_'):
80 setting = settings_model.get(k)
78 setting = settings_model.get(k)
81 setting.app_settings_value = v
79 setting.app_settings_value = v
82 self.sa.add(setting)
80 self.sa.add(setting)
83
81
84 self.sa.commit()
82 self.sa.commit()
85 h.flash(_('Ldap settings updated successfully'),
83 h.flash(_('Ldap settings updated successfully'),
86 category='success')
84 category='success')
87 except (DatabaseError,):
85 except (DatabaseError,):
88 raise
86 raise
89 except LdapImportError:
87 except LdapImportError:
90 h.flash(_('Unable to activate ldap. The "python-ldap" library '
88 h.flash(_('Unable to activate ldap. The "python-ldap" library '
91 'is missing.'), category='warning')
89 'is missing.'), category='warning')
92
90
93 except formencode.Invalid, errors:
91 except formencode.Invalid, errors:
94
92
95 return htmlfill.render(
93 return htmlfill.render(
96 render('admin/ldap/ldap.html'),
94 render('admin/ldap/ldap.html'),
97 defaults=errors.value,
95 defaults=errors.value,
98 errors=errors.error_dict or {},
96 errors=errors.error_dict or {},
99 prefix_error=False,
97 prefix_error=False,
100 encoding="UTF-8")
98 encoding="UTF-8")
101 except Exception:
99 except Exception:
102 log.error(traceback.format_exc())
100 log.error(traceback.format_exc())
103 h.flash(_('error occurred during update of ldap settings'),
101 h.flash(_('error occurred during update of ldap settings'),
104 category='error')
102 category='error')
105
103
106 return redirect(url('ldap_home'))
104 return redirect(url('ldap_home'))
@@ -1,171 +1,169 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.permissions
3 rhodecode.controllers.admin.permissions
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 permissions controller for Rhodecode
6 permissions controller for Rhodecode
7
7
8 :created_on: Apr 27, 2010
8 :created_on: Apr 27, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 from formencode import htmlfill
26 from formencode import htmlfill
29 from pylons import request, session, tmpl_context as c, url
27 from pylons import request, session, tmpl_context as c, url
30 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
31 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
32 from rhodecode.lib import helpers as h
30 from rhodecode.lib import helpers as h
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
34 from rhodecode.lib.auth_ldap import LdapImportError
32 from rhodecode.lib.auth_ldap import LdapImportError
35 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
36 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
34 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
37 from rhodecode.model.permission import PermissionModel
35 from rhodecode.model.permission import PermissionModel
38 from rhodecode.model.settings import SettingsModel
36 from rhodecode.model.settings import SettingsModel
39 from rhodecode.model.user import UserModel
37 from rhodecode.model.user import UserModel
40 import formencode
38 import formencode
41 import logging
39 import logging
42 import traceback
40 import traceback
43
41
44 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
45
43
46 class PermissionsController(BaseController):
44 class PermissionsController(BaseController):
47 """REST Controller styled on the Atom Publishing Protocol"""
45 """REST Controller styled on the Atom Publishing Protocol"""
48 # To properly map this controller, ensure your config/routing.py
46 # To properly map this controller, ensure your config/routing.py
49 # file has a resource setup:
47 # file has a resource setup:
50 # map.resource('permission', 'permissions')
48 # map.resource('permission', 'permissions')
51
49
52 @LoginRequired()
50 @LoginRequired()
53 @HasPermissionAllDecorator('hg.admin')
51 @HasPermissionAllDecorator('hg.admin')
54 def __before__(self):
52 def __before__(self):
55 c.admin_user = session.get('admin_user')
53 c.admin_user = session.get('admin_user')
56 c.admin_username = session.get('admin_username')
54 c.admin_username = session.get('admin_username')
57 super(PermissionsController, self).__before__()
55 super(PermissionsController, self).__before__()
58
56
59 self.perms_choices = [('repository.none', _('None'),),
57 self.perms_choices = [('repository.none', _('None'),),
60 ('repository.read', _('Read'),),
58 ('repository.read', _('Read'),),
61 ('repository.write', _('Write'),),
59 ('repository.write', _('Write'),),
62 ('repository.admin', _('Admin'),)]
60 ('repository.admin', _('Admin'),)]
63 self.register_choices = [
61 self.register_choices = [
64 ('hg.register.none',
62 ('hg.register.none',
65 _('disabled')),
63 _('disabled')),
66 ('hg.register.manual_activate',
64 ('hg.register.manual_activate',
67 _('allowed with manual account activation')),
65 _('allowed with manual account activation')),
68 ('hg.register.auto_activate',
66 ('hg.register.auto_activate',
69 _('allowed with automatic account activation')), ]
67 _('allowed with automatic account activation')), ]
70
68
71 self.create_choices = [('hg.create.none', _('Disabled')),
69 self.create_choices = [('hg.create.none', _('Disabled')),
72 ('hg.create.repository', _('Enabled'))]
70 ('hg.create.repository', _('Enabled'))]
73
71
74
72
75 def index(self, format='html'):
73 def index(self, format='html'):
76 """GET /permissions: All items in the collection"""
74 """GET /permissions: All items in the collection"""
77 # url('permissions')
75 # url('permissions')
78
76
79 def create(self):
77 def create(self):
80 """POST /permissions: Create a new item"""
78 """POST /permissions: Create a new item"""
81 # url('permissions')
79 # url('permissions')
82
80
83 def new(self, format='html'):
81 def new(self, format='html'):
84 """GET /permissions/new: Form to create a new item"""
82 """GET /permissions/new: Form to create a new item"""
85 # url('new_permission')
83 # url('new_permission')
86
84
87 def update(self, id):
85 def update(self, id):
88 """PUT /permissions/id: Update an existing item"""
86 """PUT /permissions/id: Update an existing item"""
89 # Forms posted to this method should contain a hidden field:
87 # Forms posted to this method should contain a hidden field:
90 # <input type="hidden" name="_method" value="PUT" />
88 # <input type="hidden" name="_method" value="PUT" />
91 # Or using helpers:
89 # Or using helpers:
92 # h.form(url('permission', id=ID),
90 # h.form(url('permission', id=ID),
93 # method='put')
91 # method='put')
94 # url('permission', id=ID)
92 # url('permission', id=ID)
95
93
96 permission_model = PermissionModel()
94 permission_model = PermissionModel()
97
95
98 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
96 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
99 [x[0] for x in self.register_choices],
97 [x[0] for x in self.register_choices],
100 [x[0] for x in self.create_choices])()
98 [x[0] for x in self.create_choices])()
101
99
102 try:
100 try:
103 form_result = _form.to_python(dict(request.POST))
101 form_result = _form.to_python(dict(request.POST))
104 form_result.update({'perm_user_name':id})
102 form_result.update({'perm_user_name':id})
105 permission_model.update(form_result)
103 permission_model.update(form_result)
106 h.flash(_('Default permissions updated successfully'),
104 h.flash(_('Default permissions updated successfully'),
107 category='success')
105 category='success')
108
106
109 except formencode.Invalid, errors:
107 except formencode.Invalid, errors:
110 c.perms_choices = self.perms_choices
108 c.perms_choices = self.perms_choices
111 c.register_choices = self.register_choices
109 c.register_choices = self.register_choices
112 c.create_choices = self.create_choices
110 c.create_choices = self.create_choices
113 defaults = errors.value
111 defaults = errors.value
114
112
115 return htmlfill.render(
113 return htmlfill.render(
116 render('admin/permissions/permissions.html'),
114 render('admin/permissions/permissions.html'),
117 defaults=defaults,
115 defaults=defaults,
118 errors=errors.error_dict or {},
116 errors=errors.error_dict or {},
119 prefix_error=False,
117 prefix_error=False,
120 encoding="UTF-8")
118 encoding="UTF-8")
121 except Exception:
119 except Exception:
122 log.error(traceback.format_exc())
120 log.error(traceback.format_exc())
123 h.flash(_('error occurred during update of permissions'),
121 h.flash(_('error occurred during update of permissions'),
124 category='error')
122 category='error')
125
123
126 return redirect(url('edit_permission', id=id))
124 return redirect(url('edit_permission', id=id))
127
125
128
126
129
127
130 def delete(self, id):
128 def delete(self, id):
131 """DELETE /permissions/id: Delete an existing item"""
129 """DELETE /permissions/id: Delete an existing item"""
132 # Forms posted to this method should contain a hidden field:
130 # Forms posted to this method should contain a hidden field:
133 # <input type="hidden" name="_method" value="DELETE" />
131 # <input type="hidden" name="_method" value="DELETE" />
134 # Or using helpers:
132 # Or using helpers:
135 # h.form(url('permission', id=ID),
133 # h.form(url('permission', id=ID),
136 # method='delete')
134 # method='delete')
137 # url('permission', id=ID)
135 # url('permission', id=ID)
138
136
139 def show(self, id, format='html'):
137 def show(self, id, format='html'):
140 """GET /permissions/id: Show a specific item"""
138 """GET /permissions/id: Show a specific item"""
141 # url('permission', id=ID)
139 # url('permission', id=ID)
142
140
143 def edit(self, id, format='html'):
141 def edit(self, id, format='html'):
144 """GET /permissions/id/edit: Form to edit an existing item"""
142 """GET /permissions/id/edit: Form to edit an existing item"""
145 #url('edit_permission', id=ID)
143 #url('edit_permission', id=ID)
146 c.perms_choices = self.perms_choices
144 c.perms_choices = self.perms_choices
147 c.register_choices = self.register_choices
145 c.register_choices = self.register_choices
148 c.create_choices = self.create_choices
146 c.create_choices = self.create_choices
149
147
150 if id == 'default':
148 if id == 'default':
151 default_user = UserModel().get_by_username('default')
149 default_user = UserModel().get_by_username('default')
152 defaults = {'_method':'put',
150 defaults = {'_method':'put',
153 'anonymous':default_user.active}
151 'anonymous':default_user.active}
154
152
155 for p in default_user.user_perms:
153 for p in default_user.user_perms:
156 if p.permission.permission_name.startswith('repository.'):
154 if p.permission.permission_name.startswith('repository.'):
157 defaults['default_perm'] = p.permission.permission_name
155 defaults['default_perm'] = p.permission.permission_name
158
156
159 if p.permission.permission_name.startswith('hg.register.'):
157 if p.permission.permission_name.startswith('hg.register.'):
160 defaults['default_register'] = p.permission.permission_name
158 defaults['default_register'] = p.permission.permission_name
161
159
162 if p.permission.permission_name.startswith('hg.create.'):
160 if p.permission.permission_name.startswith('hg.create.'):
163 defaults['default_create'] = p.permission.permission_name
161 defaults['default_create'] = p.permission.permission_name
164
162
165 return htmlfill.render(
163 return htmlfill.render(
166 render('admin/permissions/permissions.html'),
164 render('admin/permissions/permissions.html'),
167 defaults=defaults,
165 defaults=defaults,
168 encoding="UTF-8",
166 encoding="UTF-8",
169 force_defaults=True,)
167 force_defaults=True,)
170 else:
168 else:
171 return redirect(url('admin_home'))
169 return redirect(url('admin_home'))
@@ -1,313 +1,311 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Admin controller for RhodeCode
6 Admin controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29 import traceback
27 import traceback
30 import formencode
28 import formencode
31 from operator import itemgetter
29 from operator import itemgetter
32 from formencode import htmlfill
30 from formencode import htmlfill
33
31
34 from paste.httpexceptions import HTTPInternalServerError
32 from paste.httpexceptions import HTTPInternalServerError
35 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
36 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
37 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
38
36
39 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
41 HasPermissionAnyDecorator
39 HasPermissionAnyDecorator
42 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.utils import invalidate_cache, action_logger
41 from rhodecode.lib.utils import invalidate_cache, action_logger
44 from rhodecode.model.db import User
42 from rhodecode.model.db import User
45 from rhodecode.model.forms import RepoForm
43 from rhodecode.model.forms import RepoForm
46 from rhodecode.model.scm import ScmModel
44 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.repo import RepoModel
45 from rhodecode.model.repo import RepoModel
48
46
49
47
50 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
51
49
52 class ReposController(BaseController):
50 class ReposController(BaseController):
53 """REST Controller styled on the Atom Publishing Protocol"""
51 """REST Controller styled on the Atom Publishing Protocol"""
54 # To properly map this controller, ensure your config/routing.py
52 # To properly map this controller, ensure your config/routing.py
55 # file has a resource setup:
53 # file has a resource setup:
56 # map.resource('repo', 'repos')
54 # map.resource('repo', 'repos')
57
55
58 @LoginRequired()
56 @LoginRequired()
59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
57 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 def __before__(self):
58 def __before__(self):
61 c.admin_user = session.get('admin_user')
59 c.admin_user = session.get('admin_user')
62 c.admin_username = session.get('admin_username')
60 c.admin_username = session.get('admin_username')
63 super(ReposController, self).__before__()
61 super(ReposController, self).__before__()
64
62
65 @HasPermissionAllDecorator('hg.admin')
63 @HasPermissionAllDecorator('hg.admin')
66 def index(self, format='html'):
64 def index(self, format='html'):
67 """GET /repos: All items in the collection"""
65 """GET /repos: All items in the collection"""
68 # url('repos')
66 # url('repos')
69 cached_repo_list = ScmModel().get_repos()
67 cached_repo_list = ScmModel().get_repos()
70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
68 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
71 return render('admin/repos/repos.html')
69 return render('admin/repos/repos.html')
72
70
73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
71 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
74 def create(self):
72 def create(self):
75 """POST /repos: Create a new item"""
73 """POST /repos: Create a new item"""
76 # url('repos')
74 # url('repos')
77 repo_model = RepoModel()
75 repo_model = RepoModel()
78 _form = RepoForm()()
76 _form = RepoForm()()
79 form_result = {}
77 form_result = {}
80 try:
78 try:
81 form_result = _form.to_python(dict(request.POST))
79 form_result = _form.to_python(dict(request.POST))
82 repo_model.create(form_result, c.rhodecode_user)
80 repo_model.create(form_result, c.rhodecode_user)
83 h.flash(_('created repository %s') % form_result['repo_name'],
81 h.flash(_('created repository %s') % form_result['repo_name'],
84 category='success')
82 category='success')
85
83
86 if request.POST.get('user_created'):
84 if request.POST.get('user_created'):
87 action_logger(self.rhodecode_user, 'user_created_repo',
85 action_logger(self.rhodecode_user, 'user_created_repo',
88 form_result['repo_name'], '', self.sa)
86 form_result['repo_name'], '', self.sa)
89 else:
87 else:
90 action_logger(self.rhodecode_user, 'admin_created_repo',
88 action_logger(self.rhodecode_user, 'admin_created_repo',
91 form_result['repo_name'], '', self.sa)
89 form_result['repo_name'], '', self.sa)
92
90
93 except formencode.Invalid, errors:
91 except formencode.Invalid, errors:
94 c.new_repo = errors.value['repo_name']
92 c.new_repo = errors.value['repo_name']
95
93
96 if request.POST.get('user_created'):
94 if request.POST.get('user_created'):
97 r = render('admin/repos/repo_add_create_repository.html')
95 r = render('admin/repos/repo_add_create_repository.html')
98 else:
96 else:
99 r = render('admin/repos/repo_add.html')
97 r = render('admin/repos/repo_add.html')
100
98
101 return htmlfill.render(
99 return htmlfill.render(
102 r,
100 r,
103 defaults=errors.value,
101 defaults=errors.value,
104 errors=errors.error_dict or {},
102 errors=errors.error_dict or {},
105 prefix_error=False,
103 prefix_error=False,
106 encoding="UTF-8")
104 encoding="UTF-8")
107
105
108 except Exception:
106 except Exception:
109 log.error(traceback.format_exc())
107 log.error(traceback.format_exc())
110 msg = _('error occurred during creation of repository %s') \
108 msg = _('error occurred during creation of repository %s') \
111 % form_result.get('repo_name')
109 % form_result.get('repo_name')
112 h.flash(msg, category='error')
110 h.flash(msg, category='error')
113 if request.POST.get('user_created'):
111 if request.POST.get('user_created'):
114 return redirect(url('home'))
112 return redirect(url('home'))
115 return redirect(url('repos'))
113 return redirect(url('repos'))
116
114
117 @HasPermissionAllDecorator('hg.admin')
115 @HasPermissionAllDecorator('hg.admin')
118 def new(self, format='html'):
116 def new(self, format='html'):
119 """GET /repos/new: Form to create a new item"""
117 """GET /repos/new: Form to create a new item"""
120 new_repo = request.GET.get('repo', '')
118 new_repo = request.GET.get('repo', '')
121 c.new_repo = h.repo_name_slug(new_repo)
119 c.new_repo = h.repo_name_slug(new_repo)
122
120
123 return render('admin/repos/repo_add.html')
121 return render('admin/repos/repo_add.html')
124
122
125 @HasPermissionAllDecorator('hg.admin')
123 @HasPermissionAllDecorator('hg.admin')
126 def update(self, repo_name):
124 def update(self, repo_name):
127 """PUT /repos/repo_name: Update an existing item"""
125 """PUT /repos/repo_name: Update an existing item"""
128 # Forms posted to this method should contain a hidden field:
126 # Forms posted to this method should contain a hidden field:
129 # <input type="hidden" name="_method" value="PUT" />
127 # <input type="hidden" name="_method" value="PUT" />
130 # Or using helpers:
128 # Or using helpers:
131 # h.form(url('repo', repo_name=ID),
129 # h.form(url('repo', repo_name=ID),
132 # method='put')
130 # method='put')
133 # url('repo', repo_name=ID)
131 # url('repo', repo_name=ID)
134 repo_model = RepoModel()
132 repo_model = RepoModel()
135 changed_name = repo_name
133 changed_name = repo_name
136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
134 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
137
135
138 try:
136 try:
139 form_result = _form.to_python(dict(request.POST))
137 form_result = _form.to_python(dict(request.POST))
140 repo_model.update(repo_name, form_result)
138 repo_model.update(repo_name, form_result)
141 invalidate_cache('get_repo_cached_%s' % repo_name)
139 invalidate_cache('get_repo_cached_%s' % repo_name)
142 h.flash(_('Repository %s updated successfully' % repo_name),
140 h.flash(_('Repository %s updated successfully' % repo_name),
143 category='success')
141 category='success')
144 changed_name = form_result['repo_name']
142 changed_name = form_result['repo_name']
145 action_logger(self.rhodecode_user, 'admin_updated_repo',
143 action_logger(self.rhodecode_user, 'admin_updated_repo',
146 changed_name, '', self.sa)
144 changed_name, '', self.sa)
147
145
148 except formencode.Invalid, errors:
146 except formencode.Invalid, errors:
149 c.repo_info = repo_model.get_by_repo_name(repo_name)
147 c.repo_info = repo_model.get_by_repo_name(repo_name)
150 if c.repo_info.stats:
148 if c.repo_info.stats:
151 last_rev = c.repo_info.stats.stat_on_revision
149 last_rev = c.repo_info.stats.stat_on_revision
152 else:
150 else:
153 last_rev = 0
151 last_rev = 0
154 c.stats_revision = last_rev
152 c.stats_revision = last_rev
155 r = ScmModel().get(repo_name)
153 r = ScmModel().get(repo_name)
156 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
154 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
157
155
158 if last_rev == 0:
156 if last_rev == 0:
159 c.stats_percentage = 0
157 c.stats_percentage = 0
160 else:
158 else:
161 c.stats_percentage = '%.2f' % ((float((last_rev)) /
159 c.stats_percentage = '%.2f' % ((float((last_rev)) /
162 c.repo_last_rev) * 100)
160 c.repo_last_rev) * 100)
163
161
164 c.users_array = repo_model.get_users_js()
162 c.users_array = repo_model.get_users_js()
165 errors.value.update({'user':c.repo_info.user.username})
163 errors.value.update({'user':c.repo_info.user.username})
166 return htmlfill.render(
164 return htmlfill.render(
167 render('admin/repos/repo_edit.html'),
165 render('admin/repos/repo_edit.html'),
168 defaults=errors.value,
166 defaults=errors.value,
169 errors=errors.error_dict or {},
167 errors=errors.error_dict or {},
170 prefix_error=False,
168 prefix_error=False,
171 encoding="UTF-8")
169 encoding="UTF-8")
172
170
173 except Exception:
171 except Exception:
174 log.error(traceback.format_exc())
172 log.error(traceback.format_exc())
175 h.flash(_('error occurred during update of repository %s') \
173 h.flash(_('error occurred during update of repository %s') \
176 % repo_name, category='error')
174 % repo_name, category='error')
177
175
178 return redirect(url('edit_repo', repo_name=changed_name))
176 return redirect(url('edit_repo', repo_name=changed_name))
179
177
180 @HasPermissionAllDecorator('hg.admin')
178 @HasPermissionAllDecorator('hg.admin')
181 def delete(self, repo_name):
179 def delete(self, repo_name):
182 """DELETE /repos/repo_name: Delete an existing item"""
180 """DELETE /repos/repo_name: Delete an existing item"""
183 # Forms posted to this method should contain a hidden field:
181 # Forms posted to this method should contain a hidden field:
184 # <input type="hidden" name="_method" value="DELETE" />
182 # <input type="hidden" name="_method" value="DELETE" />
185 # Or using helpers:
183 # Or using helpers:
186 # h.form(url('repo', repo_name=ID),
184 # h.form(url('repo', repo_name=ID),
187 # method='delete')
185 # method='delete')
188 # url('repo', repo_name=ID)
186 # url('repo', repo_name=ID)
189
187
190 repo_model = RepoModel()
188 repo_model = RepoModel()
191 repo = repo_model.get_by_repo_name(repo_name)
189 repo = repo_model.get_by_repo_name(repo_name)
192 if not repo:
190 if not repo:
193 h.flash(_('%s repository is not mapped to db perhaps'
191 h.flash(_('%s repository is not mapped to db perhaps'
194 ' it was moved or renamed from the filesystem'
192 ' it was moved or renamed from the filesystem'
195 ' please run the application again'
193 ' please run the application again'
196 ' in order to rescan repositories') % repo_name,
194 ' in order to rescan repositories') % repo_name,
197 category='error')
195 category='error')
198
196
199 return redirect(url('repos'))
197 return redirect(url('repos'))
200 try:
198 try:
201 action_logger(self.rhodecode_user, 'admin_deleted_repo',
199 action_logger(self.rhodecode_user, 'admin_deleted_repo',
202 repo_name, '', self.sa)
200 repo_name, '', self.sa)
203 repo_model.delete(repo)
201 repo_model.delete(repo)
204 invalidate_cache('get_repo_cached_%s' % repo_name)
202 invalidate_cache('get_repo_cached_%s' % repo_name)
205 h.flash(_('deleted repository %s') % repo_name, category='success')
203 h.flash(_('deleted repository %s') % repo_name, category='success')
206
204
207 except Exception, e:
205 except Exception, e:
208 log.error(traceback.format_exc())
206 log.error(traceback.format_exc())
209 h.flash(_('An error occurred during deletion of %s') % repo_name,
207 h.flash(_('An error occurred during deletion of %s') % repo_name,
210 category='error')
208 category='error')
211
209
212 return redirect(url('repos'))
210 return redirect(url('repos'))
213
211
214 @HasPermissionAllDecorator('hg.admin')
212 @HasPermissionAllDecorator('hg.admin')
215 def delete_perm_user(self, repo_name):
213 def delete_perm_user(self, repo_name):
216 """
214 """
217 DELETE an existing repository permission user
215 DELETE an existing repository permission user
218 :param repo_name:
216 :param repo_name:
219 """
217 """
220
218
221 try:
219 try:
222 repo_model = RepoModel()
220 repo_model = RepoModel()
223 repo_model.delete_perm_user(request.POST, repo_name)
221 repo_model.delete_perm_user(request.POST, repo_name)
224 except Exception, e:
222 except Exception, e:
225 h.flash(_('An error occurred during deletion of repository user'),
223 h.flash(_('An error occurred during deletion of repository user'),
226 category='error')
224 category='error')
227 raise HTTPInternalServerError()
225 raise HTTPInternalServerError()
228
226
229 @HasPermissionAllDecorator('hg.admin')
227 @HasPermissionAllDecorator('hg.admin')
230 def repo_stats(self, repo_name):
228 def repo_stats(self, repo_name):
231 """
229 """
232 DELETE an existing repository statistics
230 DELETE an existing repository statistics
233 :param repo_name:
231 :param repo_name:
234 """
232 """
235
233
236 try:
234 try:
237 repo_model = RepoModel()
235 repo_model = RepoModel()
238 repo_model.delete_stats(repo_name)
236 repo_model.delete_stats(repo_name)
239 except Exception, e:
237 except Exception, e:
240 h.flash(_('An error occurred during deletion of repository stats'),
238 h.flash(_('An error occurred during deletion of repository stats'),
241 category='error')
239 category='error')
242 return redirect(url('edit_repo', repo_name=repo_name))
240 return redirect(url('edit_repo', repo_name=repo_name))
243
241
244 @HasPermissionAllDecorator('hg.admin')
242 @HasPermissionAllDecorator('hg.admin')
245 def repo_cache(self, repo_name):
243 def repo_cache(self, repo_name):
246 """
244 """
247 INVALIDATE existing repository cache
245 INVALIDATE existing repository cache
248 :param repo_name:
246 :param repo_name:
249 """
247 """
250
248
251 try:
249 try:
252 ScmModel().mark_for_invalidation(repo_name)
250 ScmModel().mark_for_invalidation(repo_name)
253 except Exception, e:
251 except Exception, e:
254 h.flash(_('An error occurred during cache invalidation'),
252 h.flash(_('An error occurred during cache invalidation'),
255 category='error')
253 category='error')
256 return redirect(url('edit_repo', repo_name=repo_name))
254 return redirect(url('edit_repo', repo_name=repo_name))
257
255
258 @HasPermissionAllDecorator('hg.admin')
256 @HasPermissionAllDecorator('hg.admin')
259 def show(self, repo_name, format='html'):
257 def show(self, repo_name, format='html'):
260 """GET /repos/repo_name: Show a specific item"""
258 """GET /repos/repo_name: Show a specific item"""
261 # url('repo', repo_name=ID)
259 # url('repo', repo_name=ID)
262
260
263 @HasPermissionAllDecorator('hg.admin')
261 @HasPermissionAllDecorator('hg.admin')
264 def edit(self, repo_name, format='html'):
262 def edit(self, repo_name, format='html'):
265 """GET /repos/repo_name/edit: Form to edit an existing item"""
263 """GET /repos/repo_name/edit: Form to edit an existing item"""
266 # url('edit_repo', repo_name=ID)
264 # url('edit_repo', repo_name=ID)
267 repo_model = RepoModel()
265 repo_model = RepoModel()
268 r = ScmModel().get(repo_name)
266 r = ScmModel().get(repo_name)
269 c.repo_info = repo_model.get_by_repo_name(repo_name)
267 c.repo_info = repo_model.get_by_repo_name(repo_name)
270
268
271 if c.repo_info is None:
269 if c.repo_info is None:
272 h.flash(_('%s repository is not mapped to db perhaps'
270 h.flash(_('%s repository is not mapped to db perhaps'
273 ' it was created or renamed from the filesystem'
271 ' it was created or renamed from the filesystem'
274 ' please run the application again'
272 ' please run the application again'
275 ' in order to rescan repositories') % repo_name,
273 ' in order to rescan repositories') % repo_name,
276 category='error')
274 category='error')
277
275
278 return redirect(url('repos'))
276 return redirect(url('repos'))
279
277
280 if c.repo_info.stats:
278 if c.repo_info.stats:
281 last_rev = c.repo_info.stats.stat_on_revision
279 last_rev = c.repo_info.stats.stat_on_revision
282 else:
280 else:
283 last_rev = 0
281 last_rev = 0
284 c.stats_revision = last_rev
282 c.stats_revision = last_rev
285
283
286 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
284 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
287
285
288 if last_rev == 0 or c.repo_last_rev == 0:
286 if last_rev == 0 or c.repo_last_rev == 0:
289 c.stats_percentage = 0
287 c.stats_percentage = 0
290 else:
288 else:
291 c.stats_percentage = '%.2f' % ((float((last_rev)) /
289 c.stats_percentage = '%.2f' % ((float((last_rev)) /
292 c.repo_last_rev) * 100)
290 c.repo_last_rev) * 100)
293
291
294 defaults = c.repo_info.get_dict()
292 defaults = c.repo_info.get_dict()
295 if c.repo_info.user:
293 if c.repo_info.user:
296 defaults.update({'user':c.repo_info.user.username})
294 defaults.update({'user':c.repo_info.user.username})
297 else:
295 else:
298 replacement_user = self.sa.query(User)\
296 replacement_user = self.sa.query(User)\
299 .filter(User.admin == True).first().username
297 .filter(User.admin == True).first().username
300 defaults.update({'user':replacement_user})
298 defaults.update({'user':replacement_user})
301
299
302 c.users_array = repo_model.get_users_js()
300 c.users_array = repo_model.get_users_js()
303
301
304 for p in c.repo_info.repo_to_perm:
302 for p in c.repo_info.repo_to_perm:
305 defaults.update({'perm_%s' % p.user.username:
303 defaults.update({'perm_%s' % p.user.username:
306 p.permission.permission_name})
304 p.permission.permission_name})
307
305
308 return htmlfill.render(
306 return htmlfill.render(
309 render('admin/repos/repo_edit.html'),
307 render('admin/repos/repo_edit.html'),
310 defaults=defaults,
308 defaults=defaults,
311 encoding="UTF-8",
309 encoding="UTF-8",
312 force_defaults=False
310 force_defaults=False
313 )
311 )
@@ -1,340 +1,338 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 from formencode import htmlfill
26 from formencode import htmlfill
29 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
27 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
30 config
28 config
31 from pylons.controllers.util import abort, redirect
29 from pylons.controllers.util import abort, redirect
32 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
33 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
34 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
35 HasPermissionAnyDecorator, NotAnonymous
33 HasPermissionAnyDecorator, NotAnonymous
36 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.celerylib import tasks, run_task
35 from rhodecode.lib.celerylib import tasks, run_task
38 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
36 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
39 set_rhodecode_config
37 set_rhodecode_config
40 from rhodecode.model.db import RhodeCodeUi, Repository
38 from rhodecode.model.db import RhodeCodeUi, Repository
41 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
39 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
42 ApplicationUiSettingsForm
40 ApplicationUiSettingsForm
43 from rhodecode.model.scm import ScmModel
41 from rhodecode.model.scm import ScmModel
44 from rhodecode.model.settings import SettingsModel
42 from rhodecode.model.settings import SettingsModel
45 from rhodecode.model.user import UserModel
43 from rhodecode.model.user import UserModel
46 from sqlalchemy import func
44 from sqlalchemy import func
47 import formencode
45 import formencode
48 import logging
46 import logging
49 import traceback
47 import traceback
50
48
51 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
52
50
53
51
54 class SettingsController(BaseController):
52 class SettingsController(BaseController):
55 """REST Controller styled on the Atom Publishing Protocol"""
53 """REST Controller styled on the Atom Publishing Protocol"""
56 # To properly map this controller, ensure your config/routing.py
54 # To properly map this controller, ensure your config/routing.py
57 # file has a resource setup:
55 # file has a resource setup:
58 # map.resource('setting', 'settings', controller='admin/settings',
56 # map.resource('setting', 'settings', controller='admin/settings',
59 # path_prefix='/admin', name_prefix='admin_')
57 # path_prefix='/admin', name_prefix='admin_')
60
58
61
59
62 @LoginRequired()
60 @LoginRequired()
63 def __before__(self):
61 def __before__(self):
64 c.admin_user = session.get('admin_user')
62 c.admin_user = session.get('admin_user')
65 c.admin_username = session.get('admin_username')
63 c.admin_username = session.get('admin_username')
66 super(SettingsController, self).__before__()
64 super(SettingsController, self).__before__()
67
65
68
66
69 @HasPermissionAllDecorator('hg.admin')
67 @HasPermissionAllDecorator('hg.admin')
70 def index(self, format='html'):
68 def index(self, format='html'):
71 """GET /admin/settings: All items in the collection"""
69 """GET /admin/settings: All items in the collection"""
72 # url('admin_settings')
70 # url('admin_settings')
73
71
74 defaults = SettingsModel().get_app_settings()
72 defaults = SettingsModel().get_app_settings()
75 defaults.update(self.get_hg_ui_settings())
73 defaults.update(self.get_hg_ui_settings())
76 return htmlfill.render(
74 return htmlfill.render(
77 render('admin/settings/settings.html'),
75 render('admin/settings/settings.html'),
78 defaults=defaults,
76 defaults=defaults,
79 encoding="UTF-8",
77 encoding="UTF-8",
80 force_defaults=False
78 force_defaults=False
81 )
79 )
82
80
83 @HasPermissionAllDecorator('hg.admin')
81 @HasPermissionAllDecorator('hg.admin')
84 def create(self):
82 def create(self):
85 """POST /admin/settings: Create a new item"""
83 """POST /admin/settings: Create a new item"""
86 # url('admin_settings')
84 # url('admin_settings')
87
85
88 @HasPermissionAllDecorator('hg.admin')
86 @HasPermissionAllDecorator('hg.admin')
89 def new(self, format='html'):
87 def new(self, format='html'):
90 """GET /admin/settings/new: Form to create a new item"""
88 """GET /admin/settings/new: Form to create a new item"""
91 # url('admin_new_setting')
89 # url('admin_new_setting')
92
90
93 @HasPermissionAllDecorator('hg.admin')
91 @HasPermissionAllDecorator('hg.admin')
94 def update(self, setting_id):
92 def update(self, setting_id):
95 """PUT /admin/settings/setting_id: Update an existing item"""
93 """PUT /admin/settings/setting_id: Update an existing item"""
96 # Forms posted to this method should contain a hidden field:
94 # Forms posted to this method should contain a hidden field:
97 # <input type="hidden" name="_method" value="PUT" />
95 # <input type="hidden" name="_method" value="PUT" />
98 # Or using helpers:
96 # Or using helpers:
99 # h.form(url('admin_setting', setting_id=ID),
97 # h.form(url('admin_setting', setting_id=ID),
100 # method='put')
98 # method='put')
101 # url('admin_setting', setting_id=ID)
99 # url('admin_setting', setting_id=ID)
102 if setting_id == 'mapping':
100 if setting_id == 'mapping':
103 rm_obsolete = request.POST.get('destroy', False)
101 rm_obsolete = request.POST.get('destroy', False)
104 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
102 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
105
103
106 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
104 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
107 for repo_name in initial.keys():
105 for repo_name in initial.keys():
108 invalidate_cache('get_repo_cached_%s' % repo_name)
106 invalidate_cache('get_repo_cached_%s' % repo_name)
109
107
110 repo2db_mapper(initial, rm_obsolete)
108 repo2db_mapper(initial, rm_obsolete)
111
109
112 h.flash(_('Repositories successfully rescanned'), category='success')
110 h.flash(_('Repositories successfully rescanned'), category='success')
113
111
114 if setting_id == 'whoosh':
112 if setting_id == 'whoosh':
115 repo_location = self.get_hg_ui_settings()['paths_root_path']
113 repo_location = self.get_hg_ui_settings()['paths_root_path']
116 full_index = request.POST.get('full_index', False)
114 full_index = request.POST.get('full_index', False)
117 task = run_task(tasks.whoosh_index, repo_location, full_index)
115 task = run_task(tasks.whoosh_index, repo_location, full_index)
118
116
119 h.flash(_('Whoosh reindex task scheduled'), category='success')
117 h.flash(_('Whoosh reindex task scheduled'), category='success')
120 if setting_id == 'global':
118 if setting_id == 'global':
121
119
122 application_form = ApplicationSettingsForm()()
120 application_form = ApplicationSettingsForm()()
123 try:
121 try:
124 form_result = application_form.to_python(dict(request.POST))
122 form_result = application_form.to_python(dict(request.POST))
125 settings_model = SettingsModel()
123 settings_model = SettingsModel()
126 try:
124 try:
127 hgsettings1 = settings_model.get('title')
125 hgsettings1 = settings_model.get('title')
128 hgsettings1.app_settings_value = form_result['rhodecode_title']
126 hgsettings1.app_settings_value = form_result['rhodecode_title']
129
127
130 hgsettings2 = settings_model.get('realm')
128 hgsettings2 = settings_model.get('realm')
131 hgsettings2.app_settings_value = form_result['rhodecode_realm']
129 hgsettings2.app_settings_value = form_result['rhodecode_realm']
132
130
133
131
134 self.sa.add(hgsettings1)
132 self.sa.add(hgsettings1)
135 self.sa.add(hgsettings2)
133 self.sa.add(hgsettings2)
136 self.sa.commit()
134 self.sa.commit()
137 set_rhodecode_config(config)
135 set_rhodecode_config(config)
138 h.flash(_('Updated application settings'),
136 h.flash(_('Updated application settings'),
139 category='success')
137 category='success')
140
138
141 except:
139 except:
142 log.error(traceback.format_exc())
140 log.error(traceback.format_exc())
143 h.flash(_('error occurred during updating'
141 h.flash(_('error occurred during updating'
144 ' application settings'), category='error')
142 ' application settings'), category='error')
145
143
146 self.sa.rollback()
144 self.sa.rollback()
147
145
148
146
149 except formencode.Invalid, errors:
147 except formencode.Invalid, errors:
150 return htmlfill.render(
148 return htmlfill.render(
151 render('admin/settings/settings.html'),
149 render('admin/settings/settings.html'),
152 defaults=errors.value,
150 defaults=errors.value,
153 errors=errors.error_dict or {},
151 errors=errors.error_dict or {},
154 prefix_error=False,
152 prefix_error=False,
155 encoding="UTF-8")
153 encoding="UTF-8")
156
154
157 if setting_id == 'mercurial':
155 if setting_id == 'mercurial':
158 application_form = ApplicationUiSettingsForm()()
156 application_form = ApplicationUiSettingsForm()()
159 try:
157 try:
160 form_result = application_form.to_python(dict(request.POST))
158 form_result = application_form.to_python(dict(request.POST))
161
159
162 try:
160 try:
163
161
164 hgsettings1 = self.sa.query(RhodeCodeUi)\
162 hgsettings1 = self.sa.query(RhodeCodeUi)\
165 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
163 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
166 hgsettings1.ui_value = form_result['web_push_ssl']
164 hgsettings1.ui_value = form_result['web_push_ssl']
167
165
168 hgsettings2 = self.sa.query(RhodeCodeUi)\
166 hgsettings2 = self.sa.query(RhodeCodeUi)\
169 .filter(RhodeCodeUi.ui_key == '/').one()
167 .filter(RhodeCodeUi.ui_key == '/').one()
170 hgsettings2.ui_value = form_result['paths_root_path']
168 hgsettings2.ui_value = form_result['paths_root_path']
171
169
172
170
173 #HOOKS
171 #HOOKS
174 hgsettings3 = self.sa.query(RhodeCodeUi)\
172 hgsettings3 = self.sa.query(RhodeCodeUi)\
175 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
173 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
176 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
174 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
177
175
178 hgsettings4 = self.sa.query(RhodeCodeUi)\
176 hgsettings4 = self.sa.query(RhodeCodeUi)\
179 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
177 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
180 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
178 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
181
179
182 hgsettings5 = self.sa.query(RhodeCodeUi)\
180 hgsettings5 = self.sa.query(RhodeCodeUi)\
183 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
181 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
184 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
182 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
185
183
186 hgsettings6 = self.sa.query(RhodeCodeUi)\
184 hgsettings6 = self.sa.query(RhodeCodeUi)\
187 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
185 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
188 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
186 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
189
187
190
188
191 self.sa.add(hgsettings1)
189 self.sa.add(hgsettings1)
192 self.sa.add(hgsettings2)
190 self.sa.add(hgsettings2)
193 self.sa.add(hgsettings3)
191 self.sa.add(hgsettings3)
194 self.sa.add(hgsettings4)
192 self.sa.add(hgsettings4)
195 self.sa.add(hgsettings5)
193 self.sa.add(hgsettings5)
196 self.sa.add(hgsettings6)
194 self.sa.add(hgsettings6)
197 self.sa.commit()
195 self.sa.commit()
198
196
199 h.flash(_('Updated mercurial settings'),
197 h.flash(_('Updated mercurial settings'),
200 category='success')
198 category='success')
201
199
202 except:
200 except:
203 log.error(traceback.format_exc())
201 log.error(traceback.format_exc())
204 h.flash(_('error occurred during updating application settings'),
202 h.flash(_('error occurred during updating application settings'),
205 category='error')
203 category='error')
206
204
207 self.sa.rollback()
205 self.sa.rollback()
208
206
209
207
210 except formencode.Invalid, errors:
208 except formencode.Invalid, errors:
211 return htmlfill.render(
209 return htmlfill.render(
212 render('admin/settings/settings.html'),
210 render('admin/settings/settings.html'),
213 defaults=errors.value,
211 defaults=errors.value,
214 errors=errors.error_dict or {},
212 errors=errors.error_dict or {},
215 prefix_error=False,
213 prefix_error=False,
216 encoding="UTF-8")
214 encoding="UTF-8")
217
215
218
216
219
217
220 return redirect(url('admin_settings'))
218 return redirect(url('admin_settings'))
221
219
222 @HasPermissionAllDecorator('hg.admin')
220 @HasPermissionAllDecorator('hg.admin')
223 def delete(self, setting_id):
221 def delete(self, setting_id):
224 """DELETE /admin/settings/setting_id: Delete an existing item"""
222 """DELETE /admin/settings/setting_id: Delete an existing item"""
225 # Forms posted to this method should contain a hidden field:
223 # Forms posted to this method should contain a hidden field:
226 # <input type="hidden" name="_method" value="DELETE" />
224 # <input type="hidden" name="_method" value="DELETE" />
227 # Or using helpers:
225 # Or using helpers:
228 # h.form(url('admin_setting', setting_id=ID),
226 # h.form(url('admin_setting', setting_id=ID),
229 # method='delete')
227 # method='delete')
230 # url('admin_setting', setting_id=ID)
228 # url('admin_setting', setting_id=ID)
231
229
232 @HasPermissionAllDecorator('hg.admin')
230 @HasPermissionAllDecorator('hg.admin')
233 def show(self, setting_id, format='html'):
231 def show(self, setting_id, format='html'):
234 """GET /admin/settings/setting_id: Show a specific item"""
232 """GET /admin/settings/setting_id: Show a specific item"""
235 # url('admin_setting', setting_id=ID)
233 # url('admin_setting', setting_id=ID)
236
234
237 @HasPermissionAllDecorator('hg.admin')
235 @HasPermissionAllDecorator('hg.admin')
238 def edit(self, setting_id, format='html'):
236 def edit(self, setting_id, format='html'):
239 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
237 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
240 # url('admin_edit_setting', setting_id=ID)
238 # url('admin_edit_setting', setting_id=ID)
241
239
242 @NotAnonymous()
240 @NotAnonymous()
243 def my_account(self):
241 def my_account(self):
244 """
242 """
245 GET /_admin/my_account Displays info about my account
243 GET /_admin/my_account Displays info about my account
246 """
244 """
247 # url('admin_settings_my_account')
245 # url('admin_settings_my_account')
248
246
249 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
247 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
250 all_repos = self.sa.query(Repository)\
248 all_repos = self.sa.query(Repository)\
251 .filter(Repository.user_id == c.user.user_id)\
249 .filter(Repository.user_id == c.user.user_id)\
252 .order_by(func.lower(Repository.repo_name))\
250 .order_by(func.lower(Repository.repo_name))\
253 .all()
251 .all()
254
252
255 c.user_repos = ScmModel().get_repos(all_repos)
253 c.user_repos = ScmModel().get_repos(all_repos)
256
254
257 if c.user.username == 'default':
255 if c.user.username == 'default':
258 h.flash(_("You can't edit this user since it's"
256 h.flash(_("You can't edit this user since it's"
259 " crucial for entire application"), category='warning')
257 " crucial for entire application"), category='warning')
260 return redirect(url('users'))
258 return redirect(url('users'))
261
259
262 defaults = c.user.get_dict()
260 defaults = c.user.get_dict()
263 return htmlfill.render(
261 return htmlfill.render(
264 render('admin/users/user_edit_my_account.html'),
262 render('admin/users/user_edit_my_account.html'),
265 defaults=defaults,
263 defaults=defaults,
266 encoding="UTF-8",
264 encoding="UTF-8",
267 force_defaults=False
265 force_defaults=False
268 )
266 )
269
267
270 def my_account_update(self):
268 def my_account_update(self):
271 """PUT /_admin/my_account_update: Update an existing item"""
269 """PUT /_admin/my_account_update: Update an existing item"""
272 # Forms posted to this method should contain a hidden field:
270 # Forms posted to this method should contain a hidden field:
273 # <input type="hidden" name="_method" value="PUT" />
271 # <input type="hidden" name="_method" value="PUT" />
274 # Or using helpers:
272 # Or using helpers:
275 # h.form(url('admin_settings_my_account_update'),
273 # h.form(url('admin_settings_my_account_update'),
276 # method='put')
274 # method='put')
277 # url('admin_settings_my_account_update', id=ID)
275 # url('admin_settings_my_account_update', id=ID)
278 user_model = UserModel()
276 user_model = UserModel()
279 uid = c.rhodecode_user.user_id
277 uid = c.rhodecode_user.user_id
280 _form = UserForm(edit=True, old_data={'user_id':uid,
278 _form = UserForm(edit=True, old_data={'user_id':uid,
281 'email':c.rhodecode_user.email})()
279 'email':c.rhodecode_user.email})()
282 form_result = {}
280 form_result = {}
283 try:
281 try:
284 form_result = _form.to_python(dict(request.POST))
282 form_result = _form.to_python(dict(request.POST))
285 user_model.update_my_account(uid, form_result)
283 user_model.update_my_account(uid, form_result)
286 h.flash(_('Your account was updated successfully'),
284 h.flash(_('Your account was updated successfully'),
287 category='success')
285 category='success')
288
286
289 except formencode.Invalid, errors:
287 except formencode.Invalid, errors:
290 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
288 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
291 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
289 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
292 all_repos = self.sa.query(Repository)\
290 all_repos = self.sa.query(Repository)\
293 .filter(Repository.user_id == c.user.user_id)\
291 .filter(Repository.user_id == c.user.user_id)\
294 .order_by(func.lower(Repository.repo_name))\
292 .order_by(func.lower(Repository.repo_name))\
295 .all()
293 .all()
296 c.user_repos = ScmModel().get_repos(all_repos)
294 c.user_repos = ScmModel().get_repos(all_repos)
297
295
298 return htmlfill.render(
296 return htmlfill.render(
299 render('admin/users/user_edit_my_account.html'),
297 render('admin/users/user_edit_my_account.html'),
300 defaults=errors.value,
298 defaults=errors.value,
301 errors=errors.error_dict or {},
299 errors=errors.error_dict or {},
302 prefix_error=False,
300 prefix_error=False,
303 encoding="UTF-8")
301 encoding="UTF-8")
304 except Exception:
302 except Exception:
305 log.error(traceback.format_exc())
303 log.error(traceback.format_exc())
306 h.flash(_('error occurred during update of user %s') \
304 h.flash(_('error occurred during update of user %s') \
307 % form_result.get('username'), category='error')
305 % form_result.get('username'), category='error')
308
306
309 return redirect(url('my_account'))
307 return redirect(url('my_account'))
310
308
311 @NotAnonymous()
309 @NotAnonymous()
312 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
310 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
313 def create_repository(self):
311 def create_repository(self):
314 """GET /_admin/create_repository: Form to create a new item"""
312 """GET /_admin/create_repository: Form to create a new item"""
315 new_repo = request.GET.get('repo', '')
313 new_repo = request.GET.get('repo', '')
316 c.new_repo = h.repo_name_slug(new_repo)
314 c.new_repo = h.repo_name_slug(new_repo)
317
315
318 return render('admin/repos/repo_add_create_repository.html')
316 return render('admin/repos/repo_add_create_repository.html')
319
317
320 def get_hg_ui_settings(self):
318 def get_hg_ui_settings(self):
321 ret = self.sa.query(RhodeCodeUi).all()
319 ret = self.sa.query(RhodeCodeUi).all()
322
320
323 if not ret:
321 if not ret:
324 raise Exception('Could not get application ui settings !')
322 raise Exception('Could not get application ui settings !')
325 settings = {}
323 settings = {}
326 for each in ret:
324 for each in ret:
327 k = each.ui_key
325 k = each.ui_key
328 v = each.ui_value
326 v = each.ui_value
329 if k == '/':
327 if k == '/':
330 k = 'root_path'
328 k = 'root_path'
331
329
332 if k.find('.') != -1:
330 if k.find('.') != -1:
333 k = k.replace('.', '_')
331 k = k.replace('.', '_')
334
332
335 if each.ui_section == 'hooks':
333 if each.ui_section == 'hooks':
336 v = each.ui_active
334 v = each.ui_active
337
335
338 settings[each.ui_section + '_' + k] = v
336 settings[each.ui_section + '_' + k] = v
339
337
340 return settings
338 return settings
@@ -1,172 +1,170 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users
3 rhodecode.controllers.admin.users
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Users crud controller for pylons
6 Users crud controller for pylons
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29 import traceback
27 import traceback
30 import formencode
28 import formencode
31
29
32 from formencode import htmlfill
30 from formencode import htmlfill
33 from pylons import request, session, tmpl_context as c, url
31 from pylons import request, session, tmpl_context as c, url
34 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
36
34
37 from rhodecode.lib.exceptions import *
35 from rhodecode.lib.exceptions import *
38 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
40 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib.base import BaseController, render
41
39
42 from rhodecode.model.db import User
40 from rhodecode.model.db import User
43 from rhodecode.model.forms import UserForm
41 from rhodecode.model.forms import UserForm
44 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
45
43
46 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
47
45
48 class UsersController(BaseController):
46 class UsersController(BaseController):
49 """REST Controller styled on the Atom Publishing Protocol"""
47 """REST Controller styled on the Atom Publishing Protocol"""
50 # To properly map this controller, ensure your config/routing.py
48 # To properly map this controller, ensure your config/routing.py
51 # file has a resource setup:
49 # file has a resource setup:
52 # map.resource('user', 'users')
50 # map.resource('user', 'users')
53
51
54 @LoginRequired()
52 @LoginRequired()
55 @HasPermissionAllDecorator('hg.admin')
53 @HasPermissionAllDecorator('hg.admin')
56 def __before__(self):
54 def __before__(self):
57 c.admin_user = session.get('admin_user')
55 c.admin_user = session.get('admin_user')
58 c.admin_username = session.get('admin_username')
56 c.admin_username = session.get('admin_username')
59 super(UsersController, self).__before__()
57 super(UsersController, self).__before__()
60
58
61
59
62 def index(self, format='html'):
60 def index(self, format='html'):
63 """GET /users: All items in the collection"""
61 """GET /users: All items in the collection"""
64 # url('users')
62 # url('users')
65
63
66 c.users_list = self.sa.query(User).all()
64 c.users_list = self.sa.query(User).all()
67 return render('admin/users/users.html')
65 return render('admin/users/users.html')
68
66
69 def create(self):
67 def create(self):
70 """POST /users: Create a new item"""
68 """POST /users: Create a new item"""
71 # url('users')
69 # url('users')
72
70
73 user_model = UserModel()
71 user_model = UserModel()
74 login_form = UserForm()()
72 login_form = UserForm()()
75 try:
73 try:
76 form_result = login_form.to_python(dict(request.POST))
74 form_result = login_form.to_python(dict(request.POST))
77 user_model.create(form_result)
75 user_model.create(form_result)
78 h.flash(_('created user %s') % form_result['username'],
76 h.flash(_('created user %s') % form_result['username'],
79 category='success')
77 category='success')
80 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
78 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
81 except formencode.Invalid, errors:
79 except formencode.Invalid, errors:
82 return htmlfill.render(
80 return htmlfill.render(
83 render('admin/users/user_add.html'),
81 render('admin/users/user_add.html'),
84 defaults=errors.value,
82 defaults=errors.value,
85 errors=errors.error_dict or {},
83 errors=errors.error_dict or {},
86 prefix_error=False,
84 prefix_error=False,
87 encoding="UTF-8")
85 encoding="UTF-8")
88 except Exception:
86 except Exception:
89 log.error(traceback.format_exc())
87 log.error(traceback.format_exc())
90 h.flash(_('error occurred during creation of user %s') \
88 h.flash(_('error occurred during creation of user %s') \
91 % request.POST.get('username'), category='error')
89 % request.POST.get('username'), category='error')
92 return redirect(url('users'))
90 return redirect(url('users'))
93
91
94 def new(self, format='html'):
92 def new(self, format='html'):
95 """GET /users/new: Form to create a new item"""
93 """GET /users/new: Form to create a new item"""
96 # url('new_user')
94 # url('new_user')
97 return render('admin/users/user_add.html')
95 return render('admin/users/user_add.html')
98
96
99 def update(self, id):
97 def update(self, id):
100 """PUT /users/id: Update an existing item"""
98 """PUT /users/id: Update an existing item"""
101 # Forms posted to this method should contain a hidden field:
99 # Forms posted to this method should contain a hidden field:
102 # <input type="hidden" name="_method" value="PUT" />
100 # <input type="hidden" name="_method" value="PUT" />
103 # Or using helpers:
101 # Or using helpers:
104 # h.form(url('user', id=ID),
102 # h.form(url('user', id=ID),
105 # method='put')
103 # method='put')
106 # url('user', id=ID)
104 # url('user', id=ID)
107 user_model = UserModel()
105 user_model = UserModel()
108 c.user = user_model.get(id)
106 c.user = user_model.get(id)
109
107
110 _form = UserForm(edit=True, old_data={'user_id':id,
108 _form = UserForm(edit=True, old_data={'user_id':id,
111 'email':c.user.email})()
109 'email':c.user.email})()
112 form_result = {}
110 form_result = {}
113 try:
111 try:
114 form_result = _form.to_python(dict(request.POST))
112 form_result = _form.to_python(dict(request.POST))
115 user_model.update(id, form_result)
113 user_model.update(id, form_result)
116 h.flash(_('User updated succesfully'), category='success')
114 h.flash(_('User updated succesfully'), category='success')
117
115
118 except formencode.Invalid, errors:
116 except formencode.Invalid, errors:
119 return htmlfill.render(
117 return htmlfill.render(
120 render('admin/users/user_edit.html'),
118 render('admin/users/user_edit.html'),
121 defaults=errors.value,
119 defaults=errors.value,
122 errors=errors.error_dict or {},
120 errors=errors.error_dict or {},
123 prefix_error=False,
121 prefix_error=False,
124 encoding="UTF-8")
122 encoding="UTF-8")
125 except Exception:
123 except Exception:
126 log.error(traceback.format_exc())
124 log.error(traceback.format_exc())
127 h.flash(_('error occurred during update of user %s') \
125 h.flash(_('error occurred during update of user %s') \
128 % form_result.get('username'), category='error')
126 % form_result.get('username'), category='error')
129
127
130 return redirect(url('users'))
128 return redirect(url('users'))
131
129
132 def delete(self, id):
130 def delete(self, id):
133 """DELETE /users/id: Delete an existing item"""
131 """DELETE /users/id: Delete an existing item"""
134 # Forms posted to this method should contain a hidden field:
132 # Forms posted to this method should contain a hidden field:
135 # <input type="hidden" name="_method" value="DELETE" />
133 # <input type="hidden" name="_method" value="DELETE" />
136 # Or using helpers:
134 # Or using helpers:
137 # h.form(url('user', id=ID),
135 # h.form(url('user', id=ID),
138 # method='delete')
136 # method='delete')
139 # url('user', id=ID)
137 # url('user', id=ID)
140 user_model = UserModel()
138 user_model = UserModel()
141 try:
139 try:
142 user_model.delete(id)
140 user_model.delete(id)
143 h.flash(_('sucessfully deleted user'), category='success')
141 h.flash(_('sucessfully deleted user'), category='success')
144 except (UserOwnsReposException, DefaultUserException), e:
142 except (UserOwnsReposException, DefaultUserException), e:
145 h.flash(str(e), category='warning')
143 h.flash(str(e), category='warning')
146 except Exception:
144 except Exception:
147 h.flash(_('An error occurred during deletion of user'),
145 h.flash(_('An error occurred during deletion of user'),
148 category='error')
146 category='error')
149 return redirect(url('users'))
147 return redirect(url('users'))
150
148
151 def show(self, id, format='html'):
149 def show(self, id, format='html'):
152 """GET /users/id: Show a specific item"""
150 """GET /users/id: Show a specific item"""
153 # url('user', id=ID)
151 # url('user', id=ID)
154
152
155
153
156 def edit(self, id, format='html'):
154 def edit(self, id, format='html'):
157 """GET /users/id/edit: Form to edit an existing item"""
155 """GET /users/id/edit: Form to edit an existing item"""
158 # url('edit_user', id=ID)
156 # url('edit_user', id=ID)
159 c.user = self.sa.query(User).get(id)
157 c.user = self.sa.query(User).get(id)
160 if not c.user:
158 if not c.user:
161 return redirect(url('users'))
159 return redirect(url('users'))
162 if c.user.username == 'default':
160 if c.user.username == 'default':
163 h.flash(_("You can't edit this user"), category='warning')
161 h.flash(_("You can't edit this user"), category='warning')
164 return redirect(url('users'))
162 return redirect(url('users'))
165
163
166 defaults = c.user.get_dict()
164 defaults = c.user.get_dict()
167 return htmlfill.render(
165 return htmlfill.render(
168 render('admin/users/user_edit.html'),
166 render('admin/users/user_edit.html'),
169 defaults=defaults,
167 defaults=defaults,
170 encoding="UTF-8",
168 encoding="UTF-8",
171 force_defaults=False
169 force_defaults=False
172 )
170 )
@@ -1,54 +1,52 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.branches
3 rhodecode.controllers.branches
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 branches controller for rhodecode
6 branches controller for rhodecode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29
27
30 from pylons import tmpl_context as c
28 from pylons import tmpl_context as c
31
29
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
30 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 from rhodecode.lib.base import BaseController, render
31 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.utils import OrderedDict
32 from rhodecode.lib.utils import OrderedDict
35 from rhodecode.model.scm import ScmModel
33 from rhodecode.model.scm import ScmModel
36
34
37 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
38
36
39 class BranchesController(BaseController):
37 class BranchesController(BaseController):
40
38
41 @LoginRequired()
39 @LoginRequired()
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
40 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 'repository.admin')
41 'repository.admin')
44 def __before__(self):
42 def __before__(self):
45 super(BranchesController, self).__before__()
43 super(BranchesController, self).__before__()
46
44
47 def index(self):
45 def index(self):
48 hg_model = ScmModel()
46 hg_model = ScmModel()
49 c.repo_info = hg_model.get_repo(c.repo_name)
47 c.repo_info = hg_model.get_repo(c.repo_name)
50 c.repo_branches = OrderedDict()
48 c.repo_branches = OrderedDict()
51 for name, hash_ in c.repo_info.branches.items():
49 for name, hash_ in c.repo_info.branches.items():
52 c.repo_branches[name] = c.repo_info.get_changeset(hash_)
50 c.repo_branches[name] = c.repo_info.get_changeset(hash_)
53
51
54 return render('branches/branches.html')
52 return render('branches/branches.html')
@@ -1,106 +1,104 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changelog
3 rhodecode.controllers.changelog
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 changelog controller for rhodecode
6 changelog controller for rhodecode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29
27
30 try:
28 try:
31 import json
29 import json
32 except ImportError:
30 except ImportError:
33 #python 2.5 compatibility
31 #python 2.5 compatibility
34 import simplejson as json
32 import simplejson as json
35
33
36 from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev
34 from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev
37 from pylons import request, session, tmpl_context as c
35 from pylons import request, session, tmpl_context as c
38
36
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
40 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib.base import BaseController, render
41 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.scm import ScmModel
42
40
43 from webhelpers.paginate import Page
41 from webhelpers.paginate import Page
44
42
45 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
46
44
47 class ChangelogController(BaseController):
45 class ChangelogController(BaseController):
48
46
49 @LoginRequired()
47 @LoginRequired()
50 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
48 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
51 'repository.admin')
49 'repository.admin')
52 def __before__(self):
50 def __before__(self):
53 super(ChangelogController, self).__before__()
51 super(ChangelogController, self).__before__()
54
52
55 def index(self):
53 def index(self):
56 limit = 100
54 limit = 100
57 default = 20
55 default = 20
58 if request.params.get('size'):
56 if request.params.get('size'):
59 try:
57 try:
60 int_size = int(request.params.get('size'))
58 int_size = int(request.params.get('size'))
61 except ValueError:
59 except ValueError:
62 int_size = default
60 int_size = default
63 int_size = int_size if int_size <= limit else limit
61 int_size = int_size if int_size <= limit else limit
64 c.size = int_size
62 c.size = int_size
65 session['changelog_size'] = c.size
63 session['changelog_size'] = c.size
66 session.save()
64 session.save()
67 else:
65 else:
68 c.size = int(session.get('changelog_size', default))
66 c.size = int(session.get('changelog_size', default))
69
67
70 changesets = ScmModel().get_repo(c.repo_name)
68 changesets = ScmModel().get_repo(c.repo_name)
71
69
72 p = int(request.params.get('page', 1))
70 p = int(request.params.get('page', 1))
73 c.total_cs = len(changesets)
71 c.total_cs = len(changesets)
74 c.pagination = Page(changesets, page=p, item_count=c.total_cs,
72 c.pagination = Page(changesets, page=p, item_count=c.total_cs,
75 items_per_page=c.size)
73 items_per_page=c.size)
76
74
77 self._graph(changesets, c.size, p)
75 self._graph(changesets, c.size, p)
78
76
79 return render('changelog/changelog.html')
77 return render('changelog/changelog.html')
80
78
81
79
82 def _graph(self, repo, size, p):
80 def _graph(self, repo, size, p):
83 revcount = size
81 revcount = size
84 if not repo.revisions or repo.alias == 'git':
82 if not repo.revisions or repo.alias == 'git':
85 c.jsdata = json.dumps([])
83 c.jsdata = json.dumps([])
86 return
84 return
87
85
88 max_rev = repo.revisions[-1]
86 max_rev = repo.revisions[-1]
89
87
90 offset = 1 if p == 1 else ((p - 1) * revcount + 1)
88 offset = 1 if p == 1 else ((p - 1) * revcount + 1)
91
89
92 rev_start = repo.revisions[(-1 * offset)]
90 rev_start = repo.revisions[(-1 * offset)]
93
91
94 revcount = min(max_rev, revcount)
92 revcount = min(max_rev, revcount)
95 rev_end = max(0, rev_start - revcount)
93 rev_end = max(0, rev_start - revcount)
96 dag = graph_rev(repo.repo, rev_start, rev_end)
94 dag = graph_rev(repo.repo, rev_start, rev_end)
97
95
98 c.dag = tree = list(colored(dag))
96 c.dag = tree = list(colored(dag))
99 data = []
97 data = []
100 for (id, type, ctx, vtx, edges) in tree:
98 for (id, type, ctx, vtx, edges) in tree:
101 if type != CHANGESET:
99 if type != CHANGESET:
102 continue
100 continue
103 data.append(('', vtx, edges))
101 data.append(('', vtx, edges))
104
102
105 c.jsdata = json.dumps(data)
103 c.jsdata = json.dumps(data)
106
104
@@ -1,192 +1,190 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changeset
3 rhodecode.controllers.changeset
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 changeset controller for pylons
6 changeset controller for pylons
7
7
8 :created_on: Apr 25, 2010
8 :created_on: Apr 25, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27 import logging
25 import logging
28 import traceback
26 import traceback
29
27
30 from pylons import tmpl_context as c, url, request, response
28 from pylons import tmpl_context as c, url, request, response
31 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
32 from pylons.controllers.util import redirect
30 from pylons.controllers.util import redirect
33
31
34 import rhodecode.lib.helpers as h
32 import rhodecode.lib.helpers as h
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.utils import EmptyChangeset
35 from rhodecode.lib.utils import EmptyChangeset
38 from rhodecode.model.scm import ScmModel
36 from rhodecode.model.scm import ScmModel
39
37
40 from vcs.exceptions import RepositoryError, ChangesetError
38 from vcs.exceptions import RepositoryError, ChangesetError
41 from vcs.nodes import FileNode
39 from vcs.nodes import FileNode
42 from vcs.utils import diffs as differ
40 from vcs.utils import diffs as differ
43
41
44 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
45
43
46 class ChangesetController(BaseController):
44 class ChangesetController(BaseController):
47
45
48 @LoginRequired()
46 @LoginRequired()
49 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
50 'repository.admin')
48 'repository.admin')
51 def __before__(self):
49 def __before__(self):
52 super(ChangesetController, self).__before__()
50 super(ChangesetController, self).__before__()
53
51
54 def index(self, revision):
52 def index(self, revision):
55 hg_model = ScmModel()
53 hg_model = ScmModel()
56
54
57 def wrap_to_table(str):
55 def wrap_to_table(str):
58
56
59 return '''<table class="code-difftable">
57 return '''<table class="code-difftable">
60 <tr class="line">
58 <tr class="line">
61 <td class="lineno new"></td>
59 <td class="lineno new"></td>
62 <td class="code"><pre>%s</pre></td>
60 <td class="code"><pre>%s</pre></td>
63 </tr>
61 </tr>
64 </table>''' % str
62 </table>''' % str
65
63
66 try:
64 try:
67 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
65 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
68 except RepositoryError, e:
66 except RepositoryError, e:
69 log.error(traceback.format_exc())
67 log.error(traceback.format_exc())
70 h.flash(str(e), category='warning')
68 h.flash(str(e), category='warning')
71 return redirect(url('home'))
69 return redirect(url('home'))
72 else:
70 else:
73 try:
71 try:
74 c.changeset_old = c.changeset.parents[0]
72 c.changeset_old = c.changeset.parents[0]
75 except IndexError:
73 except IndexError:
76 c.changeset_old = None
74 c.changeset_old = None
77 c.changes = []
75 c.changes = []
78
76
79 #===================================================================
77 #===================================================================
80 # ADDED FILES
78 # ADDED FILES
81 #===================================================================
79 #===================================================================
82 c.sum_added = 0
80 c.sum_added = 0
83 for node in c.changeset.added:
81 for node in c.changeset.added:
84
82
85 filenode_old = FileNode(node.path, '', EmptyChangeset())
83 filenode_old = FileNode(node.path, '', EmptyChangeset())
86 if filenode_old.is_binary or node.is_binary:
84 if filenode_old.is_binary or node.is_binary:
87 diff = wrap_to_table(_('binary file'))
85 diff = wrap_to_table(_('binary file'))
88 else:
86 else:
89 c.sum_added += node.size
87 c.sum_added += node.size
90 if c.sum_added < self.cut_off_limit:
88 if c.sum_added < self.cut_off_limit:
91 f_udiff = differ.get_udiff(filenode_old, node)
89 f_udiff = differ.get_udiff(filenode_old, node)
92 diff = differ.DiffProcessor(f_udiff).as_html()
90 diff = differ.DiffProcessor(f_udiff).as_html()
93
91
94 else:
92 else:
95 diff = wrap_to_table(_('Changeset is to big and was cut'
93 diff = wrap_to_table(_('Changeset is to big and was cut'
96 ' off, see raw changeset instead'))
94 ' off, see raw changeset instead'))
97
95
98 cs1 = None
96 cs1 = None
99 cs2 = node.last_changeset.raw_id
97 cs2 = node.last_changeset.raw_id
100 c.changes.append(('added', node, diff, cs1, cs2))
98 c.changes.append(('added', node, diff, cs1, cs2))
101
99
102 #===================================================================
100 #===================================================================
103 # CHANGED FILES
101 # CHANGED FILES
104 #===================================================================
102 #===================================================================
105 c.sum_removed = 0
103 c.sum_removed = 0
106 for node in c.changeset.changed:
104 for node in c.changeset.changed:
107 try:
105 try:
108 filenode_old = c.changeset_old.get_node(node.path)
106 filenode_old = c.changeset_old.get_node(node.path)
109 except ChangesetError:
107 except ChangesetError:
110 filenode_old = FileNode(node.path, '', EmptyChangeset())
108 filenode_old = FileNode(node.path, '', EmptyChangeset())
111
109
112 if filenode_old.is_binary or node.is_binary:
110 if filenode_old.is_binary or node.is_binary:
113 diff = wrap_to_table(_('binary file'))
111 diff = wrap_to_table(_('binary file'))
114 else:
112 else:
115
113
116 if c.sum_removed < self.cut_off_limit:
114 if c.sum_removed < self.cut_off_limit:
117 f_udiff = differ.get_udiff(filenode_old, node)
115 f_udiff = differ.get_udiff(filenode_old, node)
118 diff = differ.DiffProcessor(f_udiff).as_html()
116 diff = differ.DiffProcessor(f_udiff).as_html()
119 if diff:
117 if diff:
120 c.sum_removed += len(diff)
118 c.sum_removed += len(diff)
121 else:
119 else:
122 diff = wrap_to_table(_('Changeset is to big and was cut'
120 diff = wrap_to_table(_('Changeset is to big and was cut'
123 ' off, see raw changeset instead'))
121 ' off, see raw changeset instead'))
124
122
125
123
126 cs1 = filenode_old.last_changeset.raw_id
124 cs1 = filenode_old.last_changeset.raw_id
127 cs2 = node.last_changeset.raw_id
125 cs2 = node.last_changeset.raw_id
128 c.changes.append(('changed', node, diff, cs1, cs2))
126 c.changes.append(('changed', node, diff, cs1, cs2))
129
127
130 #===================================================================
128 #===================================================================
131 # REMOVED FILES
129 # REMOVED FILES
132 #===================================================================
130 #===================================================================
133 for node in c.changeset.removed:
131 for node in c.changeset.removed:
134 c.changes.append(('removed', node, None, None, None))
132 c.changes.append(('removed', node, None, None, None))
135
133
136 return render('changeset/changeset.html')
134 return render('changeset/changeset.html')
137
135
138 def raw_changeset(self, revision):
136 def raw_changeset(self, revision):
139
137
140 hg_model = ScmModel()
138 hg_model = ScmModel()
141 method = request.GET.get('diff', 'show')
139 method = request.GET.get('diff', 'show')
142 try:
140 try:
143 r = hg_model.get_repo(c.repo_name)
141 r = hg_model.get_repo(c.repo_name)
144 c.scm_type = r.alias
142 c.scm_type = r.alias
145 c.changeset = r.get_changeset(revision)
143 c.changeset = r.get_changeset(revision)
146 except RepositoryError:
144 except RepositoryError:
147 log.error(traceback.format_exc())
145 log.error(traceback.format_exc())
148 return redirect(url('home'))
146 return redirect(url('home'))
149 else:
147 else:
150 try:
148 try:
151 c.changeset_old = c.changeset.parents[0]
149 c.changeset_old = c.changeset.parents[0]
152 except IndexError:
150 except IndexError:
153 c.changeset_old = None
151 c.changeset_old = None
154 c.changes = []
152 c.changes = []
155
153
156 for node in c.changeset.added:
154 for node in c.changeset.added:
157 filenode_old = FileNode(node.path, '')
155 filenode_old = FileNode(node.path, '')
158 if filenode_old.is_binary or node.is_binary:
156 if filenode_old.is_binary or node.is_binary:
159 diff = _('binary file') + '\n'
157 diff = _('binary file') + '\n'
160 else:
158 else:
161 f_udiff = differ.get_udiff(filenode_old, node)
159 f_udiff = differ.get_udiff(filenode_old, node)
162 diff = differ.DiffProcessor(f_udiff).raw_diff()
160 diff = differ.DiffProcessor(f_udiff).raw_diff()
163
161
164 cs1 = None
162 cs1 = None
165 cs2 = node.last_changeset.raw_id
163 cs2 = node.last_changeset.raw_id
166 c.changes.append(('added', node, diff, cs1, cs2))
164 c.changes.append(('added', node, diff, cs1, cs2))
167
165
168 for node in c.changeset.changed:
166 for node in c.changeset.changed:
169 filenode_old = c.changeset_old.get_node(node.path)
167 filenode_old = c.changeset_old.get_node(node.path)
170 if filenode_old.is_binary or node.is_binary:
168 if filenode_old.is_binary or node.is_binary:
171 diff = _('binary file')
169 diff = _('binary file')
172 else:
170 else:
173 f_udiff = differ.get_udiff(filenode_old, node)
171 f_udiff = differ.get_udiff(filenode_old, node)
174 diff = differ.DiffProcessor(f_udiff).raw_diff()
172 diff = differ.DiffProcessor(f_udiff).raw_diff()
175
173
176 cs1 = filenode_old.last_changeset.raw_id
174 cs1 = filenode_old.last_changeset.raw_id
177 cs2 = node.last_changeset.raw_id
175 cs2 = node.last_changeset.raw_id
178 c.changes.append(('changed', node, diff, cs1, cs2))
176 c.changes.append(('changed', node, diff, cs1, cs2))
179
177
180 response.content_type = 'text/plain'
178 response.content_type = 'text/plain'
181
179
182 if method == 'download':
180 if method == 'download':
183 response.content_disposition = 'attachment; filename=%s.patch' % revision
181 response.content_disposition = 'attachment; filename=%s.patch' % revision
184
182
185 parent = True if len(c.changeset.parents) > 0 else False
183 parent = True if len(c.changeset.parents) > 0 else False
186 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0].raw_id if parent else ''
184 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0].raw_id if parent else ''
187
185
188 c.diffs = ''
186 c.diffs = ''
189 for x in c.changes:
187 for x in c.changes:
190 c.diffs += x[2]
188 c.diffs += x[2]
191
189
192 return render('changeset/raw_changeset.html')
190 return render('changeset/raw_changeset.html')
@@ -1,110 +1,108 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.rhodecode.controllers.error
3 package.rhodecode.controllers.error
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~
5
5
6 RhodeCode error controller
6 RhodeCode error controller
7
7
8 :created_on: Dec 8, 2010
8 :created_on: Dec 8, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27 import os
25 import os
28 import cgi
26 import cgi
29 import logging
27 import logging
30 import paste.fileapp
28 import paste.fileapp
31
29
32 from pylons import tmpl_context as c, request, config
30 from pylons import tmpl_context as c, request, config
33 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
34 from pylons.middleware import media_path
32 from pylons.middleware import media_path
35
33
36 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
37
35
38 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
39
37
40 class ErrorController(BaseController):
38 class ErrorController(BaseController):
41 """Generates error documents as and when they are required.
39 """Generates error documents as and when they are required.
42
40
43 The ErrorDocuments middleware forwards to ErrorController when error
41 The ErrorDocuments middleware forwards to ErrorController when error
44 related status codes are returned from the application.
42 related status codes are returned from the application.
45
43
46 This behavior can be altered by changing the parameters to the
44 This behavior can be altered by changing the parameters to the
47 ErrorDocuments middleware in your config/middleware.py file.
45 ErrorDocuments middleware in your config/middleware.py file.
48 """
46 """
49
47
50 def __before__(self):
48 def __before__(self):
51 c.rhodecode_name = config.get('rhodecode_title')
49 c.rhodecode_name = config.get('rhodecode_title')
52
50
53 def document(self):
51 def document(self):
54 resp = request.environ.get('pylons.original_response')
52 resp = request.environ.get('pylons.original_response')
55
53
56 log.debug('### %s ###', resp.status)
54 log.debug('### %s ###', resp.status)
57
55
58 e = request.environ
56 e = request.environ
59 c.serv_p = r'%(protocol)s://%(host)s/' % {
57 c.serv_p = r'%(protocol)s://%(host)s/' % {
60 'protocol': e.get('wsgi.url_scheme'),
58 'protocol': e.get('wsgi.url_scheme'),
61 'host':e.get('HTTP_HOST'),
59 'host':e.get('HTTP_HOST'),
62 }
60 }
63
61
64
62
65 c.error_message = cgi.escape(request.GET.get('code', str(resp.status)))
63 c.error_message = cgi.escape(request.GET.get('code', str(resp.status)))
66 c.error_explanation = self.get_error_explanation(resp.status_int)
64 c.error_explanation = self.get_error_explanation(resp.status_int)
67
65
68 #redirect to when error with given seconds
66 #redirect to when error with given seconds
69 c.redirect_time = 0
67 c.redirect_time = 0
70 c.redirect_module = _('Home page')# name to what your going to be redirected
68 c.redirect_module = _('Home page')# name to what your going to be redirected
71 c.url_redirect = "/"
69 c.url_redirect = "/"
72
70
73 return render('/errors/error_document.html')
71 return render('/errors/error_document.html')
74
72
75
73
76 def img(self, id):
74 def img(self, id):
77 """Serve Pylons' stock images"""
75 """Serve Pylons' stock images"""
78 return self._serve_file(os.path.join(media_path, 'img', id))
76 return self._serve_file(os.path.join(media_path, 'img', id))
79
77
80 def style(self, id):
78 def style(self, id):
81 """Serve Pylons' stock stylesheets"""
79 """Serve Pylons' stock stylesheets"""
82 return self._serve_file(os.path.join(media_path, 'style', id))
80 return self._serve_file(os.path.join(media_path, 'style', id))
83
81
84 def _serve_file(self, path):
82 def _serve_file(self, path):
85 """Call Paste's FileApp (a WSGI application) to serve the file
83 """Call Paste's FileApp (a WSGI application) to serve the file
86 at the specified path
84 at the specified path
87 """
85 """
88 fapp = paste.fileapp.FileApp(path)
86 fapp = paste.fileapp.FileApp(path)
89 return fapp(request.environ, self.start_response)
87 return fapp(request.environ, self.start_response)
90
88
91 def get_error_explanation(self, code):
89 def get_error_explanation(self, code):
92 ''' get the error explanations of int codes
90 ''' get the error explanations of int codes
93 [400, 401, 403, 404, 500]'''
91 [400, 401, 403, 404, 500]'''
94 try:
92 try:
95 code = int(code)
93 code = int(code)
96 except:
94 except:
97 code = 500
95 code = 500
98
96
99 if code == 400:
97 if code == 400:
100 return _('The request could not be understood by the server due to malformed syntax.')
98 return _('The request could not be understood by the server due to malformed syntax.')
101 if code == 401:
99 if code == 401:
102 return _('Unauthorized access to resource')
100 return _('Unauthorized access to resource')
103 if code == 403:
101 if code == 403:
104 return _("You don't have permission to view this page")
102 return _("You don't have permission to view this page")
105 if code == 404:
103 if code == 404:
106 return _('The resource could not be found')
104 return _('The resource could not be found')
107 if code == 500:
105 if code == 500:
108 return _('The server encountered an unexpected condition which prevented it from fulfilling the request.')
106 return _('The server encountered an unexpected condition which prevented it from fulfilling the request.')
109
107
110
108
@@ -1,90 +1,88 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.feed
3 rhodecode.controllers.feed
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Feed controller for rhodecode
6 Feed controller for rhodecode
7
7
8 :created_on: Apr 23, 2010
8 :created_on: Apr 23, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29
27
30 from pylons import url, response
28 from pylons import url, response
31
29
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
30 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 from rhodecode.lib.base import BaseController
31 from rhodecode.lib.base import BaseController
34 from rhodecode.model.scm import ScmModel
32 from rhodecode.model.scm import ScmModel
35
33
36 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
34 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
37
35
38 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
39
37
40 class FeedController(BaseController):
38 class FeedController(BaseController):
41
39
42 @LoginRequired()
40 @LoginRequired()
43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 'repository.admin')
42 'repository.admin')
45 def __before__(self):
43 def __before__(self):
46 super(FeedController, self).__before__()
44 super(FeedController, self).__before__()
47 #common values for feeds
45 #common values for feeds
48 self.description = 'Changes on %s repository'
46 self.description = 'Changes on %s repository'
49 self.title = "%s feed"
47 self.title = "%s feed"
50 self.language = 'en-us'
48 self.language = 'en-us'
51 self.ttl = "5"
49 self.ttl = "5"
52 self.feed_nr = 10
50 self.feed_nr = 10
53
51
54 def atom(self, repo_name):
52 def atom(self, repo_name):
55 """Produce an atom-1.0 feed via feedgenerator module"""
53 """Produce an atom-1.0 feed via feedgenerator module"""
56 feed = Atom1Feed(title=self.title % repo_name,
54 feed = Atom1Feed(title=self.title % repo_name,
57 link=url('summary_home', repo_name=repo_name, qualified=True),
55 link=url('summary_home', repo_name=repo_name, qualified=True),
58 description=self.description % repo_name,
56 description=self.description % repo_name,
59 language=self.language,
57 language=self.language,
60 ttl=self.ttl)
58 ttl=self.ttl)
61
59
62 changesets = ScmModel().get_repo(repo_name)
60 changesets = ScmModel().get_repo(repo_name)
63
61
64 for cs in changesets[:self.feed_nr]:
62 for cs in changesets[:self.feed_nr]:
65 feed.add_item(title=cs.message,
63 feed.add_item(title=cs.message,
66 link=url('changeset_home', repo_name=repo_name,
64 link=url('changeset_home', repo_name=repo_name,
67 revision=cs.raw_id, qualified=True),
65 revision=cs.raw_id, qualified=True),
68 description=str(cs.date))
66 description=str(cs.date))
69
67
70 response.content_type = feed.mime_type
68 response.content_type = feed.mime_type
71 return feed.writeString('utf-8')
69 return feed.writeString('utf-8')
72
70
73
71
74 def rss(self, repo_name):
72 def rss(self, repo_name):
75 """Produce an rss2 feed via feedgenerator module"""
73 """Produce an rss2 feed via feedgenerator module"""
76 feed = Rss201rev2Feed(title=self.title % repo_name,
74 feed = Rss201rev2Feed(title=self.title % repo_name,
77 link=url('summary_home', repo_name=repo_name, qualified=True),
75 link=url('summary_home', repo_name=repo_name, qualified=True),
78 description=self.description % repo_name,
76 description=self.description % repo_name,
79 language=self.language,
77 language=self.language,
80 ttl=self.ttl)
78 ttl=self.ttl)
81
79
82 changesets = ScmModel().get_repo(repo_name)
80 changesets = ScmModel().get_repo(repo_name)
83 for cs in changesets[:self.feed_nr]:
81 for cs in changesets[:self.feed_nr]:
84 feed.add_item(title=cs.message,
82 feed.add_item(title=cs.message,
85 link=url('changeset_home', repo_name=repo_name,
83 link=url('changeset_home', repo_name=repo_name,
86 revision=cs.raw_id, qualified=True),
84 revision=cs.raw_id, qualified=True),
87 description=str(cs.date))
85 description=str(cs.date))
88
86
89 response.content_type = feed.mime_type
87 response.content_type = feed.mime_type
90 return feed.writeString('utf-8')
88 return feed.writeString('utf-8')
@@ -1,278 +1,276 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.files
3 rhodecode.controllers.files
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Files controller for RhodeCode
6 Files controller for RhodeCode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27 import tempfile
25 import tempfile
28 import logging
26 import logging
29 import rhodecode.lib.helpers as h
27 import rhodecode.lib.helpers as h
30
28
31 from mercurial import archival
29 from mercurial import archival
32
30
33 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
35 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
36
34
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.utils import EmptyChangeset
37 from rhodecode.lib.utils import EmptyChangeset
40 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.scm import ScmModel
41
39
42 from vcs.exceptions import RepositoryError, ChangesetError, \
40 from vcs.exceptions import RepositoryError, ChangesetError, \
43 ChangesetDoesNotExistError, EmptyRepositoryError
41 ChangesetDoesNotExistError, EmptyRepositoryError
44 from vcs.nodes import FileNode
42 from vcs.nodes import FileNode
45 from vcs.utils import diffs as differ
43 from vcs.utils import diffs as differ
46
44
47 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
48
46
49 class FilesController(BaseController):
47 class FilesController(BaseController):
50
48
51 @LoginRequired()
49 @LoginRequired()
52 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
50 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
53 'repository.admin')
51 'repository.admin')
54 def __before__(self):
52 def __before__(self):
55 super(FilesController, self).__before__()
53 super(FilesController, self).__before__()
56 c.cut_off_limit = self.cut_off_limit
54 c.cut_off_limit = self.cut_off_limit
57
55
58 def __get_cs_or_redirect(self, rev, repo_name):
56 def __get_cs_or_redirect(self, rev, repo_name):
59 """
57 """
60 Safe way to get changeset if error occur it redirects to tip with
58 Safe way to get changeset if error occur it redirects to tip with
61 proper message
59 proper message
62
60
63 :param rev: revision to fetch
61 :param rev: revision to fetch
64 :param repo_name: repo name to redirect after
62 :param repo_name: repo name to redirect after
65 """
63 """
66
64
67 _repo = ScmModel().get_repo(c.repo_name)
65 _repo = ScmModel().get_repo(c.repo_name)
68 try:
66 try:
69 return _repo.get_changeset(rev)
67 return _repo.get_changeset(rev)
70 except EmptyRepositoryError, e:
68 except EmptyRepositoryError, e:
71 h.flash(_('There are no files yet'), category='warning')
69 h.flash(_('There are no files yet'), category='warning')
72 redirect(h.url('summary_home', repo_name=repo_name))
70 redirect(h.url('summary_home', repo_name=repo_name))
73
71
74 except RepositoryError, e:
72 except RepositoryError, e:
75 h.flash(str(e), category='warning')
73 h.flash(str(e), category='warning')
76 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
74 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
77
75
78 def index(self, repo_name, revision, f_path):
76 def index(self, repo_name, revision, f_path):
79 cs = self.__get_cs_or_redirect(revision, repo_name)
77 cs = self.__get_cs_or_redirect(revision, repo_name)
80 c.repo = ScmModel().get_repo(c.repo_name)
78 c.repo = ScmModel().get_repo(c.repo_name)
81
79
82 revision = request.POST.get('at_rev', None) or revision
80 revision = request.POST.get('at_rev', None) or revision
83
81
84 def get_next_rev(cur):
82 def get_next_rev(cur):
85 max_rev = len(c.repo.revisions) - 1
83 max_rev = len(c.repo.revisions) - 1
86 r = cur + 1
84 r = cur + 1
87 if r > max_rev:
85 if r > max_rev:
88 r = max_rev
86 r = max_rev
89 return r
87 return r
90
88
91 def get_prev_rev(cur):
89 def get_prev_rev(cur):
92 r = cur - 1
90 r = cur - 1
93 return r
91 return r
94
92
95 c.f_path = f_path
93 c.f_path = f_path
96 c.changeset = cs
94 c.changeset = cs
97 cur_rev = c.changeset.revision
95 cur_rev = c.changeset.revision
98 prev_rev = c.repo.get_changeset(get_prev_rev(cur_rev)).raw_id
96 prev_rev = c.repo.get_changeset(get_prev_rev(cur_rev)).raw_id
99 next_rev = c.repo.get_changeset(get_next_rev(cur_rev)).raw_id
97 next_rev = c.repo.get_changeset(get_next_rev(cur_rev)).raw_id
100
98
101 c.url_prev = url('files_home', repo_name=c.repo_name,
99 c.url_prev = url('files_home', repo_name=c.repo_name,
102 revision=prev_rev, f_path=f_path)
100 revision=prev_rev, f_path=f_path)
103 c.url_next = url('files_home', repo_name=c.repo_name,
101 c.url_next = url('files_home', repo_name=c.repo_name,
104 revision=next_rev, f_path=f_path)
102 revision=next_rev, f_path=f_path)
105
103
106 try:
104 try:
107 c.files_list = c.changeset.get_node(f_path)
105 c.files_list = c.changeset.get_node(f_path)
108 c.file_history = self._get_history(c.repo, c.files_list, f_path)
106 c.file_history = self._get_history(c.repo, c.files_list, f_path)
109 except RepositoryError, e:
107 except RepositoryError, e:
110 h.flash(str(e), category='warning')
108 h.flash(str(e), category='warning')
111 redirect(h.url('files_home', repo_name=repo_name,
109 redirect(h.url('files_home', repo_name=repo_name,
112 revision=revision))
110 revision=revision))
113
111
114
112
115 return render('files/files.html')
113 return render('files/files.html')
116
114
117 def rawfile(self, repo_name, revision, f_path):
115 def rawfile(self, repo_name, revision, f_path):
118 cs = self.__get_cs_or_redirect(revision, repo_name)
116 cs = self.__get_cs_or_redirect(revision, repo_name)
119 try:
117 try:
120 file_node = cs.get_node(f_path)
118 file_node = cs.get_node(f_path)
121 except RepositoryError, e:
119 except RepositoryError, e:
122 h.flash(str(e), category='warning')
120 h.flash(str(e), category='warning')
123 redirect(h.url('files_home', repo_name=repo_name,
121 redirect(h.url('files_home', repo_name=repo_name,
124 revision=cs.raw_id))
122 revision=cs.raw_id))
125
123
126 fname = f_path.split('/')[-1].encode('utf8', 'replace')
124 fname = f_path.split('/')[-1].encode('utf8', 'replace')
127
125
128 response.content_disposition = 'attachment; filename=%s' % fname
126 response.content_disposition = 'attachment; filename=%s' % fname
129 response.content_type = file_node.mimetype
127 response.content_type = file_node.mimetype
130 return file_node.content
128 return file_node.content
131
129
132 def raw(self, repo_name, revision, f_path):
130 def raw(self, repo_name, revision, f_path):
133 cs = self.__get_cs_or_redirect(revision, repo_name)
131 cs = self.__get_cs_or_redirect(revision, repo_name)
134 try:
132 try:
135 file_node = cs.get_node(f_path)
133 file_node = cs.get_node(f_path)
136 except RepositoryError, e:
134 except RepositoryError, e:
137 h.flash(str(e), category='warning')
135 h.flash(str(e), category='warning')
138 redirect(h.url('files_home', repo_name=repo_name,
136 redirect(h.url('files_home', repo_name=repo_name,
139 revision=cs.raw_id))
137 revision=cs.raw_id))
140
138
141 response.content_type = 'text/plain'
139 response.content_type = 'text/plain'
142 return file_node.content
140 return file_node.content
143
141
144 def annotate(self, repo_name, revision, f_path):
142 def annotate(self, repo_name, revision, f_path):
145 cs = self.__get_cs_or_redirect(revision, repo_name)
143 cs = self.__get_cs_or_redirect(revision, repo_name)
146 try:
144 try:
147 c.file = cs.get_node(f_path)
145 c.file = cs.get_node(f_path)
148 except RepositoryError, e:
146 except RepositoryError, e:
149 h.flash(str(e), category='warning')
147 h.flash(str(e), category='warning')
150 redirect(h.url('files_home', repo_name=repo_name, revision=cs.raw_id))
148 redirect(h.url('files_home', repo_name=repo_name, revision=cs.raw_id))
151
149
152 c.file_history = self._get_history(ScmModel().get_repo(c.repo_name), c.file, f_path)
150 c.file_history = self._get_history(ScmModel().get_repo(c.repo_name), c.file, f_path)
153 c.cs = cs
151 c.cs = cs
154 c.f_path = f_path
152 c.f_path = f_path
155
153
156 return render('files/files_annotate.html')
154 return render('files/files_annotate.html')
157
155
158 def archivefile(self, repo_name, revision, fileformat):
156 def archivefile(self, repo_name, revision, fileformat):
159 archive_specs = {
157 archive_specs = {
160 '.tar.bz2': ('application/x-tar', 'tbz2'),
158 '.tar.bz2': ('application/x-tar', 'tbz2'),
161 '.tar.gz': ('application/x-tar', 'tgz'),
159 '.tar.gz': ('application/x-tar', 'tgz'),
162 '.zip': ('application/zip', 'zip'),
160 '.zip': ('application/zip', 'zip'),
163 }
161 }
164 if not archive_specs.has_key(fileformat):
162 if not archive_specs.has_key(fileformat):
165 return 'Unknown archive type %s' % fileformat
163 return 'Unknown archive type %s' % fileformat
166
164
167 def read_in_chunks(file_object, chunk_size=1024 * 40):
165 def read_in_chunks(file_object, chunk_size=1024 * 40):
168 """Lazy function (generator) to read a file piece by piece.
166 """Lazy function (generator) to read a file piece by piece.
169 Default chunk size: 40k."""
167 Default chunk size: 40k."""
170 while True:
168 while True:
171 data = file_object.read(chunk_size)
169 data = file_object.read(chunk_size)
172 if not data:
170 if not data:
173 break
171 break
174 yield data
172 yield data
175
173
176 archive = tempfile.TemporaryFile()
174 archive = tempfile.TemporaryFile()
177 repo = ScmModel().get_repo(repo_name).repo
175 repo = ScmModel().get_repo(repo_name).repo
178 fname = '%s-%s%s' % (repo_name, revision, fileformat)
176 fname = '%s-%s%s' % (repo_name, revision, fileformat)
179 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
177 archival.archive(repo, archive, revision, archive_specs[fileformat][1],
180 prefix='%s-%s' % (repo_name, revision))
178 prefix='%s-%s' % (repo_name, revision))
181 response.content_type = archive_specs[fileformat][0]
179 response.content_type = archive_specs[fileformat][0]
182 response.content_disposition = 'attachment; filename=%s' % fname
180 response.content_disposition = 'attachment; filename=%s' % fname
183 archive.seek(0)
181 archive.seek(0)
184 return read_in_chunks(archive)
182 return read_in_chunks(archive)
185
183
186 def diff(self, repo_name, f_path):
184 def diff(self, repo_name, f_path):
187 hg_model = ScmModel()
185 hg_model = ScmModel()
188 diff1 = request.GET.get('diff1')
186 diff1 = request.GET.get('diff1')
189 diff2 = request.GET.get('diff2')
187 diff2 = request.GET.get('diff2')
190 c.action = request.GET.get('diff')
188 c.action = request.GET.get('diff')
191 c.no_changes = diff1 == diff2
189 c.no_changes = diff1 == diff2
192 c.f_path = f_path
190 c.f_path = f_path
193 c.repo = hg_model.get_repo(c.repo_name)
191 c.repo = hg_model.get_repo(c.repo_name)
194
192
195 try:
193 try:
196 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
194 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
197 c.changeset_1 = c.repo.get_changeset(diff1)
195 c.changeset_1 = c.repo.get_changeset(diff1)
198 node1 = c.changeset_1.get_node(f_path)
196 node1 = c.changeset_1.get_node(f_path)
199 else:
197 else:
200 c.changeset_1 = EmptyChangeset()
198 c.changeset_1 = EmptyChangeset()
201 node1 = FileNode('.', '', changeset=c.changeset_1)
199 node1 = FileNode('.', '', changeset=c.changeset_1)
202
200
203 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
201 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
204 c.changeset_2 = c.repo.get_changeset(diff2)
202 c.changeset_2 = c.repo.get_changeset(diff2)
205 node2 = c.changeset_2.get_node(f_path)
203 node2 = c.changeset_2.get_node(f_path)
206 else:
204 else:
207 c.changeset_2 = EmptyChangeset()
205 c.changeset_2 = EmptyChangeset()
208 node2 = FileNode('.', '', changeset=c.changeset_2)
206 node2 = FileNode('.', '', changeset=c.changeset_2)
209 except RepositoryError:
207 except RepositoryError:
210 return redirect(url('files_home',
208 return redirect(url('files_home',
211 repo_name=c.repo_name, f_path=f_path))
209 repo_name=c.repo_name, f_path=f_path))
212
210
213 f_udiff = differ.get_udiff(node1, node2)
211 f_udiff = differ.get_udiff(node1, node2)
214 diff = differ.DiffProcessor(f_udiff)
212 diff = differ.DiffProcessor(f_udiff)
215
213
216 if c.action == 'download':
214 if c.action == 'download':
217 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
215 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
218 response.content_type = 'text/plain'
216 response.content_type = 'text/plain'
219 response.content_disposition = 'attachment; filename=%s' \
217 response.content_disposition = 'attachment; filename=%s' \
220 % diff_name
218 % diff_name
221 if node1.is_binary or node2.is_binary:
219 if node1.is_binary or node2.is_binary:
222 return _('binary file changed')
220 return _('binary file changed')
223 return diff.raw_diff()
221 return diff.raw_diff()
224
222
225 elif c.action == 'raw':
223 elif c.action == 'raw':
226 response.content_type = 'text/plain'
224 response.content_type = 'text/plain'
227 if node1.is_binary or node2.is_binary:
225 if node1.is_binary or node2.is_binary:
228 return _('binary file changed')
226 return _('binary file changed')
229 return diff.raw_diff()
227 return diff.raw_diff()
230
228
231 elif c.action == 'diff':
229 elif c.action == 'diff':
232 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
230 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
233 c.cur_diff = _('Diff is to big to display')
231 c.cur_diff = _('Diff is to big to display')
234 elif node1.is_binary or node2.is_binary:
232 elif node1.is_binary or node2.is_binary:
235 c.cur_diff = _('Binary file')
233 c.cur_diff = _('Binary file')
236 else:
234 else:
237 c.cur_diff = diff.as_html()
235 c.cur_diff = diff.as_html()
238 else:
236 else:
239 #default option
237 #default option
240 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
238 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
241 c.cur_diff = _('Diff is to big to display')
239 c.cur_diff = _('Diff is to big to display')
242 elif node1.is_binary or node2.is_binary:
240 elif node1.is_binary or node2.is_binary:
243 c.cur_diff = _('Binary file')
241 c.cur_diff = _('Binary file')
244 else:
242 else:
245 c.cur_diff = diff.as_html()
243 c.cur_diff = diff.as_html()
246
244
247 if not c.cur_diff:
245 if not c.cur_diff:
248 c.no_changes = True
246 c.no_changes = True
249 return render('files/file_diff.html')
247 return render('files/file_diff.html')
250
248
251 def _get_history(self, repo, node, f_path):
249 def _get_history(self, repo, node, f_path):
252 from vcs.nodes import NodeKind
250 from vcs.nodes import NodeKind
253 if not node.kind is NodeKind.FILE:
251 if not node.kind is NodeKind.FILE:
254 return []
252 return []
255 changesets = node.history
253 changesets = node.history
256 hist_l = []
254 hist_l = []
257
255
258 changesets_group = ([], _("Changesets"))
256 changesets_group = ([], _("Changesets"))
259 branches_group = ([], _("Branches"))
257 branches_group = ([], _("Branches"))
260 tags_group = ([], _("Tags"))
258 tags_group = ([], _("Tags"))
261
259
262 for chs in changesets:
260 for chs in changesets:
263 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
261 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
264 changesets_group[0].append((chs.raw_id, n_desc,))
262 changesets_group[0].append((chs.raw_id, n_desc,))
265
263
266 hist_l.append(changesets_group)
264 hist_l.append(changesets_group)
267
265
268 for name, chs in c.repository_branches.items():
266 for name, chs in c.repository_branches.items():
269 #chs = chs.split(':')[-1]
267 #chs = chs.split(':')[-1]
270 branches_group[0].append((chs, name),)
268 branches_group[0].append((chs, name),)
271 hist_l.append(branches_group)
269 hist_l.append(branches_group)
272
270
273 for name, chs in c.repository_tags.items():
271 for name, chs in c.repository_tags.items():
274 #chs = chs.split(':')[-1]
272 #chs = chs.split(':')[-1]
275 tags_group[0].append((chs, name),)
273 tags_group[0].append((chs, name),)
276 hist_l.append(tags_group)
274 hist_l.append(tags_group)
277
275
278 return hist_l
276 return hist_l
@@ -1,66 +1,64 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.home
3 rhodecode.controllers.home
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Home controller for Rhodecode
6 Home controller for Rhodecode
7
7
8 :created_on: Feb 18, 2010
8 :created_on: Feb 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29 from operator import itemgetter
27 from operator import itemgetter
30
28
31 from pylons import tmpl_context as c, request
29 from pylons import tmpl_context as c, request
32
30
33 from rhodecode.lib.auth import LoginRequired
31 from rhodecode.lib.auth import LoginRequired
34 from rhodecode.lib.base import BaseController, render
32 from rhodecode.lib.base import BaseController, render
35 from rhodecode.model.scm import ScmModel
33 from rhodecode.model.scm import ScmModel
36
34
37 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
38
36
39 class HomeController(BaseController):
37 class HomeController(BaseController):
40
38
41 @LoginRequired()
39 @LoginRequired()
42 def __before__(self):
40 def __before__(self):
43 super(HomeController, self).__before__()
41 super(HomeController, self).__before__()
44
42
45 def index(self):
43 def index(self):
46 sortables = ['name', 'description', 'last_change', 'tip', 'owner']
44 sortables = ['name', 'description', 'last_change', 'tip', 'owner']
47 current_sort = request.GET.get('sort', 'name')
45 current_sort = request.GET.get('sort', 'name')
48 current_sort_slug = current_sort.replace('-', '')
46 current_sort_slug = current_sort.replace('-', '')
49
47
50 if current_sort_slug not in sortables:
48 if current_sort_slug not in sortables:
51 c.sort_by = 'name'
49 c.sort_by = 'name'
52 current_sort_slug = c.sort_by
50 current_sort_slug = c.sort_by
53 else:
51 else:
54 c.sort_by = current_sort
52 c.sort_by = current_sort
55 c.sort_slug = current_sort_slug
53 c.sort_slug = current_sort_slug
56 cached_repo_list = ScmModel().get_repos()
54 cached_repo_list = ScmModel().get_repos()
57
55
58 sort_key = current_sort_slug + '_sort'
56 sort_key = current_sort_slug + '_sort'
59 if c.sort_by.startswith('-'):
57 if c.sort_by.startswith('-'):
60 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
58 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
61 reverse=True)
59 reverse=True)
62 else:
60 else:
63 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
61 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
64 reverse=False)
62 reverse=False)
65
63
66 return render('/index.html')
64 return render('/index.html')
@@ -1,100 +1,98 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.journal
3 rhodecode.controllers.journal
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Journal controller for pylons
6 Journal controller for pylons
7
7
8 :created_on: Nov 21, 2010
8 :created_on: Nov 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29 import traceback
27 import traceback
30
28
31 from pylons import request, response, session, tmpl_context as c, url
29 from pylons import request, response, session, tmpl_context as c, url
32 from paste.httpexceptions import HTTPInternalServerError, HTTPBadRequest
30 from paste.httpexceptions import HTTPInternalServerError, HTTPBadRequest
33
31
34 from sqlalchemy import or_
32 from sqlalchemy import or_
35
33
36 from rhodecode.lib.auth import LoginRequired, NotAnonymous
34 from rhodecode.lib.auth import LoginRequired, NotAnonymous
37 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib.helpers import get_token
36 from rhodecode.lib.helpers import get_token
39 from rhodecode.model.db import UserLog, UserFollowing
37 from rhodecode.model.db import UserLog, UserFollowing
40 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.scm import ScmModel
41
39
42 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
43
41
44 class JournalController(BaseController):
42 class JournalController(BaseController):
45
43
46
44
47 @LoginRequired()
45 @LoginRequired()
48 @NotAnonymous()
46 @NotAnonymous()
49 def __before__(self):
47 def __before__(self):
50 super(JournalController, self).__before__()
48 super(JournalController, self).__before__()
51
49
52 def index(self):
50 def index(self):
53 # Return a rendered template
51 # Return a rendered template
54
52
55 c.following = self.sa.query(UserFollowing)\
53 c.following = self.sa.query(UserFollowing)\
56 .filter(UserFollowing.user_id == c.rhodecode_user.user_id).all()
54 .filter(UserFollowing.user_id == c.rhodecode_user.user_id).all()
57
55
58 repo_ids = [x.follows_repository.repo_id for x in c.following
56 repo_ids = [x.follows_repository.repo_id for x in c.following
59 if x.follows_repository is not None]
57 if x.follows_repository is not None]
60 user_ids = [x.follows_user.user_id for x in c.following
58 user_ids = [x.follows_user.user_id for x in c.following
61 if x.follows_user is not None]
59 if x.follows_user is not None]
62
60
63 c.journal = self.sa.query(UserLog)\
61 c.journal = self.sa.query(UserLog)\
64 .filter(or_(
62 .filter(or_(
65 UserLog.repository_id.in_(repo_ids),
63 UserLog.repository_id.in_(repo_ids),
66 UserLog.user_id.in_(user_ids),
64 UserLog.user_id.in_(user_ids),
67 ))\
65 ))\
68 .order_by(UserLog.action_date.desc())\
66 .order_by(UserLog.action_date.desc())\
69 .limit(20)\
67 .limit(20)\
70 .all()
68 .all()
71 return render('/journal.html')
69 return render('/journal.html')
72
70
73 def toggle_following(self):
71 def toggle_following(self):
74
72
75 if request.POST.get('auth_token') == get_token():
73 if request.POST.get('auth_token') == get_token():
76 scm_model = ScmModel()
74 scm_model = ScmModel()
77
75
78 user_id = request.POST.get('follows_user_id')
76 user_id = request.POST.get('follows_user_id')
79 if user_id:
77 if user_id:
80 try:
78 try:
81 scm_model.toggle_following_user(user_id,
79 scm_model.toggle_following_user(user_id,
82 c.rhodecode_user.user_id)
80 c.rhodecode_user.user_id)
83 return 'ok'
81 return 'ok'
84 except:
82 except:
85 log.error(traceback.format_exc())
83 log.error(traceback.format_exc())
86 raise HTTPInternalServerError()
84 raise HTTPInternalServerError()
87
85
88 repo_id = request.POST.get('follows_repo_id')
86 repo_id = request.POST.get('follows_repo_id')
89 if repo_id:
87 if repo_id:
90 try:
88 try:
91 scm_model.toggle_following_repo(repo_id,
89 scm_model.toggle_following_repo(repo_id,
92 c.rhodecode_user.user_id)
90 c.rhodecode_user.user_id)
93 return 'ok'
91 return 'ok'
94 except:
92 except:
95 log.error(traceback.format_exc())
93 log.error(traceback.format_exc())
96 raise HTTPInternalServerError()
94 raise HTTPInternalServerError()
97
95
98
96
99
97
100 raise HTTPBadRequest()
98 raise HTTPBadRequest()
@@ -1,152 +1,150 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.login
3 rhodecode.controllers.login
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Login controller for rhodeocode
6 Login controller for rhodeocode
7
7
8 :created_on: Apr 22, 2010
8 :created_on: Apr 22, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29 import formencode
27 import formencode
30
28
31 from formencode import htmlfill
29 from formencode import htmlfill
32
30
33 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
34 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
35 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
36
34
37 import rhodecode.lib.helpers as h
35 import rhodecode.lib.helpers as h
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
36 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
39 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
38 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
41 from rhodecode.model.user import UserModel
39 from rhodecode.model.user import UserModel
42
40
43
41
44 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
45
43
46 class LoginController(BaseController):
44 class LoginController(BaseController):
47
45
48 def __before__(self):
46 def __before__(self):
49 super(LoginController, self).__before__()
47 super(LoginController, self).__before__()
50
48
51 def index(self):
49 def index(self):
52 #redirect if already logged in
50 #redirect if already logged in
53 c.came_from = request.GET.get('came_from', None)
51 c.came_from = request.GET.get('came_from', None)
54
52
55 if c.rhodecode_user.is_authenticated \
53 if c.rhodecode_user.is_authenticated \
56 and c.rhodecode_user.username != 'default':
54 and c.rhodecode_user.username != 'default':
57
55
58 return redirect(url('home'))
56 return redirect(url('home'))
59
57
60 if request.POST:
58 if request.POST:
61 #import Login Form validator class
59 #import Login Form validator class
62 login_form = LoginForm()
60 login_form = LoginForm()
63 try:
61 try:
64 c.form_result = login_form.to_python(dict(request.POST))
62 c.form_result = login_form.to_python(dict(request.POST))
65 username = c.form_result['username']
63 username = c.form_result['username']
66 user = UserModel().get_by_username(username, case_insensitive=True)
64 user = UserModel().get_by_username(username, case_insensitive=True)
67 auth_user = AuthUser()
65 auth_user = AuthUser()
68 auth_user.username = user.username
66 auth_user.username = user.username
69 auth_user.is_authenticated = True
67 auth_user.is_authenticated = True
70 auth_user.is_admin = user.admin
68 auth_user.is_admin = user.admin
71 auth_user.user_id = user.user_id
69 auth_user.user_id = user.user_id
72 auth_user.name = user.name
70 auth_user.name = user.name
73 auth_user.lastname = user.lastname
71 auth_user.lastname = user.lastname
74 session['rhodecode_user'] = auth_user
72 session['rhodecode_user'] = auth_user
75 session.save()
73 session.save()
76 log.info('user %s is now authenticated', username)
74 log.info('user %s is now authenticated', username)
77
75
78 user.update_lastlogin()
76 user.update_lastlogin()
79
77
80 if c.came_from:
78 if c.came_from:
81 return redirect(c.came_from)
79 return redirect(c.came_from)
82 else:
80 else:
83 return redirect(url('home'))
81 return redirect(url('home'))
84
82
85 except formencode.Invalid, errors:
83 except formencode.Invalid, errors:
86 return htmlfill.render(
84 return htmlfill.render(
87 render('/login.html'),
85 render('/login.html'),
88 defaults=errors.value,
86 defaults=errors.value,
89 errors=errors.error_dict or {},
87 errors=errors.error_dict or {},
90 prefix_error=False,
88 prefix_error=False,
91 encoding="UTF-8")
89 encoding="UTF-8")
92
90
93 return render('/login.html')
91 return render('/login.html')
94
92
95 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
93 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
96 'hg.register.manual_activate')
94 'hg.register.manual_activate')
97 def register(self):
95 def register(self):
98 user_model = UserModel()
96 user_model = UserModel()
99 c.auto_active = False
97 c.auto_active = False
100 for perm in user_model.get_by_username('default', cache=False).user_perms:
98 for perm in user_model.get_by_username('default', cache=False).user_perms:
101 if perm.permission.permission_name == 'hg.register.auto_activate':
99 if perm.permission.permission_name == 'hg.register.auto_activate':
102 c.auto_active = True
100 c.auto_active = True
103 break
101 break
104
102
105 if request.POST:
103 if request.POST:
106
104
107 register_form = RegisterForm()()
105 register_form = RegisterForm()()
108 try:
106 try:
109 form_result = register_form.to_python(dict(request.POST))
107 form_result = register_form.to_python(dict(request.POST))
110 form_result['active'] = c.auto_active
108 form_result['active'] = c.auto_active
111 user_model.create_registration(form_result)
109 user_model.create_registration(form_result)
112 h.flash(_('You have successfully registered into rhodecode'),
110 h.flash(_('You have successfully registered into rhodecode'),
113 category='success')
111 category='success')
114 return redirect(url('login_home'))
112 return redirect(url('login_home'))
115
113
116 except formencode.Invalid, errors:
114 except formencode.Invalid, errors:
117 return htmlfill.render(
115 return htmlfill.render(
118 render('/register.html'),
116 render('/register.html'),
119 defaults=errors.value,
117 defaults=errors.value,
120 errors=errors.error_dict or {},
118 errors=errors.error_dict or {},
121 prefix_error=False,
119 prefix_error=False,
122 encoding="UTF-8")
120 encoding="UTF-8")
123
121
124 return render('/register.html')
122 return render('/register.html')
125
123
126 def password_reset(self):
124 def password_reset(self):
127 user_model = UserModel()
125 user_model = UserModel()
128 if request.POST:
126 if request.POST:
129
127
130 password_reset_form = PasswordResetForm()()
128 password_reset_form = PasswordResetForm()()
131 try:
129 try:
132 form_result = password_reset_form.to_python(dict(request.POST))
130 form_result = password_reset_form.to_python(dict(request.POST))
133 user_model.reset_password(form_result)
131 user_model.reset_password(form_result)
134 h.flash(_('Your new password was sent'),
132 h.flash(_('Your new password was sent'),
135 category='success')
133 category='success')
136 return redirect(url('login_home'))
134 return redirect(url('login_home'))
137
135
138 except formencode.Invalid, errors:
136 except formencode.Invalid, errors:
139 return htmlfill.render(
137 return htmlfill.render(
140 render('/password_reset.html'),
138 render('/password_reset.html'),
141 defaults=errors.value,
139 defaults=errors.value,
142 errors=errors.error_dict or {},
140 errors=errors.error_dict or {},
143 prefix_error=False,
141 prefix_error=False,
144 encoding="UTF-8")
142 encoding="UTF-8")
145
143
146 return render('/password_reset.html')
144 return render('/password_reset.html')
147
145
148 def logout(self):
146 def logout(self):
149 session['rhodecode_user'] = AuthUser()
147 session['rhodecode_user'] = AuthUser()
150 session.save()
148 session.save()
151 log.info('Logging out and setting user as Empty')
149 log.info('Logging out and setting user as Empty')
152 redirect(url('home'))
150 redirect(url('home'))
@@ -1,120 +1,118 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.search
3 rhodecode.controllers.search
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Search controller for rhodecode
6 Search controller for rhodecode
7
7
8 :created_on: Aug 7, 2010
8 :created_on: Aug 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27 import logging
25 import logging
28 import traceback
26 import traceback
29
27
30 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
31 from pylons import request, response, config, session, tmpl_context as c, url
29 from pylons import request, response, config, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
30 from pylons.controllers.util import abort, redirect
33
31
34 from rhodecode.lib.auth import LoginRequired
32 from rhodecode.lib.auth import LoginRequired
35 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
34 from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
37
35
38 from webhelpers.paginate import Page
36 from webhelpers.paginate import Page
39 from webhelpers.util import update_params
37 from webhelpers.util import update_params
40
38
41 from whoosh.index import open_dir, EmptyIndexError
39 from whoosh.index import open_dir, EmptyIndexError
42 from whoosh.qparser import QueryParser, QueryParserError
40 from whoosh.qparser import QueryParser, QueryParserError
43 from whoosh.query import Phrase
41 from whoosh.query import Phrase
44
42
45 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
46
44
47 class SearchController(BaseController):
45 class SearchController(BaseController):
48
46
49 @LoginRequired()
47 @LoginRequired()
50 def __before__(self):
48 def __before__(self):
51 super(SearchController, self).__before__()
49 super(SearchController, self).__before__()
52
50
53 def index(self, search_repo=None):
51 def index(self, search_repo=None):
54 c.repo_name = search_repo
52 c.repo_name = search_repo
55 c.formated_results = []
53 c.formated_results = []
56 c.runtime = ''
54 c.runtime = ''
57 c.cur_query = request.GET.get('q', None)
55 c.cur_query = request.GET.get('q', None)
58 c.cur_type = request.GET.get('type', 'source')
56 c.cur_type = request.GET.get('type', 'source')
59 c.cur_search = search_type = {'content':'content',
57 c.cur_search = search_type = {'content':'content',
60 'commit':'content',
58 'commit':'content',
61 'path':'path',
59 'path':'path',
62 'repository':'repository'}\
60 'repository':'repository'}\
63 .get(c.cur_type, 'content')
61 .get(c.cur_type, 'content')
64
62
65
63
66 if c.cur_query:
64 if c.cur_query:
67 cur_query = c.cur_query.lower()
65 cur_query = c.cur_query.lower()
68
66
69 if c.cur_query:
67 if c.cur_query:
70 p = int(request.params.get('page', 1))
68 p = int(request.params.get('page', 1))
71 highlight_items = set()
69 highlight_items = set()
72 try:
70 try:
73 idx = open_dir(config['app_conf']['index_dir']
71 idx = open_dir(config['app_conf']['index_dir']
74 , indexname=IDX_NAME)
72 , indexname=IDX_NAME)
75 searcher = idx.searcher()
73 searcher = idx.searcher()
76
74
77 qp = QueryParser(search_type, schema=SCHEMA)
75 qp = QueryParser(search_type, schema=SCHEMA)
78 if c.repo_name:
76 if c.repo_name:
79 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
77 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
80 try:
78 try:
81 query = qp.parse(unicode(cur_query))
79 query = qp.parse(unicode(cur_query))
82
80
83 if isinstance(query, Phrase):
81 if isinstance(query, Phrase):
84 highlight_items.update(query.words)
82 highlight_items.update(query.words)
85 else:
83 else:
86 for i in query.all_terms():
84 for i in query.all_terms():
87 if i[0] == 'content':
85 if i[0] == 'content':
88 highlight_items.add(i[1])
86 highlight_items.add(i[1])
89
87
90 matcher = query.matcher(searcher)
88 matcher = query.matcher(searcher)
91
89
92 log.debug(query)
90 log.debug(query)
93 log.debug(highlight_items)
91 log.debug(highlight_items)
94 results = searcher.search(query)
92 results = searcher.search(query)
95 res_ln = len(results)
93 res_ln = len(results)
96 c.runtime = '%s results (%.3f seconds)' \
94 c.runtime = '%s results (%.3f seconds)' \
97 % (res_ln, results.runtime)
95 % (res_ln, results.runtime)
98
96
99 def url_generator(**kw):
97 def url_generator(**kw):
100 return update_params("?q=%s&type=%s" \
98 return update_params("?q=%s&type=%s" \
101 % (c.cur_query, c.cur_search), **kw)
99 % (c.cur_query, c.cur_search), **kw)
102
100
103 c.formated_results = Page(
101 c.formated_results = Page(
104 ResultWrapper(search_type, searcher, matcher,
102 ResultWrapper(search_type, searcher, matcher,
105 highlight_items),
103 highlight_items),
106 page=p, item_count=res_ln,
104 page=p, item_count=res_ln,
107 items_per_page=10, url=url_generator)
105 items_per_page=10, url=url_generator)
108
106
109
107
110 except QueryParserError:
108 except QueryParserError:
111 c.runtime = _('Invalid search query. Try quoting it.')
109 c.runtime = _('Invalid search query. Try quoting it.')
112 searcher.close()
110 searcher.close()
113 except (EmptyIndexError, IOError):
111 except (EmptyIndexError, IOError):
114 log.error(traceback.format_exc())
112 log.error(traceback.format_exc())
115 log.error('Empty Index data')
113 log.error('Empty Index data')
116 c.runtime = _('There is no index to search in. '
114 c.runtime = _('There is no index to search in. '
117 'Please run whoosh indexer')
115 'Please run whoosh indexer')
118
116
119 # Return a rendered template
117 # Return a rendered template
120 return render('/search/search.html')
118 return render('/search/search.html')
@@ -1,190 +1,188 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.settings
3 rhodecode.controllers.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Settings controller for rhodecode
6 Settings controller for rhodecode
7
7
8 :created_on: Jun 30, 2010
8 :created_on: Jun 30, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29 import traceback
27 import traceback
30 import formencode
28 import formencode
31
29
32 from formencode import htmlfill
30 from formencode import htmlfill
33
31
34 from pylons import tmpl_context as c, request, url
32 from pylons import tmpl_context as c, request, url
35 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
36 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
37
35
38 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator, \
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator, \
40 HasRepoPermissionAnyDecorator, NotAnonymous
38 HasRepoPermissionAnyDecorator, NotAnonymous
41 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.utils import invalidate_cache, action_logger
40 from rhodecode.lib.utils import invalidate_cache, action_logger
43 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
41 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
44 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
45
43
46 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
47
45
48 class SettingsController(BaseController):
46 class SettingsController(BaseController):
49
47
50 @LoginRequired()
48 @LoginRequired()
51 def __before__(self):
49 def __before__(self):
52 super(SettingsController, self).__before__()
50 super(SettingsController, self).__before__()
53
51
54 @HasRepoPermissionAllDecorator('repository.admin')
52 @HasRepoPermissionAllDecorator('repository.admin')
55 def index(self, repo_name):
53 def index(self, repo_name):
56 repo_model = RepoModel()
54 repo_model = RepoModel()
57 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
55 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
58 if not repo:
56 if not repo:
59 h.flash(_('%s repository is not mapped to db perhaps'
57 h.flash(_('%s repository is not mapped to db perhaps'
60 ' it was created or renamed from the file system'
58 ' it was created or renamed from the file system'
61 ' please run the application again'
59 ' please run the application again'
62 ' in order to rescan repositories') % repo_name,
60 ' in order to rescan repositories') % repo_name,
63 category='error')
61 category='error')
64
62
65 return redirect(url('home'))
63 return redirect(url('home'))
66 defaults = c.repo_info.get_dict()
64 defaults = c.repo_info.get_dict()
67 defaults.update({'user':c.repo_info.user.username})
65 defaults.update({'user':c.repo_info.user.username})
68 c.users_array = repo_model.get_users_js()
66 c.users_array = repo_model.get_users_js()
69
67
70 for p in c.repo_info.repo_to_perm:
68 for p in c.repo_info.repo_to_perm:
71 defaults.update({'perm_%s' % p.user.username:
69 defaults.update({'perm_%s' % p.user.username:
72 p.permission.permission_name})
70 p.permission.permission_name})
73
71
74 return htmlfill.render(
72 return htmlfill.render(
75 render('settings/repo_settings.html'),
73 render('settings/repo_settings.html'),
76 defaults=defaults,
74 defaults=defaults,
77 encoding="UTF-8",
75 encoding="UTF-8",
78 force_defaults=False
76 force_defaults=False
79 )
77 )
80
78
81 @HasRepoPermissionAllDecorator('repository.admin')
79 @HasRepoPermissionAllDecorator('repository.admin')
82 def update(self, repo_name):
80 def update(self, repo_name):
83 repo_model = RepoModel()
81 repo_model = RepoModel()
84 changed_name = repo_name
82 changed_name = repo_name
85 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
83 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
86 try:
84 try:
87 form_result = _form.to_python(dict(request.POST))
85 form_result = _form.to_python(dict(request.POST))
88 repo_model.update(repo_name, form_result)
86 repo_model.update(repo_name, form_result)
89 invalidate_cache('get_repo_cached_%s' % repo_name)
87 invalidate_cache('get_repo_cached_%s' % repo_name)
90 h.flash(_('Repository %s updated successfully' % repo_name),
88 h.flash(_('Repository %s updated successfully' % repo_name),
91 category='success')
89 category='success')
92 changed_name = form_result['repo_name']
90 changed_name = form_result['repo_name']
93 action_logger(self.rhodecode_user, 'user_updated_repo',
91 action_logger(self.rhodecode_user, 'user_updated_repo',
94 changed_name, '', self.sa)
92 changed_name, '', self.sa)
95 except formencode.Invalid, errors:
93 except formencode.Invalid, errors:
96 c.repo_info = repo_model.get_by_repo_name(repo_name)
94 c.repo_info = repo_model.get_by_repo_name(repo_name)
97 c.users_array = repo_model.get_users_js()
95 c.users_array = repo_model.get_users_js()
98 errors.value.update({'user':c.repo_info.user.username})
96 errors.value.update({'user':c.repo_info.user.username})
99 return htmlfill.render(
97 return htmlfill.render(
100 render('settings/repo_settings.html'),
98 render('settings/repo_settings.html'),
101 defaults=errors.value,
99 defaults=errors.value,
102 errors=errors.error_dict or {},
100 errors=errors.error_dict or {},
103 prefix_error=False,
101 prefix_error=False,
104 encoding="UTF-8")
102 encoding="UTF-8")
105 except Exception:
103 except Exception:
106 log.error(traceback.format_exc())
104 log.error(traceback.format_exc())
107 h.flash(_('error occurred during update of repository %s') \
105 h.flash(_('error occurred during update of repository %s') \
108 % repo_name, category='error')
106 % repo_name, category='error')
109
107
110 return redirect(url('repo_settings_home', repo_name=changed_name))
108 return redirect(url('repo_settings_home', repo_name=changed_name))
111
109
112
110
113 @HasRepoPermissionAllDecorator('repository.admin')
111 @HasRepoPermissionAllDecorator('repository.admin')
114 def delete(self, repo_name):
112 def delete(self, repo_name):
115 """DELETE /repos/repo_name: Delete an existing item"""
113 """DELETE /repos/repo_name: Delete an existing item"""
116 # Forms posted to this method should contain a hidden field:
114 # Forms posted to this method should contain a hidden field:
117 # <input type="hidden" name="_method" value="DELETE" />
115 # <input type="hidden" name="_method" value="DELETE" />
118 # Or using helpers:
116 # Or using helpers:
119 # h.form(url('repo_settings_delete', repo_name=ID),
117 # h.form(url('repo_settings_delete', repo_name=ID),
120 # method='delete')
118 # method='delete')
121 # url('repo_settings_delete', repo_name=ID)
119 # url('repo_settings_delete', repo_name=ID)
122
120
123 repo_model = RepoModel()
121 repo_model = RepoModel()
124 repo = repo_model.get_by_repo_name(repo_name)
122 repo = repo_model.get_by_repo_name(repo_name)
125 if not repo:
123 if not repo:
126 h.flash(_('%s repository is not mapped to db perhaps'
124 h.flash(_('%s repository is not mapped to db perhaps'
127 ' it was moved or renamed from the filesystem'
125 ' it was moved or renamed from the filesystem'
128 ' please run the application again'
126 ' please run the application again'
129 ' in order to rescan repositories') % repo_name,
127 ' in order to rescan repositories') % repo_name,
130 category='error')
128 category='error')
131
129
132 return redirect(url('home'))
130 return redirect(url('home'))
133 try:
131 try:
134 action_logger(self.rhodecode_user, 'user_deleted_repo',
132 action_logger(self.rhodecode_user, 'user_deleted_repo',
135 repo_name, '', self.sa)
133 repo_name, '', self.sa)
136 repo_model.delete(repo)
134 repo_model.delete(repo)
137 invalidate_cache('get_repo_cached_%s' % repo_name)
135 invalidate_cache('get_repo_cached_%s' % repo_name)
138 h.flash(_('deleted repository %s') % repo_name, category='success')
136 h.flash(_('deleted repository %s') % repo_name, category='success')
139 except Exception:
137 except Exception:
140 h.flash(_('An error occurred during deletion of %s') % repo_name,
138 h.flash(_('An error occurred during deletion of %s') % repo_name,
141 category='error')
139 category='error')
142
140
143 return redirect(url('home'))
141 return redirect(url('home'))
144
142
145 @NotAnonymous()
143 @NotAnonymous()
146 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
144 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
147 'repository.admin')
145 'repository.admin')
148 def fork(self, repo_name):
146 def fork(self, repo_name):
149 repo_model = RepoModel()
147 repo_model = RepoModel()
150 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
148 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
151 if not repo:
149 if not repo:
152 h.flash(_('%s repository is not mapped to db perhaps'
150 h.flash(_('%s repository is not mapped to db perhaps'
153 ' it was created or renamed from the file system'
151 ' it was created or renamed from the file system'
154 ' please run the application again'
152 ' please run the application again'
155 ' in order to rescan repositories') % repo_name,
153 ' in order to rescan repositories') % repo_name,
156 category='error')
154 category='error')
157
155
158 return redirect(url('home'))
156 return redirect(url('home'))
159
157
160 return render('settings/repo_fork.html')
158 return render('settings/repo_fork.html')
161
159
162 @NotAnonymous()
160 @NotAnonymous()
163 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
161 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
164 'repository.admin')
162 'repository.admin')
165 def fork_create(self, repo_name):
163 def fork_create(self, repo_name):
166 repo_model = RepoModel()
164 repo_model = RepoModel()
167 c.repo_info = repo_model.get_by_repo_name(repo_name)
165 c.repo_info = repo_model.get_by_repo_name(repo_name)
168 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
166 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
169 form_result = {}
167 form_result = {}
170 try:
168 try:
171 form_result = _form.to_python(dict(request.POST))
169 form_result = _form.to_python(dict(request.POST))
172 form_result.update({'repo_name':repo_name})
170 form_result.update({'repo_name':repo_name})
173 repo_model.create_fork(form_result, c.rhodecode_user)
171 repo_model.create_fork(form_result, c.rhodecode_user)
174 h.flash(_('forked %s repository as %s') \
172 h.flash(_('forked %s repository as %s') \
175 % (repo_name, form_result['fork_name']),
173 % (repo_name, form_result['fork_name']),
176 category='success')
174 category='success')
177 action_logger(self.rhodecode_user,
175 action_logger(self.rhodecode_user,
178 'user_forked_repo:%s' % form_result['fork_name'],
176 'user_forked_repo:%s' % form_result['fork_name'],
179 repo_name, '', self.sa)
177 repo_name, '', self.sa)
180 except formencode.Invalid, errors:
178 except formencode.Invalid, errors:
181 c.new_repo = errors.value['fork_name']
179 c.new_repo = errors.value['fork_name']
182 r = render('settings/repo_fork.html')
180 r = render('settings/repo_fork.html')
183
181
184 return htmlfill.render(
182 return htmlfill.render(
185 r,
183 r,
186 defaults=errors.value,
184 defaults=errors.value,
187 errors=errors.error_dict or {},
185 errors=errors.error_dict or {},
188 prefix_error=False,
186 prefix_error=False,
189 encoding="UTF-8")
187 encoding="UTF-8")
190 return redirect(url('home'))
188 return redirect(url('home'))
@@ -1,56 +1,54 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.shortlog
3 rhodecode.controllers.shortlog
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Shortlog controller for rhodecode
6 Shortlog controller for rhodecode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29
27
30 from pylons import tmpl_context as c, request
28 from pylons import tmpl_context as c, request
31
29
32 from webhelpers.paginate import Page
30 from webhelpers.paginate import Page
33
31
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
36 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.scm import ScmModel
37
35
38 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
39
37
40 class ShortlogController(BaseController):
38 class ShortlogController(BaseController):
41
39
42 @LoginRequired()
40 @LoginRequired()
43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 'repository.admin')
42 'repository.admin')
45 def __before__(self):
43 def __before__(self):
46 super(ShortlogController, self).__before__()
44 super(ShortlogController, self).__before__()
47
45
48 def index(self):
46 def index(self):
49 p = int(request.params.get('page', 1))
47 p = int(request.params.get('page', 1))
50 repo = ScmModel().get_repo(c.repo_name)
48 repo = ScmModel().get_repo(c.repo_name)
51 c.repo_changesets = Page(repo, page=p, items_per_page=20)
49 c.repo_changesets = Page(repo, page=p, items_per_page=20)
52 c.shortlog_data = render('shortlog/shortlog_data.html')
50 c.shortlog_data = render('shortlog/shortlog_data.html')
53 if request.params.get('partial'):
51 if request.params.get('partial'):
54 return c.shortlog_data
52 return c.shortlog_data
55 r = render('shortlog/shortlog.html')
53 r = render('shortlog/shortlog.html')
56 return r
54 return r
@@ -1,143 +1,141 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.summary
3 rhodecode.controllers.summary
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Summary controller for Rhodecode
6 Summary controller for Rhodecode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import calendar
26 import calendar
29 import logging
27 import logging
30 from time import mktime
28 from time import mktime
31 from datetime import datetime, timedelta, date
29 from datetime import datetime, timedelta, date
32
30
33 from vcs.exceptions import ChangesetError
31 from vcs.exceptions import ChangesetError
34
32
35 from pylons import tmpl_context as c, request, url
33 from pylons import tmpl_context as c, request, url
36 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
37
35
38 from rhodecode.model.scm import ScmModel
36 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.db import Statistics
37 from rhodecode.model.db import Statistics
40
38
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
41 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
44
42
45 from rhodecode.lib.celerylib import run_task
43 from rhodecode.lib.celerylib import run_task
46 from rhodecode.lib.celerylib.tasks import get_commits_stats
44 from rhodecode.lib.celerylib.tasks import get_commits_stats
47
45
48 from webhelpers.paginate import Page
46 from webhelpers.paginate import Page
49
47
50 try:
48 try:
51 import json
49 import json
52 except ImportError:
50 except ImportError:
53 #python 2.5 compatibility
51 #python 2.5 compatibility
54 import simplejson as json
52 import simplejson as json
55 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
56
54
57 class SummaryController(BaseController):
55 class SummaryController(BaseController):
58
56
59 @LoginRequired()
57 @LoginRequired()
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
58 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 'repository.admin')
59 'repository.admin')
62 def __before__(self):
60 def __before__(self):
63 super(SummaryController, self).__before__()
61 super(SummaryController, self).__before__()
64
62
65 def index(self):
63 def index(self):
66 scm_model = ScmModel()
64 scm_model = ScmModel()
67 c.repo_info = scm_model.get_repo(c.repo_name)
65 c.repo_info = scm_model.get_repo(c.repo_name)
68 c.following = scm_model.is_following_repo(c.repo_name,
66 c.following = scm_model.is_following_repo(c.repo_name,
69 c.rhodecode_user.user_id)
67 c.rhodecode_user.user_id)
70 def url_generator(**kw):
68 def url_generator(**kw):
71 return url('shortlog_home', repo_name=c.repo_name, **kw)
69 return url('shortlog_home', repo_name=c.repo_name, **kw)
72
70
73 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
71 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
74 url=url_generator)
72 url=url_generator)
75
73
76 e = request.environ
74 e = request.environ
77
75
78 if self.rhodecode_user.username == 'default':
76 if self.rhodecode_user.username == 'default':
79 password = ':default'
77 password = ':default'
80 else:
78 else:
81 password = ''
79 password = ''
82
80
83 uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
81 uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
84 'protocol': e.get('wsgi.url_scheme'),
82 'protocol': e.get('wsgi.url_scheme'),
85 'user':str(c.rhodecode_user.username),
83 'user':str(c.rhodecode_user.username),
86 'password':password,
84 'password':password,
87 'host':e.get('HTTP_HOST'),
85 'host':e.get('HTTP_HOST'),
88 'prefix':e.get('SCRIPT_NAME'),
86 'prefix':e.get('SCRIPT_NAME'),
89 'repo_name':c.repo_name, }
87 'repo_name':c.repo_name, }
90 c.clone_repo_url = uri
88 c.clone_repo_url = uri
91 c.repo_tags = OrderedDict()
89 c.repo_tags = OrderedDict()
92 for name, hash in c.repo_info.tags.items()[:10]:
90 for name, hash in c.repo_info.tags.items()[:10]:
93 try:
91 try:
94 c.repo_tags[name] = c.repo_info.get_changeset(hash)
92 c.repo_tags[name] = c.repo_info.get_changeset(hash)
95 except ChangesetError:
93 except ChangesetError:
96 c.repo_tags[name] = EmptyChangeset(hash)
94 c.repo_tags[name] = EmptyChangeset(hash)
97
95
98 c.repo_branches = OrderedDict()
96 c.repo_branches = OrderedDict()
99 for name, hash in c.repo_info.branches.items()[:10]:
97 for name, hash in c.repo_info.branches.items()[:10]:
100 try:
98 try:
101 c.repo_branches[name] = c.repo_info.get_changeset(hash)
99 c.repo_branches[name] = c.repo_info.get_changeset(hash)
102 except ChangesetError:
100 except ChangesetError:
103 c.repo_branches[name] = EmptyChangeset(hash)
101 c.repo_branches[name] = EmptyChangeset(hash)
104
102
105 td = date.today() + timedelta(days=1)
103 td = date.today() + timedelta(days=1)
106 td_1m = td - timedelta(days=calendar.mdays[td.month])
104 td_1m = td - timedelta(days=calendar.mdays[td.month])
107 td_1y = td - timedelta(days=365)
105 td_1y = td - timedelta(days=365)
108
106
109 ts_min_m = mktime(td_1m.timetuple())
107 ts_min_m = mktime(td_1m.timetuple())
110 ts_min_y = mktime(td_1y.timetuple())
108 ts_min_y = mktime(td_1y.timetuple())
111 ts_max_y = mktime(td.timetuple())
109 ts_max_y = mktime(td.timetuple())
112
110
113 if c.repo_info.dbrepo.enable_statistics:
111 if c.repo_info.dbrepo.enable_statistics:
114 c.no_data_msg = _('No data loaded yet')
112 c.no_data_msg = _('No data loaded yet')
115 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
113 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
116 else:
114 else:
117 c.no_data_msg = _('Statistics update are disabled for this repository')
115 c.no_data_msg = _('Statistics update are disabled for this repository')
118 c.ts_min = ts_min_m
116 c.ts_min = ts_min_m
119 c.ts_max = ts_max_y
117 c.ts_max = ts_max_y
120
118
121 stats = self.sa.query(Statistics)\
119 stats = self.sa.query(Statistics)\
122 .filter(Statistics.repository == c.repo_info.dbrepo)\
120 .filter(Statistics.repository == c.repo_info.dbrepo)\
123 .scalar()
121 .scalar()
124
122
125
123
126 if stats and stats.languages:
124 if stats and stats.languages:
127 c.no_data = False is c.repo_info.dbrepo.enable_statistics
125 c.no_data = False is c.repo_info.dbrepo.enable_statistics
128 lang_stats = json.loads(stats.languages)
126 lang_stats = json.loads(stats.languages)
129 c.commit_data = stats.commit_activity
127 c.commit_data = stats.commit_activity
130 c.overview_data = stats.commit_activity_combined
128 c.overview_data = stats.commit_activity_combined
131 c.trending_languages = json.dumps(OrderedDict(
129 c.trending_languages = json.dumps(OrderedDict(
132 sorted(lang_stats.items(), reverse=True,
130 sorted(lang_stats.items(), reverse=True,
133 key=lambda k: k[1])[:10]
131 key=lambda k: k[1])[:10]
134 )
132 )
135 )
133 )
136 else:
134 else:
137 c.commit_data = json.dumps({})
135 c.commit_data = json.dumps({})
138 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
136 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
139 c.trending_languages = json.dumps({})
137 c.trending_languages = json.dumps({})
140 c.no_data = True
138 c.no_data = True
141
139
142 return render('summary/summary.html')
140 return render('summary/summary.html')
143
141
@@ -1,53 +1,51 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.tags
3 rhodecode.controllers.tags
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Tags controller for rhodecode
6 Tags controller for rhodecode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27 import logging
25 import logging
28
26
29 from pylons import tmpl_context as c
27 from pylons import tmpl_context as c
30
28
31 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
32 from rhodecode.lib.base import BaseController, render
30 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.utils import OrderedDict
31 from rhodecode.lib.utils import OrderedDict
34 from rhodecode.model.scm import ScmModel
32 from rhodecode.model.scm import ScmModel
35
33
36 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
37
35
38 class TagsController(BaseController):
36 class TagsController(BaseController):
39
37
40 @LoginRequired()
38 @LoginRequired()
41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
39 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
42 'repository.admin')
40 'repository.admin')
43 def __before__(self):
41 def __before__(self):
44 super(TagsController, self).__before__()
42 super(TagsController, self).__before__()
45
43
46 def index(self):
44 def index(self):
47 hg_model = ScmModel()
45 hg_model = ScmModel()
48 c.repo_info = hg_model.get_repo(c.repo_name)
46 c.repo_info = hg_model.get_repo(c.repo_name)
49 c.repo_tags = OrderedDict()
47 c.repo_tags = OrderedDict()
50 for name, hash_ in c.repo_info.tags.items():
48 for name, hash_ in c.repo_info.tags.items():
51 c.repo_tags[name] = c.repo_info.get_changeset(hash_)
49 c.repo_tags[name] = c.repo_info.get_changeset(hash_)
52
50
53 return render('tags/tags.html')
51 return render('tags/tags.html')
@@ -1,614 +1,612 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
11 """
12 # This program is free software; you can redistribute it and/or
12 # This program is free software: you can redistribute it and/or modify
13 # modify it under the terms of the GNU General Public License
13 # it under the terms of the GNU General Public License as published by
14 # as published by the Free Software Foundation; version 2
14 # the Free Software Foundation, either version 3 of the License, or
15 # of the License or (at your opinion) any later version of the license.
15 # (at your option) any later version.
16 #
16 #
17 # This program is distributed in the hope that it will be useful,
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
20 # GNU General Public License for more details.
21 #
21 #
22 # You should have received a copy of the GNU General Public License
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
26
24
27 import random
25 import random
28 import logging
26 import logging
29 import traceback
27 import traceback
30
28
31 from decorator import decorator
29 from decorator import decorator
32
30
33 from pylons import config, session, url, request
31 from pylons import config, session, url, request
34 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
36
34
37 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
35 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
38
36
39 if __platform__ in PLATFORM_WIN:
37 if __platform__ in PLATFORM_WIN:
40 from hashlib import sha256
38 from hashlib import sha256
41 if __platform__ in PLATFORM_OTHERS:
39 if __platform__ in PLATFORM_OTHERS:
42 import bcrypt
40 import bcrypt
43
41
44 from rhodecode.lib import str2bool
42 from rhodecode.lib import str2bool
45 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
43 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
46 from rhodecode.lib.utils import get_repo_slug
44 from rhodecode.lib.utils import get_repo_slug
47 from rhodecode.lib.auth_ldap import AuthLdap
45 from rhodecode.lib.auth_ldap import AuthLdap
48
46
49 from rhodecode.model import meta
47 from rhodecode.model import meta
50 from rhodecode.model.user import UserModel
48 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import Permission, RepoToPerm, Repository, \
49 from rhodecode.model.db import Permission, RepoToPerm, Repository, \
52 User, UserToPerm
50 User, UserToPerm
53
51
54
52
55 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
56
54
57 class PasswordGenerator(object):
55 class PasswordGenerator(object):
58 """This is a simple class for generating password from
56 """This is a simple class for generating password from
59 different sets of characters
57 different sets of characters
60 usage:
58 usage:
61 passwd_gen = PasswordGenerator()
59 passwd_gen = PasswordGenerator()
62 #print 8-letter password containing only big and small letters of alphabet
60 #print 8-letter password containing only big and small letters of alphabet
63 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
61 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
64 """
62 """
65 ALPHABETS_NUM = r'''1234567890'''#[0]
63 ALPHABETS_NUM = r'''1234567890'''#[0]
66 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
64 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
67 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
65 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
68 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
66 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
69 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
67 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
70 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
68 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
71 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
69 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
72 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
70 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
73 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
71 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
74
72
75 def __init__(self, passwd=''):
73 def __init__(self, passwd=''):
76 self.passwd = passwd
74 self.passwd = passwd
77
75
78 def gen_password(self, len, type):
76 def gen_password(self, len, type):
79 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
77 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
80 return self.passwd
78 return self.passwd
81
79
82 class RhodeCodeCrypto(object):
80 class RhodeCodeCrypto(object):
83
81
84 @classmethod
82 @classmethod
85 def hash_string(cls, str_):
83 def hash_string(cls, str_):
86 """
84 """
87 Cryptographic function used for password hashing based on pybcrypt
85 Cryptographic function used for password hashing based on pybcrypt
88 or pycrypto in windows
86 or pycrypto in windows
89
87
90 :param password: password to hash
88 :param password: password to hash
91 """
89 """
92 if __platform__ in PLATFORM_WIN:
90 if __platform__ in PLATFORM_WIN:
93 return sha256(str_).hexdigest()
91 return sha256(str_).hexdigest()
94 elif __platform__ in PLATFORM_OTHERS:
92 elif __platform__ in PLATFORM_OTHERS:
95 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
93 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
96 else:
94 else:
97 raise Exception('Unknown or unsupported platform %s' % __platform__)
95 raise Exception('Unknown or unsupported platform %s' % __platform__)
98
96
99 @classmethod
97 @classmethod
100 def hash_check(cls, password, hashed):
98 def hash_check(cls, password, hashed):
101 """
99 """
102 Checks matching password with it's hashed value, runs different
100 Checks matching password with it's hashed value, runs different
103 implementation based on platform it runs on
101 implementation based on platform it runs on
104
102
105 :param password: password
103 :param password: password
106 :param hashed: password in hashed form
104 :param hashed: password in hashed form
107 """
105 """
108
106
109 if __platform__ in PLATFORM_WIN:
107 if __platform__ in PLATFORM_WIN:
110 return sha256(password).hexdigest() == hashed
108 return sha256(password).hexdigest() == hashed
111 elif __platform__ in PLATFORM_OTHERS:
109 elif __platform__ in PLATFORM_OTHERS:
112 return bcrypt.hashpw(password, hashed) == hashed
110 return bcrypt.hashpw(password, hashed) == hashed
113 else:
111 else:
114 raise Exception('Unknown or unsupported platform %s' % __platform__)
112 raise Exception('Unknown or unsupported platform %s' % __platform__)
115
113
116
114
117 def get_crypt_password(password):
115 def get_crypt_password(password):
118 return RhodeCodeCrypto.hash_string(password)
116 return RhodeCodeCrypto.hash_string(password)
119
117
120 def check_password(password, hashed):
118 def check_password(password, hashed):
121 return RhodeCodeCrypto.hash_check(password, hashed)
119 return RhodeCodeCrypto.hash_check(password, hashed)
122
120
123 def authfunc(environ, username, password):
121 def authfunc(environ, username, password):
124 """
122 """
125 Dummy authentication function used in Mercurial/Git/ and access control,
123 Dummy authentication function used in Mercurial/Git/ and access control,
126
124
127 :param environ: needed only for using in Basic auth
125 :param environ: needed only for using in Basic auth
128 """
126 """
129 return authenticate(username, password)
127 return authenticate(username, password)
130
128
131
129
132 def authenticate(username, password):
130 def authenticate(username, password):
133 """
131 """
134 Authentication function used for access control,
132 Authentication function used for access control,
135 firstly checks for db authentication then if ldap is enabled for ldap
133 firstly checks for db authentication then if ldap is enabled for ldap
136 authentication, also creates ldap user if not in database
134 authentication, also creates ldap user if not in database
137
135
138 :param username: username
136 :param username: username
139 :param password: password
137 :param password: password
140 """
138 """
141 user_model = UserModel()
139 user_model = UserModel()
142 user = user_model.get_by_username(username, cache=False)
140 user = user_model.get_by_username(username, cache=False)
143
141
144 log.debug('Authenticating user using RhodeCode account')
142 log.debug('Authenticating user using RhodeCode account')
145 if user is not None and user.is_ldap is False:
143 if user is not None and user.is_ldap is False:
146 if user.active:
144 if user.active:
147
145
148 if user.username == 'default' and user.active:
146 if user.username == 'default' and user.active:
149 log.info('user %s authenticated correctly as anonymous user',
147 log.info('user %s authenticated correctly as anonymous user',
150 username)
148 username)
151 return True
149 return True
152
150
153 elif user.username == username and check_password(password, user.password):
151 elif user.username == username and check_password(password, user.password):
154 log.info('user %s authenticated correctly', username)
152 log.info('user %s authenticated correctly', username)
155 return True
153 return True
156 else:
154 else:
157 log.warning('user %s is disabled', username)
155 log.warning('user %s is disabled', username)
158
156
159 else:
157 else:
160 log.debug('Regular authentication failed')
158 log.debug('Regular authentication failed')
161 user_obj = user_model.get_by_username(username, cache=False,
159 user_obj = user_model.get_by_username(username, cache=False,
162 case_insensitive=True)
160 case_insensitive=True)
163
161
164 if user_obj is not None and user_obj.is_ldap is False:
162 if user_obj is not None and user_obj.is_ldap is False:
165 log.debug('this user already exists as non ldap')
163 log.debug('this user already exists as non ldap')
166 return False
164 return False
167
165
168 from rhodecode.model.settings import SettingsModel
166 from rhodecode.model.settings import SettingsModel
169 ldap_settings = SettingsModel().get_ldap_settings()
167 ldap_settings = SettingsModel().get_ldap_settings()
170
168
171 #======================================================================
169 #======================================================================
172 # FALLBACK TO LDAP AUTH IN ENABLE
170 # FALLBACK TO LDAP AUTH IN ENABLE
173 #======================================================================
171 #======================================================================
174 if str2bool(ldap_settings.get('ldap_active')):
172 if str2bool(ldap_settings.get('ldap_active')):
175 log.debug("Authenticating user using ldap")
173 log.debug("Authenticating user using ldap")
176 kwargs = {
174 kwargs = {
177 'server':ldap_settings.get('ldap_host', ''),
175 'server':ldap_settings.get('ldap_host', ''),
178 'base_dn':ldap_settings.get('ldap_base_dn', ''),
176 'base_dn':ldap_settings.get('ldap_base_dn', ''),
179 'port':ldap_settings.get('ldap_port'),
177 'port':ldap_settings.get('ldap_port'),
180 'bind_dn':ldap_settings.get('ldap_dn_user'),
178 'bind_dn':ldap_settings.get('ldap_dn_user'),
181 'bind_pass':ldap_settings.get('ldap_dn_pass'),
179 'bind_pass':ldap_settings.get('ldap_dn_pass'),
182 'use_ldaps':str2bool(ldap_settings.get('ldap_ldaps')),
180 'use_ldaps':str2bool(ldap_settings.get('ldap_ldaps')),
183 'ldap_version':3,
181 'ldap_version':3,
184 }
182 }
185 log.debug('Checking for ldap authentication')
183 log.debug('Checking for ldap authentication')
186 try:
184 try:
187 aldap = AuthLdap(**kwargs)
185 aldap = AuthLdap(**kwargs)
188 res = aldap.authenticate_ldap(username, password)
186 res = aldap.authenticate_ldap(username, password)
189 log.debug('Got ldap response %s', res)
187 log.debug('Got ldap response %s', res)
190
188
191 if user_model.create_ldap(username, password):
189 if user_model.create_ldap(username, password):
192 log.info('created new ldap user')
190 log.info('created new ldap user')
193
191
194 return True
192 return True
195 except (LdapUsernameError, LdapPasswordError,):
193 except (LdapUsernameError, LdapPasswordError,):
196 pass
194 pass
197 except (Exception,):
195 except (Exception,):
198 log.error(traceback.format_exc())
196 log.error(traceback.format_exc())
199 pass
197 pass
200 return False
198 return False
201
199
202 class AuthUser(object):
200 class AuthUser(object):
203 """
201 """
204 A simple object that handles a mercurial username for authentication
202 A simple object that handles a mercurial username for authentication
205 """
203 """
206 def __init__(self):
204 def __init__(self):
207 self.username = 'None'
205 self.username = 'None'
208 self.name = ''
206 self.name = ''
209 self.lastname = ''
207 self.lastname = ''
210 self.email = ''
208 self.email = ''
211 self.user_id = None
209 self.user_id = None
212 self.is_authenticated = False
210 self.is_authenticated = False
213 self.is_admin = False
211 self.is_admin = False
214 self.permissions = {}
212 self.permissions = {}
215
213
216 def __repr__(self):
214 def __repr__(self):
217 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
215 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
218
216
219 def set_available_permissions(config):
217 def set_available_permissions(config):
220 """
218 """
221 This function will propagate pylons globals with all available defined
219 This function will propagate pylons globals with all available defined
222 permission given in db. We don't wannt to check each time from db for new
220 permission given in db. We don't wannt to check each time from db for new
223 permissions since adding a new permission also requires application restart
221 permissions since adding a new permission also requires application restart
224 ie. to decorate new views with the newly created permission
222 ie. to decorate new views with the newly created permission
225 :param config:
223 :param config:
226 """
224 """
227 log.info('getting information about all available permissions')
225 log.info('getting information about all available permissions')
228 try:
226 try:
229 sa = meta.Session()
227 sa = meta.Session()
230 all_perms = sa.query(Permission).all()
228 all_perms = sa.query(Permission).all()
231 except:
229 except:
232 pass
230 pass
233 finally:
231 finally:
234 meta.Session.remove()
232 meta.Session.remove()
235
233
236 config['available_permissions'] = [x.permission_name for x in all_perms]
234 config['available_permissions'] = [x.permission_name for x in all_perms]
237
235
238 def set_base_path(config):
236 def set_base_path(config):
239 config['base_path'] = config['pylons.app_globals'].base_path
237 config['base_path'] = config['pylons.app_globals'].base_path
240
238
241
239
242 def fill_perms(user):
240 def fill_perms(user):
243 """
241 """
244 Fills user permission attribute with permissions taken from database
242 Fills user permission attribute with permissions taken from database
245 :param user:
243 :param user:
246 """
244 """
247
245
248 sa = meta.Session()
246 sa = meta.Session()
249 user.permissions['repositories'] = {}
247 user.permissions['repositories'] = {}
250 user.permissions['global'] = set()
248 user.permissions['global'] = set()
251
249
252 #===========================================================================
250 #===========================================================================
253 # fetch default permissions
251 # fetch default permissions
254 #===========================================================================
252 #===========================================================================
255 default_user = UserModel().get_by_username('default', cache=True)
253 default_user = UserModel().get_by_username('default', cache=True)
256
254
257 default_perms = sa.query(RepoToPerm, Repository, Permission)\
255 default_perms = sa.query(RepoToPerm, Repository, Permission)\
258 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
256 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
259 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
257 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
260 .filter(RepoToPerm.user == default_user).all()
258 .filter(RepoToPerm.user == default_user).all()
261
259
262 if user.is_admin:
260 if user.is_admin:
263 #=======================================================================
261 #=======================================================================
264 # #admin have all default rights set to admin
262 # #admin have all default rights set to admin
265 #=======================================================================
263 #=======================================================================
266 user.permissions['global'].add('hg.admin')
264 user.permissions['global'].add('hg.admin')
267
265
268 for perm in default_perms:
266 for perm in default_perms:
269 p = 'repository.admin'
267 p = 'repository.admin'
270 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
268 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
271
269
272 else:
270 else:
273 #=======================================================================
271 #=======================================================================
274 # set default permissions
272 # set default permissions
275 #=======================================================================
273 #=======================================================================
276
274
277 #default global
275 #default global
278 default_global_perms = sa.query(UserToPerm)\
276 default_global_perms = sa.query(UserToPerm)\
279 .filter(UserToPerm.user == sa.query(User)\
277 .filter(UserToPerm.user == sa.query(User)\
280 .filter(User.username == 'default').one())
278 .filter(User.username == 'default').one())
281
279
282 for perm in default_global_perms:
280 for perm in default_global_perms:
283 user.permissions['global'].add(perm.permission.permission_name)
281 user.permissions['global'].add(perm.permission.permission_name)
284
282
285 #default repositories
283 #default repositories
286 for perm in default_perms:
284 for perm in default_perms:
287 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
285 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
288 #disable defaults for private repos,
286 #disable defaults for private repos,
289 p = 'repository.none'
287 p = 'repository.none'
290 elif perm.Repository.user_id == user.user_id:
288 elif perm.Repository.user_id == user.user_id:
291 #set admin if owner
289 #set admin if owner
292 p = 'repository.admin'
290 p = 'repository.admin'
293 else:
291 else:
294 p = perm.Permission.permission_name
292 p = perm.Permission.permission_name
295
293
296 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
294 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
297
295
298 #=======================================================================
296 #=======================================================================
299 # #overwrite default with user permissions if any
297 # #overwrite default with user permissions if any
300 #=======================================================================
298 #=======================================================================
301 user_perms = sa.query(RepoToPerm, Permission, Repository)\
299 user_perms = sa.query(RepoToPerm, Permission, Repository)\
302 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
300 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
303 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
301 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
304 .filter(RepoToPerm.user_id == user.user_id).all()
302 .filter(RepoToPerm.user_id == user.user_id).all()
305
303
306 for perm in user_perms:
304 for perm in user_perms:
307 if perm.Repository.user_id == user.user_id:#set admin if owner
305 if perm.Repository.user_id == user.user_id:#set admin if owner
308 p = 'repository.admin'
306 p = 'repository.admin'
309 else:
307 else:
310 p = perm.Permission.permission_name
308 p = perm.Permission.permission_name
311 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
309 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
312 meta.Session.remove()
310 meta.Session.remove()
313 return user
311 return user
314
312
315 def get_user(session):
313 def get_user(session):
316 """
314 """
317 Gets user from session, and wraps permissions into user
315 Gets user from session, and wraps permissions into user
318 :param session:
316 :param session:
319 """
317 """
320 user = session.get('rhodecode_user', AuthUser())
318 user = session.get('rhodecode_user', AuthUser())
321 #if the user is not logged in we check for anonymous access
319 #if the user is not logged in we check for anonymous access
322 #if user is logged and it's a default user check if we still have anonymous
320 #if user is logged and it's a default user check if we still have anonymous
323 #access enabled
321 #access enabled
324 if user.user_id is None or user.username == 'default':
322 if user.user_id is None or user.username == 'default':
325 anonymous_user = UserModel().get_by_username('default', cache=True)
323 anonymous_user = UserModel().get_by_username('default', cache=True)
326 if anonymous_user.active is True:
324 if anonymous_user.active is True:
327 #then we set this user is logged in
325 #then we set this user is logged in
328 user.is_authenticated = True
326 user.is_authenticated = True
329 user.user_id = anonymous_user.user_id
327 user.user_id = anonymous_user.user_id
330 else:
328 else:
331 user.is_authenticated = False
329 user.is_authenticated = False
332
330
333 if user.is_authenticated:
331 if user.is_authenticated:
334 user = UserModel().fill_data(user)
332 user = UserModel().fill_data(user)
335
333
336 user = fill_perms(user)
334 user = fill_perms(user)
337 session['rhodecode_user'] = user
335 session['rhodecode_user'] = user
338 session.save()
336 session.save()
339 return user
337 return user
340
338
341 #===============================================================================
339 #===============================================================================
342 # CHECK DECORATORS
340 # CHECK DECORATORS
343 #===============================================================================
341 #===============================================================================
344 class LoginRequired(object):
342 class LoginRequired(object):
345 """Must be logged in to execute this function else
343 """Must be logged in to execute this function else
346 redirect to login page"""
344 redirect to login page"""
347
345
348 def __call__(self, func):
346 def __call__(self, func):
349 return decorator(self.__wrapper, func)
347 return decorator(self.__wrapper, func)
350
348
351 def __wrapper(self, func, *fargs, **fkwargs):
349 def __wrapper(self, func, *fargs, **fkwargs):
352 user = session.get('rhodecode_user', AuthUser())
350 user = session.get('rhodecode_user', AuthUser())
353 log.debug('Checking login required for user:%s', user.username)
351 log.debug('Checking login required for user:%s', user.username)
354 if user.is_authenticated:
352 if user.is_authenticated:
355 log.debug('user %s is authenticated', user.username)
353 log.debug('user %s is authenticated', user.username)
356 return func(*fargs, **fkwargs)
354 return func(*fargs, **fkwargs)
357 else:
355 else:
358 log.warn('user %s not authenticated', user.username)
356 log.warn('user %s not authenticated', user.username)
359
357
360 p = ''
358 p = ''
361 if request.environ.get('SCRIPT_NAME') != '/':
359 if request.environ.get('SCRIPT_NAME') != '/':
362 p += request.environ.get('SCRIPT_NAME')
360 p += request.environ.get('SCRIPT_NAME')
363
361
364 p += request.environ.get('PATH_INFO')
362 p += request.environ.get('PATH_INFO')
365 if request.environ.get('QUERY_STRING'):
363 if request.environ.get('QUERY_STRING'):
366 p += '?' + request.environ.get('QUERY_STRING')
364 p += '?' + request.environ.get('QUERY_STRING')
367
365
368 log.debug('redirecting to login page with %s', p)
366 log.debug('redirecting to login page with %s', p)
369 return redirect(url('login_home', came_from=p))
367 return redirect(url('login_home', came_from=p))
370
368
371 class NotAnonymous(object):
369 class NotAnonymous(object):
372 """Must be logged in to execute this function else
370 """Must be logged in to execute this function else
373 redirect to login page"""
371 redirect to login page"""
374
372
375 def __call__(self, func):
373 def __call__(self, func):
376 return decorator(self.__wrapper, func)
374 return decorator(self.__wrapper, func)
377
375
378 def __wrapper(self, func, *fargs, **fkwargs):
376 def __wrapper(self, func, *fargs, **fkwargs):
379 user = session.get('rhodecode_user', AuthUser())
377 user = session.get('rhodecode_user', AuthUser())
380 log.debug('Checking if user is not anonymous')
378 log.debug('Checking if user is not anonymous')
381
379
382 anonymous = user.username == 'default'
380 anonymous = user.username == 'default'
383
381
384 if anonymous:
382 if anonymous:
385 p = ''
383 p = ''
386 if request.environ.get('SCRIPT_NAME') != '/':
384 if request.environ.get('SCRIPT_NAME') != '/':
387 p += request.environ.get('SCRIPT_NAME')
385 p += request.environ.get('SCRIPT_NAME')
388
386
389 p += request.environ.get('PATH_INFO')
387 p += request.environ.get('PATH_INFO')
390 if request.environ.get('QUERY_STRING'):
388 if request.environ.get('QUERY_STRING'):
391 p += '?' + request.environ.get('QUERY_STRING')
389 p += '?' + request.environ.get('QUERY_STRING')
392 return redirect(url('login_home', came_from=p))
390 return redirect(url('login_home', came_from=p))
393 else:
391 else:
394 return func(*fargs, **fkwargs)
392 return func(*fargs, **fkwargs)
395
393
396 class PermsDecorator(object):
394 class PermsDecorator(object):
397 """Base class for decorators"""
395 """Base class for decorators"""
398
396
399 def __init__(self, *required_perms):
397 def __init__(self, *required_perms):
400 available_perms = config['available_permissions']
398 available_perms = config['available_permissions']
401 for perm in required_perms:
399 for perm in required_perms:
402 if perm not in available_perms:
400 if perm not in available_perms:
403 raise Exception("'%s' permission is not defined" % perm)
401 raise Exception("'%s' permission is not defined" % perm)
404 self.required_perms = set(required_perms)
402 self.required_perms = set(required_perms)
405 self.user_perms = None
403 self.user_perms = None
406
404
407 def __call__(self, func):
405 def __call__(self, func):
408 return decorator(self.__wrapper, func)
406 return decorator(self.__wrapper, func)
409
407
410
408
411 def __wrapper(self, func, *fargs, **fkwargs):
409 def __wrapper(self, func, *fargs, **fkwargs):
412 # _wrapper.__name__ = func.__name__
410 # _wrapper.__name__ = func.__name__
413 # _wrapper.__dict__.update(func.__dict__)
411 # _wrapper.__dict__.update(func.__dict__)
414 # _wrapper.__doc__ = func.__doc__
412 # _wrapper.__doc__ = func.__doc__
415 self.user = session.get('rhodecode_user', AuthUser())
413 self.user = session.get('rhodecode_user', AuthUser())
416 self.user_perms = self.user.permissions
414 self.user_perms = self.user.permissions
417 log.debug('checking %s permissions %s for %s %s',
415 log.debug('checking %s permissions %s for %s %s',
418 self.__class__.__name__, self.required_perms, func.__name__,
416 self.__class__.__name__, self.required_perms, func.__name__,
419 self.user)
417 self.user)
420
418
421 if self.check_permissions():
419 if self.check_permissions():
422 log.debug('Permission granted for %s %s', func.__name__, self.user)
420 log.debug('Permission granted for %s %s', func.__name__, self.user)
423
421
424 return func(*fargs, **fkwargs)
422 return func(*fargs, **fkwargs)
425
423
426 else:
424 else:
427 log.warning('Permission denied for %s %s', func.__name__, self.user)
425 log.warning('Permission denied for %s %s', func.__name__, self.user)
428 #redirect with forbidden ret code
426 #redirect with forbidden ret code
429 return abort(403)
427 return abort(403)
430
428
431
429
432
430
433 def check_permissions(self):
431 def check_permissions(self):
434 """Dummy function for overriding"""
432 """Dummy function for overriding"""
435 raise Exception('You have to write this function in child class')
433 raise Exception('You have to write this function in child class')
436
434
437 class HasPermissionAllDecorator(PermsDecorator):
435 class HasPermissionAllDecorator(PermsDecorator):
438 """Checks for access permission for all given predicates. All of them
436 """Checks for access permission for all given predicates. All of them
439 have to be meet in order to fulfill the request
437 have to be meet in order to fulfill the request
440 """
438 """
441
439
442 def check_permissions(self):
440 def check_permissions(self):
443 if self.required_perms.issubset(self.user_perms.get('global')):
441 if self.required_perms.issubset(self.user_perms.get('global')):
444 return True
442 return True
445 return False
443 return False
446
444
447
445
448 class HasPermissionAnyDecorator(PermsDecorator):
446 class HasPermissionAnyDecorator(PermsDecorator):
449 """Checks for access permission for any of given predicates. In order to
447 """Checks for access permission for any of given predicates. In order to
450 fulfill the request any of predicates must be meet
448 fulfill the request any of predicates must be meet
451 """
449 """
452
450
453 def check_permissions(self):
451 def check_permissions(self):
454 if self.required_perms.intersection(self.user_perms.get('global')):
452 if self.required_perms.intersection(self.user_perms.get('global')):
455 return True
453 return True
456 return False
454 return False
457
455
458 class HasRepoPermissionAllDecorator(PermsDecorator):
456 class HasRepoPermissionAllDecorator(PermsDecorator):
459 """Checks for access permission for all given predicates for specific
457 """Checks for access permission for all given predicates for specific
460 repository. All of them have to be meet in order to fulfill the request
458 repository. All of them have to be meet in order to fulfill the request
461 """
459 """
462
460
463 def check_permissions(self):
461 def check_permissions(self):
464 repo_name = get_repo_slug(request)
462 repo_name = get_repo_slug(request)
465 try:
463 try:
466 user_perms = set([self.user_perms['repositories'][repo_name]])
464 user_perms = set([self.user_perms['repositories'][repo_name]])
467 except KeyError:
465 except KeyError:
468 return False
466 return False
469 if self.required_perms.issubset(user_perms):
467 if self.required_perms.issubset(user_perms):
470 return True
468 return True
471 return False
469 return False
472
470
473
471
474 class HasRepoPermissionAnyDecorator(PermsDecorator):
472 class HasRepoPermissionAnyDecorator(PermsDecorator):
475 """Checks for access permission for any of given predicates for specific
473 """Checks for access permission for any of given predicates for specific
476 repository. In order to fulfill the request any of predicates must be meet
474 repository. In order to fulfill the request any of predicates must be meet
477 """
475 """
478
476
479 def check_permissions(self):
477 def check_permissions(self):
480 repo_name = get_repo_slug(request)
478 repo_name = get_repo_slug(request)
481
479
482 try:
480 try:
483 user_perms = set([self.user_perms['repositories'][repo_name]])
481 user_perms = set([self.user_perms['repositories'][repo_name]])
484 except KeyError:
482 except KeyError:
485 return False
483 return False
486 if self.required_perms.intersection(user_perms):
484 if self.required_perms.intersection(user_perms):
487 return True
485 return True
488 return False
486 return False
489 #===============================================================================
487 #===============================================================================
490 # CHECK FUNCTIONS
488 # CHECK FUNCTIONS
491 #===============================================================================
489 #===============================================================================
492
490
493 class PermsFunction(object):
491 class PermsFunction(object):
494 """Base function for other check functions"""
492 """Base function for other check functions"""
495
493
496 def __init__(self, *perms):
494 def __init__(self, *perms):
497 available_perms = config['available_permissions']
495 available_perms = config['available_permissions']
498
496
499 for perm in perms:
497 for perm in perms:
500 if perm not in available_perms:
498 if perm not in available_perms:
501 raise Exception("'%s' permission in not defined" % perm)
499 raise Exception("'%s' permission in not defined" % perm)
502 self.required_perms = set(perms)
500 self.required_perms = set(perms)
503 self.user_perms = None
501 self.user_perms = None
504 self.granted_for = ''
502 self.granted_for = ''
505 self.repo_name = None
503 self.repo_name = None
506
504
507 def __call__(self, check_Location=''):
505 def __call__(self, check_Location=''):
508 user = session.get('rhodecode_user', False)
506 user = session.get('rhodecode_user', False)
509 if not user:
507 if not user:
510 return False
508 return False
511 self.user_perms = user.permissions
509 self.user_perms = user.permissions
512 self.granted_for = user.username
510 self.granted_for = user.username
513 log.debug('checking %s %s %s', self.__class__.__name__,
511 log.debug('checking %s %s %s', self.__class__.__name__,
514 self.required_perms, user)
512 self.required_perms, user)
515
513
516 if self.check_permissions():
514 if self.check_permissions():
517 log.debug('Permission granted for %s @ %s %s', self.granted_for,
515 log.debug('Permission granted for %s @ %s %s', self.granted_for,
518 check_Location, user)
516 check_Location, user)
519 return True
517 return True
520
518
521 else:
519 else:
522 log.warning('Permission denied for %s @ %s %s', self.granted_for,
520 log.warning('Permission denied for %s @ %s %s', self.granted_for,
523 check_Location, user)
521 check_Location, user)
524 return False
522 return False
525
523
526 def check_permissions(self):
524 def check_permissions(self):
527 """Dummy function for overriding"""
525 """Dummy function for overriding"""
528 raise Exception('You have to write this function in child class')
526 raise Exception('You have to write this function in child class')
529
527
530 class HasPermissionAll(PermsFunction):
528 class HasPermissionAll(PermsFunction):
531 def check_permissions(self):
529 def check_permissions(self):
532 if self.required_perms.issubset(self.user_perms.get('global')):
530 if self.required_perms.issubset(self.user_perms.get('global')):
533 return True
531 return True
534 return False
532 return False
535
533
536 class HasPermissionAny(PermsFunction):
534 class HasPermissionAny(PermsFunction):
537 def check_permissions(self):
535 def check_permissions(self):
538 if self.required_perms.intersection(self.user_perms.get('global')):
536 if self.required_perms.intersection(self.user_perms.get('global')):
539 return True
537 return True
540 return False
538 return False
541
539
542 class HasRepoPermissionAll(PermsFunction):
540 class HasRepoPermissionAll(PermsFunction):
543
541
544 def __call__(self, repo_name=None, check_Location=''):
542 def __call__(self, repo_name=None, check_Location=''):
545 self.repo_name = repo_name
543 self.repo_name = repo_name
546 return super(HasRepoPermissionAll, self).__call__(check_Location)
544 return super(HasRepoPermissionAll, self).__call__(check_Location)
547
545
548 def check_permissions(self):
546 def check_permissions(self):
549 if not self.repo_name:
547 if not self.repo_name:
550 self.repo_name = get_repo_slug(request)
548 self.repo_name = get_repo_slug(request)
551
549
552 try:
550 try:
553 self.user_perms = set([self.user_perms['repositories']\
551 self.user_perms = set([self.user_perms['repositories']\
554 [self.repo_name]])
552 [self.repo_name]])
555 except KeyError:
553 except KeyError:
556 return False
554 return False
557 self.granted_for = self.repo_name
555 self.granted_for = self.repo_name
558 if self.required_perms.issubset(self.user_perms):
556 if self.required_perms.issubset(self.user_perms):
559 return True
557 return True
560 return False
558 return False
561
559
562 class HasRepoPermissionAny(PermsFunction):
560 class HasRepoPermissionAny(PermsFunction):
563
561
564 def __call__(self, repo_name=None, check_Location=''):
562 def __call__(self, repo_name=None, check_Location=''):
565 self.repo_name = repo_name
563 self.repo_name = repo_name
566 return super(HasRepoPermissionAny, self).__call__(check_Location)
564 return super(HasRepoPermissionAny, self).__call__(check_Location)
567
565
568 def check_permissions(self):
566 def check_permissions(self):
569 if not self.repo_name:
567 if not self.repo_name:
570 self.repo_name = get_repo_slug(request)
568 self.repo_name = get_repo_slug(request)
571
569
572 try:
570 try:
573 self.user_perms = set([self.user_perms['repositories']\
571 self.user_perms = set([self.user_perms['repositories']\
574 [self.repo_name]])
572 [self.repo_name]])
575 except KeyError:
573 except KeyError:
576 return False
574 return False
577 self.granted_for = self.repo_name
575 self.granted_for = self.repo_name
578 if self.required_perms.intersection(self.user_perms):
576 if self.required_perms.intersection(self.user_perms):
579 return True
577 return True
580 return False
578 return False
581
579
582 #===============================================================================
580 #===============================================================================
583 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
581 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
584 #===============================================================================
582 #===============================================================================
585
583
586 class HasPermissionAnyMiddleware(object):
584 class HasPermissionAnyMiddleware(object):
587 def __init__(self, *perms):
585 def __init__(self, *perms):
588 self.required_perms = set(perms)
586 self.required_perms = set(perms)
589
587
590 def __call__(self, user, repo_name):
588 def __call__(self, user, repo_name):
591 usr = AuthUser()
589 usr = AuthUser()
592 usr.user_id = user.user_id
590 usr.user_id = user.user_id
593 usr.username = user.username
591 usr.username = user.username
594 usr.is_admin = user.admin
592 usr.is_admin = user.admin
595
593
596 try:
594 try:
597 self.user_perms = set([fill_perms(usr)\
595 self.user_perms = set([fill_perms(usr)\
598 .permissions['repositories'][repo_name]])
596 .permissions['repositories'][repo_name]])
599 except:
597 except:
600 self.user_perms = set()
598 self.user_perms = set()
601 self.granted_for = ''
599 self.granted_for = ''
602 self.username = user.username
600 self.username = user.username
603 self.repo_name = repo_name
601 self.repo_name = repo_name
604 return self.check_permissions()
602 return self.check_permissions()
605
603
606 def check_permissions(self):
604 def check_permissions(self):
607 log.debug('checking mercurial protocol '
605 log.debug('checking mercurial protocol '
608 'permissions for user:%s repository:%s',
606 'permissions for user:%s repository:%s',
609 self.username, self.repo_name)
607 self.username, self.repo_name)
610 if self.required_perms.intersection(self.user_perms):
608 if self.required_perms.intersection(self.user_perms):
611 log.debug('permission granted')
609 log.debug('permission granted')
612 return True
610 return True
613 log.debug('permission denied')
611 log.debug('permission denied')
614 return False
612 return False
@@ -1,104 +1,102 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # ldap authentication lib
3 # ldap authentication lib
4 # Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software: you can redistribute it and/or modify
7 # modify it under the terms of the GNU General Public License
7 # it under the terms of the GNU General Public License as published by
8 # as published by the Free Software Foundation; version 2
8 # the Free Software Foundation, either version 3 of the License, or
9 # of the License or (at your opinion) any later version of the license.
9 # (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
18 """
21 Created on Nov 17, 2010
19 Created on Nov 17, 2010
22
20
23 @author: marcink
21 @author: marcink
24 """
22 """
25
23
26 from rhodecode.lib.exceptions import *
24 from rhodecode.lib.exceptions import *
27 import logging
25 import logging
28
26
29 log = logging.getLogger(__name__)
27 log = logging.getLogger(__name__)
30
28
31 try:
29 try:
32 import ldap
30 import ldap
33 except ImportError:
31 except ImportError:
34 pass
32 pass
35
33
36 class AuthLdap(object):
34 class AuthLdap(object):
37
35
38 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
36 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
39 use_ldaps=False, ldap_version=3):
37 use_ldaps=False, ldap_version=3):
40 self.ldap_version = ldap_version
38 self.ldap_version = ldap_version
41 if use_ldaps:
39 if use_ldaps:
42 port = port or 689
40 port = port or 689
43 self.LDAP_USE_LDAPS = use_ldaps
41 self.LDAP_USE_LDAPS = use_ldaps
44 self.LDAP_SERVER_ADDRESS = server
42 self.LDAP_SERVER_ADDRESS = server
45 self.LDAP_SERVER_PORT = port
43 self.LDAP_SERVER_PORT = port
46
44
47 #USE FOR READ ONLY BIND TO LDAP SERVER
45 #USE FOR READ ONLY BIND TO LDAP SERVER
48 self.LDAP_BIND_DN = bind_dn
46 self.LDAP_BIND_DN = bind_dn
49 self.LDAP_BIND_PASS = bind_pass
47 self.LDAP_BIND_PASS = bind_pass
50
48
51 ldap_server_type = 'ldap'
49 ldap_server_type = 'ldap'
52 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
50 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
53 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
51 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
54 self.LDAP_SERVER_ADDRESS,
52 self.LDAP_SERVER_ADDRESS,
55 self.LDAP_SERVER_PORT)
53 self.LDAP_SERVER_PORT)
56
54
57 self.BASE_DN = base_dn
55 self.BASE_DN = base_dn
58
56
59 def authenticate_ldap(self, username, password):
57 def authenticate_ldap(self, username, password):
60 """Authenticate a user via LDAP and return his/her LDAP properties.
58 """Authenticate a user via LDAP and return his/her LDAP properties.
61
59
62 Raises AuthenticationError if the credentials are rejected, or
60 Raises AuthenticationError if the credentials are rejected, or
63 EnvironmentError if the LDAP server can't be reached.
61 EnvironmentError if the LDAP server can't be reached.
64
62
65 :param username: username
63 :param username: username
66 :param password: password
64 :param password: password
67 """
65 """
68
66
69 from rhodecode.lib.helpers import chop_at
67 from rhodecode.lib.helpers import chop_at
70
68
71 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
69 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
72
70
73 if "," in username:
71 if "," in username:
74 raise LdapUsernameError("invalid character in username: ,")
72 raise LdapUsernameError("invalid character in username: ,")
75 try:
73 try:
76 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
74 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
77 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
75 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
78 server = ldap.initialize(self.LDAP_SERVER)
76 server = ldap.initialize(self.LDAP_SERVER)
79 if self.ldap_version == 2:
77 if self.ldap_version == 2:
80 server.protocol = ldap.VERSION2
78 server.protocol = ldap.VERSION2
81 else:
79 else:
82 server.protocol = ldap.VERSION3
80 server.protocol = ldap.VERSION3
83
81
84 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
82 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
85 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
83 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
86
84
87 dn = self.BASE_DN % {'user':uid}
85 dn = self.BASE_DN % {'user':uid}
88 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
86 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
89 server.simple_bind_s(dn, password)
87 server.simple_bind_s(dn, password)
90
88
91 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
89 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
92 if not properties:
90 if not properties:
93 raise ldap.NO_SUCH_OBJECT()
91 raise ldap.NO_SUCH_OBJECT()
94 except ldap.NO_SUCH_OBJECT, e:
92 except ldap.NO_SUCH_OBJECT, e:
95 log.debug("LDAP says no such user '%s' (%s)", uid, username)
93 log.debug("LDAP says no such user '%s' (%s)", uid, username)
96 raise LdapUsernameError()
94 raise LdapUsernameError()
97 except ldap.INVALID_CREDENTIALS, e:
95 except ldap.INVALID_CREDENTIALS, e:
98 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
96 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
99 raise LdapPasswordError()
97 raise LdapPasswordError()
100 except ldap.SERVER_DOWN, e:
98 except ldap.SERVER_DOWN, e:
101 raise LdapConnectionError("LDAP can't access authentication server")
99 raise LdapConnectionError("LDAP can't access authentication server")
102
100
103 return properties[0]
101 return properties[0]
104
102
@@ -1,108 +1,106 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # mercurial repository backup manager
3 # mercurial repository backup manager
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5
5
6 # This program is free software; you can redistribute it and/or
6 # This program is free software: you can redistribute it and/or modify
7 # modify it under the terms of the GNU General Public License
7 # it under the terms of the GNU General Public License as published by
8 # as published by the Free Software Foundation; version 2
8 # the Free Software Foundation, either version 3 of the License, or
9 # of the License or (at your opinion) any later version of the license.
9 # (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20
18
21 """
19 """
22 Created on Feb 28, 2010
20 Created on Feb 28, 2010
23 Mercurial repositories backup manager
21 Mercurial repositories backup manager
24 @author: marcink
22 @author: marcink
25 """
23 """
26
24
27
25
28 import logging
26 import logging
29 import tarfile
27 import tarfile
30 import os
28 import os
31 import datetime
29 import datetime
32 import sys
30 import sys
33 import subprocess
31 import subprocess
34 logging.basicConfig(level=logging.DEBUG,
32 logging.basicConfig(level=logging.DEBUG,
35 format="%(asctime)s %(levelname)-5.5s %(message)s")
33 format="%(asctime)s %(levelname)-5.5s %(message)s")
36
34
37 class BackupManager(object):
35 class BackupManager(object):
38 def __init__(self, repos_location, rsa_key, backup_server):
36 def __init__(self, repos_location, rsa_key, backup_server):
39 today = datetime.datetime.now().weekday() + 1
37 today = datetime.datetime.now().weekday() + 1
40 self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
38 self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
41
39
42 self.id_rsa_path = self.get_id_rsa(rsa_key)
40 self.id_rsa_path = self.get_id_rsa(rsa_key)
43 self.repos_path = self.get_repos_path(repos_location)
41 self.repos_path = self.get_repos_path(repos_location)
44 self.backup_server = backup_server
42 self.backup_server = backup_server
45
43
46 self.backup_file_path = '/tmp'
44 self.backup_file_path = '/tmp'
47
45
48 logging.info('starting backup for %s', self.repos_path)
46 logging.info('starting backup for %s', self.repos_path)
49 logging.info('backup target %s', self.backup_file_path)
47 logging.info('backup target %s', self.backup_file_path)
50
48
51
49
52 def get_id_rsa(self, rsa_key):
50 def get_id_rsa(self, rsa_key):
53 if not os.path.isfile(rsa_key):
51 if not os.path.isfile(rsa_key):
54 logging.error('Could not load id_rsa key file in %s', rsa_key)
52 logging.error('Could not load id_rsa key file in %s', rsa_key)
55 sys.exit()
53 sys.exit()
56 return rsa_key
54 return rsa_key
57
55
58 def get_repos_path(self, path):
56 def get_repos_path(self, path):
59 if not os.path.isdir(path):
57 if not os.path.isdir(path):
60 logging.error('Wrong location for repositories in %s', path)
58 logging.error('Wrong location for repositories in %s', path)
61 sys.exit()
59 sys.exit()
62 return path
60 return path
63
61
64 def backup_repos(self):
62 def backup_repos(self):
65 bckp_file = os.path.join(self.backup_file_path, self.backup_file_name)
63 bckp_file = os.path.join(self.backup_file_path, self.backup_file_name)
66 tar = tarfile.open(bckp_file, "w:gz")
64 tar = tarfile.open(bckp_file, "w:gz")
67
65
68 for dir_name in os.listdir(self.repos_path):
66 for dir_name in os.listdir(self.repos_path):
69 logging.info('backing up %s', dir_name)
67 logging.info('backing up %s', dir_name)
70 tar.add(os.path.join(self.repos_path, dir_name), dir_name)
68 tar.add(os.path.join(self.repos_path, dir_name), dir_name)
71 tar.close()
69 tar.close()
72 logging.info('finished backup of mercurial repositories')
70 logging.info('finished backup of mercurial repositories')
73
71
74
72
75
73
76 def transfer_files(self):
74 def transfer_files(self):
77 params = {
75 params = {
78 'id_rsa_key': self.id_rsa_path,
76 'id_rsa_key': self.id_rsa_path,
79 'backup_file':os.path.join(self.backup_file_path,
77 'backup_file':os.path.join(self.backup_file_path,
80 self.backup_file_name),
78 self.backup_file_name),
81 'backup_server':self.backup_server
79 'backup_server':self.backup_server
82 }
80 }
83 cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params,
81 cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params,
84 '%(backup_file)s' % params,
82 '%(backup_file)s' % params,
85 '%(backup_server)s' % params]
83 '%(backup_server)s' % params]
86
84
87 subprocess.call(cmd)
85 subprocess.call(cmd)
88 logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4])
86 logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4])
89
87
90
88
91 def rm_file(self):
89 def rm_file(self):
92 logging.info('Removing file %s', self.backup_file_name)
90 logging.info('Removing file %s', self.backup_file_name)
93 os.remove(os.path.join(self.backup_file_path, self.backup_file_name))
91 os.remove(os.path.join(self.backup_file_path, self.backup_file_name))
94
92
95
93
96
94
97 if __name__ == "__main__":
95 if __name__ == "__main__":
98
96
99 repo_location = '/home/repo_path'
97 repo_location = '/home/repo_path'
100 backup_server = 'root@192.168.1.100:/backups/mercurial'
98 backup_server = 'root@192.168.1.100:/backups/mercurial'
101 rsa_key = '/home/id_rsa'
99 rsa_key = '/home/id_rsa'
102
100
103 B_MANAGER = BackupManager(repo_location, rsa_key, backup_server)
101 B_MANAGER = BackupManager(repo_location, rsa_key, backup_server)
104 B_MANAGER.backup_repos()
102 B_MANAGER.backup_repos()
105 B_MANAGER.transfer_files()
103 B_MANAGER.transfer_files()
106 B_MANAGER.rm_file()
104 B_MANAGER.rm_file()
107
105
108
106
@@ -1,106 +1,104 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.rhodecode.lib.celerylib.__init__
3 package.rhodecode.lib.celerylib.__init__
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~
5
5
6 celery libs for RhodeCode
6 celery libs for RhodeCode
7
7
8 :created_on: Nov 27, 2010
8 :created_on: Nov 27, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import os
26 import os
29 import sys
27 import sys
30 import socket
28 import socket
31 import traceback
29 import traceback
32 import logging
30 import logging
33
31
34 from hashlib import md5
32 from hashlib import md5
35 from decorator import decorator
33 from decorator import decorator
36 from vcs.utils.lazy import LazyProperty
34 from vcs.utils.lazy import LazyProperty
37
35
38 from rhodecode.lib.pidlock import DaemonLock, LockHeld
36 from rhodecode.lib.pidlock import DaemonLock, LockHeld
39
37
40 from pylons import config
38 from pylons import config
41
39
42 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
43
41
44 def str2bool(v):
42 def str2bool(v):
45 return v.lower() in ["yes", "true", "t", "1"] if v else None
43 return v.lower() in ["yes", "true", "t", "1"] if v else None
46
44
47 try:
45 try:
48 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
46 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
49 except KeyError:
47 except KeyError:
50 CELERY_ON = False
48 CELERY_ON = False
51
49
52 class ResultWrapper(object):
50 class ResultWrapper(object):
53 def __init__(self, task):
51 def __init__(self, task):
54 self.task = task
52 self.task = task
55
53
56 @LazyProperty
54 @LazyProperty
57 def result(self):
55 def result(self):
58 return self.task
56 return self.task
59
57
60 def run_task(task, *args, **kwargs):
58 def run_task(task, *args, **kwargs):
61 if CELERY_ON:
59 if CELERY_ON:
62 try:
60 try:
63 t = task.delay(*args, **kwargs)
61 t = task.delay(*args, **kwargs)
64 log.info('running task %s:%s', t.task_id, task)
62 log.info('running task %s:%s', t.task_id, task)
65 return t
63 return t
66 except socket.error, e:
64 except socket.error, e:
67 if e.errno == 111:
65 if e.errno == 111:
68 log.debug('Unable to connect to celeryd. Sync execution')
66 log.debug('Unable to connect to celeryd. Sync execution')
69 else:
67 else:
70 log.error(traceback.format_exc())
68 log.error(traceback.format_exc())
71 except KeyError, e:
69 except KeyError, e:
72 log.debug('Unable to connect to celeryd. Sync execution')
70 log.debug('Unable to connect to celeryd. Sync execution')
73 except Exception, e:
71 except Exception, e:
74 log.error(traceback.format_exc())
72 log.error(traceback.format_exc())
75
73
76 log.debug('executing task %s in sync mode', task)
74 log.debug('executing task %s in sync mode', task)
77 return ResultWrapper(task(*args, **kwargs))
75 return ResultWrapper(task(*args, **kwargs))
78
76
79
77
80 def locked_task(func):
78 def locked_task(func):
81 def __wrapper(func, *fargs, **fkwargs):
79 def __wrapper(func, *fargs, **fkwargs):
82 params = list(fargs)
80 params = list(fargs)
83 params.extend(['%s-%s' % ar for ar in fkwargs.items()])
81 params.extend(['%s-%s' % ar for ar in fkwargs.items()])
84
82
85 lockkey = 'task_%s' % \
83 lockkey = 'task_%s' % \
86 md5(str(func.__name__) + '-' + \
84 md5(str(func.__name__) + '-' + \
87 '-'.join(map(str, params))).hexdigest()
85 '-'.join(map(str, params))).hexdigest()
88 log.info('running task with lockkey %s', lockkey)
86 log.info('running task with lockkey %s', lockkey)
89 try:
87 try:
90 l = DaemonLock(lockkey)
88 l = DaemonLock(lockkey)
91 ret = func(*fargs, **fkwargs)
89 ret = func(*fargs, **fkwargs)
92 l.release()
90 l.release()
93 return ret
91 return ret
94 except LockHeld:
92 except LockHeld:
95 log.info('LockHeld')
93 log.info('LockHeld')
96 return 'Task with key %s already running' % lockkey
94 return 'Task with key %s already running' % lockkey
97
95
98 return decorator(__wrapper, func)
96 return decorator(__wrapper, func)
99
97
100
98
101
99
102
100
103
101
104
102
105
103
106
104
@@ -1,405 +1,403 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.celerylib.tasks
3 rhodecode.lib.celerylib.tasks
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode task modules, containing all task that suppose to be run
6 RhodeCode task modules, containing all task that suppose to be run
7 by celery daemon
7 by celery daemon
8
8
9 :created_on: Oct 6, 2010
9 :created_on: Oct 6, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software; you can redistribute it and/or
14 # This program is free software: you can redistribute it and/or modify
15 # modify it under the terms of the GNU General Public License
15 # it under the terms of the GNU General Public License as published by
16 # as published by the Free Software Foundation; version 2
16 # the Free Software Foundation, either version 3 of the License, or
17 # of the License or (at your opinion) any later version of the license.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 # MA 02110-1301, USA.
28 from celery.decorators import task
26 from celery.decorators import task
29
27
30 import os
28 import os
31 import traceback
29 import traceback
32 import logging
30 import logging
33
31
34 from time import mktime
32 from time import mktime
35 from operator import itemgetter
33 from operator import itemgetter
36
34
37 from pylons import config
35 from pylons import config
38 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
39
37
40 from rhodecode.lib.celerylib import run_task, locked_task, str2bool
38 from rhodecode.lib.celerylib import run_task, locked_task, str2bool
41 from rhodecode.lib.helpers import person
39 from rhodecode.lib.helpers import person
42 from rhodecode.lib.smtp_mailer import SmtpMailer
40 from rhodecode.lib.smtp_mailer import SmtpMailer
43 from rhodecode.lib.utils import OrderedDict, add_cache
41 from rhodecode.lib.utils import OrderedDict, add_cache
44 from rhodecode.model import init_model
42 from rhodecode.model import init_model
45 from rhodecode.model import meta
43 from rhodecode.model import meta
46 from rhodecode.model.db import RhodeCodeUi
44 from rhodecode.model.db import RhodeCodeUi
47
45
48 from vcs.backends import get_repo
46 from vcs.backends import get_repo
49
47
50 from sqlalchemy import engine_from_config
48 from sqlalchemy import engine_from_config
51
49
52 add_cache(config)
50 add_cache(config)
53
51
54 try:
52 try:
55 import json
53 import json
56 except ImportError:
54 except ImportError:
57 #python 2.5 compatibility
55 #python 2.5 compatibility
58 import simplejson as json
56 import simplejson as json
59
57
60 __all__ = ['whoosh_index', 'get_commits_stats',
58 __all__ = ['whoosh_index', 'get_commits_stats',
61 'reset_user_password', 'send_email']
59 'reset_user_password', 'send_email']
62
60
63 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
61 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
64
62
65 def get_session():
63 def get_session():
66 if CELERY_ON:
64 if CELERY_ON:
67 engine = engine_from_config(config, 'sqlalchemy.db1.')
65 engine = engine_from_config(config, 'sqlalchemy.db1.')
68 init_model(engine)
66 init_model(engine)
69 sa = meta.Session()
67 sa = meta.Session()
70 return sa
68 return sa
71
69
72 def get_repos_path():
70 def get_repos_path():
73 sa = get_session()
71 sa = get_session()
74 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
72 q = sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
75 return q.ui_value
73 return q.ui_value
76
74
77 @task(ignore_result=True)
75 @task(ignore_result=True)
78 @locked_task
76 @locked_task
79 def whoosh_index(repo_location, full_index):
77 def whoosh_index(repo_location, full_index):
80 #log = whoosh_index.get_logger()
78 #log = whoosh_index.get_logger()
81 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
79 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
82 index_location = config['index_dir']
80 index_location = config['index_dir']
83 WhooshIndexingDaemon(index_location=index_location,
81 WhooshIndexingDaemon(index_location=index_location,
84 repo_location=repo_location, sa=get_session())\
82 repo_location=repo_location, sa=get_session())\
85 .run(full_index=full_index)
83 .run(full_index=full_index)
86
84
87 @task(ignore_result=True)
85 @task(ignore_result=True)
88 @locked_task
86 @locked_task
89 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
87 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
90 try:
88 try:
91 log = get_commits_stats.get_logger()
89 log = get_commits_stats.get_logger()
92 except:
90 except:
93 log = logging.getLogger(__name__)
91 log = logging.getLogger(__name__)
94
92
95 from rhodecode.model.db import Statistics, Repository
93 from rhodecode.model.db import Statistics, Repository
96
94
97 #for js data compatibilty
95 #for js data compatibilty
98 author_key_cleaner = lambda k: person(k).replace('"', "")
96 author_key_cleaner = lambda k: person(k).replace('"', "")
99
97
100 commits_by_day_author_aggregate = {}
98 commits_by_day_author_aggregate = {}
101 commits_by_day_aggregate = {}
99 commits_by_day_aggregate = {}
102 repos_path = get_repos_path()
100 repos_path = get_repos_path()
103 p = os.path.join(repos_path, repo_name)
101 p = os.path.join(repos_path, repo_name)
104 repo = get_repo(p)
102 repo = get_repo(p)
105
103
106 skip_date_limit = True
104 skip_date_limit = True
107 parse_limit = 250 #limit for single task changeset parsing optimal for
105 parse_limit = 250 #limit for single task changeset parsing optimal for
108 last_rev = 0
106 last_rev = 0
109 last_cs = None
107 last_cs = None
110 timegetter = itemgetter('time')
108 timegetter = itemgetter('time')
111
109
112 sa = get_session()
110 sa = get_session()
113
111
114 dbrepo = sa.query(Repository)\
112 dbrepo = sa.query(Repository)\
115 .filter(Repository.repo_name == repo_name).scalar()
113 .filter(Repository.repo_name == repo_name).scalar()
116 cur_stats = sa.query(Statistics)\
114 cur_stats = sa.query(Statistics)\
117 .filter(Statistics.repository == dbrepo).scalar()
115 .filter(Statistics.repository == dbrepo).scalar()
118 if cur_stats:
116 if cur_stats:
119 last_rev = cur_stats.stat_on_revision
117 last_rev = cur_stats.stat_on_revision
120 if not repo.revisions:
118 if not repo.revisions:
121 return True
119 return True
122
120
123 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
121 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
124 #pass silently without any work if we're not on first revision or
122 #pass silently without any work if we're not on first revision or
125 #current state of parsing revision(from db marker) is the last revision
123 #current state of parsing revision(from db marker) is the last revision
126 return True
124 return True
127
125
128 if cur_stats:
126 if cur_stats:
129 commits_by_day_aggregate = OrderedDict(
127 commits_by_day_aggregate = OrderedDict(
130 json.loads(
128 json.loads(
131 cur_stats.commit_activity_combined))
129 cur_stats.commit_activity_combined))
132 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
130 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
133
131
134 log.debug('starting parsing %s', parse_limit)
132 log.debug('starting parsing %s', parse_limit)
135 lmktime = mktime
133 lmktime = mktime
136
134
137 last_rev = last_rev + 1 if last_rev > 0 else last_rev
135 last_rev = last_rev + 1 if last_rev > 0 else last_rev
138 for rev in repo.revisions[last_rev:last_rev + parse_limit]:
136 for rev in repo.revisions[last_rev:last_rev + parse_limit]:
139 last_cs = cs = repo.get_changeset(rev)
137 last_cs = cs = repo.get_changeset(rev)
140 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
138 k = lmktime([cs.date.timetuple()[0], cs.date.timetuple()[1],
141 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
139 cs.date.timetuple()[2], 0, 0, 0, 0, 0, 0])
142
140
143 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
141 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
144 try:
142 try:
145 l = [timegetter(x) for x in commits_by_day_author_aggregate\
143 l = [timegetter(x) for x in commits_by_day_author_aggregate\
146 [author_key_cleaner(cs.author)]['data']]
144 [author_key_cleaner(cs.author)]['data']]
147 time_pos = l.index(k)
145 time_pos = l.index(k)
148 except ValueError:
146 except ValueError:
149 time_pos = False
147 time_pos = False
150
148
151 if time_pos >= 0 and time_pos is not False:
149 if time_pos >= 0 and time_pos is not False:
152
150
153 datadict = commits_by_day_author_aggregate\
151 datadict = commits_by_day_author_aggregate\
154 [author_key_cleaner(cs.author)]['data'][time_pos]
152 [author_key_cleaner(cs.author)]['data'][time_pos]
155
153
156 datadict["commits"] += 1
154 datadict["commits"] += 1
157 datadict["added"] += len(cs.added)
155 datadict["added"] += len(cs.added)
158 datadict["changed"] += len(cs.changed)
156 datadict["changed"] += len(cs.changed)
159 datadict["removed"] += len(cs.removed)
157 datadict["removed"] += len(cs.removed)
160
158
161 else:
159 else:
162 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
160 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
163
161
164 datadict = {"time":k,
162 datadict = {"time":k,
165 "commits":1,
163 "commits":1,
166 "added":len(cs.added),
164 "added":len(cs.added),
167 "changed":len(cs.changed),
165 "changed":len(cs.changed),
168 "removed":len(cs.removed),
166 "removed":len(cs.removed),
169 }
167 }
170 commits_by_day_author_aggregate\
168 commits_by_day_author_aggregate\
171 [author_key_cleaner(cs.author)]['data'].append(datadict)
169 [author_key_cleaner(cs.author)]['data'].append(datadict)
172
170
173 else:
171 else:
174 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
172 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
175 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
173 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
176 "label":author_key_cleaner(cs.author),
174 "label":author_key_cleaner(cs.author),
177 "data":[{"time":k,
175 "data":[{"time":k,
178 "commits":1,
176 "commits":1,
179 "added":len(cs.added),
177 "added":len(cs.added),
180 "changed":len(cs.changed),
178 "changed":len(cs.changed),
181 "removed":len(cs.removed),
179 "removed":len(cs.removed),
182 }],
180 }],
183 "schema":["commits"],
181 "schema":["commits"],
184 }
182 }
185
183
186 #gather all data by day
184 #gather all data by day
187 if commits_by_day_aggregate.has_key(k):
185 if commits_by_day_aggregate.has_key(k):
188 commits_by_day_aggregate[k] += 1
186 commits_by_day_aggregate[k] += 1
189 else:
187 else:
190 commits_by_day_aggregate[k] = 1
188 commits_by_day_aggregate[k] = 1
191
189
192 overview_data = sorted(commits_by_day_aggregate.items(), key=itemgetter(0))
190 overview_data = sorted(commits_by_day_aggregate.items(), key=itemgetter(0))
193 if not commits_by_day_author_aggregate:
191 if not commits_by_day_author_aggregate:
194 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
192 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
195 "label":author_key_cleaner(repo.contact),
193 "label":author_key_cleaner(repo.contact),
196 "data":[0, 1],
194 "data":[0, 1],
197 "schema":["commits"],
195 "schema":["commits"],
198 }
196 }
199
197
200 stats = cur_stats if cur_stats else Statistics()
198 stats = cur_stats if cur_stats else Statistics()
201 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
199 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
202 stats.commit_activity_combined = json.dumps(overview_data)
200 stats.commit_activity_combined = json.dumps(overview_data)
203
201
204 log.debug('last revison %s', last_rev)
202 log.debug('last revison %s', last_rev)
205 leftovers = len(repo.revisions[last_rev:])
203 leftovers = len(repo.revisions[last_rev:])
206 log.debug('revisions to parse %s', leftovers)
204 log.debug('revisions to parse %s', leftovers)
207
205
208 if last_rev == 0 or leftovers < parse_limit:
206 if last_rev == 0 or leftovers < parse_limit:
209 log.debug('getting code trending stats')
207 log.debug('getting code trending stats')
210 stats.languages = json.dumps(__get_codes_stats(repo_name))
208 stats.languages = json.dumps(__get_codes_stats(repo_name))
211
209
212 stats.repository = dbrepo
210 stats.repository = dbrepo
213 stats.stat_on_revision = last_cs.revision
211 stats.stat_on_revision = last_cs.revision
214
212
215 try:
213 try:
216 sa.add(stats)
214 sa.add(stats)
217 sa.commit()
215 sa.commit()
218 except:
216 except:
219 log.error(traceback.format_exc())
217 log.error(traceback.format_exc())
220 sa.rollback()
218 sa.rollback()
221 return False
219 return False
222 if len(repo.revisions) > 1:
220 if len(repo.revisions) > 1:
223 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
221 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
224
222
225 return True
223 return True
226
224
227 @task(ignore_result=True)
225 @task(ignore_result=True)
228 def reset_user_password(user_email):
226 def reset_user_password(user_email):
229 try:
227 try:
230 log = reset_user_password.get_logger()
228 log = reset_user_password.get_logger()
231 except:
229 except:
232 log = logging.getLogger(__name__)
230 log = logging.getLogger(__name__)
233
231
234 from rhodecode.lib import auth
232 from rhodecode.lib import auth
235 from rhodecode.model.db import User
233 from rhodecode.model.db import User
236
234
237 try:
235 try:
238 try:
236 try:
239 sa = get_session()
237 sa = get_session()
240 user = sa.query(User).filter(User.email == user_email).scalar()
238 user = sa.query(User).filter(User.email == user_email).scalar()
241 new_passwd = auth.PasswordGenerator().gen_password(8,
239 new_passwd = auth.PasswordGenerator().gen_password(8,
242 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
240 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
243 if user:
241 if user:
244 user.password = auth.get_crypt_password(new_passwd)
242 user.password = auth.get_crypt_password(new_passwd)
245 sa.add(user)
243 sa.add(user)
246 sa.commit()
244 sa.commit()
247 log.info('change password for %s', user_email)
245 log.info('change password for %s', user_email)
248 if new_passwd is None:
246 if new_passwd is None:
249 raise Exception('unable to generate new password')
247 raise Exception('unable to generate new password')
250
248
251 except:
249 except:
252 log.error(traceback.format_exc())
250 log.error(traceback.format_exc())
253 sa.rollback()
251 sa.rollback()
254
252
255 run_task(send_email, user_email,
253 run_task(send_email, user_email,
256 "Your new rhodecode password",
254 "Your new rhodecode password",
257 'Your new rhodecode password:%s' % (new_passwd))
255 'Your new rhodecode password:%s' % (new_passwd))
258 log.info('send new password mail to %s', user_email)
256 log.info('send new password mail to %s', user_email)
259
257
260
258
261 except:
259 except:
262 log.error('Failed to update user password')
260 log.error('Failed to update user password')
263 log.error(traceback.format_exc())
261 log.error(traceback.format_exc())
264
262
265 return True
263 return True
266
264
267 @task(ignore_result=True)
265 @task(ignore_result=True)
268 def send_email(recipients, subject, body):
266 def send_email(recipients, subject, body):
269 """
267 """
270 Sends an email with defined parameters from the .ini files.
268 Sends an email with defined parameters from the .ini files.
271
269
272
270
273 :param recipients: list of recipients, it this is empty the defined email
271 :param recipients: list of recipients, it this is empty the defined email
274 address from field 'email_to' is used instead
272 address from field 'email_to' is used instead
275 :param subject: subject of the mail
273 :param subject: subject of the mail
276 :param body: body of the mail
274 :param body: body of the mail
277 """
275 """
278 try:
276 try:
279 log = send_email.get_logger()
277 log = send_email.get_logger()
280 except:
278 except:
281 log = logging.getLogger(__name__)
279 log = logging.getLogger(__name__)
282
280
283 email_config = config
281 email_config = config
284
282
285 if not recipients:
283 if not recipients:
286 recipients = [email_config.get('email_to')]
284 recipients = [email_config.get('email_to')]
287
285
288 mail_from = email_config.get('app_email_from')
286 mail_from = email_config.get('app_email_from')
289 user = email_config.get('smtp_username')
287 user = email_config.get('smtp_username')
290 passwd = email_config.get('smtp_password')
288 passwd = email_config.get('smtp_password')
291 mail_server = email_config.get('smtp_server')
289 mail_server = email_config.get('smtp_server')
292 mail_port = email_config.get('smtp_port')
290 mail_port = email_config.get('smtp_port')
293 tls = str2bool(email_config.get('smtp_use_tls'))
291 tls = str2bool(email_config.get('smtp_use_tls'))
294 ssl = str2bool(email_config.get('smtp_use_ssl'))
292 ssl = str2bool(email_config.get('smtp_use_ssl'))
295
293
296 try:
294 try:
297 m = SmtpMailer(mail_from, user, passwd, mail_server,
295 m = SmtpMailer(mail_from, user, passwd, mail_server,
298 mail_port, ssl, tls)
296 mail_port, ssl, tls)
299 m.send(recipients, subject, body)
297 m.send(recipients, subject, body)
300 except:
298 except:
301 log.error('Mail sending failed')
299 log.error('Mail sending failed')
302 log.error(traceback.format_exc())
300 log.error(traceback.format_exc())
303 return False
301 return False
304 return True
302 return True
305
303
306 @task(ignore_result=True)
304 @task(ignore_result=True)
307 def create_repo_fork(form_data, cur_user):
305 def create_repo_fork(form_data, cur_user):
308 try:
306 try:
309 log = create_repo_fork.get_logger()
307 log = create_repo_fork.get_logger()
310 except:
308 except:
311 log = logging.getLogger(__name__)
309 log = logging.getLogger(__name__)
312
310
313 from rhodecode.model.repo import RepoModel
311 from rhodecode.model.repo import RepoModel
314 from vcs import get_backend
312 from vcs import get_backend
315
313
316 repo_model = RepoModel(get_session())
314 repo_model = RepoModel(get_session())
317 repo_model.create(form_data, cur_user, just_db=True, fork=True)
315 repo_model.create(form_data, cur_user, just_db=True, fork=True)
318 repo_name = form_data['repo_name']
316 repo_name = form_data['repo_name']
319 repos_path = get_repos_path()
317 repos_path = get_repos_path()
320 repo_path = os.path.join(repos_path, repo_name)
318 repo_path = os.path.join(repos_path, repo_name)
321 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
319 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
322 alias = form_data['repo_type']
320 alias = form_data['repo_type']
323
321
324 log.info('creating repo fork %s as %s', repo_name, repo_path)
322 log.info('creating repo fork %s as %s', repo_name, repo_path)
325 backend = get_backend(alias)
323 backend = get_backend(alias)
326 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
324 backend(str(repo_fork_path), create=True, src_url=str(repo_path))
327
325
328 def __get_codes_stats(repo_name):
326 def __get_codes_stats(repo_name):
329 LANGUAGES_EXTENSIONS_MAP = {'scm': 'Scheme', 'asmx': 'VbNetAspx', 'Rout':
327 LANGUAGES_EXTENSIONS_MAP = {'scm': 'Scheme', 'asmx': 'VbNetAspx', 'Rout':
330 'RConsole', 'rest': 'Rst', 'abap': 'ABAP', 'go': 'Go', 'phtml': 'HtmlPhp',
328 'RConsole', 'rest': 'Rst', 'abap': 'ABAP', 'go': 'Go', 'phtml': 'HtmlPhp',
331 'ns2': 'Newspeak', 'xml': 'EvoqueXml', 'sh-session': 'BashSession', 'ads':
329 'ns2': 'Newspeak', 'xml': 'EvoqueXml', 'sh-session': 'BashSession', 'ads':
332 'Ada', 'clj': 'Clojure', 'll': 'Llvm', 'ebuild': 'Bash', 'adb': 'Ada',
330 'Ada', 'clj': 'Clojure', 'll': 'Llvm', 'ebuild': 'Bash', 'adb': 'Ada',
333 'ada': 'Ada', 'c++-objdump': 'CppObjdump', 'aspx':
331 'ada': 'Ada', 'c++-objdump': 'CppObjdump', 'aspx':
334 'VbNetAspx', 'ksh': 'Bash', 'coffee': 'CoffeeScript', 'vert': 'GLShader',
332 'VbNetAspx', 'ksh': 'Bash', 'coffee': 'CoffeeScript', 'vert': 'GLShader',
335 'Makefile.*': 'Makefile', 'di': 'D', 'dpatch': 'DarcsPatch', 'rake':
333 'Makefile.*': 'Makefile', 'di': 'D', 'dpatch': 'DarcsPatch', 'rake':
336 'Ruby', 'moo': 'MOOCode', 'erl-sh': 'ErlangShell', 'geo': 'GLShader',
334 'Ruby', 'moo': 'MOOCode', 'erl-sh': 'ErlangShell', 'geo': 'GLShader',
337 'pov': 'Povray', 'bas': 'VbNet', 'bat': 'Batch', 'd': 'D', 'lisp':
335 'pov': 'Povray', 'bas': 'VbNet', 'bat': 'Batch', 'd': 'D', 'lisp':
338 'CommonLisp', 'h': 'C', 'rbx': 'Ruby', 'tcl': 'Tcl', 'c++': 'Cpp', 'md':
336 'CommonLisp', 'h': 'C', 'rbx': 'Ruby', 'tcl': 'Tcl', 'c++': 'Cpp', 'md':
339 'MiniD', '.vimrc': 'Vim', 'xsd': 'Xml', 'ml': 'Ocaml', 'el': 'CommonLisp',
337 'MiniD', '.vimrc': 'Vim', 'xsd': 'Xml', 'ml': 'Ocaml', 'el': 'CommonLisp',
340 'befunge': 'Befunge', 'xsl': 'Xslt', 'pyx': 'Cython', 'cfm':
338 'befunge': 'Befunge', 'xsl': 'Xslt', 'pyx': 'Cython', 'cfm':
341 'ColdfusionHtml', 'evoque': 'Evoque', 'cfg': 'Ini', 'htm': 'Html',
339 'ColdfusionHtml', 'evoque': 'Evoque', 'cfg': 'Ini', 'htm': 'Html',
342 'Makefile': 'Makefile', 'cfc': 'ColdfusionHtml', 'tex': 'Tex', 'cs':
340 'Makefile': 'Makefile', 'cfc': 'ColdfusionHtml', 'tex': 'Tex', 'cs':
343 'CSharp', 'mxml': 'Mxml', 'patch': 'Diff', 'apache.conf': 'ApacheConf',
341 'CSharp', 'mxml': 'Mxml', 'patch': 'Diff', 'apache.conf': 'ApacheConf',
344 'scala': 'Scala', 'applescript': 'AppleScript', 'GNUmakefile': 'Makefile',
342 'scala': 'Scala', 'applescript': 'AppleScript', 'GNUmakefile': 'Makefile',
345 'c-objdump': 'CObjdump', 'lua': 'Lua', 'apache2.conf': 'ApacheConf', 'rb':
343 'c-objdump': 'CObjdump', 'lua': 'Lua', 'apache2.conf': 'ApacheConf', 'rb':
346 'Ruby', 'gemspec': 'Ruby', 'rl': 'RagelObjectiveC', 'vala': 'Vala', 'tmpl':
344 'Ruby', 'gemspec': 'Ruby', 'rl': 'RagelObjectiveC', 'vala': 'Vala', 'tmpl':
347 'Cheetah', 'bf': 'Brainfuck', 'plt': 'Gnuplot', 'G': 'AntlrRuby', 'xslt':
345 'Cheetah', 'bf': 'Brainfuck', 'plt': 'Gnuplot', 'G': 'AntlrRuby', 'xslt':
348 'Xslt', 'flxh': 'Felix', 'asax': 'VbNetAspx', 'Rakefile': 'Ruby', 'S': 'S',
346 'Xslt', 'flxh': 'Felix', 'asax': 'VbNetAspx', 'Rakefile': 'Ruby', 'S': 'S',
349 'wsdl': 'Xml', 'js': 'Javascript', 'autodelegate': 'Myghty', 'properties':
347 'wsdl': 'Xml', 'js': 'Javascript', 'autodelegate': 'Myghty', 'properties':
350 'Ini', 'bash': 'Bash', 'c': 'C', 'g': 'AntlrRuby', 'r3': 'Rebol', 's':
348 'Ini', 'bash': 'Bash', 'c': 'C', 'g': 'AntlrRuby', 'r3': 'Rebol', 's':
351 'Gas', 'ashx': 'VbNetAspx', 'cxx': 'Cpp', 'boo': 'Boo', 'prolog': 'Prolog',
349 'Gas', 'ashx': 'VbNetAspx', 'cxx': 'Cpp', 'boo': 'Boo', 'prolog': 'Prolog',
352 'sqlite3-console': 'SqliteConsole', 'cl': 'CommonLisp', 'cc': 'Cpp', 'pot':
350 'sqlite3-console': 'SqliteConsole', 'cl': 'CommonLisp', 'cc': 'Cpp', 'pot':
353 'Gettext', 'vim': 'Vim', 'pxi': 'Cython', 'yaml': 'Yaml', 'SConstruct':
351 'Gettext', 'vim': 'Vim', 'pxi': 'Cython', 'yaml': 'Yaml', 'SConstruct':
354 'Python', 'diff': 'Diff', 'txt': 'Text', 'cw': 'Redcode', 'pxd': 'Cython',
352 'Python', 'diff': 'Diff', 'txt': 'Text', 'cw': 'Redcode', 'pxd': 'Cython',
355 'plot': 'Gnuplot', 'java': 'Java', 'hrl': 'Erlang', 'py': 'Python',
353 'plot': 'Gnuplot', 'java': 'Java', 'hrl': 'Erlang', 'py': 'Python',
356 'makefile': 'Makefile', 'squid.conf': 'SquidConf', 'asm': 'Nasm', 'toc':
354 'makefile': 'Makefile', 'squid.conf': 'SquidConf', 'asm': 'Nasm', 'toc':
357 'Tex', 'kid': 'Genshi', 'rhtml': 'Rhtml', 'po': 'Gettext', 'pl': 'Prolog',
355 'Tex', 'kid': 'Genshi', 'rhtml': 'Rhtml', 'po': 'Gettext', 'pl': 'Prolog',
358 'pm': 'Perl', 'hx': 'Haxe', 'ascx': 'VbNetAspx', 'ooc': 'Ooc', 'asy':
356 'pm': 'Perl', 'hx': 'Haxe', 'ascx': 'VbNetAspx', 'ooc': 'Ooc', 'asy':
359 'Asymptote', 'hs': 'Haskell', 'SConscript': 'Python', 'pytb':
357 'Asymptote', 'hs': 'Haskell', 'SConscript': 'Python', 'pytb':
360 'PythonTraceback', 'myt': 'Myghty', 'hh': 'Cpp', 'R': 'S', 'aux': 'Tex',
358 'PythonTraceback', 'myt': 'Myghty', 'hh': 'Cpp', 'R': 'S', 'aux': 'Tex',
361 'rst': 'Rst', 'cpp-objdump': 'CppObjdump', 'lgt': 'Logtalk', 'rss': 'Xml',
359 'rst': 'Rst', 'cpp-objdump': 'CppObjdump', 'lgt': 'Logtalk', 'rss': 'Xml',
362 'flx': 'Felix', 'b': 'Brainfuck', 'f': 'Fortran', 'rbw': 'Ruby',
360 'flx': 'Felix', 'b': 'Brainfuck', 'f': 'Fortran', 'rbw': 'Ruby',
363 '.htaccess': 'ApacheConf', 'cxx-objdump': 'CppObjdump', 'j': 'ObjectiveJ',
361 '.htaccess': 'ApacheConf', 'cxx-objdump': 'CppObjdump', 'j': 'ObjectiveJ',
364 'mll': 'Ocaml', 'yml': 'Yaml', 'mu': 'MuPAD', 'r': 'Rebol', 'ASM': 'Nasm',
362 'mll': 'Ocaml', 'yml': 'Yaml', 'mu': 'MuPAD', 'r': 'Rebol', 'ASM': 'Nasm',
365 'erl': 'Erlang', 'mly': 'Ocaml', 'mo': 'Modelica', 'def': 'Modula2', 'ini':
363 'erl': 'Erlang', 'mly': 'Ocaml', 'mo': 'Modelica', 'def': 'Modula2', 'ini':
366 'Ini', 'control': 'DebianControl', 'vb': 'VbNet', 'vapi': 'Vala', 'pro':
364 'Ini', 'control': 'DebianControl', 'vb': 'VbNet', 'vapi': 'Vala', 'pro':
367 'Prolog', 'spt': 'Cheetah', 'mli': 'Ocaml', 'as': 'ActionScript3', 'cmd':
365 'Prolog', 'spt': 'Cheetah', 'mli': 'Ocaml', 'as': 'ActionScript3', 'cmd':
368 'Batch', 'cpp': 'Cpp', 'io': 'Io', 'tac': 'Python', 'haml': 'Haml', 'rkt':
366 'Batch', 'cpp': 'Cpp', 'io': 'Io', 'tac': 'Python', 'haml': 'Haml', 'rkt':
369 'Racket', 'st':'Smalltalk', 'inc': 'Povray', 'pas': 'Delphi', 'cmake':
367 'Racket', 'st':'Smalltalk', 'inc': 'Povray', 'pas': 'Delphi', 'cmake':
370 'CMake', 'csh':'Tcsh', 'hpp': 'Cpp', 'feature': 'Gherkin', 'html': 'Html',
368 'CMake', 'csh':'Tcsh', 'hpp': 'Cpp', 'feature': 'Gherkin', 'html': 'Html',
371 'php':'Php', 'php3':'Php', 'php4':'Php', 'php5':'Php', 'xhtml': 'Html',
369 'php':'Php', 'php3':'Php', 'php4':'Php', 'php5':'Php', 'xhtml': 'Html',
372 'hxx': 'Cpp', 'eclass': 'Bash', 'css': 'Css',
370 'hxx': 'Cpp', 'eclass': 'Bash', 'css': 'Css',
373 'frag': 'GLShader', 'd-objdump': 'DObjdump', 'weechatlog': 'IrcLogs',
371 'frag': 'GLShader', 'd-objdump': 'DObjdump', 'weechatlog': 'IrcLogs',
374 'tcsh': 'Tcsh', 'objdump': 'Objdump', 'pyw': 'Python', 'h++': 'Cpp',
372 'tcsh': 'Tcsh', 'objdump': 'Objdump', 'pyw': 'Python', 'h++': 'Cpp',
375 'py3tb': 'Python3Traceback', 'jsp': 'Jsp', 'sql': 'Sql', 'mak': 'Makefile',
373 'py3tb': 'Python3Traceback', 'jsp': 'Jsp', 'sql': 'Sql', 'mak': 'Makefile',
376 'php': 'Php', 'mao': 'Mako', 'man': 'Groff', 'dylan': 'Dylan', 'sass':
374 'php': 'Php', 'mao': 'Mako', 'man': 'Groff', 'dylan': 'Dylan', 'sass':
377 'Sass', 'cfml': 'ColdfusionHtml', 'darcspatch': 'DarcsPatch', 'tpl':
375 'Sass', 'cfml': 'ColdfusionHtml', 'darcspatch': 'DarcsPatch', 'tpl':
378 'Smarty', 'm': 'ObjectiveC', 'f90': 'Fortran', 'mod': 'Modula2', 'sh':
376 'Smarty', 'm': 'ObjectiveC', 'f90': 'Fortran', 'mod': 'Modula2', 'sh':
379 'Bash', 'lhs': 'LiterateHaskell', 'sources.list': 'SourcesList', 'axd':
377 'Bash', 'lhs': 'LiterateHaskell', 'sources.list': 'SourcesList', 'axd':
380 'VbNetAspx', 'sc': 'Python'}
378 'VbNetAspx', 'sc': 'Python'}
381
379
382 repos_path = get_repos_path()
380 repos_path = get_repos_path()
383 p = os.path.join(repos_path, repo_name)
381 p = os.path.join(repos_path, repo_name)
384 repo = get_repo(p)
382 repo = get_repo(p)
385 tip = repo.get_changeset()
383 tip = repo.get_changeset()
386 code_stats = {}
384 code_stats = {}
387
385
388 def aggregate(cs):
386 def aggregate(cs):
389 for f in cs[2]:
387 for f in cs[2]:
390 ext = f.extension
388 ext = f.extension
391 key = LANGUAGES_EXTENSIONS_MAP.get(ext, ext)
389 key = LANGUAGES_EXTENSIONS_MAP.get(ext, ext)
392 key = key or ext
390 key = key or ext
393 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
391 if ext in LANGUAGES_EXTENSIONS_MAP.keys() and not f.is_binary:
394 if code_stats.has_key(key):
392 if code_stats.has_key(key):
395 code_stats[key] += 1
393 code_stats[key] += 1
396 else:
394 else:
397 code_stats[key] = 1
395 code_stats[key] = 1
398
396
399 map(aggregate, tip.walk('/'))
397 map(aggregate, tip.walk('/'))
400
398
401 return code_stats or {}
399 return code_stats or {}
402
400
403
401
404
402
405
403
@@ -1,69 +1,67 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.dbmigrate.__init__
3 rhodecode.lib.dbmigrate.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database migration modules
6 Database migration modules
7
7
8 :created_on: Dec 11, 2010
8 :created_on: Dec 11, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29 from sqlalchemy import engine_from_config
27 from sqlalchemy import engine_from_config
30
28
31
29
32 from rhodecode.lib.utils import BasePasterCommand, Command, add_cache
30 from rhodecode.lib.utils import BasePasterCommand, Command, add_cache
33 from rhodecode.lib.db_manage import DbManage
31 from rhodecode.lib.db_manage import DbManage
34
32
35 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
36
34
37 class UpgradeDb(BasePasterCommand):
35 class UpgradeDb(BasePasterCommand):
38 """Command used for paster to upgrade our database to newer version
36 """Command used for paster to upgrade our database to newer version
39 """
37 """
40
38
41 max_args = 1
39 max_args = 1
42 min_args = 1
40 min_args = 1
43
41
44 usage = "CONFIG_FILE"
42 usage = "CONFIG_FILE"
45 summary = "Upgrades current db to newer version given configuration file"
43 summary = "Upgrades current db to newer version given configuration file"
46 group_name = "RhodeCode"
44 group_name = "RhodeCode"
47
45
48 parser = Command.standard_parser(verbose=True)
46 parser = Command.standard_parser(verbose=True)
49
47
50 def command(self):
48 def command(self):
51 from pylons import config
49 from pylons import config
52
50
53 add_cache(config)
51 add_cache(config)
54
52
55 db_uri = config['sqlalchemy.db1.url']
53 db_uri = config['sqlalchemy.db1.url']
56
54
57 dbmanage = DbManage(log_sql=True, dbconf=db_uri,
55 dbmanage = DbManage(log_sql=True, dbconf=db_uri,
58 root=config['here'], tests=False)
56 root=config['here'], tests=False)
59
57
60 dbmanage.upgrade()
58 dbmanage.upgrade()
61
59
62
60
63
61
64 def update_parser(self):
62 def update_parser(self):
65 self.parser.add_option('--sql',
63 self.parser.add_option('--sql',
66 action='store_true',
64 action='store_true',
67 dest='just_sql',
65 dest='just_sql',
68 help="Prints upgrade sql for further investigation",
66 help="Prints upgrade sql for further investigation",
69 default=False)
67 default=False)
@@ -1,26 +1,24 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.dbmigrate.versions.__init__
3 rhodecode.lib.dbmigrate.versions.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Package containing new versions of database models
6 Package containing new versions of database models
7
7
8 :created_on: Dec 11, 2010
8 :created_on: Dec 11, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
@@ -1,32 +1,30 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Custom Exceptions modules
3 # Custom Exceptions modules
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software: you can redistribute it and/or modify
7 # modify it under the terms of the GNU General Public License
7 # it under the terms of the GNU General Public License as published by
8 # as published by the Free Software Foundation; version 2
8 # the Free Software Foundation, either version 3 of the License, or
9 # of the License or (at your opinion) any later version of the license.
9 # (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
18 """
21 Created on Nov 17, 2010
19 Created on Nov 17, 2010
22 Custom Exceptions modules
20 Custom Exceptions modules
23 @author: marcink
21 @author: marcink
24 """
22 """
25
23
26 class LdapUsernameError(Exception):pass
24 class LdapUsernameError(Exception):pass
27 class LdapPasswordError(Exception):pass
25 class LdapPasswordError(Exception):pass
28 class LdapConnectionError(Exception):pass
26 class LdapConnectionError(Exception):pass
29 class LdapImportError(Exception):pass
27 class LdapImportError(Exception):pass
30
28
31 class DefaultUserException(Exception):pass
29 class DefaultUserException(Exception):pass
32 class UserOwnsReposException(Exception):pass
30 class UserOwnsReposException(Exception):pass
@@ -1,116 +1,114 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.hooks
3 rhodecode.lib.hooks
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Hooks runned by rhodecode
6 Hooks runned by rhodecode
7
7
8 :created_on: Aug 6, 2010
8 :created_on: Aug 6, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27 import os
25 import os
28 import sys
26 import sys
29 import getpass
27 import getpass
30
28
31 from mercurial.cmdutil import revrange
29 from mercurial.cmdutil import revrange
32 from mercurial.node import nullrev
30 from mercurial.node import nullrev
33
31
34 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
35 from rhodecode.lib.utils import action_logger
33 from rhodecode.lib.utils import action_logger
36
34
37 def repo_size(ui, repo, hooktype=None, **kwargs):
35 def repo_size(ui, repo, hooktype=None, **kwargs):
38 """Presents size of repository after push
36 """Presents size of repository after push
39
37
40 :param ui:
38 :param ui:
41 :param repo:
39 :param repo:
42 :param hooktype:
40 :param hooktype:
43 """
41 """
44
42
45 if hooktype != 'changegroup':
43 if hooktype != 'changegroup':
46 return False
44 return False
47 size_hg, size_root = 0, 0
45 size_hg, size_root = 0, 0
48 for path, dirs, files in os.walk(repo.root):
46 for path, dirs, files in os.walk(repo.root):
49 if path.find('.hg') != -1:
47 if path.find('.hg') != -1:
50 for f in files:
48 for f in files:
51 try:
49 try:
52 size_hg += os.path.getsize(os.path.join(path, f))
50 size_hg += os.path.getsize(os.path.join(path, f))
53 except OSError:
51 except OSError:
54 pass
52 pass
55 else:
53 else:
56 for f in files:
54 for f in files:
57 try:
55 try:
58 size_root += os.path.getsize(os.path.join(path, f))
56 size_root += os.path.getsize(os.path.join(path, f))
59 except OSError:
57 except OSError:
60 pass
58 pass
61
59
62 size_hg_f = h.format_byte_size(size_hg)
60 size_hg_f = h.format_byte_size(size_hg)
63 size_root_f = h.format_byte_size(size_root)
61 size_root_f = h.format_byte_size(size_root)
64 size_total_f = h.format_byte_size(size_root + size_hg)
62 size_total_f = h.format_byte_size(size_root + size_hg)
65 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
63 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
66 % (size_hg_f, size_root_f, size_total_f))
64 % (size_hg_f, size_root_f, size_total_f))
67
65
68 def log_pull_action(ui, repo, **kwargs):
66 def log_pull_action(ui, repo, **kwargs):
69 """Logs user last pull action
67 """Logs user last pull action
70
68
71 :param ui:
69 :param ui:
72 :param repo:
70 :param repo:
73 """
71 """
74
72
75 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
73 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
76 username = extra_params['username']
74 username = extra_params['username']
77 repository = extra_params['repository']
75 repository = extra_params['repository']
78 action = 'pull'
76 action = 'pull'
79
77
80 action_logger(username, action, repository, extra_params['ip'])
78 action_logger(username, action, repository, extra_params['ip'])
81
79
82 return 0
80 return 0
83
81
84 def log_push_action(ui, repo, **kwargs):
82 def log_push_action(ui, repo, **kwargs):
85 """Maps user last push action to new changeset id, from mercurial
83 """Maps user last push action to new changeset id, from mercurial
86
84
87 :param ui:
85 :param ui:
88 :param repo:
86 :param repo:
89 """
87 """
90
88
91 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
89 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
92 username = extra_params['username']
90 username = extra_params['username']
93 repository = extra_params['repository']
91 repository = extra_params['repository']
94 action = 'push:%s'
92 action = 'push:%s'
95 node = kwargs['node']
93 node = kwargs['node']
96
94
97 def get_revs(repo, rev_opt):
95 def get_revs(repo, rev_opt):
98 if rev_opt:
96 if rev_opt:
99 revs = revrange(repo, rev_opt)
97 revs = revrange(repo, rev_opt)
100
98
101 if len(revs) == 0:
99 if len(revs) == 0:
102 return (nullrev, nullrev)
100 return (nullrev, nullrev)
103 return (max(revs), min(revs))
101 return (max(revs), min(revs))
104 else:
102 else:
105 return (len(repo) - 1, 0)
103 return (len(repo) - 1, 0)
106
104
107 stop, start = get_revs(repo, [node + ':'])
105 stop, start = get_revs(repo, [node + ':'])
108
106
109 revs = (str(repo[r]) for r in xrange(start, stop + 1))
107 revs = (str(repo[r]) for r in xrange(start, stop + 1))
110
108
111 action = action % ','.join(revs)
109 action = action % ','.join(revs)
112
110
113 action_logger(username, action, repository, extra_params['ip'])
111 action_logger(username, action, repository, extra_params['ip'])
114
112
115 return 0
113 return 0
116
114
@@ -1,222 +1,220 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # whoosh indexer daemon for rhodecode
3 # whoosh indexer daemon for rhodecode
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software: you can redistribute it and/or modify
7 # modify it under the terms of the GNU General Public License
7 # it under the terms of the GNU General Public License as published by
8 # as published by the Free Software Foundation; version 2
8 # the Free Software Foundation, either version 3 of the License, or
9 # of the License or (at your opinion) any later version of the license.
9 # (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
18 """
21 Created on Jan 26, 2010
19 Created on Jan 26, 2010
22
20
23 @author: marcink
21 @author: marcink
24 A deamon will read from task table and run tasks
22 A deamon will read from task table and run tasks
25 """
23 """
26 import sys
24 import sys
27 import os
25 import os
28 from os.path import dirname as dn
26 from os.path import dirname as dn
29 from os.path import join as jn
27 from os.path import join as jn
30
28
31 #to get the rhodecode import
29 #to get the rhodecode import
32 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
30 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
33 sys.path.append(project_path)
31 sys.path.append(project_path)
34
32
35
33
36 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.scm import ScmModel
37 from rhodecode.lib.helpers import safe_unicode
35 from rhodecode.lib.helpers import safe_unicode
38 from whoosh.index import create_in, open_dir
36 from whoosh.index import create_in, open_dir
39 from shutil import rmtree
37 from shutil import rmtree
40 from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
38 from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
41
39
42 from time import mktime
40 from time import mktime
43 from vcs.exceptions import ChangesetError, RepositoryError
41 from vcs.exceptions import ChangesetError, RepositoryError
44
42
45 import logging
43 import logging
46
44
47 log = logging.getLogger('whooshIndexer')
45 log = logging.getLogger('whooshIndexer')
48 # create logger
46 # create logger
49 log.setLevel(logging.DEBUG)
47 log.setLevel(logging.DEBUG)
50 log.propagate = False
48 log.propagate = False
51 # create console handler and set level to debug
49 # create console handler and set level to debug
52 ch = logging.StreamHandler()
50 ch = logging.StreamHandler()
53 ch.setLevel(logging.DEBUG)
51 ch.setLevel(logging.DEBUG)
54
52
55 # create formatter
53 # create formatter
56 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
54 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
57
55
58 # add formatter to ch
56 # add formatter to ch
59 ch.setFormatter(formatter)
57 ch.setFormatter(formatter)
60
58
61 # add ch to logger
59 # add ch to logger
62 log.addHandler(ch)
60 log.addHandler(ch)
63
61
64 class WhooshIndexingDaemon(object):
62 class WhooshIndexingDaemon(object):
65 """
63 """
66 Deamon for atomic jobs
64 Deamon for atomic jobs
67 """
65 """
68
66
69 def __init__(self, indexname='HG_INDEX', index_location=None,
67 def __init__(self, indexname='HG_INDEX', index_location=None,
70 repo_location=None, sa=None):
68 repo_location=None, sa=None):
71 self.indexname = indexname
69 self.indexname = indexname
72
70
73 self.index_location = index_location
71 self.index_location = index_location
74 if not index_location:
72 if not index_location:
75 raise Exception('You have to provide index location')
73 raise Exception('You have to provide index location')
76
74
77 self.repo_location = repo_location
75 self.repo_location = repo_location
78 if not repo_location:
76 if not repo_location:
79 raise Exception('You have to provide repositories location')
77 raise Exception('You have to provide repositories location')
80
78
81 self.repo_paths = ScmModel(sa).repo_scan(self.repo_location, None)
79 self.repo_paths = ScmModel(sa).repo_scan(self.repo_location, None)
82 self.initial = False
80 self.initial = False
83 if not os.path.isdir(self.index_location):
81 if not os.path.isdir(self.index_location):
84 os.makedirs(self.index_location)
82 os.makedirs(self.index_location)
85 log.info('Cannot run incremental index since it does not'
83 log.info('Cannot run incremental index since it does not'
86 ' yet exist running full build')
84 ' yet exist running full build')
87 self.initial = True
85 self.initial = True
88
86
89 def get_paths(self, repo):
87 def get_paths(self, repo):
90 """recursive walk in root dir and return a set of all path in that dir
88 """recursive walk in root dir and return a set of all path in that dir
91 based on repository walk function
89 based on repository walk function
92 """
90 """
93 index_paths_ = set()
91 index_paths_ = set()
94 try:
92 try:
95 for topnode, dirs, files in repo.walk('/', 'tip'):
93 for topnode, dirs, files in repo.walk('/', 'tip'):
96 for f in files:
94 for f in files:
97 index_paths_.add(jn(repo.path, f.path))
95 index_paths_.add(jn(repo.path, f.path))
98 for dir in dirs:
96 for dir in dirs:
99 for f in files:
97 for f in files:
100 index_paths_.add(jn(repo.path, f.path))
98 index_paths_.add(jn(repo.path, f.path))
101
99
102 except RepositoryError:
100 except RepositoryError:
103 pass
101 pass
104 return index_paths_
102 return index_paths_
105
103
106 def get_node(self, repo, path):
104 def get_node(self, repo, path):
107 n_path = path[len(repo.path) + 1:]
105 n_path = path[len(repo.path) + 1:]
108 node = repo.get_changeset().get_node(n_path)
106 node = repo.get_changeset().get_node(n_path)
109 return node
107 return node
110
108
111 def get_node_mtime(self, node):
109 def get_node_mtime(self, node):
112 return mktime(node.last_changeset.date.timetuple())
110 return mktime(node.last_changeset.date.timetuple())
113
111
114 def add_doc(self, writer, path, repo):
112 def add_doc(self, writer, path, repo):
115 """Adding doc to writer this function itself fetches data from
113 """Adding doc to writer this function itself fetches data from
116 the instance of vcs backend"""
114 the instance of vcs backend"""
117 node = self.get_node(repo, path)
115 node = self.get_node(repo, path)
118
116
119 #we just index the content of chosen files, and skip binary files
117 #we just index the content of chosen files, and skip binary files
120 if node.extension in INDEX_EXTENSIONS and not node.is_binary:
118 if node.extension in INDEX_EXTENSIONS and not node.is_binary:
121
119
122 u_content = node.content
120 u_content = node.content
123 if not isinstance(u_content, unicode):
121 if not isinstance(u_content, unicode):
124 log.warning(' >> %s Could not get this content as unicode '
122 log.warning(' >> %s Could not get this content as unicode '
125 'replacing with empty content', path)
123 'replacing with empty content', path)
126 u_content = u''
124 u_content = u''
127 else:
125 else:
128 log.debug(' >> %s [WITH CONTENT]' % path)
126 log.debug(' >> %s [WITH CONTENT]' % path)
129
127
130 else:
128 else:
131 log.debug(' >> %s' % path)
129 log.debug(' >> %s' % path)
132 #just index file name without it's content
130 #just index file name without it's content
133 u_content = u''
131 u_content = u''
134
132
135 writer.add_document(owner=unicode(repo.contact),
133 writer.add_document(owner=unicode(repo.contact),
136 repository=safe_unicode(repo.name),
134 repository=safe_unicode(repo.name),
137 path=safe_unicode(path),
135 path=safe_unicode(path),
138 content=u_content,
136 content=u_content,
139 modtime=self.get_node_mtime(node),
137 modtime=self.get_node_mtime(node),
140 extension=node.extension)
138 extension=node.extension)
141
139
142
140
143 def build_index(self):
141 def build_index(self):
144 if os.path.exists(self.index_location):
142 if os.path.exists(self.index_location):
145 log.debug('removing previous index')
143 log.debug('removing previous index')
146 rmtree(self.index_location)
144 rmtree(self.index_location)
147
145
148 if not os.path.exists(self.index_location):
146 if not os.path.exists(self.index_location):
149 os.mkdir(self.index_location)
147 os.mkdir(self.index_location)
150
148
151 idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
149 idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
152 writer = idx.writer()
150 writer = idx.writer()
153
151
154 for cnt, repo in enumerate(self.repo_paths.values()):
152 for cnt, repo in enumerate(self.repo_paths.values()):
155 log.debug('building index @ %s' % repo.path)
153 log.debug('building index @ %s' % repo.path)
156
154
157 for idx_path in self.get_paths(repo):
155 for idx_path in self.get_paths(repo):
158 self.add_doc(writer, idx_path, repo)
156 self.add_doc(writer, idx_path, repo)
159
157
160 log.debug('>> COMMITING CHANGES <<')
158 log.debug('>> COMMITING CHANGES <<')
161 writer.commit(merge=True)
159 writer.commit(merge=True)
162 log.debug('>>> FINISHED BUILDING INDEX <<<')
160 log.debug('>>> FINISHED BUILDING INDEX <<<')
163
161
164
162
165 def update_index(self):
163 def update_index(self):
166 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
164 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
167
165
168 idx = open_dir(self.index_location, indexname=self.indexname)
166 idx = open_dir(self.index_location, indexname=self.indexname)
169 # The set of all paths in the index
167 # The set of all paths in the index
170 indexed_paths = set()
168 indexed_paths = set()
171 # The set of all paths we need to re-index
169 # The set of all paths we need to re-index
172 to_index = set()
170 to_index = set()
173
171
174 reader = idx.reader()
172 reader = idx.reader()
175 writer = idx.writer()
173 writer = idx.writer()
176
174
177 # Loop over the stored fields in the index
175 # Loop over the stored fields in the index
178 for fields in reader.all_stored_fields():
176 for fields in reader.all_stored_fields():
179 indexed_path = fields['path']
177 indexed_path = fields['path']
180 indexed_paths.add(indexed_path)
178 indexed_paths.add(indexed_path)
181
179
182 repo = self.repo_paths[fields['repository']]
180 repo = self.repo_paths[fields['repository']]
183
181
184 try:
182 try:
185 node = self.get_node(repo, indexed_path)
183 node = self.get_node(repo, indexed_path)
186 except ChangesetError:
184 except ChangesetError:
187 # This file was deleted since it was indexed
185 # This file was deleted since it was indexed
188 log.debug('removing from index %s' % indexed_path)
186 log.debug('removing from index %s' % indexed_path)
189 writer.delete_by_term('path', indexed_path)
187 writer.delete_by_term('path', indexed_path)
190
188
191 else:
189 else:
192 # Check if this file was changed since it was indexed
190 # Check if this file was changed since it was indexed
193 indexed_time = fields['modtime']
191 indexed_time = fields['modtime']
194 mtime = self.get_node_mtime(node)
192 mtime = self.get_node_mtime(node)
195 if mtime > indexed_time:
193 if mtime > indexed_time:
196 # The file has changed, delete it and add it to the list of
194 # The file has changed, delete it and add it to the list of
197 # files to reindex
195 # files to reindex
198 log.debug('adding to reindex list %s' % indexed_path)
196 log.debug('adding to reindex list %s' % indexed_path)
199 writer.delete_by_term('path', indexed_path)
197 writer.delete_by_term('path', indexed_path)
200 to_index.add(indexed_path)
198 to_index.add(indexed_path)
201
199
202 # Loop over the files in the filesystem
200 # Loop over the files in the filesystem
203 # Assume we have a function that gathers the filenames of the
201 # Assume we have a function that gathers the filenames of the
204 # documents to be indexed
202 # documents to be indexed
205 for repo in self.repo_paths.values():
203 for repo in self.repo_paths.values():
206 for path in self.get_paths(repo):
204 for path in self.get_paths(repo):
207 if path in to_index or path not in indexed_paths:
205 if path in to_index or path not in indexed_paths:
208 # This is either a file that's changed, or a new file
206 # This is either a file that's changed, or a new file
209 # that wasn't indexed before. So index it!
207 # that wasn't indexed before. So index it!
210 self.add_doc(writer, path, repo)
208 self.add_doc(writer, path, repo)
211 log.debug('re indexing %s' % path)
209 log.debug('re indexing %s' % path)
212
210
213 log.debug('>> COMMITING CHANGES <<')
211 log.debug('>> COMMITING CHANGES <<')
214 writer.commit(merge=True)
212 writer.commit(merge=True)
215 log.debug('>>> FINISHED REBUILDING INDEX <<<')
213 log.debug('>>> FINISHED REBUILDING INDEX <<<')
216
214
217 def run(self, full_index=False):
215 def run(self, full_index=False):
218 """Run daemon"""
216 """Run daemon"""
219 if full_index or self.initial:
217 if full_index or self.initial:
220 self.build_index()
218 self.build_index()
221 else:
219 else:
222 self.update_index()
220 self.update_index()
@@ -1,54 +1,52 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.https_fixup
3 rhodecode.lib.middleware.https_fixup
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 middleware to handle https correctly
6 middleware to handle https correctly
7
7
8 :created_on: May 23, 2010
8 :created_on: May 23, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 from rhodecode.lib import str2bool
26 from rhodecode.lib import str2bool
29
27
30 class HttpsFixup(object):
28 class HttpsFixup(object):
31 def __init__(self, app, config):
29 def __init__(self, app, config):
32 self.application = app
30 self.application = app
33 self.config = config
31 self.config = config
34
32
35 def __call__(self, environ, start_response):
33 def __call__(self, environ, start_response):
36 self.__fixup(environ)
34 self.__fixup(environ)
37 return self.application(environ, start_response)
35 return self.application(environ, start_response)
38
36
39
37
40 def __fixup(self, environ):
38 def __fixup(self, environ):
41 """Function to fixup the environ as needed. In order to use this
39 """Function to fixup the environ as needed. In order to use this
42 middleware you should set this header inside your
40 middleware you should set this header inside your
43 proxy ie. nginx, apache etc.
41 proxy ie. nginx, apache etc.
44 """
42 """
45 proto = environ.get('HTTP_X_URL_SCHEME')
43 proto = environ.get('HTTP_X_URL_SCHEME')
46
44
47 if str2bool(self.config.get('force_https')):
45 if str2bool(self.config.get('force_https')):
48 proto = 'https'
46 proto = 'https'
49
47
50 if proto == 'https':
48 if proto == 'https':
51 environ['wsgi.url_scheme'] = proto
49 environ['wsgi.url_scheme'] = proto
52 else:
50 else:
53 environ['wsgi.url_scheme'] = 'http'
51 environ['wsgi.url_scheme'] = 'http'
54 return None
52 return None
@@ -1,221 +1,219 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # middleware to handle git api calls
3 # middleware to handle git api calls
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software: you can redistribute it and/or modify
7 # modify it under the terms of the GNU General Public License
7 # it under the terms of the GNU General Public License as published by
8 # as published by the Free Software Foundation; version 2
8 # the Free Software Foundation, either version 3 of the License, or
9 # of the License or (at your opinion) any later version of the license.
9 # (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
18 """
21 Created on 2010-04-28
19 Created on 2010-04-28
22
20
23 @author: marcink
21 @author: marcink
24 SimpleGit middleware for handling git protocol request (push/clone etc.)
22 SimpleGit middleware for handling git protocol request (push/clone etc.)
25 It's implemented with basic auth function
23 It's implemented with basic auth function
26 """
24 """
27
25
28 from dulwich import server as dulserver
26 from dulwich import server as dulserver
29
27
30 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
28 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
31
29
32 def handle(self):
30 def handle(self):
33 write = lambda x: self.proto.write_sideband(1, x)
31 write = lambda x: self.proto.write_sideband(1, x)
34
32
35 graph_walker = dulserver.ProtocolGraphWalker(self, self.repo.object_store,
33 graph_walker = dulserver.ProtocolGraphWalker(self, self.repo.object_store,
36 self.repo.get_peeled)
34 self.repo.get_peeled)
37 objects_iter = self.repo.fetch_objects(
35 objects_iter = self.repo.fetch_objects(
38 graph_walker.determine_wants, graph_walker, self.progress,
36 graph_walker.determine_wants, graph_walker, self.progress,
39 get_tagged=self.get_tagged)
37 get_tagged=self.get_tagged)
40
38
41 # Do they want any objects?
39 # Do they want any objects?
42 if len(objects_iter) == 0:
40 if len(objects_iter) == 0:
43 return
41 return
44
42
45 self.progress("counting objects: %d, done.\n" % len(objects_iter))
43 self.progress("counting objects: %d, done.\n" % len(objects_iter))
46 dulserver.write_pack_data(dulserver.ProtocolFile(None, write), objects_iter,
44 dulserver.write_pack_data(dulserver.ProtocolFile(None, write), objects_iter,
47 len(objects_iter))
45 len(objects_iter))
48 messages = []
46 messages = []
49 messages.append('thank you for using rhodecode')
47 messages.append('thank you for using rhodecode')
50
48
51 for msg in messages:
49 for msg in messages:
52 self.progress(msg + "\n")
50 self.progress(msg + "\n")
53 # we are done
51 # we are done
54 self.proto.write("0000")
52 self.proto.write("0000")
55
53
56 dulserver.DEFAULT_HANDLERS = {
54 dulserver.DEFAULT_HANDLERS = {
57 'git-upload-pack': SimpleGitUploadPackHandler,
55 'git-upload-pack': SimpleGitUploadPackHandler,
58 'git-receive-pack': dulserver.ReceivePackHandler,
56 'git-receive-pack': dulserver.ReceivePackHandler,
59 }
57 }
60
58
61 from dulwich.repo import Repo
59 from dulwich.repo import Repo
62 from dulwich.web import HTTPGitApplication
60 from dulwich.web import HTTPGitApplication
63 from paste.auth.basic import AuthBasicAuthenticator
61 from paste.auth.basic import AuthBasicAuthenticator
64 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
62 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
65 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
63 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
66 from rhodecode.lib.utils import invalidate_cache, check_repo_fast
64 from rhodecode.lib.utils import invalidate_cache, check_repo_fast
67 from rhodecode.model.user import UserModel
65 from rhodecode.model.user import UserModel
68 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
66 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
69 import logging
67 import logging
70 import os
68 import os
71 import traceback
69 import traceback
72
70
73 log = logging.getLogger(__name__)
71 log = logging.getLogger(__name__)
74
72
75 def is_git(environ):
73 def is_git(environ):
76 """
74 """
77 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
75 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
78 then have git client version given.
76 then have git client version given.
79
77
80 :param environ:
78 :param environ:
81 """
79 """
82 http_user_agent = environ.get('HTTP_USER_AGENT')
80 http_user_agent = environ.get('HTTP_USER_AGENT')
83 if http_user_agent and http_user_agent.startswith('git'):
81 if http_user_agent and http_user_agent.startswith('git'):
84 return True
82 return True
85 return False
83 return False
86
84
87 class SimpleGit(object):
85 class SimpleGit(object):
88
86
89 def __init__(self, application, config):
87 def __init__(self, application, config):
90 self.application = application
88 self.application = application
91 self.config = config
89 self.config = config
92 #authenticate this git request using
90 #authenticate this git request using
93 self.authenticate = AuthBasicAuthenticator('', authfunc)
91 self.authenticate = AuthBasicAuthenticator('', authfunc)
94 self.ipaddr = '0.0.0.0'
92 self.ipaddr = '0.0.0.0'
95 self.repository = None
93 self.repository = None
96 self.username = None
94 self.username = None
97 self.action = None
95 self.action = None
98
96
99 def __call__(self, environ, start_response):
97 def __call__(self, environ, start_response):
100 if not is_git(environ):
98 if not is_git(environ):
101 return self.application(environ, start_response)
99 return self.application(environ, start_response)
102
100
103 proxy_key = 'HTTP_X_REAL_IP'
101 proxy_key = 'HTTP_X_REAL_IP'
104 def_key = 'REMOTE_ADDR'
102 def_key = 'REMOTE_ADDR'
105 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
103 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
106
104
107 #===================================================================
105 #===================================================================
108 # AUTHENTICATE THIS GIT REQUEST
106 # AUTHENTICATE THIS GIT REQUEST
109 #===================================================================
107 #===================================================================
110 username = REMOTE_USER(environ)
108 username = REMOTE_USER(environ)
111 if not username:
109 if not username:
112 self.authenticate.realm = self.config['rhodecode_realm']
110 self.authenticate.realm = self.config['rhodecode_realm']
113 result = self.authenticate(environ)
111 result = self.authenticate(environ)
114 if isinstance(result, str):
112 if isinstance(result, str):
115 AUTH_TYPE.update(environ, 'basic')
113 AUTH_TYPE.update(environ, 'basic')
116 REMOTE_USER.update(environ, result)
114 REMOTE_USER.update(environ, result)
117 else:
115 else:
118 return result.wsgi_application(environ, start_response)
116 return result.wsgi_application(environ, start_response)
119
117
120 #=======================================================================
118 #=======================================================================
121 # GET REPOSITORY
119 # GET REPOSITORY
122 #=======================================================================
120 #=======================================================================
123 try:
121 try:
124 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
122 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
125 if repo_name.endswith('/'):
123 if repo_name.endswith('/'):
126 repo_name = repo_name.rstrip('/')
124 repo_name = repo_name.rstrip('/')
127 self.repository = repo_name
125 self.repository = repo_name
128 except:
126 except:
129 log.error(traceback.format_exc())
127 log.error(traceback.format_exc())
130 return HTTPInternalServerError()(environ, start_response)
128 return HTTPInternalServerError()(environ, start_response)
131
129
132 #===================================================================
130 #===================================================================
133 # CHECK PERMISSIONS FOR THIS REQUEST
131 # CHECK PERMISSIONS FOR THIS REQUEST
134 #===================================================================
132 #===================================================================
135 self.action = self.__get_action(environ)
133 self.action = self.__get_action(environ)
136 if self.action:
134 if self.action:
137 username = self.__get_environ_user(environ)
135 username = self.__get_environ_user(environ)
138 try:
136 try:
139 user = self.__get_user(username)
137 user = self.__get_user(username)
140 self.username = user.username
138 self.username = user.username
141 except:
139 except:
142 log.error(traceback.format_exc())
140 log.error(traceback.format_exc())
143 return HTTPInternalServerError()(environ, start_response)
141 return HTTPInternalServerError()(environ, start_response)
144
142
145 #check permissions for this repository
143 #check permissions for this repository
146 if self.action == 'push':
144 if self.action == 'push':
147 if not HasPermissionAnyMiddleware('repository.write',
145 if not HasPermissionAnyMiddleware('repository.write',
148 'repository.admin')\
146 'repository.admin')\
149 (user, repo_name):
147 (user, repo_name):
150 return HTTPForbidden()(environ, start_response)
148 return HTTPForbidden()(environ, start_response)
151
149
152 else:
150 else:
153 #any other action need at least read permission
151 #any other action need at least read permission
154 if not HasPermissionAnyMiddleware('repository.read',
152 if not HasPermissionAnyMiddleware('repository.read',
155 'repository.write',
153 'repository.write',
156 'repository.admin')\
154 'repository.admin')\
157 (user, repo_name):
155 (user, repo_name):
158 return HTTPForbidden()(environ, start_response)
156 return HTTPForbidden()(environ, start_response)
159
157
160 self.extras = {'ip':self.ipaddr,
158 self.extras = {'ip':self.ipaddr,
161 'username':self.username,
159 'username':self.username,
162 'action':self.action,
160 'action':self.action,
163 'repository':self.repository}
161 'repository':self.repository}
164
162
165 #===================================================================
163 #===================================================================
166 # GIT REQUEST HANDLING
164 # GIT REQUEST HANDLING
167 #===================================================================
165 #===================================================================
168 self.basepath = self.config['base_path']
166 self.basepath = self.config['base_path']
169 self.repo_path = os.path.join(self.basepath, self.repo_name)
167 self.repo_path = os.path.join(self.basepath, self.repo_name)
170 #quick check if that dir exists...
168 #quick check if that dir exists...
171 if check_repo_fast(self.repo_name, self.basepath):
169 if check_repo_fast(self.repo_name, self.basepath):
172 return HTTPNotFound()(environ, start_response)
170 return HTTPNotFound()(environ, start_response)
173 try:
171 try:
174 app = self.__make_app()
172 app = self.__make_app()
175 except:
173 except:
176 log.error(traceback.format_exc())
174 log.error(traceback.format_exc())
177 return HTTPInternalServerError()(environ, start_response)
175 return HTTPInternalServerError()(environ, start_response)
178
176
179 #invalidate cache on push
177 #invalidate cache on push
180 if self.action == 'push':
178 if self.action == 'push':
181 self.__invalidate_cache(self.repo_name)
179 self.__invalidate_cache(self.repo_name)
182 messages = []
180 messages = []
183 messages.append('thank you for using rhodecode')
181 messages.append('thank you for using rhodecode')
184 return app(environ, start_response)
182 return app(environ, start_response)
185 else:
183 else:
186 return app(environ, start_response)
184 return app(environ, start_response)
187
185
188
186
189 def __make_app(self):
187 def __make_app(self):
190 backend = dulserver.DictBackend({'/' + self.repo_name: Repo(self.repo_path)})
188 backend = dulserver.DictBackend({'/' + self.repo_name: Repo(self.repo_path)})
191 gitserve = HTTPGitApplication(backend)
189 gitserve = HTTPGitApplication(backend)
192
190
193 return gitserve
191 return gitserve
194
192
195 def __get_environ_user(self, environ):
193 def __get_environ_user(self, environ):
196 return environ.get('REMOTE_USER')
194 return environ.get('REMOTE_USER')
197
195
198 def __get_user(self, username):
196 def __get_user(self, username):
199 return UserModel().get_by_username(username, cache=True)
197 return UserModel().get_by_username(username, cache=True)
200
198
201 def __get_action(self, environ):
199 def __get_action(self, environ):
202 """
200 """
203 Maps git request commands into a pull or push command.
201 Maps git request commands into a pull or push command.
204 :param environ:
202 :param environ:
205 """
203 """
206 service = environ['QUERY_STRING'].split('=')
204 service = environ['QUERY_STRING'].split('=')
207 if len(service) > 1:
205 if len(service) > 1:
208 service_cmd = service[1]
206 service_cmd = service[1]
209 mapping = {'git-receive-pack': 'push',
207 mapping = {'git-receive-pack': 'push',
210 'git-upload-pack': 'pull',
208 'git-upload-pack': 'pull',
211 }
209 }
212
210
213 return mapping.get(service_cmd, service_cmd if service_cmd else 'other')
211 return mapping.get(service_cmd, service_cmd if service_cmd else 'other')
214 else:
212 else:
215 return 'other'
213 return 'other'
216
214
217 def __invalidate_cache(self, repo_name):
215 def __invalidate_cache(self, repo_name):
218 """we know that some change was made to repositories and we should
216 """we know that some change was made to repositories and we should
219 invalidate the cache to see the changes right away but only for
217 invalidate the cache to see the changes right away but only for
220 push requests"""
218 push requests"""
221 invalidate_cache('get_repo_cached_%s' % repo_name)
219 invalidate_cache('get_repo_cached_%s' % repo_name)
@@ -1,231 +1,229 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # middleware to handle mercurial api calls
3 # middleware to handle mercurial api calls
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software: you can redistribute it and/or modify
7 # modify it under the terms of the GNU General Public License
7 # it under the terms of the GNU General Public License as published by
8 # as published by the Free Software Foundation; version 2
8 # the Free Software Foundation, either version 3 of the License, or
9 # of the License or (at your opinion) any later version of the license.
9 # (at your option) any later version.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
20 """
18 """
21 Created on 2010-04-28
19 Created on 2010-04-28
22
20
23 @author: marcink
21 @author: marcink
24 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
22 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
25 It's implemented with basic auth function
23 It's implemented with basic auth function
26 """
24 """
27 from mercurial.error import RepoError
25 from mercurial.error import RepoError
28 from mercurial.hgweb import hgweb
26 from mercurial.hgweb import hgweb
29 from mercurial.hgweb.request import wsgiapplication
27 from mercurial.hgweb.request import wsgiapplication
30 from paste.auth.basic import AuthBasicAuthenticator
28 from paste.auth.basic import AuthBasicAuthenticator
31 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
29 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
32 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
30 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
33 from rhodecode.lib.utils import make_ui, invalidate_cache, \
31 from rhodecode.lib.utils import make_ui, invalidate_cache, \
34 check_repo_fast, ui_sections
32 check_repo_fast, ui_sections
35 from rhodecode.model.user import UserModel
33 from rhodecode.model.user import UserModel
36 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
34 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
37 import logging
35 import logging
38 import os
36 import os
39 import traceback
37 import traceback
40
38
41 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
42
40
43 def is_mercurial(environ):
41 def is_mercurial(environ):
44 """
42 """
45 Returns True if request's target is mercurial server - header
43 Returns True if request's target is mercurial server - header
46 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
44 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
47 """
45 """
48 http_accept = environ.get('HTTP_ACCEPT')
46 http_accept = environ.get('HTTP_ACCEPT')
49 if http_accept and http_accept.startswith('application/mercurial'):
47 if http_accept and http_accept.startswith('application/mercurial'):
50 return True
48 return True
51 return False
49 return False
52
50
53 class SimpleHg(object):
51 class SimpleHg(object):
54
52
55 def __init__(self, application, config):
53 def __init__(self, application, config):
56 self.application = application
54 self.application = application
57 self.config = config
55 self.config = config
58 #authenticate this mercurial request using authfunc
56 #authenticate this mercurial request using authfunc
59 self.authenticate = AuthBasicAuthenticator('', authfunc)
57 self.authenticate = AuthBasicAuthenticator('', authfunc)
60 self.ipaddr = '0.0.0.0'
58 self.ipaddr = '0.0.0.0'
61 self.repository = None
59 self.repository = None
62 self.username = None
60 self.username = None
63 self.action = None
61 self.action = None
64
62
65 def __call__(self, environ, start_response):
63 def __call__(self, environ, start_response):
66 if not is_mercurial(environ):
64 if not is_mercurial(environ):
67 return self.application(environ, start_response)
65 return self.application(environ, start_response)
68
66
69 proxy_key = 'HTTP_X_REAL_IP'
67 proxy_key = 'HTTP_X_REAL_IP'
70 def_key = 'REMOTE_ADDR'
68 def_key = 'REMOTE_ADDR'
71 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
69 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
72 # skip passing error to error controller
70 # skip passing error to error controller
73 environ['pylons.status_code_redirect'] = True
71 environ['pylons.status_code_redirect'] = True
74
72
75 #===================================================================
73 #===================================================================
76 # AUTHENTICATE THIS MERCURIAL REQUEST
74 # AUTHENTICATE THIS MERCURIAL REQUEST
77 #===================================================================
75 #===================================================================
78 username = REMOTE_USER(environ)
76 username = REMOTE_USER(environ)
79
77
80 if not username:
78 if not username:
81 self.authenticate.realm = str(self.config['rhodecode_realm'])
79 self.authenticate.realm = str(self.config['rhodecode_realm'])
82 result = self.authenticate(environ)
80 result = self.authenticate(environ)
83 if isinstance(result, str):
81 if isinstance(result, str):
84 AUTH_TYPE.update(environ, 'basic')
82 AUTH_TYPE.update(environ, 'basic')
85 REMOTE_USER.update(environ, result)
83 REMOTE_USER.update(environ, result)
86 else:
84 else:
87 return result.wsgi_application(environ, start_response)
85 return result.wsgi_application(environ, start_response)
88
86
89 #=======================================================================
87 #=======================================================================
90 # GET REPOSITORY
88 # GET REPOSITORY
91 #=======================================================================
89 #=======================================================================
92 try:
90 try:
93 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
91 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
94 if repo_name.endswith('/'):
92 if repo_name.endswith('/'):
95 repo_name = repo_name.rstrip('/')
93 repo_name = repo_name.rstrip('/')
96 self.repository = repo_name
94 self.repository = repo_name
97 except:
95 except:
98 log.error(traceback.format_exc())
96 log.error(traceback.format_exc())
99 return HTTPInternalServerError()(environ, start_response)
97 return HTTPInternalServerError()(environ, start_response)
100
98
101 #===================================================================
99 #===================================================================
102 # CHECK PERMISSIONS FOR THIS REQUEST
100 # CHECK PERMISSIONS FOR THIS REQUEST
103 #===================================================================
101 #===================================================================
104 self.action = self.__get_action(environ)
102 self.action = self.__get_action(environ)
105 if self.action:
103 if self.action:
106 username = self.__get_environ_user(environ)
104 username = self.__get_environ_user(environ)
107 try:
105 try:
108 user = self.__get_user(username)
106 user = self.__get_user(username)
109 self.username = user.username
107 self.username = user.username
110 except:
108 except:
111 log.error(traceback.format_exc())
109 log.error(traceback.format_exc())
112 return HTTPInternalServerError()(environ, start_response)
110 return HTTPInternalServerError()(environ, start_response)
113
111
114 #check permissions for this repository
112 #check permissions for this repository
115 if self.action == 'push':
113 if self.action == 'push':
116 if not HasPermissionAnyMiddleware('repository.write',
114 if not HasPermissionAnyMiddleware('repository.write',
117 'repository.admin')\
115 'repository.admin')\
118 (user, repo_name):
116 (user, repo_name):
119 return HTTPForbidden()(environ, start_response)
117 return HTTPForbidden()(environ, start_response)
120
118
121 else:
119 else:
122 #any other action need at least read permission
120 #any other action need at least read permission
123 if not HasPermissionAnyMiddleware('repository.read',
121 if not HasPermissionAnyMiddleware('repository.read',
124 'repository.write',
122 'repository.write',
125 'repository.admin')\
123 'repository.admin')\
126 (user, repo_name):
124 (user, repo_name):
127 return HTTPForbidden()(environ, start_response)
125 return HTTPForbidden()(environ, start_response)
128
126
129 self.extras = {'ip':self.ipaddr,
127 self.extras = {'ip':self.ipaddr,
130 'username':self.username,
128 'username':self.username,
131 'action':self.action,
129 'action':self.action,
132 'repository':self.repository}
130 'repository':self.repository}
133
131
134 #===================================================================
132 #===================================================================
135 # MERCURIAL REQUEST HANDLING
133 # MERCURIAL REQUEST HANDLING
136 #===================================================================
134 #===================================================================
137 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
135 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
138 self.baseui = make_ui('db')
136 self.baseui = make_ui('db')
139 self.basepath = self.config['base_path']
137 self.basepath = self.config['base_path']
140 self.repo_path = os.path.join(self.basepath, repo_name)
138 self.repo_path = os.path.join(self.basepath, repo_name)
141
139
142 #quick check if that dir exists...
140 #quick check if that dir exists...
143 if check_repo_fast(repo_name, self.basepath):
141 if check_repo_fast(repo_name, self.basepath):
144 return HTTPNotFound()(environ, start_response)
142 return HTTPNotFound()(environ, start_response)
145 try:
143 try:
146 app = wsgiapplication(self.__make_app)
144 app = wsgiapplication(self.__make_app)
147 except RepoError, e:
145 except RepoError, e:
148 if str(e).find('not found') != -1:
146 if str(e).find('not found') != -1:
149 return HTTPNotFound()(environ, start_response)
147 return HTTPNotFound()(environ, start_response)
150 except Exception:
148 except Exception:
151 log.error(traceback.format_exc())
149 log.error(traceback.format_exc())
152 return HTTPInternalServerError()(environ, start_response)
150 return HTTPInternalServerError()(environ, start_response)
153
151
154 #invalidate cache on push
152 #invalidate cache on push
155 if self.action == 'push':
153 if self.action == 'push':
156 self.__invalidate_cache(repo_name)
154 self.__invalidate_cache(repo_name)
157
155
158 return app(environ, start_response)
156 return app(environ, start_response)
159
157
160
158
161 def __make_app(self):
159 def __make_app(self):
162 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
160 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
163 return self.__load_web_settings(hgserve, self.extras)
161 return self.__load_web_settings(hgserve, self.extras)
164
162
165 def __get_environ_user(self, environ):
163 def __get_environ_user(self, environ):
166 return environ.get('REMOTE_USER')
164 return environ.get('REMOTE_USER')
167
165
168 def __get_user(self, username):
166 def __get_user(self, username):
169 return UserModel().get_by_username(username, cache=True)
167 return UserModel().get_by_username(username, cache=True)
170
168
171 def __get_action(self, environ):
169 def __get_action(self, environ):
172 """
170 """
173 Maps mercurial request commands into a clone,pull or push command.
171 Maps mercurial request commands into a clone,pull or push command.
174 This should always return a valid command string
172 This should always return a valid command string
175 :param environ:
173 :param environ:
176 """
174 """
177 mapping = {'changegroup': 'pull',
175 mapping = {'changegroup': 'pull',
178 'changegroupsubset': 'pull',
176 'changegroupsubset': 'pull',
179 'stream_out': 'pull',
177 'stream_out': 'pull',
180 'listkeys': 'pull',
178 'listkeys': 'pull',
181 'unbundle': 'push',
179 'unbundle': 'push',
182 'pushkey': 'push', }
180 'pushkey': 'push', }
183 for qry in environ['QUERY_STRING'].split('&'):
181 for qry in environ['QUERY_STRING'].split('&'):
184 if qry.startswith('cmd'):
182 if qry.startswith('cmd'):
185 cmd = qry.split('=')[-1]
183 cmd = qry.split('=')[-1]
186 if mapping.has_key(cmd):
184 if mapping.has_key(cmd):
187 return mapping[cmd]
185 return mapping[cmd]
188 else:
186 else:
189 return cmd
187 return cmd
190
188
191 def __invalidate_cache(self, repo_name):
189 def __invalidate_cache(self, repo_name):
192 """we know that some change was made to repositories and we should
190 """we know that some change was made to repositories and we should
193 invalidate the cache to see the changes right away but only for
191 invalidate the cache to see the changes right away but only for
194 push requests"""
192 push requests"""
195 invalidate_cache('get_repo_cached_%s' % repo_name)
193 invalidate_cache('get_repo_cached_%s' % repo_name)
196
194
197
195
198 def __load_web_settings(self, hgserve, extras={}):
196 def __load_web_settings(self, hgserve, extras={}):
199 #set the global ui for hgserve instance passed
197 #set the global ui for hgserve instance passed
200 hgserve.repo.ui = self.baseui
198 hgserve.repo.ui = self.baseui
201
199
202 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
200 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
203
201
204 #inject some additional parameters that will be available in ui
202 #inject some additional parameters that will be available in ui
205 #for hooks
203 #for hooks
206 for k, v in extras.items():
204 for k, v in extras.items():
207 hgserve.repo.ui.setconfig('rhodecode_extras', k, v)
205 hgserve.repo.ui.setconfig('rhodecode_extras', k, v)
208
206
209 repoui = make_ui('file', hgrc, False)
207 repoui = make_ui('file', hgrc, False)
210
208
211 if repoui:
209 if repoui:
212 #overwrite our ui instance with the section from hgrc file
210 #overwrite our ui instance with the section from hgrc file
213 for section in ui_sections:
211 for section in ui_sections:
214 for k, v in repoui.configitems(section):
212 for k, v in repoui.configitems(section):
215 hgserve.repo.ui.setconfig(section, k, v)
213 hgserve.repo.ui.setconfig(section, k, v)
216
214
217 return hgserve
215 return hgserve
218
216
219
217
220
218
221
219
222
220
223
221
224
222
225
223
226
224
227
225
228
226
229
227
230
228
231
229
@@ -1,614 +1,612 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import os
26 import os
29 import logging
27 import logging
30 import datetime
28 import datetime
31 import traceback
29 import traceback
32
30
33 from UserDict import DictMixin
31 from UserDict import DictMixin
34
32
35 from mercurial import ui, config, hg
33 from mercurial import ui, config, hg
36 from mercurial.error import RepoError
34 from mercurial.error import RepoError
37
35
38 import paste
36 import paste
39 import beaker
37 import beaker
40 from paste.script.command import Command, BadCommand
38 from paste.script.command import Command, BadCommand
41
39
42 from vcs.backends.base import BaseChangeset
40 from vcs.backends.base import BaseChangeset
43 from vcs.utils.lazy import LazyProperty
41 from vcs.utils.lazy import LazyProperty
44
42
45 from rhodecode.model import meta
43 from rhodecode.model import meta
46 from rhodecode.model.caching_query import FromCache
44 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog
45 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog
48 from rhodecode.model.repo import RepoModel
46 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.user import UserModel
47 from rhodecode.model.user import UserModel
50
48
51 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
52
50
53
51
54 def get_repo_slug(request):
52 def get_repo_slug(request):
55 return request.environ['pylons.routes_dict'].get('repo_name')
53 return request.environ['pylons.routes_dict'].get('repo_name')
56
54
57 def action_logger(user, action, repo, ipaddr='', sa=None):
55 def action_logger(user, action, repo, ipaddr='', sa=None):
58 """
56 """
59 Action logger for various actions made by users
57 Action logger for various actions made by users
60
58
61 :param user: user that made this action, can be a unique username string or
59 :param user: user that made this action, can be a unique username string or
62 object containing user_id attribute
60 object containing user_id attribute
63 :param action: action to log, should be on of predefined unique actions for
61 :param action: action to log, should be on of predefined unique actions for
64 easy translations
62 easy translations
65 :param repo: string name of repository or object containing repo_id,
63 :param repo: string name of repository or object containing repo_id,
66 that action was made on
64 that action was made on
67 :param ipaddr: optional ip address from what the action was made
65 :param ipaddr: optional ip address from what the action was made
68 :param sa: optional sqlalchemy session
66 :param sa: optional sqlalchemy session
69
67
70 """
68 """
71
69
72 if not sa:
70 if not sa:
73 sa = meta.Session()
71 sa = meta.Session()
74
72
75 try:
73 try:
76 um = UserModel()
74 um = UserModel()
77 if hasattr(user, 'user_id'):
75 if hasattr(user, 'user_id'):
78 user_obj = user
76 user_obj = user
79 elif isinstance(user, basestring):
77 elif isinstance(user, basestring):
80 user_obj = um.get_by_username(user, cache=False)
78 user_obj = um.get_by_username(user, cache=False)
81 else:
79 else:
82 raise Exception('You have to provide user object or username')
80 raise Exception('You have to provide user object or username')
83
81
84
82
85 rm = RepoModel()
83 rm = RepoModel()
86 if hasattr(repo, 'repo_id'):
84 if hasattr(repo, 'repo_id'):
87 repo_obj = rm.get(repo.repo_id, cache=False)
85 repo_obj = rm.get(repo.repo_id, cache=False)
88 repo_name = repo_obj.repo_name
86 repo_name = repo_obj.repo_name
89 elif isinstance(repo, basestring):
87 elif isinstance(repo, basestring):
90 repo_name = repo.lstrip('/')
88 repo_name = repo.lstrip('/')
91 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
89 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
92 else:
90 else:
93 raise Exception('You have to provide repository to action logger')
91 raise Exception('You have to provide repository to action logger')
94
92
95
93
96 user_log = UserLog()
94 user_log = UserLog()
97 user_log.user_id = user_obj.user_id
95 user_log.user_id = user_obj.user_id
98 user_log.action = action
96 user_log.action = action
99
97
100 user_log.repository_id = repo_obj.repo_id
98 user_log.repository_id = repo_obj.repo_id
101 user_log.repository_name = repo_name
99 user_log.repository_name = repo_name
102
100
103 user_log.action_date = datetime.datetime.now()
101 user_log.action_date = datetime.datetime.now()
104 user_log.user_ip = ipaddr
102 user_log.user_ip = ipaddr
105 sa.add(user_log)
103 sa.add(user_log)
106 sa.commit()
104 sa.commit()
107
105
108 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
106 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
109 except:
107 except:
110 log.error(traceback.format_exc())
108 log.error(traceback.format_exc())
111 sa.rollback()
109 sa.rollback()
112
110
113 def get_repos(path, recursive=False, initial=False):
111 def get_repos(path, recursive=False, initial=False):
114 """
112 """
115 Scans given path for repos and return (name,(type,path)) tuple
113 Scans given path for repos and return (name,(type,path)) tuple
116
114
117 :param prefix:
115 :param prefix:
118 :param path:
116 :param path:
119 :param recursive:
117 :param recursive:
120 :param initial:
118 :param initial:
121 """
119 """
122 from vcs.utils.helpers import get_scm
120 from vcs.utils.helpers import get_scm
123 from vcs.exceptions import VCSError
121 from vcs.exceptions import VCSError
124
122
125 try:
123 try:
126 scm = get_scm(path)
124 scm = get_scm(path)
127 except:
125 except:
128 pass
126 pass
129 else:
127 else:
130 raise Exception('The given path %s should not be a repository got %s',
128 raise Exception('The given path %s should not be a repository got %s',
131 path, scm)
129 path, scm)
132
130
133 for dirpath in os.listdir(path):
131 for dirpath in os.listdir(path):
134 try:
132 try:
135 yield dirpath, get_scm(os.path.join(path, dirpath))
133 yield dirpath, get_scm(os.path.join(path, dirpath))
136 except VCSError:
134 except VCSError:
137 pass
135 pass
138
136
139 def check_repo_fast(repo_name, base_path):
137 def check_repo_fast(repo_name, base_path):
140 """
138 """
141 Check given path for existence of directory
139 Check given path for existence of directory
142 :param repo_name:
140 :param repo_name:
143 :param base_path:
141 :param base_path:
144
142
145 :return False: if this directory is present
143 :return False: if this directory is present
146 """
144 """
147 if os.path.isdir(os.path.join(base_path, repo_name)):return False
145 if os.path.isdir(os.path.join(base_path, repo_name)):return False
148 return True
146 return True
149
147
150 def check_repo(repo_name, base_path, verify=True):
148 def check_repo(repo_name, base_path, verify=True):
151
149
152 repo_path = os.path.join(base_path, repo_name)
150 repo_path = os.path.join(base_path, repo_name)
153
151
154 try:
152 try:
155 if not check_repo_fast(repo_name, base_path):
153 if not check_repo_fast(repo_name, base_path):
156 return False
154 return False
157 r = hg.repository(ui.ui(), repo_path)
155 r = hg.repository(ui.ui(), repo_path)
158 if verify:
156 if verify:
159 hg.verify(r)
157 hg.verify(r)
160 #here we hnow that repo exists it was verified
158 #here we hnow that repo exists it was verified
161 log.info('%s repo is already created', repo_name)
159 log.info('%s repo is already created', repo_name)
162 return False
160 return False
163 except RepoError:
161 except RepoError:
164 #it means that there is no valid repo there...
162 #it means that there is no valid repo there...
165 log.info('%s repo is free for creation', repo_name)
163 log.info('%s repo is free for creation', repo_name)
166 return True
164 return True
167
165
168 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
166 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
169 while True:
167 while True:
170 ok = raw_input(prompt)
168 ok = raw_input(prompt)
171 if ok in ('y', 'ye', 'yes'): return True
169 if ok in ('y', 'ye', 'yes'): return True
172 if ok in ('n', 'no', 'nop', 'nope'): return False
170 if ok in ('n', 'no', 'nop', 'nope'): return False
173 retries = retries - 1
171 retries = retries - 1
174 if retries < 0: raise IOError
172 if retries < 0: raise IOError
175 print complaint
173 print complaint
176
174
177 #propagated from mercurial documentation
175 #propagated from mercurial documentation
178 ui_sections = ['alias', 'auth',
176 ui_sections = ['alias', 'auth',
179 'decode/encode', 'defaults',
177 'decode/encode', 'defaults',
180 'diff', 'email',
178 'diff', 'email',
181 'extensions', 'format',
179 'extensions', 'format',
182 'merge-patterns', 'merge-tools',
180 'merge-patterns', 'merge-tools',
183 'hooks', 'http_proxy',
181 'hooks', 'http_proxy',
184 'smtp', 'patch',
182 'smtp', 'patch',
185 'paths', 'profiling',
183 'paths', 'profiling',
186 'server', 'trusted',
184 'server', 'trusted',
187 'ui', 'web', ]
185 'ui', 'web', ]
188
186
189 def make_ui(read_from='file', path=None, checkpaths=True):
187 def make_ui(read_from='file', path=None, checkpaths=True):
190 """
188 """
191 A function that will read python rc files or database
189 A function that will read python rc files or database
192 and make an mercurial ui object from read options
190 and make an mercurial ui object from read options
193
191
194 :param path: path to mercurial config file
192 :param path: path to mercurial config file
195 :param checkpaths: check the path
193 :param checkpaths: check the path
196 :param read_from: read from 'file' or 'db'
194 :param read_from: read from 'file' or 'db'
197 """
195 """
198
196
199 baseui = ui.ui()
197 baseui = ui.ui()
200
198
201 #clean the baseui object
199 #clean the baseui object
202 baseui._ocfg = config.config()
200 baseui._ocfg = config.config()
203 baseui._ucfg = config.config()
201 baseui._ucfg = config.config()
204 baseui._tcfg = config.config()
202 baseui._tcfg = config.config()
205
203
206 if read_from == 'file':
204 if read_from == 'file':
207 if not os.path.isfile(path):
205 if not os.path.isfile(path):
208 log.warning('Unable to read config file %s' % path)
206 log.warning('Unable to read config file %s' % path)
209 return False
207 return False
210 log.debug('reading hgrc from %s', path)
208 log.debug('reading hgrc from %s', path)
211 cfg = config.config()
209 cfg = config.config()
212 cfg.read(path)
210 cfg.read(path)
213 for section in ui_sections:
211 for section in ui_sections:
214 for k, v in cfg.items(section):
212 for k, v in cfg.items(section):
215 log.debug('settings ui from file[%s]%s:%s', section, k, v)
213 log.debug('settings ui from file[%s]%s:%s', section, k, v)
216 baseui.setconfig(section, k, v)
214 baseui.setconfig(section, k, v)
217
215
218
216
219 elif read_from == 'db':
217 elif read_from == 'db':
220 sa = meta.Session()
218 sa = meta.Session()
221 ret = sa.query(RhodeCodeUi)\
219 ret = sa.query(RhodeCodeUi)\
222 .options(FromCache("sql_cache_short",
220 .options(FromCache("sql_cache_short",
223 "get_hg_ui_settings")).all()
221 "get_hg_ui_settings")).all()
224
222
225 hg_ui = ret
223 hg_ui = ret
226 for ui_ in hg_ui:
224 for ui_ in hg_ui:
227 if ui_.ui_active:
225 if ui_.ui_active:
228 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
226 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
229 ui_.ui_key, ui_.ui_value)
227 ui_.ui_key, ui_.ui_value)
230 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
228 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
231
229
232 meta.Session.remove()
230 meta.Session.remove()
233 return baseui
231 return baseui
234
232
235
233
236 def set_rhodecode_config(config):
234 def set_rhodecode_config(config):
237 """Updates pylons config with new settings from database
235 """Updates pylons config with new settings from database
238
236
239 :param config:
237 :param config:
240 """
238 """
241 from rhodecode.model.settings import SettingsModel
239 from rhodecode.model.settings import SettingsModel
242 hgsettings = SettingsModel().get_app_settings()
240 hgsettings = SettingsModel().get_app_settings()
243
241
244 for k, v in hgsettings.items():
242 for k, v in hgsettings.items():
245 config[k] = v
243 config[k] = v
246
244
247 def invalidate_cache(cache_key, *args):
245 def invalidate_cache(cache_key, *args):
248 """Puts cache invalidation task into db for
246 """Puts cache invalidation task into db for
249 further global cache invalidation
247 further global cache invalidation
250 """
248 """
251
249
252 from rhodecode.model.scm import ScmModel
250 from rhodecode.model.scm import ScmModel
253
251
254 if cache_key.startswith('get_repo_cached_'):
252 if cache_key.startswith('get_repo_cached_'):
255 name = cache_key.split('get_repo_cached_')[-1]
253 name = cache_key.split('get_repo_cached_')[-1]
256 ScmModel().mark_for_invalidation(name)
254 ScmModel().mark_for_invalidation(name)
257
255
258 class EmptyChangeset(BaseChangeset):
256 class EmptyChangeset(BaseChangeset):
259 """
257 """
260 An dummy empty changeset. It's possible to pass hash when creating
258 An dummy empty changeset. It's possible to pass hash when creating
261 an EmptyChangeset
259 an EmptyChangeset
262 """
260 """
263
261
264 def __init__(self, cs='0' * 40):
262 def __init__(self, cs='0' * 40):
265 self._empty_cs = cs
263 self._empty_cs = cs
266 self.revision = -1
264 self.revision = -1
267 self.message = ''
265 self.message = ''
268 self.author = ''
266 self.author = ''
269 self.date = ''
267 self.date = ''
270
268
271 @LazyProperty
269 @LazyProperty
272 def raw_id(self):
270 def raw_id(self):
273 """Returns raw string identifying this changeset, useful for web
271 """Returns raw string identifying this changeset, useful for web
274 representation.
272 representation.
275 """
273 """
276
274
277 return self._empty_cs
275 return self._empty_cs
278
276
279 @LazyProperty
277 @LazyProperty
280 def short_id(self):
278 def short_id(self):
281 return self.raw_id[:12]
279 return self.raw_id[:12]
282
280
283 def get_file_changeset(self, path):
281 def get_file_changeset(self, path):
284 return self
282 return self
285
283
286 def get_file_content(self, path):
284 def get_file_content(self, path):
287 return u''
285 return u''
288
286
289 def get_file_size(self, path):
287 def get_file_size(self, path):
290 return 0
288 return 0
291
289
292 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
290 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
293 """maps all found repositories into db
291 """maps all found repositories into db
294 """
292 """
295
293
296 sa = meta.Session()
294 sa = meta.Session()
297 rm = RepoModel()
295 rm = RepoModel()
298 user = sa.query(User).filter(User.admin == True).first()
296 user = sa.query(User).filter(User.admin == True).first()
299
297
300 for name, repo in initial_repo_list.items():
298 for name, repo in initial_repo_list.items():
301 if not rm.get_by_repo_name(name, cache=False):
299 if not rm.get_by_repo_name(name, cache=False):
302 log.info('repository %s not found creating default', name)
300 log.info('repository %s not found creating default', name)
303
301
304 form_data = {
302 form_data = {
305 'repo_name':name,
303 'repo_name':name,
306 'repo_type':repo.alias,
304 'repo_type':repo.alias,
307 'description':repo.description \
305 'description':repo.description \
308 if repo.description != 'unknown' else \
306 if repo.description != 'unknown' else \
309 '%s repository' % name,
307 '%s repository' % name,
310 'private':False
308 'private':False
311 }
309 }
312 rm.create(form_data, user, just_db=True)
310 rm.create(form_data, user, just_db=True)
313
311
314 if remove_obsolete:
312 if remove_obsolete:
315 #remove from database those repositories that are not in the filesystem
313 #remove from database those repositories that are not in the filesystem
316 for repo in sa.query(Repository).all():
314 for repo in sa.query(Repository).all():
317 if repo.repo_name not in initial_repo_list.keys():
315 if repo.repo_name not in initial_repo_list.keys():
318 sa.delete(repo)
316 sa.delete(repo)
319 sa.commit()
317 sa.commit()
320
318
321 class OrderedDict(dict, DictMixin):
319 class OrderedDict(dict, DictMixin):
322
320
323 def __init__(self, *args, **kwds):
321 def __init__(self, *args, **kwds):
324 if len(args) > 1:
322 if len(args) > 1:
325 raise TypeError('expected at most 1 arguments, got %d' % len(args))
323 raise TypeError('expected at most 1 arguments, got %d' % len(args))
326 try:
324 try:
327 self.__end
325 self.__end
328 except AttributeError:
326 except AttributeError:
329 self.clear()
327 self.clear()
330 self.update(*args, **kwds)
328 self.update(*args, **kwds)
331
329
332 def clear(self):
330 def clear(self):
333 self.__end = end = []
331 self.__end = end = []
334 end += [None, end, end] # sentinel node for doubly linked list
332 end += [None, end, end] # sentinel node for doubly linked list
335 self.__map = {} # key --> [key, prev, next]
333 self.__map = {} # key --> [key, prev, next]
336 dict.clear(self)
334 dict.clear(self)
337
335
338 def __setitem__(self, key, value):
336 def __setitem__(self, key, value):
339 if key not in self:
337 if key not in self:
340 end = self.__end
338 end = self.__end
341 curr = end[1]
339 curr = end[1]
342 curr[2] = end[1] = self.__map[key] = [key, curr, end]
340 curr[2] = end[1] = self.__map[key] = [key, curr, end]
343 dict.__setitem__(self, key, value)
341 dict.__setitem__(self, key, value)
344
342
345 def __delitem__(self, key):
343 def __delitem__(self, key):
346 dict.__delitem__(self, key)
344 dict.__delitem__(self, key)
347 key, prev, next = self.__map.pop(key)
345 key, prev, next = self.__map.pop(key)
348 prev[2] = next
346 prev[2] = next
349 next[1] = prev
347 next[1] = prev
350
348
351 def __iter__(self):
349 def __iter__(self):
352 end = self.__end
350 end = self.__end
353 curr = end[2]
351 curr = end[2]
354 while curr is not end:
352 while curr is not end:
355 yield curr[0]
353 yield curr[0]
356 curr = curr[2]
354 curr = curr[2]
357
355
358 def __reversed__(self):
356 def __reversed__(self):
359 end = self.__end
357 end = self.__end
360 curr = end[1]
358 curr = end[1]
361 while curr is not end:
359 while curr is not end:
362 yield curr[0]
360 yield curr[0]
363 curr = curr[1]
361 curr = curr[1]
364
362
365 def popitem(self, last=True):
363 def popitem(self, last=True):
366 if not self:
364 if not self:
367 raise KeyError('dictionary is empty')
365 raise KeyError('dictionary is empty')
368 if last:
366 if last:
369 key = reversed(self).next()
367 key = reversed(self).next()
370 else:
368 else:
371 key = iter(self).next()
369 key = iter(self).next()
372 value = self.pop(key)
370 value = self.pop(key)
373 return key, value
371 return key, value
374
372
375 def __reduce__(self):
373 def __reduce__(self):
376 items = [[k, self[k]] for k in self]
374 items = [[k, self[k]] for k in self]
377 tmp = self.__map, self.__end
375 tmp = self.__map, self.__end
378 del self.__map, self.__end
376 del self.__map, self.__end
379 inst_dict = vars(self).copy()
377 inst_dict = vars(self).copy()
380 self.__map, self.__end = tmp
378 self.__map, self.__end = tmp
381 if inst_dict:
379 if inst_dict:
382 return (self.__class__, (items,), inst_dict)
380 return (self.__class__, (items,), inst_dict)
383 return self.__class__, (items,)
381 return self.__class__, (items,)
384
382
385 def keys(self):
383 def keys(self):
386 return list(self)
384 return list(self)
387
385
388 setdefault = DictMixin.setdefault
386 setdefault = DictMixin.setdefault
389 update = DictMixin.update
387 update = DictMixin.update
390 pop = DictMixin.pop
388 pop = DictMixin.pop
391 values = DictMixin.values
389 values = DictMixin.values
392 items = DictMixin.items
390 items = DictMixin.items
393 iterkeys = DictMixin.iterkeys
391 iterkeys = DictMixin.iterkeys
394 itervalues = DictMixin.itervalues
392 itervalues = DictMixin.itervalues
395 iteritems = DictMixin.iteritems
393 iteritems = DictMixin.iteritems
396
394
397 def __repr__(self):
395 def __repr__(self):
398 if not self:
396 if not self:
399 return '%s()' % (self.__class__.__name__,)
397 return '%s()' % (self.__class__.__name__,)
400 return '%s(%r)' % (self.__class__.__name__, self.items())
398 return '%s(%r)' % (self.__class__.__name__, self.items())
401
399
402 def copy(self):
400 def copy(self):
403 return self.__class__(self)
401 return self.__class__(self)
404
402
405 @classmethod
403 @classmethod
406 def fromkeys(cls, iterable, value=None):
404 def fromkeys(cls, iterable, value=None):
407 d = cls()
405 d = cls()
408 for key in iterable:
406 for key in iterable:
409 d[key] = value
407 d[key] = value
410 return d
408 return d
411
409
412 def __eq__(self, other):
410 def __eq__(self, other):
413 if isinstance(other, OrderedDict):
411 if isinstance(other, OrderedDict):
414 return len(self) == len(other) and self.items() == other.items()
412 return len(self) == len(other) and self.items() == other.items()
415 return dict.__eq__(self, other)
413 return dict.__eq__(self, other)
416
414
417 def __ne__(self, other):
415 def __ne__(self, other):
418 return not self == other
416 return not self == other
419
417
420
418
421 #set cache regions for beaker so celery can utilise it
419 #set cache regions for beaker so celery can utilise it
422 def add_cache(settings):
420 def add_cache(settings):
423 cache_settings = {'regions':None}
421 cache_settings = {'regions':None}
424 for key in settings.keys():
422 for key in settings.keys():
425 for prefix in ['beaker.cache.', 'cache.']:
423 for prefix in ['beaker.cache.', 'cache.']:
426 if key.startswith(prefix):
424 if key.startswith(prefix):
427 name = key.split(prefix)[1].strip()
425 name = key.split(prefix)[1].strip()
428 cache_settings[name] = settings[key].strip()
426 cache_settings[name] = settings[key].strip()
429 if cache_settings['regions']:
427 if cache_settings['regions']:
430 for region in cache_settings['regions'].split(','):
428 for region in cache_settings['regions'].split(','):
431 region = region.strip()
429 region = region.strip()
432 region_settings = {}
430 region_settings = {}
433 for key, value in cache_settings.items():
431 for key, value in cache_settings.items():
434 if key.startswith(region):
432 if key.startswith(region):
435 region_settings[key.split('.')[1]] = value
433 region_settings[key.split('.')[1]] = value
436 region_settings['expire'] = int(region_settings.get('expire',
434 region_settings['expire'] = int(region_settings.get('expire',
437 60))
435 60))
438 region_settings.setdefault('lock_dir',
436 region_settings.setdefault('lock_dir',
439 cache_settings.get('lock_dir'))
437 cache_settings.get('lock_dir'))
440 if 'type' not in region_settings:
438 if 'type' not in region_settings:
441 region_settings['type'] = cache_settings.get('type',
439 region_settings['type'] = cache_settings.get('type',
442 'memory')
440 'memory')
443 beaker.cache.cache_regions[region] = region_settings
441 beaker.cache.cache_regions[region] = region_settings
444
442
445 def get_current_revision():
443 def get_current_revision():
446 """Returns tuple of (number, id) from repository containing this package
444 """Returns tuple of (number, id) from repository containing this package
447 or None if repository could not be found.
445 or None if repository could not be found.
448 """
446 """
449
447
450 try:
448 try:
451 from vcs import get_repo
449 from vcs import get_repo
452 from vcs.utils.helpers import get_scm
450 from vcs.utils.helpers import get_scm
453 from vcs.exceptions import RepositoryError, VCSError
451 from vcs.exceptions import RepositoryError, VCSError
454 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
452 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
455 scm = get_scm(repopath)[0]
453 scm = get_scm(repopath)[0]
456 repo = get_repo(path=repopath, alias=scm)
454 repo = get_repo(path=repopath, alias=scm)
457 tip = repo.get_changeset()
455 tip = repo.get_changeset()
458 return (tip.revision, tip.short_id)
456 return (tip.revision, tip.short_id)
459 except (ImportError, RepositoryError, VCSError), err:
457 except (ImportError, RepositoryError, VCSError), err:
460 logging.debug("Cannot retrieve rhodecode's revision. Original error "
458 logging.debug("Cannot retrieve rhodecode's revision. Original error "
461 "was: %s" % err)
459 "was: %s" % err)
462 return None
460 return None
463
461
464 #===============================================================================
462 #===============================================================================
465 # TEST FUNCTIONS AND CREATORS
463 # TEST FUNCTIONS AND CREATORS
466 #===============================================================================
464 #===============================================================================
467 def create_test_index(repo_location, full_index):
465 def create_test_index(repo_location, full_index):
468 """Makes default test index
466 """Makes default test index
469 :param repo_location:
467 :param repo_location:
470 :param full_index:
468 :param full_index:
471 """
469 """
472 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
470 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
473 from rhodecode.lib.pidlock import DaemonLock, LockHeld
471 from rhodecode.lib.pidlock import DaemonLock, LockHeld
474 import shutil
472 import shutil
475
473
476 index_location = os.path.join(repo_location, 'index')
474 index_location = os.path.join(repo_location, 'index')
477 if os.path.exists(index_location):
475 if os.path.exists(index_location):
478 shutil.rmtree(index_location)
476 shutil.rmtree(index_location)
479
477
480 try:
478 try:
481 l = DaemonLock()
479 l = DaemonLock()
482 WhooshIndexingDaemon(index_location=index_location,
480 WhooshIndexingDaemon(index_location=index_location,
483 repo_location=repo_location)\
481 repo_location=repo_location)\
484 .run(full_index=full_index)
482 .run(full_index=full_index)
485 l.release()
483 l.release()
486 except LockHeld:
484 except LockHeld:
487 pass
485 pass
488
486
489 def create_test_env(repos_test_path, config):
487 def create_test_env(repos_test_path, config):
490 """Makes a fresh database and
488 """Makes a fresh database and
491 install test repository into tmp dir
489 install test repository into tmp dir
492 """
490 """
493 from rhodecode.lib.db_manage import DbManage
491 from rhodecode.lib.db_manage import DbManage
494 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
492 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
495 HG_FORK, GIT_FORK, TESTS_TMP_PATH
493 HG_FORK, GIT_FORK, TESTS_TMP_PATH
496 import tarfile
494 import tarfile
497 import shutil
495 import shutil
498 from os.path import dirname as dn, join as jn, abspath
496 from os.path import dirname as dn, join as jn, abspath
499
497
500 log = logging.getLogger('TestEnvCreator')
498 log = logging.getLogger('TestEnvCreator')
501 # create logger
499 # create logger
502 log.setLevel(logging.DEBUG)
500 log.setLevel(logging.DEBUG)
503 log.propagate = True
501 log.propagate = True
504 # create console handler and set level to debug
502 # create console handler and set level to debug
505 ch = logging.StreamHandler()
503 ch = logging.StreamHandler()
506 ch.setLevel(logging.DEBUG)
504 ch.setLevel(logging.DEBUG)
507
505
508 # create formatter
506 # create formatter
509 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
507 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
510
508
511 # add formatter to ch
509 # add formatter to ch
512 ch.setFormatter(formatter)
510 ch.setFormatter(formatter)
513
511
514 # add ch to logger
512 # add ch to logger
515 log.addHandler(ch)
513 log.addHandler(ch)
516
514
517 #PART ONE create db
515 #PART ONE create db
518 dbconf = config['sqlalchemy.db1.url']
516 dbconf = config['sqlalchemy.db1.url']
519 log.debug('making test db %s', dbconf)
517 log.debug('making test db %s', dbconf)
520
518
521 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
519 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
522 tests=True)
520 tests=True)
523 dbmanage.create_tables(override=True)
521 dbmanage.create_tables(override=True)
524 dbmanage.config_prompt(repos_test_path)
522 dbmanage.config_prompt(repos_test_path)
525 dbmanage.create_default_user()
523 dbmanage.create_default_user()
526 dbmanage.admin_prompt()
524 dbmanage.admin_prompt()
527 dbmanage.create_permissions()
525 dbmanage.create_permissions()
528 dbmanage.populate_default_permissions()
526 dbmanage.populate_default_permissions()
529
527
530 #PART TWO make test repo
528 #PART TWO make test repo
531 log.debug('making test vcs repositories')
529 log.debug('making test vcs repositories')
532
530
533 #remove old one from previos tests
531 #remove old one from previos tests
534 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
532 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
535
533
536 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
534 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
537 log.debug('removing %s', r)
535 log.debug('removing %s', r)
538 shutil.rmtree(jn(TESTS_TMP_PATH, r))
536 shutil.rmtree(jn(TESTS_TMP_PATH, r))
539
537
540 #CREATE DEFAULT HG REPOSITORY
538 #CREATE DEFAULT HG REPOSITORY
541 cur_dir = dn(dn(abspath(__file__)))
539 cur_dir = dn(dn(abspath(__file__)))
542 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
540 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
543 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
541 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
544 tar.close()
542 tar.close()
545
543
546
544
547 #==============================================================================
545 #==============================================================================
548 # PASTER COMMANDS
546 # PASTER COMMANDS
549 #==============================================================================
547 #==============================================================================
550
548
551 class BasePasterCommand(Command):
549 class BasePasterCommand(Command):
552 """
550 """
553 Abstract Base Class for paster commands.
551 Abstract Base Class for paster commands.
554
552
555 The celery commands are somewhat aggressive about loading
553 The celery commands are somewhat aggressive about loading
556 celery.conf, and since our module sets the `CELERY_LOADER`
554 celery.conf, and since our module sets the `CELERY_LOADER`
557 environment variable to our loader, we have to bootstrap a bit and
555 environment variable to our loader, we have to bootstrap a bit and
558 make sure we've had a chance to load the pylons config off of the
556 make sure we've had a chance to load the pylons config off of the
559 command line, otherwise everything fails.
557 command line, otherwise everything fails.
560 """
558 """
561 min_args = 1
559 min_args = 1
562 min_args_error = "Please provide a paster config file as an argument."
560 min_args_error = "Please provide a paster config file as an argument."
563 takes_config_file = 1
561 takes_config_file = 1
564 requires_config_file = True
562 requires_config_file = True
565
563
566 def notify_msg(self, msg, log=False):
564 def notify_msg(self, msg, log=False):
567 """Make a notification to user, additionally if logger is passed
565 """Make a notification to user, additionally if logger is passed
568 it logs this action using given logger
566 it logs this action using given logger
569
567
570 :param msg: message that will be printed to user
568 :param msg: message that will be printed to user
571 :param log: logging instance, to use to additionally log this message
569 :param log: logging instance, to use to additionally log this message
572
570
573 """
571 """
574 print msg
572 print msg
575 if log and isinstance(log, logging):
573 if log and isinstance(log, logging):
576 log(msg)
574 log(msg)
577
575
578
576
579 def run(self, args):
577 def run(self, args):
580 """
578 """
581 Overrides Command.run
579 Overrides Command.run
582
580
583 Checks for a config file argument and loads it.
581 Checks for a config file argument and loads it.
584 """
582 """
585 if len(args) < self.min_args:
583 if len(args) < self.min_args:
586 raise BadCommand(
584 raise BadCommand(
587 self.min_args_error % {'min_args': self.min_args,
585 self.min_args_error % {'min_args': self.min_args,
588 'actual_args': len(args)})
586 'actual_args': len(args)})
589
587
590 # Decrement because we're going to lob off the first argument.
588 # Decrement because we're going to lob off the first argument.
591 # @@ This is hacky
589 # @@ This is hacky
592 self.min_args -= 1
590 self.min_args -= 1
593 self.bootstrap_config(args[0])
591 self.bootstrap_config(args[0])
594 self.update_parser()
592 self.update_parser()
595 return super(BasePasterCommand, self).run(args[1:])
593 return super(BasePasterCommand, self).run(args[1:])
596
594
597 def update_parser(self):
595 def update_parser(self):
598 """
596 """
599 Abstract method. Allows for the class's parser to be updated
597 Abstract method. Allows for the class's parser to be updated
600 before the superclass's `run` method is called. Necessary to
598 before the superclass's `run` method is called. Necessary to
601 allow options/arguments to be passed through to the underlying
599 allow options/arguments to be passed through to the underlying
602 celery command.
600 celery command.
603 """
601 """
604 raise NotImplementedError("Abstract Method.")
602 raise NotImplementedError("Abstract Method.")
605
603
606 def bootstrap_config(self, conf):
604 def bootstrap_config(self, conf):
607 """
605 """
608 Loads the pylons configuration.
606 Loads the pylons configuration.
609 """
607 """
610 from pylons import config as pylonsconfig
608 from pylons import config as pylonsconfig
611
609
612 path_to_ini_file = os.path.realpath(conf)
610 path_to_ini_file = os.path.realpath(conf)
613 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
611 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
614 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
612 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
@@ -1,71 +1,69 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.__init__
3 rhodecode.model.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 The application's model objects
6 The application's model objects
7
7
8 :created_on: Nov 25, 2010
8 :created_on: Nov 25, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12
12
13
13
14 :example:
14 :example:
15
15
16 .. code-block:: python
16 .. code-block:: python
17
17
18 from paste.deploy import appconfig
18 from paste.deploy import appconfig
19 from pylons import config
19 from pylons import config
20 from sqlalchemy import engine_from_config
20 from sqlalchemy import engine_from_config
21 from rhodecode.config.environment import load_environment
21 from rhodecode.config.environment import load_environment
22
22
23 conf = appconfig('config:development.ini', relative_to = './../../')
23 conf = appconfig('config:development.ini', relative_to = './../../')
24 load_environment(conf.global_conf, conf.local_conf)
24 load_environment(conf.global_conf, conf.local_conf)
25
25
26 engine = engine_from_config(config, 'sqlalchemy.')
26 engine = engine_from_config(config, 'sqlalchemy.')
27 init_model(engine)
27 init_model(engine)
28 # RUN YOUR CODE HERE
28 # RUN YOUR CODE HERE
29
29
30 """
30 """
31 # This program is free software; you can redistribute it and/or
31 # This program is free software: you can redistribute it and/or modify
32 # modify it under the terms of the GNU General Public License
32 # it under the terms of the GNU General Public License as published by
33 # as published by the Free Software Foundation; version 2
33 # the Free Software Foundation, either version 3 of the License, or
34 # of the License or (at your opinion) any later version of the license.
34 # (at your option) any later version.
35 #
35 #
36 # This program is distributed in the hope that it will be useful,
36 # This program is distributed in the hope that it will be useful,
37 # but WITHOUT ANY WARRANTY; without even the implied warranty of
37 # but WITHOUT ANY WARRANTY; without even the implied warranty of
38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 # GNU General Public License for more details.
39 # GNU General Public License for more details.
40 #
40 #
41 # You should have received a copy of the GNU General Public License
41 # You should have received a copy of the GNU General Public License
42 # along with this program; if not, write to the Free Software
42 # along with this program. If not, see <http://www.gnu.org/licenses/>.
43 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
44 # MA 02110-1301, USA.
45
43
46 import logging
44 import logging
47 from rhodecode.model import meta
45 from rhodecode.model import meta
48 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
49
47
50 def init_model(engine):
48 def init_model(engine):
51 """Initializes db session, bind the engine with the metadata,
49 """Initializes db session, bind the engine with the metadata,
52 Call this before using any of the tables or classes in the model, preferably
50 Call this before using any of the tables or classes in the model, preferably
53 once in application start
51 once in application start
54
52
55 :param engine: engine to bind to
53 :param engine: engine to bind to
56 """
54 """
57 log.info("initializing db models for %s", engine)
55 log.info("initializing db models for %s", engine)
58 meta.Base.metadata.bind = engine
56 meta.Base.metadata.bind = engine
59
57
60 class BaseModel(object):
58 class BaseModel(object):
61 """Base Model for all RhodeCode models, it adds sql alchemy session
59 """Base Model for all RhodeCode models, it adds sql alchemy session
62 into instance of model
60 into instance of model
63
61
64 :param sa: If passed it reuses this session instead of creating a new one
62 :param sa: If passed it reuses this session instead of creating a new one
65 """
63 """
66
64
67 def __init__(self, sa=None):
65 def __init__(self, sa=None):
68 if sa is not None:
66 if sa is not None:
69 self.sa = sa
67 self.sa = sa
70 else:
68 else:
71 self.sa = meta.Session()
69 self.sa = meta.Session()
@@ -1,249 +1,247 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27 import logging
25 import logging
28 import datetime
26 import datetime
29 from datetime import date
27 from datetime import date
30
28
31 from sqlalchemy import *
29 from sqlalchemy import *
32 from sqlalchemy.exc import DatabaseError
30 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.orm import relationship, backref
31 from sqlalchemy.orm import relationship, backref
34 from sqlalchemy.orm.interfaces import MapperExtension
32 from sqlalchemy.orm.interfaces import MapperExtension
35
33
36 from rhodecode.model.meta import Base, Session
34 from rhodecode.model.meta import Base, Session
37
35
38 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
39
37
40
38
41 class RhodeCodeSettings(Base):
39 class RhodeCodeSettings(Base):
42 __tablename__ = 'rhodecode_settings'
40 __tablename__ = 'rhodecode_settings'
43 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
41 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
44 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
42 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
45 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
43 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
46 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
44 app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
47
45
48 def __init__(self, k='', v=''):
46 def __init__(self, k='', v=''):
49 self.app_settings_name = k
47 self.app_settings_name = k
50 self.app_settings_value = v
48 self.app_settings_value = v
51
49
52 def __repr__(self):
50 def __repr__(self):
53 return "<%s('%s:%s')>" % (self.__class__.__name__,
51 return "<%s('%s:%s')>" % (self.__class__.__name__,
54 self.app_settings_name, self.app_settings_value)
52 self.app_settings_name, self.app_settings_value)
55
53
56 class RhodeCodeUi(Base):
54 class RhodeCodeUi(Base):
57 __tablename__ = 'rhodecode_ui'
55 __tablename__ = 'rhodecode_ui'
58 __table_args__ = {'useexisting':True}
56 __table_args__ = {'useexisting':True}
59 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
57 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
60 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
58 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
61 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
59 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
62 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
60 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
63 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
61 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
64
62
65
63
66 class User(Base):
64 class User(Base):
67 __tablename__ = 'users'
65 __tablename__ = 'users'
68 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
66 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
69 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
67 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
70 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
68 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
71 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
69 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
72 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
70 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
73 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
71 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
74 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
72 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
75 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
73 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
74 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
77 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
75 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
78 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
76 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
79
77
80 user_log = relationship('UserLog', cascade='all')
78 user_log = relationship('UserLog', cascade='all')
81 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
79 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
82
80
83 repositories = relationship('Repository')
81 repositories = relationship('Repository')
84 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
82 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
85
83
86 @property
84 @property
87 def full_contact(self):
85 def full_contact(self):
88 return '%s %s <%s>' % (self.name, self.lastname, self.email)
86 return '%s %s <%s>' % (self.name, self.lastname, self.email)
89
87
90 @property
88 @property
91 def short_contact(self):
89 def short_contact(self):
92 return '%s %s' % (self.name, self.lastname)
90 return '%s %s' % (self.name, self.lastname)
93
91
94
92
95 @property
93 @property
96 def is_admin(self):
94 def is_admin(self):
97 return self.admin
95 return self.admin
98
96
99 def __repr__(self):
97 def __repr__(self):
100 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
98 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
101 self.user_id, self.username)
99 self.user_id, self.username)
102
100
103 @classmethod
101 @classmethod
104 def by_username(cls, username):
102 def by_username(cls, username):
105 return Session.query(cls).filter(cls.username == username).one()
103 return Session.query(cls).filter(cls.username == username).one()
106
104
107
105
108 def update_lastlogin(self):
106 def update_lastlogin(self):
109 """Update user lastlogin"""
107 """Update user lastlogin"""
110
108
111 try:
109 try:
112 session = Session.object_session(self)
110 session = Session.object_session(self)
113 self.last_login = datetime.datetime.now()
111 self.last_login = datetime.datetime.now()
114 session.add(self)
112 session.add(self)
115 session.commit()
113 session.commit()
116 log.debug('updated user %s lastlogin', self.username)
114 log.debug('updated user %s lastlogin', self.username)
117 except (DatabaseError,):
115 except (DatabaseError,):
118 session.rollback()
116 session.rollback()
119
117
120
118
121 class UserLog(Base):
119 class UserLog(Base):
122 __tablename__ = 'user_logs'
120 __tablename__ = 'user_logs'
123 __table_args__ = {'useexisting':True}
121 __table_args__ = {'useexisting':True}
124 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
122 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
125 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
123 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
126 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
124 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
127 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
125 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
128 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
126 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
129 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
127 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
130 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
128 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
131
129
132 @property
130 @property
133 def action_as_day(self):
131 def action_as_day(self):
134 return date(*self.action_date.timetuple()[:3])
132 return date(*self.action_date.timetuple()[:3])
135
133
136 user = relationship('User')
134 user = relationship('User')
137 repository = relationship('Repository')
135 repository = relationship('Repository')
138
136
139 class Repository(Base):
137 class Repository(Base):
140 __tablename__ = 'repositories'
138 __tablename__ = 'repositories'
141 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
139 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
142 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
140 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
143 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
141 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
144 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
142 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
145 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
143 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
146 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
144 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
147 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
145 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
148 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
146 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
147 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
150
148
151 user = relationship('User')
149 user = relationship('User')
152 fork = relationship('Repository', remote_side=repo_id)
150 fork = relationship('Repository', remote_side=repo_id)
153 repo_to_perm = relationship('RepoToPerm', cascade='all')
151 repo_to_perm = relationship('RepoToPerm', cascade='all')
154 stats = relationship('Statistics', cascade='all', uselist=False)
152 stats = relationship('Statistics', cascade='all', uselist=False)
155
153
156 repo_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
154 repo_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
157
155
158 logs = relationship('UserLog', cascade='all')
156 logs = relationship('UserLog', cascade='all')
159
157
160 def __repr__(self):
158 def __repr__(self):
161 return "<%s('%s:%s')>" % (self.__class__.__name__,
159 return "<%s('%s:%s')>" % (self.__class__.__name__,
162 self.repo_id, self.repo_name)
160 self.repo_id, self.repo_name)
163
161
164 class Permission(Base):
162 class Permission(Base):
165 __tablename__ = 'permissions'
163 __tablename__ = 'permissions'
166 __table_args__ = {'useexisting':True}
164 __table_args__ = {'useexisting':True}
167 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
165 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
168 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
166 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
169 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
167 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
170
168
171 def __repr__(self):
169 def __repr__(self):
172 return "<%s('%s:%s')>" % (self.__class__.__name__,
170 return "<%s('%s:%s')>" % (self.__class__.__name__,
173 self.permission_id, self.permission_name)
171 self.permission_id, self.permission_name)
174
172
175 class RepoToPerm(Base):
173 class RepoToPerm(Base):
176 __tablename__ = 'repo_to_perm'
174 __tablename__ = 'repo_to_perm'
177 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
175 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
178 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
176 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
179 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
177 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
180 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
178 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
181 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
179 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
182
180
183 user = relationship('User')
181 user = relationship('User')
184 permission = relationship('Permission')
182 permission = relationship('Permission')
185 repository = relationship('Repository')
183 repository = relationship('Repository')
186
184
187 class UserToPerm(Base):
185 class UserToPerm(Base):
188 __tablename__ = 'user_to_perm'
186 __tablename__ = 'user_to_perm'
189 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
187 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
190 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
188 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
191 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
189 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
192 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
190 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
193
191
194 user = relationship('User')
192 user = relationship('User')
195 permission = relationship('Permission')
193 permission = relationship('Permission')
196
194
197 class Statistics(Base):
195 class Statistics(Base):
198 __tablename__ = 'statistics'
196 __tablename__ = 'statistics'
199 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
197 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
200 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
198 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
201 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
199 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
202 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
200 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
203 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
201 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
204 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
202 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
205 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
203 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
206
204
207 repository = relationship('Repository', single_parent=True)
205 repository = relationship('Repository', single_parent=True)
208
206
209 class UserFollowing(Base):
207 class UserFollowing(Base):
210 __tablename__ = 'user_followings'
208 __tablename__ = 'user_followings'
211 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
209 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
212 UniqueConstraint('user_id', 'follows_user_id')
210 UniqueConstraint('user_id', 'follows_user_id')
213 , {'useexisting':True})
211 , {'useexisting':True})
214
212
215 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
213 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
216 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
214 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
217 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
215 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
218 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
216 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
219
217
220 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
218 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
221
219
222 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
220 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
223 follows_repository = relationship('Repository', order_by='Repository.repo_name')
221 follows_repository = relationship('Repository', order_by='Repository.repo_name')
224
222
225 class CacheInvalidation(Base):
223 class CacheInvalidation(Base):
226 __tablename__ = 'cache_invalidation'
224 __tablename__ = 'cache_invalidation'
227 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
225 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
228 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
226 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
229 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
227 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
230 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
228 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
231 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
229 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
232
230
233
231
234 def __init__(self, cache_key, cache_args=''):
232 def __init__(self, cache_key, cache_args=''):
235 self.cache_key = cache_key
233 self.cache_key = cache_key
236 self.cache_args = cache_args
234 self.cache_args = cache_args
237 self.cache_active = False
235 self.cache_active = False
238
236
239 def __repr__(self):
237 def __repr__(self):
240 return "<%s('%s:%s')>" % (self.__class__.__name__,
238 return "<%s('%s:%s')>" % (self.__class__.__name__,
241 self.cache_id, self.cache_key)
239 self.cache_id, self.cache_key)
242
240
243 class DbMigrateVersion(Base):
241 class DbMigrateVersion(Base):
244 __tablename__ = 'db_migrate_version'
242 __tablename__ = 'db_migrate_version'
245 __table_args__ = {'useexisting':True}
243 __table_args__ = {'useexisting':True}
246 repository_id = Column('repository_id', String(255), primary_key=True)
244 repository_id = Column('repository_id', String(255), primary_key=True)
247 repository_path = Column('repository_path', Text)
245 repository_path = Column('repository_path', Text)
248 version = Column('version', Integer)
246 version = Column('version', Integer)
249
247
@@ -1,114 +1,112 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.permission
3 rhodecode.model.permission
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 permissions model for RhodeCode
6 permissions model for RhodeCode
7
7
8 :created_on: Aug 20, 2010
8 :created_on: Aug 20, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29 import traceback
27 import traceback
30
28
31 from sqlalchemy.exc import DatabaseError
29 from sqlalchemy.exc import DatabaseError
32
30
33 from rhodecode.model import BaseModel
31 from rhodecode.model import BaseModel
34 from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
32 from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
35 from rhodecode.model.caching_query import FromCache
33 from rhodecode.model.caching_query import FromCache
36
34
37 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
38
36
39
37
40 class PermissionModel(BaseModel):
38 class PermissionModel(BaseModel):
41 """Permissions model for RhodeCode
39 """Permissions model for RhodeCode
42 """
40 """
43
41
44 def get_permission(self, permission_id, cache=False):
42 def get_permission(self, permission_id, cache=False):
45 """Get's permissions by id
43 """Get's permissions by id
46
44
47 :param permission_id: id of permission to get from database
45 :param permission_id: id of permission to get from database
48 :param cache: use Cache for this query
46 :param cache: use Cache for this query
49 """
47 """
50 perm = self.sa.query(Permission)
48 perm = self.sa.query(Permission)
51 if cache:
49 if cache:
52 perm = perm.options(FromCache("sql_cache_short",
50 perm = perm.options(FromCache("sql_cache_short",
53 "get_permission_%s" % permission_id))
51 "get_permission_%s" % permission_id))
54 return perm.get(permission_id)
52 return perm.get(permission_id)
55
53
56 def get_permission_by_name(self, name, cache=False):
54 def get_permission_by_name(self, name, cache=False):
57 """Get's permissions by given name
55 """Get's permissions by given name
58
56
59 :param name: name to fetch
57 :param name: name to fetch
60 :param cache: Use cache for this query
58 :param cache: Use cache for this query
61 """
59 """
62 perm = self.sa.query(Permission)\
60 perm = self.sa.query(Permission)\
63 .filter(Permission.permission_name == name)
61 .filter(Permission.permission_name == name)
64 if cache:
62 if cache:
65 perm = perm.options(FromCache("sql_cache_short",
63 perm = perm.options(FromCache("sql_cache_short",
66 "get_permission_%s" % name))
64 "get_permission_%s" % name))
67 return perm.scalar()
65 return perm.scalar()
68
66
69 def update(self, form_result):
67 def update(self, form_result):
70 perm_user = self.sa.query(User)\
68 perm_user = self.sa.query(User)\
71 .filter(User.username == form_result['perm_user_name']).scalar()
69 .filter(User.username == form_result['perm_user_name']).scalar()
72 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
70 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
73 if len(u2p) != 3:
71 if len(u2p) != 3:
74 raise Exception('Defined: %s should be 3 permissions for default'
72 raise Exception('Defined: %s should be 3 permissions for default'
75 ' user. This should not happen please verify'
73 ' user. This should not happen please verify'
76 ' your database' % len(u2p))
74 ' your database' % len(u2p))
77
75
78 try:
76 try:
79 #stage 1 change defaults
77 #stage 1 change defaults
80 for p in u2p:
78 for p in u2p:
81 if p.permission.permission_name.startswith('repository.'):
79 if p.permission.permission_name.startswith('repository.'):
82 p.permission = self.get_permission_by_name(
80 p.permission = self.get_permission_by_name(
83 form_result['default_perm'])
81 form_result['default_perm'])
84 self.sa.add(p)
82 self.sa.add(p)
85
83
86 if p.permission.permission_name.startswith('hg.register.'):
84 if p.permission.permission_name.startswith('hg.register.'):
87 p.permission = self.get_permission_by_name(
85 p.permission = self.get_permission_by_name(
88 form_result['default_register'])
86 form_result['default_register'])
89 self.sa.add(p)
87 self.sa.add(p)
90
88
91 if p.permission.permission_name.startswith('hg.create.'):
89 if p.permission.permission_name.startswith('hg.create.'):
92 p.permission = self.get_permission_by_name(
90 p.permission = self.get_permission_by_name(
93 form_result['default_create'])
91 form_result['default_create'])
94 self.sa.add(p)
92 self.sa.add(p)
95
93
96 #stage 2 update all default permissions for repos if checked
94 #stage 2 update all default permissions for repos if checked
97 if form_result['overwrite_default'] == True:
95 if form_result['overwrite_default'] == True:
98 for r2p in self.sa.query(RepoToPerm)\
96 for r2p in self.sa.query(RepoToPerm)\
99 .filter(RepoToPerm.user == perm_user).all():
97 .filter(RepoToPerm.user == perm_user).all():
100 r2p.permission = self.get_permission_by_name(
98 r2p.permission = self.get_permission_by_name(
101 form_result['default_perm'])
99 form_result['default_perm'])
102 self.sa.add(r2p)
100 self.sa.add(r2p)
103
101
104 #stage 3 set anonymous access
102 #stage 3 set anonymous access
105 if perm_user.username == 'default':
103 if perm_user.username == 'default':
106 perm_user.active = bool(form_result['anonymous'])
104 perm_user.active = bool(form_result['anonymous'])
107 self.sa.add(perm_user)
105 self.sa.add(perm_user)
108
106
109
107
110 self.sa.commit()
108 self.sa.commit()
111 except (DatabaseError,):
109 except (DatabaseError,):
112 log.error(traceback.format_exc())
110 log.error(traceback.format_exc())
113 self.sa.rollback()
111 self.sa.rollback()
114 raise
112 raise
@@ -1,274 +1,272 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27 import os
25 import os
28 import shutil
26 import shutil
29 import logging
27 import logging
30 import traceback
28 import traceback
31 from datetime import datetime
29 from datetime import datetime
32
30
33 from sqlalchemy.orm import joinedload, make_transient
31 from sqlalchemy.orm import joinedload, make_transient
34
32
35 from vcs.utils.lazy import LazyProperty
33 from vcs.utils.lazy import LazyProperty
36 from vcs.backends import get_backend
34 from vcs.backends import get_backend
37
35
38 from rhodecode.model import BaseModel
36 from rhodecode.model import BaseModel
39 from rhodecode.model.caching_query import FromCache
37 from rhodecode.model.caching_query import FromCache
40 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
38 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
41 Statistics, RhodeCodeUi
39 Statistics, RhodeCodeUi
42 from rhodecode.model.user import UserModel
40 from rhodecode.model.user import UserModel
43
41
44 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
45
43
46 class RepoModel(BaseModel):
44 class RepoModel(BaseModel):
47
45
48 @LazyProperty
46 @LazyProperty
49 def repos_path(self):
47 def repos_path(self):
50 """Get's the repositories root path from database
48 """Get's the repositories root path from database
51 """
49 """
52
50
53 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
51 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
54 return q.ui_value
52 return q.ui_value
55
53
56 def get(self, repo_id, cache=False):
54 def get(self, repo_id, cache=False):
57 repo = self.sa.query(Repository)\
55 repo = self.sa.query(Repository)\
58 .filter(Repository.repo_id == repo_id)
56 .filter(Repository.repo_id == repo_id)
59
57
60 if cache:
58 if cache:
61 repo = repo.options(FromCache("sql_cache_short",
59 repo = repo.options(FromCache("sql_cache_short",
62 "get_repo_%s" % repo_id))
60 "get_repo_%s" % repo_id))
63 return repo.scalar()
61 return repo.scalar()
64
62
65
63
66 def get_by_repo_name(self, repo_name, cache=False):
64 def get_by_repo_name(self, repo_name, cache=False):
67 repo = self.sa.query(Repository)\
65 repo = self.sa.query(Repository)\
68 .filter(Repository.repo_name == repo_name)
66 .filter(Repository.repo_name == repo_name)
69
67
70 if cache:
68 if cache:
71 repo = repo.options(FromCache("sql_cache_short",
69 repo = repo.options(FromCache("sql_cache_short",
72 "get_repo_%s" % repo_name))
70 "get_repo_%s" % repo_name))
73 return repo.scalar()
71 return repo.scalar()
74
72
75 def get_users_js(self):
73 def get_users_js(self):
76
74
77 users = self.sa.query(User).filter(User.active == True).all()
75 users = self.sa.query(User).filter(User.active == True).all()
78 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
76 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
79 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
77 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
80 u.lastname, u.username)
78 u.lastname, u.username)
81 for u in users])
79 for u in users])
82 return users_array
80 return users_array
83
81
84
82
85 def update(self, repo_name, form_data):
83 def update(self, repo_name, form_data):
86 try:
84 try:
87 cur_repo = self.get_by_repo_name(repo_name, cache=False)
85 cur_repo = self.get_by_repo_name(repo_name, cache=False)
88 user_model = UserModel(self.sa)
86 user_model = UserModel(self.sa)
89
87
90 #update permissions
88 #update permissions
91 for username, perm in form_data['perms_updates']:
89 for username, perm in form_data['perms_updates']:
92 r2p = self.sa.query(RepoToPerm)\
90 r2p = self.sa.query(RepoToPerm)\
93 .filter(RepoToPerm.user == user_model.get_by_username(username))\
91 .filter(RepoToPerm.user == user_model.get_by_username(username))\
94 .filter(RepoToPerm.repository == cur_repo)\
92 .filter(RepoToPerm.repository == cur_repo)\
95 .one()
93 .one()
96
94
97 r2p.permission = self.sa.query(Permission)\
95 r2p.permission = self.sa.query(Permission)\
98 .filter(Permission.permission_name == perm)\
96 .filter(Permission.permission_name == perm)\
99 .scalar()
97 .scalar()
100 self.sa.add(r2p)
98 self.sa.add(r2p)
101
99
102 #set new permissions
100 #set new permissions
103 for username, perm in form_data['perms_new']:
101 for username, perm in form_data['perms_new']:
104 r2p = RepoToPerm()
102 r2p = RepoToPerm()
105 r2p.repository = cur_repo
103 r2p.repository = cur_repo
106 r2p.user = user_model.get_by_username(username, cache=False)
104 r2p.user = user_model.get_by_username(username, cache=False)
107
105
108 r2p.permission = self.sa.query(Permission)\
106 r2p.permission = self.sa.query(Permission)\
109 .filter(Permission.permission_name == perm)\
107 .filter(Permission.permission_name == perm)\
110 .scalar()
108 .scalar()
111 self.sa.add(r2p)
109 self.sa.add(r2p)
112
110
113 #update current repo
111 #update current repo
114 for k, v in form_data.items():
112 for k, v in form_data.items():
115 if k == 'user':
113 if k == 'user':
116 cur_repo.user = user_model.get(v)
114 cur_repo.user = user_model.get(v)
117 else:
115 else:
118 setattr(cur_repo, k, v)
116 setattr(cur_repo, k, v)
119
117
120 self.sa.add(cur_repo)
118 self.sa.add(cur_repo)
121
119
122 if repo_name != form_data['repo_name']:
120 if repo_name != form_data['repo_name']:
123 #rename our data
121 #rename our data
124 self.__rename_repo(repo_name, form_data['repo_name'])
122 self.__rename_repo(repo_name, form_data['repo_name'])
125
123
126 self.sa.commit()
124 self.sa.commit()
127 except:
125 except:
128 log.error(traceback.format_exc())
126 log.error(traceback.format_exc())
129 self.sa.rollback()
127 self.sa.rollback()
130 raise
128 raise
131
129
132 def create(self, form_data, cur_user, just_db=False, fork=False):
130 def create(self, form_data, cur_user, just_db=False, fork=False):
133 try:
131 try:
134 if fork:
132 if fork:
135 #force str since hg doesn't go with unicode
133 #force str since hg doesn't go with unicode
136 repo_name = str(form_data['fork_name'])
134 repo_name = str(form_data['fork_name'])
137 org_name = str(form_data['repo_name'])
135 org_name = str(form_data['repo_name'])
138
136
139 else:
137 else:
140 org_name = repo_name = str(form_data['repo_name'])
138 org_name = repo_name = str(form_data['repo_name'])
141 new_repo = Repository()
139 new_repo = Repository()
142 new_repo.enable_statistics = True
140 new_repo.enable_statistics = True
143 for k, v in form_data.items():
141 for k, v in form_data.items():
144 if k == 'repo_name':
142 if k == 'repo_name':
145 v = repo_name
143 v = repo_name
146 setattr(new_repo, k, v)
144 setattr(new_repo, k, v)
147
145
148 if fork:
146 if fork:
149 parent_repo = self.sa.query(Repository)\
147 parent_repo = self.sa.query(Repository)\
150 .filter(Repository.repo_name == org_name).scalar()
148 .filter(Repository.repo_name == org_name).scalar()
151 new_repo.fork = parent_repo
149 new_repo.fork = parent_repo
152
150
153 new_repo.user_id = cur_user.user_id
151 new_repo.user_id = cur_user.user_id
154 self.sa.add(new_repo)
152 self.sa.add(new_repo)
155
153
156 #create default permission
154 #create default permission
157 repo_to_perm = RepoToPerm()
155 repo_to_perm = RepoToPerm()
158 default = 'repository.read'
156 default = 'repository.read'
159 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
157 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
160 if p.permission.permission_name.startswith('repository.'):
158 if p.permission.permission_name.startswith('repository.'):
161 default = p.permission.permission_name
159 default = p.permission.permission_name
162 break
160 break
163
161
164 default_perm = 'repository.none' if form_data['private'] else default
162 default_perm = 'repository.none' if form_data['private'] else default
165
163
166 repo_to_perm.permission_id = self.sa.query(Permission)\
164 repo_to_perm.permission_id = self.sa.query(Permission)\
167 .filter(Permission.permission_name == default_perm)\
165 .filter(Permission.permission_name == default_perm)\
168 .one().permission_id
166 .one().permission_id
169
167
170 repo_to_perm.repository = new_repo
168 repo_to_perm.repository = new_repo
171 repo_to_perm.user_id = UserModel(self.sa)\
169 repo_to_perm.user_id = UserModel(self.sa)\
172 .get_by_username('default', cache=False).user_id
170 .get_by_username('default', cache=False).user_id
173
171
174 self.sa.add(repo_to_perm)
172 self.sa.add(repo_to_perm)
175
173
176 if not just_db:
174 if not just_db:
177 self.__create_repo(repo_name, form_data['repo_type'])
175 self.__create_repo(repo_name, form_data['repo_type'])
178
176
179 self.sa.commit()
177 self.sa.commit()
180
178
181 #now automatically start following this repository as owner
179 #now automatically start following this repository as owner
182 from rhodecode.model.scm import ScmModel
180 from rhodecode.model.scm import ScmModel
183 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
181 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
184 cur_user.user_id)
182 cur_user.user_id)
185
183
186 except:
184 except:
187 log.error(traceback.format_exc())
185 log.error(traceback.format_exc())
188 self.sa.rollback()
186 self.sa.rollback()
189 raise
187 raise
190
188
191 def create_fork(self, form_data, cur_user):
189 def create_fork(self, form_data, cur_user):
192 from rhodecode.lib.celerylib import tasks, run_task
190 from rhodecode.lib.celerylib import tasks, run_task
193 run_task(tasks.create_repo_fork, form_data, cur_user)
191 run_task(tasks.create_repo_fork, form_data, cur_user)
194
192
195 def delete(self, repo):
193 def delete(self, repo):
196 try:
194 try:
197 self.sa.delete(repo)
195 self.sa.delete(repo)
198 self.__delete_repo(repo)
196 self.__delete_repo(repo)
199 self.sa.commit()
197 self.sa.commit()
200 except:
198 except:
201 log.error(traceback.format_exc())
199 log.error(traceback.format_exc())
202 self.sa.rollback()
200 self.sa.rollback()
203 raise
201 raise
204
202
205 def delete_perm_user(self, form_data, repo_name):
203 def delete_perm_user(self, form_data, repo_name):
206 try:
204 try:
207 self.sa.query(RepoToPerm)\
205 self.sa.query(RepoToPerm)\
208 .filter(RepoToPerm.repository \
206 .filter(RepoToPerm.repository \
209 == self.get_by_repo_name(repo_name))\
207 == self.get_by_repo_name(repo_name))\
210 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
208 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
211 self.sa.commit()
209 self.sa.commit()
212 except:
210 except:
213 log.error(traceback.format_exc())
211 log.error(traceback.format_exc())
214 self.sa.rollback()
212 self.sa.rollback()
215 raise
213 raise
216
214
217 def delete_stats(self, repo_name):
215 def delete_stats(self, repo_name):
218 try:
216 try:
219 self.sa.query(Statistics)\
217 self.sa.query(Statistics)\
220 .filter(Statistics.repository == \
218 .filter(Statistics.repository == \
221 self.get_by_repo_name(repo_name)).delete()
219 self.get_by_repo_name(repo_name)).delete()
222 self.sa.commit()
220 self.sa.commit()
223 except:
221 except:
224 log.error(traceback.format_exc())
222 log.error(traceback.format_exc())
225 self.sa.rollback()
223 self.sa.rollback()
226 raise
224 raise
227
225
228
226
229 def __create_repo(self, repo_name, alias):
227 def __create_repo(self, repo_name, alias):
230 """
228 """
231 makes repository on filesystem
229 makes repository on filesystem
232 :param repo_name:
230 :param repo_name:
233 :param alias:
231 :param alias:
234 """
232 """
235 from rhodecode.lib.utils import check_repo
233 from rhodecode.lib.utils import check_repo
236 repo_path = os.path.join(self.repos_path, repo_name)
234 repo_path = os.path.join(self.repos_path, repo_name)
237 if check_repo(repo_name, self.repos_path):
235 if check_repo(repo_name, self.repos_path):
238 log.info('creating repo %s in %s', repo_name, repo_path)
236 log.info('creating repo %s in %s', repo_name, repo_path)
239 backend = get_backend(alias)
237 backend = get_backend(alias)
240 backend(repo_path, create=True)
238 backend(repo_path, create=True)
241
239
242 def __rename_repo(self, old, new):
240 def __rename_repo(self, old, new):
243 """
241 """
244 renames repository on filesystem
242 renames repository on filesystem
245 :param old: old name
243 :param old: old name
246 :param new: new name
244 :param new: new name
247 """
245 """
248 log.info('renaming repo from %s to %s', old, new)
246 log.info('renaming repo from %s to %s', old, new)
249
247
250 old_path = os.path.join(self.repos_path, old)
248 old_path = os.path.join(self.repos_path, old)
251 new_path = os.path.join(self.repos_path, new)
249 new_path = os.path.join(self.repos_path, new)
252 if os.path.isdir(new_path):
250 if os.path.isdir(new_path):
253 raise Exception('Was trying to rename to already existing dir %s',
251 raise Exception('Was trying to rename to already existing dir %s',
254 new_path)
252 new_path)
255 shutil.move(old_path, new_path)
253 shutil.move(old_path, new_path)
256
254
257 def __delete_repo(self, repo):
255 def __delete_repo(self, repo):
258 """
256 """
259 removes repo from filesystem, the removal is acctually made by
257 removes repo from filesystem, the removal is acctually made by
260 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
258 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
261 repository is no longer valid for rhodecode, can be undeleted later on
259 repository is no longer valid for rhodecode, can be undeleted later on
262 by reverting the renames on this repository
260 by reverting the renames on this repository
263 :param repo: repo object
261 :param repo: repo object
264 """
262 """
265 rm_path = os.path.join(self.repos_path, repo.repo_name)
263 rm_path = os.path.join(self.repos_path, repo.repo_name)
266 log.info("Removing %s", rm_path)
264 log.info("Removing %s", rm_path)
267 #disable hg/git
265 #disable hg/git
268 alias = repo.repo_type
266 alias = repo.repo_type
269 shutil.move(os.path.join(rm_path, '.%s' % alias),
267 shutil.move(os.path.join(rm_path, '.%s' % alias),
270 os.path.join(rm_path, 'rm__.%s' % alias))
268 os.path.join(rm_path, 'rm__.%s' % alias))
271 #disable repo
269 #disable repo
272 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
270 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
273 % (datetime.today().isoformat(),
271 % (datetime.today().isoformat(),
274 repo.repo_name)))
272 repo.repo_name)))
@@ -1,105 +1,103 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.settings
3 rhodecode.model.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Settings model for RhodeCode
6 Settings model for RhodeCode
7
7
8 :created on Nov 17, 2010
8 :created on Nov 17, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29
27
30 from rhodecode.model import BaseModel
28 from rhodecode.model import BaseModel
31 from rhodecode.model.caching_query import FromCache
29 from rhodecode.model.caching_query import FromCache
32 from rhodecode.model.db import RhodeCodeSettings
30 from rhodecode.model.db import RhodeCodeSettings
33
31
34 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
35
33
36 class SettingsModel(BaseModel):
34 class SettingsModel(BaseModel):
37 """
35 """
38 Settings model
36 Settings model
39 """
37 """
40
38
41 def get(self, settings_key, cache=False):
39 def get(self, settings_key, cache=False):
42 r = self.sa.query(RhodeCodeSettings)\
40 r = self.sa.query(RhodeCodeSettings)\
43 .filter(RhodeCodeSettings.app_settings_name == settings_key).scalar()
41 .filter(RhodeCodeSettings.app_settings_name == settings_key).scalar()
44 if cache:
42 if cache:
45 r = r.options(FromCache("sql_cache_short",
43 r = r.options(FromCache("sql_cache_short",
46 "get_setting_%s" % settings_key))
44 "get_setting_%s" % settings_key))
47 return r
45 return r
48
46
49 def get_app_settings(self, cache=False):
47 def get_app_settings(self, cache=False):
50 """Get's config from database, each config key is prefixed with
48 """Get's config from database, each config key is prefixed with
51 'rhodecode_' prefix, than global pylons config is updated with such
49 'rhodecode_' prefix, than global pylons config is updated with such
52 keys
50 keys
53 """
51 """
54
52
55 ret = self.sa.query(RhodeCodeSettings)
53 ret = self.sa.query(RhodeCodeSettings)
56
54
57 if cache:
55 if cache:
58 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
56 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
59
57
60 if not ret:
58 if not ret:
61 raise Exception('Could not get application settings !')
59 raise Exception('Could not get application settings !')
62 settings = {}
60 settings = {}
63 for each in ret:
61 for each in ret:
64 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
62 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
65
63
66 return settings
64 return settings
67
65
68 def get_ldap_settings(self):
66 def get_ldap_settings(self):
69 """
67 """
70 Returns ldap settings from database
68 Returns ldap settings from database
71 :returns:
69 :returns:
72 ldap_active
70 ldap_active
73 ldap_host
71 ldap_host
74 ldap_port
72 ldap_port
75 ldap_ldaps
73 ldap_ldaps
76 ldap_tls_reqcert
74 ldap_tls_reqcert
77 ldap_dn_user
75 ldap_dn_user
78 ldap_dn_pass
76 ldap_dn_pass
79 ldap_base_dn
77 ldap_base_dn
80 ldap_filter
78 ldap_filter
81 ldap_search_scope
79 ldap_search_scope
82 ldap_attr_login
80 ldap_attr_login
83 ldap_attr_firstname
81 ldap_attr_firstname
84 ldap_attr_lastname
82 ldap_attr_lastname
85 ldap_attr_email
83 ldap_attr_email
86 """
84 """
87 # ldap_search_scope
85 # ldap_search_scope
88
86
89 r = self.sa.query(RhodeCodeSettings)\
87 r = self.sa.query(RhodeCodeSettings)\
90 .filter(RhodeCodeSettings.app_settings_name\
88 .filter(RhodeCodeSettings.app_settings_name\
91 .startswith('ldap_'))\
89 .startswith('ldap_'))\
92 .all()
90 .all()
93
91
94 fd = {}
92 fd = {}
95
93
96 for row in r:
94 for row in r:
97 v = row.app_settings_value
95 v = row.app_settings_value
98 if v in ['true', 'yes', 'on', 'y', 't', '1']:
96 if v in ['true', 'yes', 'on', 'y', 't', '1']:
99 v = True
97 v = True
100 elif v in ['false', 'no', 'off', 'n', 'f', '0']:
98 elif v in ['false', 'no', 'off', 'n', 'f', '0']:
101 v = False
99 v = False
102
100
103 fd.update({row.app_settings_name:v})
101 fd.update({row.app_settings_name:v})
104
102
105 return fd
103 return fd
@@ -1,223 +1,221 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.user
3 rhodecode.model.user
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 users model for RhodeCode
6 users model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software: you can redistribute it and/or modify
14 # modify it under the terms of the GNU General Public License
14 # it under the terms of the GNU General Public License as published by
15 # as published by the Free Software Foundation; version 2
15 # the Free Software Foundation, either version 3 of the License, or
16 # of the License or (at your opinion) any later version of the license.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
27
25
28 import logging
26 import logging
29 import traceback
27 import traceback
30
28
31 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
32
30
33 from rhodecode.model import BaseModel
31 from rhodecode.model import BaseModel
34 from rhodecode.model.caching_query import FromCache
32 from rhodecode.model.caching_query import FromCache
35 from rhodecode.model.db import User
33 from rhodecode.model.db import User
36
34
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
35 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
38
36
39 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
40
38
41 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
42
40
43 class UserModel(BaseModel):
41 class UserModel(BaseModel):
44
42
45 def get(self, user_id, cache=False):
43 def get(self, user_id, cache=False):
46 user = self.sa.query(User)
44 user = self.sa.query(User)
47 if cache:
45 if cache:
48 user = user.options(FromCache("sql_cache_short",
46 user = user.options(FromCache("sql_cache_short",
49 "get_user_%s" % user_id))
47 "get_user_%s" % user_id))
50 return user.get(user_id)
48 return user.get(user_id)
51
49
52
50
53 def get_by_username(self, username, cache=False, case_insensitive=False):
51 def get_by_username(self, username, cache=False, case_insensitive=False):
54
52
55 if case_insensitive:
53 if case_insensitive:
56 user = self.sa.query(User).filter(User.username.ilike(username))
54 user = self.sa.query(User).filter(User.username.ilike(username))
57 else:
55 else:
58 user = self.sa.query(User)\
56 user = self.sa.query(User)\
59 .filter(User.username == username)
57 .filter(User.username == username)
60 if cache:
58 if cache:
61 user = user.options(FromCache("sql_cache_short",
59 user = user.options(FromCache("sql_cache_short",
62 "get_user_%s" % username))
60 "get_user_%s" % username))
63 return user.scalar()
61 return user.scalar()
64
62
65 def create(self, form_data):
63 def create(self, form_data):
66 try:
64 try:
67 new_user = User()
65 new_user = User()
68 for k, v in form_data.items():
66 for k, v in form_data.items():
69 setattr(new_user, k, v)
67 setattr(new_user, k, v)
70
68
71 self.sa.add(new_user)
69 self.sa.add(new_user)
72 self.sa.commit()
70 self.sa.commit()
73 except:
71 except:
74 log.error(traceback.format_exc())
72 log.error(traceback.format_exc())
75 self.sa.rollback()
73 self.sa.rollback()
76 raise
74 raise
77
75
78 def create_ldap(self, username, password):
76 def create_ldap(self, username, password):
79 """
77 """
80 Checks if user is in database, if not creates this user marked
78 Checks if user is in database, if not creates this user marked
81 as ldap user
79 as ldap user
82 :param username:
80 :param username:
83 :param password:
81 :param password:
84 """
82 """
85 from rhodecode.lib.auth import get_crypt_password
83 from rhodecode.lib.auth import get_crypt_password
86 log.debug('Checking for such ldap account in RhodeCode database')
84 log.debug('Checking for such ldap account in RhodeCode database')
87 if self.get_by_username(username, case_insensitive=True) is None:
85 if self.get_by_username(username, case_insensitive=True) is None:
88 try:
86 try:
89 new_user = User()
87 new_user = User()
90 new_user.username = username.lower()#add ldap account always lowercase
88 new_user.username = username.lower()#add ldap account always lowercase
91 new_user.password = get_crypt_password(password)
89 new_user.password = get_crypt_password(password)
92 new_user.email = '%s@ldap.server' % username
90 new_user.email = '%s@ldap.server' % username
93 new_user.active = True
91 new_user.active = True
94 new_user.is_ldap = True
92 new_user.is_ldap = True
95 new_user.name = '%s@ldap' % username
93 new_user.name = '%s@ldap' % username
96 new_user.lastname = ''
94 new_user.lastname = ''
97
95
98
96
99 self.sa.add(new_user)
97 self.sa.add(new_user)
100 self.sa.commit()
98 self.sa.commit()
101 return True
99 return True
102 except (DatabaseError,):
100 except (DatabaseError,):
103 log.error(traceback.format_exc())
101 log.error(traceback.format_exc())
104 self.sa.rollback()
102 self.sa.rollback()
105 raise
103 raise
106 log.debug('this %s user exists skipping creation of ldap account',
104 log.debug('this %s user exists skipping creation of ldap account',
107 username)
105 username)
108 return False
106 return False
109
107
110 def create_registration(self, form_data):
108 def create_registration(self, form_data):
111 from rhodecode.lib.celerylib import tasks, run_task
109 from rhodecode.lib.celerylib import tasks, run_task
112 try:
110 try:
113 new_user = User()
111 new_user = User()
114 for k, v in form_data.items():
112 for k, v in form_data.items():
115 if k != 'admin':
113 if k != 'admin':
116 setattr(new_user, k, v)
114 setattr(new_user, k, v)
117
115
118 self.sa.add(new_user)
116 self.sa.add(new_user)
119 self.sa.commit()
117 self.sa.commit()
120 body = ('New user registration\n'
118 body = ('New user registration\n'
121 'username: %s\n'
119 'username: %s\n'
122 'email: %s\n')
120 'email: %s\n')
123 body = body % (form_data['username'], form_data['email'])
121 body = body % (form_data['username'], form_data['email'])
124
122
125 run_task(tasks.send_email, None,
123 run_task(tasks.send_email, None,
126 _('[RhodeCode] New User registration'),
124 _('[RhodeCode] New User registration'),
127 body)
125 body)
128 except:
126 except:
129 log.error(traceback.format_exc())
127 log.error(traceback.format_exc())
130 self.sa.rollback()
128 self.sa.rollback()
131 raise
129 raise
132
130
133 def update(self, user_id, form_data):
131 def update(self, user_id, form_data):
134 try:
132 try:
135 new_user = self.get(user_id, cache=False)
133 new_user = self.get(user_id, cache=False)
136 if new_user.username == 'default':
134 if new_user.username == 'default':
137 raise DefaultUserException(
135 raise DefaultUserException(
138 _("You can't Edit this user since it's"
136 _("You can't Edit this user since it's"
139 " crucial for entire application"))
137 " crucial for entire application"))
140
138
141 for k, v in form_data.items():
139 for k, v in form_data.items():
142 if k == 'new_password' and v != '':
140 if k == 'new_password' and v != '':
143 new_user.password = v
141 new_user.password = v
144 else:
142 else:
145 setattr(new_user, k, v)
143 setattr(new_user, k, v)
146
144
147 self.sa.add(new_user)
145 self.sa.add(new_user)
148 self.sa.commit()
146 self.sa.commit()
149 except:
147 except:
150 log.error(traceback.format_exc())
148 log.error(traceback.format_exc())
151 self.sa.rollback()
149 self.sa.rollback()
152 raise
150 raise
153
151
154 def update_my_account(self, user_id, form_data):
152 def update_my_account(self, user_id, form_data):
155 try:
153 try:
156 new_user = self.get(user_id, cache=False)
154 new_user = self.get(user_id, cache=False)
157 if new_user.username == 'default':
155 if new_user.username == 'default':
158 raise DefaultUserException(
156 raise DefaultUserException(
159 _("You can't Edit this user since it's"
157 _("You can't Edit this user since it's"
160 " crucial for entire application"))
158 " crucial for entire application"))
161 for k, v in form_data.items():
159 for k, v in form_data.items():
162 if k == 'new_password' and v != '':
160 if k == 'new_password' and v != '':
163 new_user.password = v
161 new_user.password = v
164 else:
162 else:
165 if k not in ['admin', 'active']:
163 if k not in ['admin', 'active']:
166 setattr(new_user, k, v)
164 setattr(new_user, k, v)
167
165
168 self.sa.add(new_user)
166 self.sa.add(new_user)
169 self.sa.commit()
167 self.sa.commit()
170 except:
168 except:
171 log.error(traceback.format_exc())
169 log.error(traceback.format_exc())
172 self.sa.rollback()
170 self.sa.rollback()
173 raise
171 raise
174
172
175 def delete(self, user_id):
173 def delete(self, user_id):
176 try:
174 try:
177 user = self.get(user_id, cache=False)
175 user = self.get(user_id, cache=False)
178 if user.username == 'default':
176 if user.username == 'default':
179 raise DefaultUserException(
177 raise DefaultUserException(
180 _("You can't remove this user since it's"
178 _("You can't remove this user since it's"
181 " crucial for entire application"))
179 " crucial for entire application"))
182 if user.repositories:
180 if user.repositories:
183 raise UserOwnsReposException(_('This user still owns %s '
181 raise UserOwnsReposException(_('This user still owns %s '
184 'repositories and cannot be '
182 'repositories and cannot be '
185 'removed. Switch owners or '
183 'removed. Switch owners or '
186 'remove those repositories') \
184 'remove those repositories') \
187 % user.repositories)
185 % user.repositories)
188 self.sa.delete(user)
186 self.sa.delete(user)
189 self.sa.commit()
187 self.sa.commit()
190 except:
188 except:
191 log.error(traceback.format_exc())
189 log.error(traceback.format_exc())
192 self.sa.rollback()
190 self.sa.rollback()
193 raise
191 raise
194
192
195 def reset_password(self, data):
193 def reset_password(self, data):
196 from rhodecode.lib.celerylib import tasks, run_task
194 from rhodecode.lib.celerylib import tasks, run_task
197 run_task(tasks.reset_user_password, data['email'])
195 run_task(tasks.reset_user_password, data['email'])
198
196
199
197
200 def fill_data(self, user):
198 def fill_data(self, user):
201 """
199 """
202 Fills user data with those from database and log out user if not
200 Fills user data with those from database and log out user if not
203 present in database
201 present in database
204 :param user:
202 :param user:
205 """
203 """
206
204
207 if not hasattr(user, 'user_id') or user.user_id is None:
205 if not hasattr(user, 'user_id') or user.user_id is None:
208 raise Exception('passed in user has to have the user_id attribute')
206 raise Exception('passed in user has to have the user_id attribute')
209
207
210
208
211 log.debug('filling auth user data')
209 log.debug('filling auth user data')
212 try:
210 try:
213 dbuser = self.get(user.user_id)
211 dbuser = self.get(user.user_id)
214 user.username = dbuser.username
212 user.username = dbuser.username
215 user.is_admin = dbuser.admin
213 user.is_admin = dbuser.admin
216 user.name = dbuser.name
214 user.name = dbuser.name
217 user.lastname = dbuser.lastname
215 user.lastname = dbuser.lastname
218 user.email = dbuser.email
216 user.email = dbuser.email
219 except:
217 except:
220 log.error(traceback.format_exc())
218 log.error(traceback.format_exc())
221 user.is_authenticated = False
219 user.is_authenticated = False
222
220
223 return user
221 return user
General Comments 0
You need to be logged in to leave comments. Login now