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