##// END OF EJS Templates
PEP8ify - controllers
marcink -
r1245:5f2fbab7 beta
parent child Browse files
Show More
@@ -1,57 +1,58
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.admin
3 rhodecode.controllers.admin.admin
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Controller for Admin panel of Rhodecode
6 Controller for Admin panel of Rhodecode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27
27
28 from pylons import request, tmpl_context as c
28 from pylons import request, tmpl_context as c
29 from sqlalchemy.orm import joinedload
29 from sqlalchemy.orm import joinedload
30 from webhelpers.paginate import Page
30 from webhelpers.paginate import Page
31
31
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
33 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
34 from rhodecode.model.db import UserLog
34 from rhodecode.model.db import UserLog
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38 class AdminController(BaseController):
39 class AdminController(BaseController):
39
40
40 @LoginRequired()
41 @LoginRequired()
41 def __before__(self):
42 def __before__(self):
42 super(AdminController, self).__before__()
43 super(AdminController, self).__before__()
43
44
44 @HasPermissionAllDecorator('hg.admin')
45 @HasPermissionAllDecorator('hg.admin')
45 def index(self):
46 def index(self):
46
47
47 users_log = self.sa.query(UserLog)\
48 users_log = self.sa.query(UserLog)\
48 .options(joinedload(UserLog.user))\
49 .options(joinedload(UserLog.user))\
49 .options(joinedload(UserLog.repository))\
50 .options(joinedload(UserLog.repository))\
50 .order_by(UserLog.action_date.desc())
51 .order_by(UserLog.action_date.desc())
51
52
52 p = int(request.params.get('page', 1))
53 p = int(request.params.get('page', 1))
53 c.users_log = Page(users_log, page=p, items_per_page=10)
54 c.users_log = Page(users_log, page=p, items_per_page=10)
54 c.log_data = render('admin/admin_log.html')
55 c.log_data = render('admin/admin_log.html')
55 if request.params.get('partial'):
56 if request.params.get('partial'):
56 return c.log_data
57 return c.log_data
57 return render('admin/admin.html')
58 return render('admin/admin.html')
@@ -1,125 +1,124
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.ldap_settings
3 rhodecode.controllers.admin.ldap_settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 ldap controller for RhodeCode
6 ldap controller for RhodeCode
7
7
8 :created_on: Nov 26, 2010
8 :created_on: Nov 26, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 import formencode
26 import formencode
27 import traceback
27 import traceback
28
28
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
38 from rhodecode.lib.auth_ldap import LdapImportError
38 from rhodecode.lib.auth_ldap import LdapImportError
39 from rhodecode.model.settings import SettingsModel
39 from rhodecode.model.settings import SettingsModel
40 from rhodecode.model.forms import LdapSettingsForm
40 from rhodecode.model.forms import LdapSettingsForm
41 from sqlalchemy.exc import DatabaseError
41 from sqlalchemy.exc import DatabaseError
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46
47 class LdapSettingsController(BaseController):
46 class LdapSettingsController(BaseController):
48
47
49 search_scope_choices = [('BASE', _('BASE'),),
48 search_scope_choices = [('BASE', _('BASE'),),
50 ('ONELEVEL', _('ONELEVEL'),),
49 ('ONELEVEL', _('ONELEVEL'),),
51 ('SUBTREE', _('SUBTREE'),),
50 ('SUBTREE', _('SUBTREE'),),
52 ]
51 ]
53 search_scope_default = 'SUBTREE'
52 search_scope_default = 'SUBTREE'
54
53
55 tls_reqcert_choices = [('NEVER', _('NEVER'),),
54 tls_reqcert_choices = [('NEVER', _('NEVER'),),
56 ('ALLOW', _('ALLOW'),),
55 ('ALLOW', _('ALLOW'),),
57 ('TRY', _('TRY'),),
56 ('TRY', _('TRY'),),
58 ('DEMAND', _('DEMAND'),),
57 ('DEMAND', _('DEMAND'),),
59 ('HARD', _('HARD'),),
58 ('HARD', _('HARD'),),
60 ]
59 ]
61 tls_reqcert_default = 'DEMAND'
60 tls_reqcert_default = 'DEMAND'
62
61
63 @LoginRequired()
62 @LoginRequired()
64 @HasPermissionAllDecorator('hg.admin')
63 @HasPermissionAllDecorator('hg.admin')
65 def __before__(self):
64 def __before__(self):
66 c.admin_user = session.get('admin_user')
65 c.admin_user = session.get('admin_user')
67 c.admin_username = session.get('admin_username')
66 c.admin_username = session.get('admin_username')
68 c.search_scope_choices = self.search_scope_choices
67 c.search_scope_choices = self.search_scope_choices
69 c.tls_reqcert_choices = self.tls_reqcert_choices
68 c.tls_reqcert_choices = self.tls_reqcert_choices
70 super(LdapSettingsController, self).__before__()
69 super(LdapSettingsController, self).__before__()
71
70
72 def index(self):
71 def index(self):
73 defaults = SettingsModel().get_ldap_settings()
72 defaults = SettingsModel().get_ldap_settings()
74 c.search_scope_cur = defaults.get('ldap_search_scope')
73 c.search_scope_cur = defaults.get('ldap_search_scope')
75 c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert')
74 c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert')
76
75
77 return htmlfill.render(
76 return htmlfill.render(
78 render('admin/ldap/ldap.html'),
77 render('admin/ldap/ldap.html'),
79 defaults=defaults,
78 defaults=defaults,
80 encoding="UTF-8",
79 encoding="UTF-8",
81 force_defaults=True,)
80 force_defaults=True,)
82
81
83 def ldap_settings(self):
82 def ldap_settings(self):
84 """POST ldap create and store ldap settings"""
83 """POST ldap create and store ldap settings"""
85
84
86 settings_model = SettingsModel()
85 settings_model = SettingsModel()
87 _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
86 _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
88 [x[0] for x in self.search_scope_choices])()
87 [x[0] for x in self.search_scope_choices])()
89
88
90 try:
89 try:
91 form_result = _form.to_python(dict(request.POST))
90 form_result = _form.to_python(dict(request.POST))
92 try:
91 try:
93
92
94 for k, v in form_result.items():
93 for k, v in form_result.items():
95 if k.startswith('ldap_'):
94 if k.startswith('ldap_'):
96 setting = settings_model.get(k)
95 setting = settings_model.get(k)
97 setting.app_settings_value = v
96 setting.app_settings_value = v
98 self.sa.add(setting)
97 self.sa.add(setting)
99
98
100 self.sa.commit()
99 self.sa.commit()
101 h.flash(_('Ldap settings updated successfully'),
100 h.flash(_('Ldap settings updated successfully'),
102 category='success')
101 category='success')
103 except (DatabaseError,):
102 except (DatabaseError,):
104 raise
103 raise
105 except LdapImportError:
104 except LdapImportError:
106 h.flash(_('Unable to activate ldap. The "python-ldap" library '
105 h.flash(_('Unable to activate ldap. The "python-ldap" library '
107 'is missing.'), category='warning')
106 'is missing.'), category='warning')
108
107
109 except formencode.Invalid, errors:
108 except formencode.Invalid, errors:
110
109
111 c.search_scope_cur = self.search_scope_default
110 c.search_scope_cur = self.search_scope_default
112 c.tls_reqcert_cur = self.search_scope_default
111 c.tls_reqcert_cur = self.search_scope_default
113
112
114 return htmlfill.render(
113 return htmlfill.render(
115 render('admin/ldap/ldap.html'),
114 render('admin/ldap/ldap.html'),
116 defaults=errors.value,
115 defaults=errors.value,
117 errors=errors.error_dict or {},
116 errors=errors.error_dict or {},
118 prefix_error=False,
117 prefix_error=False,
119 encoding="UTF-8")
118 encoding="UTF-8")
120 except Exception:
119 except Exception:
121 log.error(traceback.format_exc())
120 log.error(traceback.format_exc())
122 h.flash(_('error occurred during update of ldap settings'),
121 h.flash(_('error occurred during update of ldap settings'),
123 category='error')
122 category='error')
124
123
125 return redirect(url('ldap_home'))
124 return redirect(url('ldap_home'))
@@ -1,169 +1,167
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-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 from formencode import htmlfill
26 from formencode import htmlfill
27 from pylons import request, session, tmpl_context as c, url
27 from pylons import request, session, tmpl_context as c, url
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from rhodecode.lib import helpers as h
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.auth_ldap import LdapImportError
32 from rhodecode.lib.auth_ldap import LdapImportError
33 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
34 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
34 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
35 from rhodecode.model.permission import PermissionModel
35 from rhodecode.model.permission import PermissionModel
36 from rhodecode.model.settings import SettingsModel
36 from rhodecode.model.settings import SettingsModel
37 from rhodecode.model.user import UserModel
37 from rhodecode.model.user import UserModel
38 import formencode
38 import formencode
39 import logging
39 import logging
40 import traceback
40 import traceback
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44 class PermissionsController(BaseController):
45 class PermissionsController(BaseController):
45 """REST Controller styled on the Atom Publishing Protocol"""
46 """REST Controller styled on the Atom Publishing Protocol"""
46 # To properly map this controller, ensure your config/routing.py
47 # To properly map this controller, ensure your config/routing.py
47 # file has a resource setup:
48 # file has a resource setup:
48 # map.resource('permission', 'permissions')
49 # map.resource('permission', 'permissions')
49
50
50 @LoginRequired()
51 @LoginRequired()
51 @HasPermissionAllDecorator('hg.admin')
52 @HasPermissionAllDecorator('hg.admin')
52 def __before__(self):
53 def __before__(self):
53 c.admin_user = session.get('admin_user')
54 c.admin_user = session.get('admin_user')
54 c.admin_username = session.get('admin_username')
55 c.admin_username = session.get('admin_username')
55 super(PermissionsController, self).__before__()
56 super(PermissionsController, self).__before__()
56
57
57 self.perms_choices = [('repository.none', _('None'),),
58 self.perms_choices = [('repository.none', _('None'),),
58 ('repository.read', _('Read'),),
59 ('repository.read', _('Read'),),
59 ('repository.write', _('Write'),),
60 ('repository.write', _('Write'),),
60 ('repository.admin', _('Admin'),)]
61 ('repository.admin', _('Admin'),)]
61 self.register_choices = [
62 self.register_choices = [
62 ('hg.register.none',
63 ('hg.register.none',
63 _('disabled')),
64 _('disabled')),
64 ('hg.register.manual_activate',
65 ('hg.register.manual_activate',
65 _('allowed with manual account activation')),
66 _('allowed with manual account activation')),
66 ('hg.register.auto_activate',
67 ('hg.register.auto_activate',
67 _('allowed with automatic account activation')), ]
68 _('allowed with automatic account activation')), ]
68
69
69 self.create_choices = [('hg.create.none', _('Disabled')),
70 self.create_choices = [('hg.create.none', _('Disabled')),
70 ('hg.create.repository', _('Enabled'))]
71 ('hg.create.repository', _('Enabled'))]
71
72
72
73 def index(self, format='html'):
73 def index(self, format='html'):
74 """GET /permissions: All items in the collection"""
74 """GET /permissions: All items in the collection"""
75 # url('permissions')
75 # url('permissions')
76
76
77 def create(self):
77 def create(self):
78 """POST /permissions: Create a new item"""
78 """POST /permissions: Create a new item"""
79 # url('permissions')
79 # url('permissions')
80
80
81 def new(self, format='html'):
81 def new(self, format='html'):
82 """GET /permissions/new: Form to create a new item"""
82 """GET /permissions/new: Form to create a new item"""
83 # url('new_permission')
83 # url('new_permission')
84
84
85 def update(self, id):
85 def update(self, id):
86 """PUT /permissions/id: Update an existing item"""
86 """PUT /permissions/id: Update an existing item"""
87 # Forms posted to this method should contain a hidden field:
87 # Forms posted to this method should contain a hidden field:
88 # <input type="hidden" name="_method" value="PUT" />
88 # <input type="hidden" name="_method" value="PUT" />
89 # Or using helpers:
89 # Or using helpers:
90 # h.form(url('permission', id=ID),
90 # h.form(url('permission', id=ID),
91 # method='put')
91 # method='put')
92 # url('permission', id=ID)
92 # url('permission', id=ID)
93
93
94 permission_model = PermissionModel()
94 permission_model = PermissionModel()
95
95
96 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
96 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
97 [x[0] for x in self.register_choices],
97 [x[0] for x in self.register_choices],
98 [x[0] for x in self.create_choices])()
98 [x[0] for x in self.create_choices])()
99
99
100 try:
100 try:
101 form_result = _form.to_python(dict(request.POST))
101 form_result = _form.to_python(dict(request.POST))
102 form_result.update({'perm_user_name':id})
102 form_result.update({'perm_user_name': id})
103 permission_model.update(form_result)
103 permission_model.update(form_result)
104 h.flash(_('Default permissions updated successfully'),
104 h.flash(_('Default permissions updated successfully'),
105 category='success')
105 category='success')
106
106
107 except formencode.Invalid, errors:
107 except formencode.Invalid, errors:
108 c.perms_choices = self.perms_choices
108 c.perms_choices = self.perms_choices
109 c.register_choices = self.register_choices
109 c.register_choices = self.register_choices
110 c.create_choices = self.create_choices
110 c.create_choices = self.create_choices
111 defaults = errors.value
111 defaults = errors.value
112
112
113 return htmlfill.render(
113 return htmlfill.render(
114 render('admin/permissions/permissions.html'),
114 render('admin/permissions/permissions.html'),
115 defaults=defaults,
115 defaults=defaults,
116 errors=errors.error_dict or {},
116 errors=errors.error_dict or {},
117 prefix_error=False,
117 prefix_error=False,
118 encoding="UTF-8")
118 encoding="UTF-8")
119 except Exception:
119 except Exception:
120 log.error(traceback.format_exc())
120 log.error(traceback.format_exc())
121 h.flash(_('error occurred during update of permissions'),
121 h.flash(_('error occurred during update of permissions'),
122 category='error')
122 category='error')
123
123
124 return redirect(url('edit_permission', id=id))
124 return redirect(url('edit_permission', id=id))
125
125
126
127
128 def delete(self, id):
126 def delete(self, id):
129 """DELETE /permissions/id: Delete an existing item"""
127 """DELETE /permissions/id: Delete an existing item"""
130 # Forms posted to this method should contain a hidden field:
128 # Forms posted to this method should contain a hidden field:
131 # <input type="hidden" name="_method" value="DELETE" />
129 # <input type="hidden" name="_method" value="DELETE" />
132 # Or using helpers:
130 # Or using helpers:
133 # h.form(url('permission', id=ID),
131 # h.form(url('permission', id=ID),
134 # method='delete')
132 # method='delete')
135 # url('permission', id=ID)
133 # url('permission', id=ID)
136
134
137 def show(self, id, format='html'):
135 def show(self, id, format='html'):
138 """GET /permissions/id: Show a specific item"""
136 """GET /permissions/id: Show a specific item"""
139 # url('permission', id=ID)
137 # url('permission', id=ID)
140
138
141 def edit(self, id, format='html'):
139 def edit(self, id, format='html'):
142 """GET /permissions/id/edit: Form to edit an existing item"""
140 """GET /permissions/id/edit: Form to edit an existing item"""
143 #url('edit_permission', id=ID)
141 #url('edit_permission', id=ID)
144 c.perms_choices = self.perms_choices
142 c.perms_choices = self.perms_choices
145 c.register_choices = self.register_choices
143 c.register_choices = self.register_choices
146 c.create_choices = self.create_choices
144 c.create_choices = self.create_choices
147
145
148 if id == 'default':
146 if id == 'default':
149 default_user = UserModel().get_by_username('default')
147 default_user = UserModel().get_by_username('default')
150 defaults = {'_method':'put',
148 defaults = {'_method': 'put',
151 'anonymous':default_user.active}
149 'anonymous': default_user.active}
152
150
153 for p in default_user.user_perms:
151 for p in default_user.user_perms:
154 if p.permission.permission_name.startswith('repository.'):
152 if p.permission.permission_name.startswith('repository.'):
155 defaults['default_perm'] = p.permission.permission_name
153 defaults['default_perm'] = p.permission.permission_name
156
154
157 if p.permission.permission_name.startswith('hg.register.'):
155 if p.permission.permission_name.startswith('hg.register.'):
158 defaults['default_register'] = p.permission.permission_name
156 defaults['default_register'] = p.permission.permission_name
159
157
160 if p.permission.permission_name.startswith('hg.create.'):
158 if p.permission.permission_name.startswith('hg.create.'):
161 defaults['default_create'] = p.permission.permission_name
159 defaults['default_create'] = p.permission.permission_name
162
160
163 return htmlfill.render(
161 return htmlfill.render(
164 render('admin/permissions/permissions.html'),
162 render('admin/permissions/permissions.html'),
165 defaults=defaults,
163 defaults=defaults,
166 encoding="UTF-8",
164 encoding="UTF-8",
167 force_defaults=True,)
165 force_defaults=True,)
168 else:
166 else:
169 return redirect(url('admin_home'))
167 return redirect(url('admin_home'))
@@ -1,421 +1,415
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Admin controller for RhodeCode
6 Admin controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from operator import itemgetter
29 from operator import itemgetter
30 from formencode import htmlfill
30 from formencode import htmlfill
31
31
32 from paste.httpexceptions import HTTPInternalServerError
32 from paste.httpexceptions import HTTPInternalServerError
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, 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 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 HasPermissionAnyDecorator
39 HasPermissionAnyDecorator
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
42 from rhodecode.lib.helpers import get_token
42 from rhodecode.lib.helpers import get_token
43 from rhodecode.model.db import User, Repository, UserFollowing, Group
43 from rhodecode.model.db import User, Repository, UserFollowing, Group
44 from rhodecode.model.forms import RepoForm
44 from rhodecode.model.forms import RepoForm
45 from rhodecode.model.scm import ScmModel
45 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.repo import RepoModel
46 from rhodecode.model.repo import RepoModel
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50 class ReposController(BaseController):
51 class ReposController(BaseController):
51 """
52 """
52 REST Controller styled on the Atom Publishing Protocol"""
53 REST Controller styled on the Atom Publishing Protocol"""
53 # To properly map this controller, ensure your config/routing.py
54 # To properly map this controller, ensure your config/routing.py
54 # file has a resource setup:
55 # file has a resource setup:
55 # map.resource('repo', 'repos')
56 # map.resource('repo', 'repos')
56
57
57 @LoginRequired()
58 @LoginRequired()
58 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
59 def __before__(self):
60 def __before__(self):
60 c.admin_user = session.get('admin_user')
61 c.admin_user = session.get('admin_user')
61 c.admin_username = session.get('admin_username')
62 c.admin_username = session.get('admin_username')
62 super(ReposController, self).__before__()
63 super(ReposController, self).__before__()
63
64
64 def __load_defaults(self):
65 def __load_defaults(self):
65 repo_model = RepoModel()
66 repo_model = RepoModel()
66
67
67 c.repo_groups = [('', '')]
68 c.repo_groups = [('', '')]
68 parents_link = lambda k:h.literal('&raquo;'.join(
69 parents_link = lambda k: h.literal('&raquo;'.join(
69 map(lambda k:k.group_name,
70 map(lambda k: k.group_name,
70 k.parents + [k])
71 k.parents + [k])
71 )
72 )
72 )
73 )
73
74
74 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
75 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
75 x in self.sa.query(Group).all()])
76 x in self.sa.query(Group).all()])
76 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
77 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
77 c.users_array = repo_model.get_users_js()
78 c.users_array = repo_model.get_users_js()
78 c.users_groups_array = repo_model.get_users_groups_js()
79 c.users_groups_array = repo_model.get_users_groups_js()
79
80
80 def __load_data(self, repo_name=None):
81 def __load_data(self, repo_name=None):
81 """
82 """
82 Load defaults settings for edit, and update
83 Load defaults settings for edit, and update
83
84
84 :param repo_name:
85 :param repo_name:
85 """
86 """
86 self.__load_defaults()
87 self.__load_defaults()
87
88
88 repo, dbrepo = ScmModel().get(repo_name, retval='repo')
89 repo, dbrepo = ScmModel().get(repo_name, retval='repo')
89
90
90 repo_model = RepoModel()
91 repo_model = RepoModel()
91 c.repo_info = repo_model.get_by_repo_name(repo_name)
92 c.repo_info = repo_model.get_by_repo_name(repo_name)
92
93
93
94 if c.repo_info is None:
94 if c.repo_info is None:
95 h.flash(_('%s repository is not mapped to db perhaps'
95 h.flash(_('%s repository is not mapped to db perhaps'
96 ' it was created or renamed from the filesystem'
96 ' it was created or renamed from the filesystem'
97 ' please run the application again'
97 ' please run the application again'
98 ' in order to rescan repositories') % repo_name,
98 ' in order to rescan repositories') % repo_name,
99 category='error')
99 category='error')
100
100
101 return redirect(url('repos'))
101 return redirect(url('repos'))
102
102
103
104 c.default_user_id = User.by_username('default').user_id
103 c.default_user_id = User.by_username('default').user_id
105 c.in_public_journal = self.sa.query(UserFollowing)\
104 c.in_public_journal = self.sa.query(UserFollowing)\
106 .filter(UserFollowing.user_id == c.default_user_id)\
105 .filter(UserFollowing.user_id == c.default_user_id)\
107 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
106 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
108
107
109 if c.repo_info.stats:
108 if c.repo_info.stats:
110 last_rev = c.repo_info.stats.stat_on_revision
109 last_rev = c.repo_info.stats.stat_on_revision
111 else:
110 else:
112 last_rev = 0
111 last_rev = 0
113 c.stats_revision = last_rev
112 c.stats_revision = last_rev
114
113
115 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
114 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
116
115
117 if last_rev == 0 or c.repo_last_rev == 0:
116 if last_rev == 0 or c.repo_last_rev == 0:
118 c.stats_percentage = 0
117 c.stats_percentage = 0
119 else:
118 else:
120 c.stats_percentage = '%.2f' % ((float((last_rev)) /
119 c.stats_percentage = '%.2f' % ((float((last_rev)) /
121 c.repo_last_rev) * 100)
120 c.repo_last_rev) * 100)
122
121
123
124
125 defaults = c.repo_info.get_dict()
122 defaults = c.repo_info.get_dict()
126 group, repo_name = c.repo_info.groups_and_repo
123 group, repo_name = c.repo_info.groups_and_repo
127 defaults['repo_name'] = repo_name
124 defaults['repo_name'] = repo_name
128 defaults['repo_group'] = getattr(group[-1] if group else None,
125 defaults['repo_group'] = getattr(group[-1] if group else None,
129 'group_id', None)
126 'group_id', None)
130
127
131 #fill owner
128 #fill owner
132 if c.repo_info.user:
129 if c.repo_info.user:
133 defaults.update({'user':c.repo_info.user.username})
130 defaults.update({'user': c.repo_info.user.username})
134 else:
131 else:
135 replacement_user = self.sa.query(User)\
132 replacement_user = self.sa.query(User)\
136 .filter(User.admin == True).first().username
133 .filter(User.admin == True).first().username
137 defaults.update({'user':replacement_user})
134 defaults.update({'user': replacement_user})
138
135
139
140 #fill repository users
136 #fill repository users
141 for p in c.repo_info.repo_to_perm:
137 for p in c.repo_info.repo_to_perm:
142 defaults.update({'u_perm_%s' % p.user.username:
138 defaults.update({'u_perm_%s' % p.user.username:
143 p.permission.permission_name})
139 p.permission.permission_name})
144
140
145 #fill repository groups
141 #fill repository groups
146 for p in c.repo_info.users_group_to_perm:
142 for p in c.repo_info.users_group_to_perm:
147 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
143 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
148 p.permission.permission_name})
144 p.permission.permission_name})
149
145
150
151 return defaults
146 return defaults
152
147
153
154 @HasPermissionAllDecorator('hg.admin')
148 @HasPermissionAllDecorator('hg.admin')
155 def index(self, format='html'):
149 def index(self, format='html'):
156 """GET /repos: All items in the collection"""
150 """GET /repos: All items in the collection"""
157 # url('repos')
151 # url('repos')
158 cached_repo_list = ScmModel().get_repos()
152 cached_repo_list = ScmModel().get_repos()
159 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
153 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
160 return render('admin/repos/repos.html')
154 return render('admin/repos/repos.html')
161
155
162 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
156 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
163 def create(self):
157 def create(self):
164 """
158 """
165 POST /repos: Create a new item"""
159 POST /repos: Create a new item"""
166 # url('repos')
160 # url('repos')
167 repo_model = RepoModel()
161 repo_model = RepoModel()
168 self.__load_defaults()
162 self.__load_defaults()
169 form_result = {}
163 form_result = {}
170 try:
164 try:
171 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
165 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
172 .to_python(dict(request.POST))
166 .to_python(dict(request.POST))
173 repo_model.create(form_result, self.rhodecode_user)
167 repo_model.create(form_result, self.rhodecode_user)
174 if form_result['clone_uri']:
168 if form_result['clone_uri']:
175 h.flash(_('created repository %s from %s') \
169 h.flash(_('created repository %s from %s') \
176 % (form_result['repo_name'], form_result['clone_uri']),
170 % (form_result['repo_name'], form_result['clone_uri']),
177 category='success')
171 category='success')
178 else:
172 else:
179 h.flash(_('created repository %s') % form_result['repo_name'],
173 h.flash(_('created repository %s') % form_result['repo_name'],
180 category='success')
174 category='success')
181
175
182 if request.POST.get('user_created'):
176 if request.POST.get('user_created'):
183 action_logger(self.rhodecode_user, 'user_created_repo',
177 action_logger(self.rhodecode_user, 'user_created_repo',
184 form_result['repo_name'], '', self.sa)
178 form_result['repo_name'], '', self.sa)
185 else:
179 else:
186 action_logger(self.rhodecode_user, 'admin_created_repo',
180 action_logger(self.rhodecode_user, 'admin_created_repo',
187 form_result['repo_name'], '', self.sa)
181 form_result['repo_name'], '', self.sa)
188
182
189 except formencode.Invalid, errors:
183 except formencode.Invalid, errors:
190
184
191 c.new_repo = errors.value['repo_name']
185 c.new_repo = errors.value['repo_name']
192
186
193 if request.POST.get('user_created'):
187 if request.POST.get('user_created'):
194 r = render('admin/repos/repo_add_create_repository.html')
188 r = render('admin/repos/repo_add_create_repository.html')
195 else:
189 else:
196 r = render('admin/repos/repo_add.html')
190 r = render('admin/repos/repo_add.html')
197
191
198 return htmlfill.render(
192 return htmlfill.render(
199 r,
193 r,
200 defaults=errors.value,
194 defaults=errors.value,
201 errors=errors.error_dict or {},
195 errors=errors.error_dict or {},
202 prefix_error=False,
196 prefix_error=False,
203 encoding="UTF-8")
197 encoding="UTF-8")
204
198
205 except Exception:
199 except Exception:
206 log.error(traceback.format_exc())
200 log.error(traceback.format_exc())
207 msg = _('error occurred during creation of repository %s') \
201 msg = _('error occurred during creation of repository %s') \
208 % form_result.get('repo_name')
202 % form_result.get('repo_name')
209 h.flash(msg, category='error')
203 h.flash(msg, category='error')
210 if request.POST.get('user_created'):
204 if request.POST.get('user_created'):
211 return redirect(url('home'))
205 return redirect(url('home'))
212 return redirect(url('repos'))
206 return redirect(url('repos'))
213
207
214 @HasPermissionAllDecorator('hg.admin')
208 @HasPermissionAllDecorator('hg.admin')
215 def new(self, format='html'):
209 def new(self, format='html'):
216 """GET /repos/new: Form to create a new item"""
210 """GET /repos/new: Form to create a new item"""
217 new_repo = request.GET.get('repo', '')
211 new_repo = request.GET.get('repo', '')
218 c.new_repo = repo_name_slug(new_repo)
212 c.new_repo = repo_name_slug(new_repo)
219 self.__load_defaults()
213 self.__load_defaults()
220 return render('admin/repos/repo_add.html')
214 return render('admin/repos/repo_add.html')
221
215
222 @HasPermissionAllDecorator('hg.admin')
216 @HasPermissionAllDecorator('hg.admin')
223 def update(self, repo_name):
217 def update(self, repo_name):
224 """
218 """
225 PUT /repos/repo_name: Update an existing item"""
219 PUT /repos/repo_name: Update an existing item"""
226 # Forms posted to this method should contain a hidden field:
220 # Forms posted to this method should contain a hidden field:
227 # <input type="hidden" name="_method" value="PUT" />
221 # <input type="hidden" name="_method" value="PUT" />
228 # Or using helpers:
222 # Or using helpers:
229 # h.form(url('repo', repo_name=ID),
223 # h.form(url('repo', repo_name=ID),
230 # method='put')
224 # method='put')
231 # url('repo', repo_name=ID)
225 # url('repo', repo_name=ID)
232 self.__load_defaults()
226 self.__load_defaults()
233 repo_model = RepoModel()
227 repo_model = RepoModel()
234 changed_name = repo_name
228 changed_name = repo_name
235 _form = RepoForm(edit=True, old_data={'repo_name':repo_name},
229 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
236 repo_groups=c.repo_groups_choices)()
230 repo_groups=c.repo_groups_choices)()
237 try:
231 try:
238 form_result = _form.to_python(dict(request.POST))
232 form_result = _form.to_python(dict(request.POST))
239 repo_model.update(repo_name, form_result)
233 repo_model.update(repo_name, form_result)
240 invalidate_cache('get_repo_cached_%s' % repo_name)
234 invalidate_cache('get_repo_cached_%s' % repo_name)
241 h.flash(_('Repository %s updated successfully' % repo_name),
235 h.flash(_('Repository %s updated successfully' % repo_name),
242 category='success')
236 category='success')
243 changed_name = form_result['repo_name']
237 changed_name = form_result['repo_name']
244 action_logger(self.rhodecode_user, 'admin_updated_repo',
238 action_logger(self.rhodecode_user, 'admin_updated_repo',
245 changed_name, '', self.sa)
239 changed_name, '', self.sa)
246
240
247 except formencode.Invalid, errors:
241 except formencode.Invalid, errors:
248 defaults = self.__load_data(repo_name)
242 defaults = self.__load_data(repo_name)
249 defaults.update(errors.value)
243 defaults.update(errors.value)
250 return htmlfill.render(
244 return htmlfill.render(
251 render('admin/repos/repo_edit.html'),
245 render('admin/repos/repo_edit.html'),
252 defaults=defaults,
246 defaults=defaults,
253 errors=errors.error_dict or {},
247 errors=errors.error_dict or {},
254 prefix_error=False,
248 prefix_error=False,
255 encoding="UTF-8")
249 encoding="UTF-8")
256
250
257 except Exception:
251 except Exception:
258 log.error(traceback.format_exc())
252 log.error(traceback.format_exc())
259 h.flash(_('error occurred during update of repository %s') \
253 h.flash(_('error occurred during update of repository %s') \
260 % repo_name, category='error')
254 % repo_name, category='error')
261 return redirect(url('edit_repo', repo_name=changed_name))
255 return redirect(url('edit_repo', repo_name=changed_name))
262
256
263 @HasPermissionAllDecorator('hg.admin')
257 @HasPermissionAllDecorator('hg.admin')
264 def delete(self, repo_name):
258 def delete(self, repo_name):
265 """
259 """
266 DELETE /repos/repo_name: Delete an existing item"""
260 DELETE /repos/repo_name: Delete an existing item"""
267 # Forms posted to this method should contain a hidden field:
261 # Forms posted to this method should contain a hidden field:
268 # <input type="hidden" name="_method" value="DELETE" />
262 # <input type="hidden" name="_method" value="DELETE" />
269 # Or using helpers:
263 # Or using helpers:
270 # h.form(url('repo', repo_name=ID),
264 # h.form(url('repo', repo_name=ID),
271 # method='delete')
265 # method='delete')
272 # url('repo', repo_name=ID)
266 # url('repo', repo_name=ID)
273
267
274 repo_model = RepoModel()
268 repo_model = RepoModel()
275 repo = repo_model.get_by_repo_name(repo_name)
269 repo = repo_model.get_by_repo_name(repo_name)
276 if not repo:
270 if not repo:
277 h.flash(_('%s repository is not mapped to db perhaps'
271 h.flash(_('%s repository is not mapped to db perhaps'
278 ' it was moved or renamed from the filesystem'
272 ' it was moved or renamed from the filesystem'
279 ' please run the application again'
273 ' please run the application again'
280 ' in order to rescan repositories') % repo_name,
274 ' in order to rescan repositories') % repo_name,
281 category='error')
275 category='error')
282
276
283 return redirect(url('repos'))
277 return redirect(url('repos'))
284 try:
278 try:
285 action_logger(self.rhodecode_user, 'admin_deleted_repo',
279 action_logger(self.rhodecode_user, 'admin_deleted_repo',
286 repo_name, '', self.sa)
280 repo_name, '', self.sa)
287 repo_model.delete(repo)
281 repo_model.delete(repo)
288 invalidate_cache('get_repo_cached_%s' % repo_name)
282 invalidate_cache('get_repo_cached_%s' % repo_name)
289 h.flash(_('deleted repository %s') % repo_name, category='success')
283 h.flash(_('deleted repository %s') % repo_name, category='success')
290
284
291 except Exception, e:
285 except Exception, e:
292 log.error(traceback.format_exc())
286 log.error(traceback.format_exc())
293 h.flash(_('An error occurred during deletion of %s') % repo_name,
287 h.flash(_('An error occurred during deletion of %s') % repo_name,
294 category='error')
288 category='error')
295
289
296 return redirect(url('repos'))
290 return redirect(url('repos'))
297
291
298 @HasPermissionAllDecorator('hg.admin')
292 @HasPermissionAllDecorator('hg.admin')
299 def delete_perm_user(self, repo_name):
293 def delete_perm_user(self, repo_name):
300 """
294 """
301 DELETE an existing repository permission user
295 DELETE an existing repository permission user
302
296
303 :param repo_name:
297 :param repo_name:
304 """
298 """
305
299
306 try:
300 try:
307 repo_model = RepoModel()
301 repo_model = RepoModel()
308 repo_model.delete_perm_user(request.POST, repo_name)
302 repo_model.delete_perm_user(request.POST, repo_name)
309 except Exception, e:
303 except Exception, e:
310 h.flash(_('An error occurred during deletion of repository user'),
304 h.flash(_('An error occurred during deletion of repository user'),
311 category='error')
305 category='error')
312 raise HTTPInternalServerError()
306 raise HTTPInternalServerError()
313
307
314 @HasPermissionAllDecorator('hg.admin')
308 @HasPermissionAllDecorator('hg.admin')
315 def delete_perm_users_group(self, repo_name):
309 def delete_perm_users_group(self, repo_name):
316 """
310 """
317 DELETE an existing repository permission users group
311 DELETE an existing repository permission users group
318
312
319 :param repo_name:
313 :param repo_name:
320 """
314 """
321 try:
315 try:
322 repo_model = RepoModel()
316 repo_model = RepoModel()
323 repo_model.delete_perm_users_group(request.POST, repo_name)
317 repo_model.delete_perm_users_group(request.POST, repo_name)
324 except Exception, e:
318 except Exception, e:
325 h.flash(_('An error occurred during deletion of repository'
319 h.flash(_('An error occurred during deletion of repository'
326 ' users groups'),
320 ' users groups'),
327 category='error')
321 category='error')
328 raise HTTPInternalServerError()
322 raise HTTPInternalServerError()
329
323
330 @HasPermissionAllDecorator('hg.admin')
324 @HasPermissionAllDecorator('hg.admin')
331 def repo_stats(self, repo_name):
325 def repo_stats(self, repo_name):
332 """
326 """
333 DELETE an existing repository statistics
327 DELETE an existing repository statistics
334
328
335 :param repo_name:
329 :param repo_name:
336 """
330 """
337
331
338 try:
332 try:
339 repo_model = RepoModel()
333 repo_model = RepoModel()
340 repo_model.delete_stats(repo_name)
334 repo_model.delete_stats(repo_name)
341 except Exception, e:
335 except Exception, e:
342 h.flash(_('An error occurred during deletion of repository stats'),
336 h.flash(_('An error occurred during deletion of repository stats'),
343 category='error')
337 category='error')
344 return redirect(url('edit_repo', repo_name=repo_name))
338 return redirect(url('edit_repo', repo_name=repo_name))
345
339
346 @HasPermissionAllDecorator('hg.admin')
340 @HasPermissionAllDecorator('hg.admin')
347 def repo_cache(self, repo_name):
341 def repo_cache(self, repo_name):
348 """
342 """
349 INVALIDATE existing repository cache
343 INVALIDATE existing repository cache
350
344
351 :param repo_name:
345 :param repo_name:
352 """
346 """
353
347
354 try:
348 try:
355 ScmModel().mark_for_invalidation(repo_name)
349 ScmModel().mark_for_invalidation(repo_name)
356 except Exception, e:
350 except Exception, e:
357 h.flash(_('An error occurred during cache invalidation'),
351 h.flash(_('An error occurred during cache invalidation'),
358 category='error')
352 category='error')
359 return redirect(url('edit_repo', repo_name=repo_name))
353 return redirect(url('edit_repo', repo_name=repo_name))
360
354
361 @HasPermissionAllDecorator('hg.admin')
355 @HasPermissionAllDecorator('hg.admin')
362 def repo_public_journal(self, repo_name):
356 def repo_public_journal(self, repo_name):
363 """
357 """
364 Set's this repository to be visible in public journal,
358 Set's this repository to be visible in public journal,
365 in other words assing default user to follow this repo
359 in other words assing default user to follow this repo
366
360
367 :param repo_name:
361 :param repo_name:
368 """
362 """
369
363
370 cur_token = request.POST.get('auth_token')
364 cur_token = request.POST.get('auth_token')
371 token = get_token()
365 token = get_token()
372 if cur_token == token:
366 if cur_token == token:
373 try:
367 try:
374 repo_id = Repository.by_repo_name(repo_name).repo_id
368 repo_id = Repository.by_repo_name(repo_name).repo_id
375 user_id = User.by_username('default').user_id
369 user_id = User.by_username('default').user_id
376 self.scm_model.toggle_following_repo(repo_id, user_id)
370 self.scm_model.toggle_following_repo(repo_id, user_id)
377 h.flash(_('Updated repository visibility in public journal'),
371 h.flash(_('Updated repository visibility in public journal'),
378 category='success')
372 category='success')
379 except:
373 except:
380 h.flash(_('An error occurred during setting this'
374 h.flash(_('An error occurred during setting this'
381 ' repository in public journal'),
375 ' repository in public journal'),
382 category='error')
376 category='error')
383
377
384 else:
378 else:
385 h.flash(_('Token mismatch'), category='error')
379 h.flash(_('Token mismatch'), category='error')
386 return redirect(url('edit_repo', repo_name=repo_name))
380 return redirect(url('edit_repo', repo_name=repo_name))
387
381
388 @HasPermissionAllDecorator('hg.admin')
382 @HasPermissionAllDecorator('hg.admin')
389 def repo_pull(self, repo_name):
383 def repo_pull(self, repo_name):
390 """
384 """
391 Runs task to update given repository with remote changes,
385 Runs task to update given repository with remote changes,
392 ie. make pull on remote location
386 ie. make pull on remote location
393
387
394 :param repo_name:
388 :param repo_name:
395 """
389 """
396 try:
390 try:
397 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
391 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
398 h.flash(_('Pulled from remote location'), category='success')
392 h.flash(_('Pulled from remote location'), category='success')
399 except Exception, e:
393 except Exception, e:
400 h.flash(_('An error occurred during pull from remote location'),
394 h.flash(_('An error occurred during pull from remote location'),
401 category='error')
395 category='error')
402
396
403 return redirect(url('edit_repo', repo_name=repo_name))
397 return redirect(url('edit_repo', repo_name=repo_name))
404
398
405 @HasPermissionAllDecorator('hg.admin')
399 @HasPermissionAllDecorator('hg.admin')
406 def show(self, repo_name, format='html'):
400 def show(self, repo_name, format='html'):
407 """GET /repos/repo_name: Show a specific item"""
401 """GET /repos/repo_name: Show a specific item"""
408 # url('repo', repo_name=ID)
402 # url('repo', repo_name=ID)
409
403
410 @HasPermissionAllDecorator('hg.admin')
404 @HasPermissionAllDecorator('hg.admin')
411 def edit(self, repo_name, format='html'):
405 def edit(self, repo_name, format='html'):
412 """GET /repos/repo_name/edit: Form to edit an existing item"""
406 """GET /repos/repo_name/edit: Form to edit an existing item"""
413 # url('edit_repo', repo_name=ID)
407 # url('edit_repo', repo_name=ID)
414 defaults = self.__load_data(repo_name)
408 defaults = self.__load_data(repo_name)
415
409
416 return htmlfill.render(
410 return htmlfill.render(
417 render('admin/repos/repo_edit.html'),
411 render('admin/repos/repo_edit.html'),
418 defaults=defaults,
412 defaults=defaults,
419 encoding="UTF-8",
413 encoding="UTF-8",
420 force_defaults=False
414 force_defaults=False
421 )
415 )
@@ -1,91 +1,89
1 import logging
1 import logging
2 from operator import itemgetter
2 from operator import itemgetter
3
3
4 from pylons import request, response, session, tmpl_context as c, url
4 from pylons import request, response, session, tmpl_context as c, url
5 from pylons.controllers.util import abort, redirect
5 from pylons.controllers.util import abort, redirect
6
6
7 from rhodecode.lib.base import BaseController, render
7 from rhodecode.lib.base import BaseController, render
8 from rhodecode.model.db import Group
8 from rhodecode.model.db import Group
9
9
10 log = logging.getLogger(__name__)
10 log = logging.getLogger(__name__)
11
11
12
12 class ReposGroupsController(BaseController):
13 class ReposGroupsController(BaseController):
13 """REST Controller styled on the Atom Publishing Protocol"""
14 """REST Controller styled on the Atom Publishing Protocol"""
14 # To properly map this controller, ensure your config/routing.py
15 # To properly map this controller, ensure your config/routing.py
15 # file has a resource setup:
16 # file has a resource setup:
16 # map.resource('repos_group', 'repos_groups')
17 # map.resource('repos_group', 'repos_groups')
17
18
18 def index(self, format='html'):
19 def index(self, format='html'):
19 """GET /repos_groups: All items in the collection"""
20 """GET /repos_groups: All items in the collection"""
20 # url('repos_groups')
21 # url('repos_groups')
21
22
22 def create(self):
23 def create(self):
23 """POST /repos_groups: Create a new item"""
24 """POST /repos_groups: Create a new item"""
24 # url('repos_groups')
25 # url('repos_groups')
25
26
26 def new(self, format='html'):
27 def new(self, format='html'):
27 """GET /repos_groups/new: Form to create a new item"""
28 """GET /repos_groups/new: Form to create a new item"""
28 # url('new_repos_group')
29 # url('new_repos_group')
29
30
30 def update(self, id):
31 def update(self, id):
31 """PUT /repos_groups/id: Update an existing item"""
32 """PUT /repos_groups/id: Update an existing item"""
32 # Forms posted to this method should contain a hidden field:
33 # Forms posted to this method should contain a hidden field:
33 # <input type="hidden" name="_method" value="PUT" />
34 # <input type="hidden" name="_method" value="PUT" />
34 # Or using helpers:
35 # Or using helpers:
35 # h.form(url('repos_group', id=ID),
36 # h.form(url('repos_group', id=ID),
36 # method='put')
37 # method='put')
37 # url('repos_group', id=ID)
38 # url('repos_group', id=ID)
38
39
39 def delete(self, id):
40 def delete(self, id):
40 """DELETE /repos_groups/id: Delete an existing item"""
41 """DELETE /repos_groups/id: Delete an existing item"""
41 # Forms posted to this method should contain a hidden field:
42 # Forms posted to this method should contain a hidden field:
42 # <input type="hidden" name="_method" value="DELETE" />
43 # <input type="hidden" name="_method" value="DELETE" />
43 # Or using helpers:
44 # Or using helpers:
44 # h.form(url('repos_group', id=ID),
45 # h.form(url('repos_group', id=ID),
45 # method='delete')
46 # method='delete')
46 # url('repos_group', id=ID)
47 # url('repos_group', id=ID)
47
48
48 def show(self, id, format='html'):
49 def show(self, id, format='html'):
49 """GET /repos_groups/id: Show a specific item"""
50 """GET /repos_groups/id: Show a specific item"""
50 # url('repos_group', id=ID)
51 # url('repos_group', id=ID)
51
52
52 c.group = Group.get(id)
53 c.group = Group.get(id)
53 if c.group:
54 if c.group:
54 c.group_repos = c.group.repositories
55 c.group_repos = c.group.repositories
55 else:
56 else:
56 return redirect(url('repos_group'))
57 return redirect(url('repos_group'))
57
58
58 sortables = ['name', 'description', 'last_change', 'tip', 'owner']
59 sortables = ['name', 'description', 'last_change', 'tip', 'owner']
59 current_sort = request.GET.get('sort', 'name')
60 current_sort = request.GET.get('sort', 'name')
60 current_sort_slug = current_sort.replace('-', '')
61 current_sort_slug = current_sort.replace('-', '')
61
62
62 if current_sort_slug not in sortables:
63 if current_sort_slug not in sortables:
63 c.sort_by = 'name'
64 c.sort_by = 'name'
64 current_sort_slug = c.sort_by
65 current_sort_slug = c.sort_by
65 else:
66 else:
66 c.sort_by = current_sort
67 c.sort_by = current_sort
67 c.sort_slug = current_sort_slug
68 c.sort_slug = current_sort_slug
68
69
69 sort_key = current_sort_slug + '_sort'
70 sort_key = current_sort_slug + '_sort'
70
71
71
72 #overwrite our cached list with current filter
72 #overwrite our cached list with current filter
73 gr_filter = [r.repo_name for r in c.group_repos]
73 gr_filter = [r.repo_name for r in c.group_repos]
74 c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
74 c.cached_repo_list = self.scm_model.get_repos(all_repos=gr_filter)
75
75
76 if c.sort_by.startswith('-'):
76 if c.sort_by.startswith('-'):
77 c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key),
77 c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key),
78 reverse=True)
78 reverse=True)
79 else:
79 else:
80 c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key),
80 c.repos_list = sorted(c.cached_repo_list, key=itemgetter(sort_key),
81 reverse=False)
81 reverse=False)
82
82
83 c.repo_cnt = len(c.repos_list)
83 c.repo_cnt = len(c.repos_list)
84
84
85
86 return render('admin/repos_groups/repos_groups.html')
85 return render('admin/repos_groups/repos_groups.html')
87
86
88
89 def edit(self, id, format='html'):
87 def edit(self, id, format='html'):
90 """GET /repos_groups/id/edit: Form to edit an existing item"""
88 """GET /repos_groups/id/edit: Form to edit an existing item"""
91 # url('edit_repos_group', id=ID)
89 # url('edit_repos_group', id=ID)
@@ -1,357 +1,363
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from sqlalchemy import func
30 from sqlalchemy import func
31 from formencode import htmlfill
31 from formencode import htmlfill
32 from pylons import request, session, tmpl_context as c, url, config
32 from pylons import request, session, tmpl_context as c, url, config
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 import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
38 HasPermissionAnyDecorator, NotAnonymous
38 HasPermissionAnyDecorator, NotAnonymous
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.celerylib import tasks, run_task
40 from rhodecode.lib.celerylib import tasks, run_task
41 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
41 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
42 set_rhodecode_config, repo_name_slug
42 set_rhodecode_config, repo_name_slug
43 from rhodecode.model.db import RhodeCodeUi, Repository, Group
43 from rhodecode.model.db import RhodeCodeUi, Repository, Group
44 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
44 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
45 ApplicationUiSettingsForm
45 ApplicationUiSettingsForm
46 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.settings import SettingsModel
47 from rhodecode.model.settings import SettingsModel
48 from rhodecode.model.user import UserModel
48 from rhodecode.model.user import UserModel
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52
52
53 class SettingsController(BaseController):
53 class SettingsController(BaseController):
54 """REST Controller styled on the Atom Publishing Protocol"""
54 """REST Controller styled on the Atom Publishing Protocol"""
55 # To properly map this controller, ensure your config/routing.py
55 # To properly map this controller, ensure your config/routing.py
56 # file has a resource setup:
56 # file has a resource setup:
57 # map.resource('setting', 'settings', controller='admin/settings',
57 # map.resource('setting', 'settings', controller='admin/settings',
58 # path_prefix='/admin', name_prefix='admin_')
58 # path_prefix='/admin', name_prefix='admin_')
59
59
60
61 @LoginRequired()
60 @LoginRequired()
62 def __before__(self):
61 def __before__(self):
63 c.admin_user = session.get('admin_user')
62 c.admin_user = session.get('admin_user')
64 c.admin_username = session.get('admin_username')
63 c.admin_username = session.get('admin_username')
65 super(SettingsController, self).__before__()
64 super(SettingsController, self).__before__()
66
65
67
68 @HasPermissionAllDecorator('hg.admin')
66 @HasPermissionAllDecorator('hg.admin')
69 def index(self, format='html'):
67 def index(self, format='html'):
70 """GET /admin/settings: All items in the collection"""
68 """GET /admin/settings: All items in the collection"""
71 # url('admin_settings')
69 # url('admin_settings')
72
70
73 defaults = SettingsModel().get_app_settings()
71 defaults = SettingsModel().get_app_settings()
74 defaults.update(self.get_hg_ui_settings())
72 defaults.update(self.get_hg_ui_settings())
75 return htmlfill.render(
73 return htmlfill.render(
76 render('admin/settings/settings.html'),
74 render('admin/settings/settings.html'),
77 defaults=defaults,
75 defaults=defaults,
78 encoding="UTF-8",
76 encoding="UTF-8",
79 force_defaults=False
77 force_defaults=False
80 )
78 )
81
79
82 @HasPermissionAllDecorator('hg.admin')
80 @HasPermissionAllDecorator('hg.admin')
83 def create(self):
81 def create(self):
84 """POST /admin/settings: Create a new item"""
82 """POST /admin/settings: Create a new item"""
85 # url('admin_settings')
83 # url('admin_settings')
86
84
87 @HasPermissionAllDecorator('hg.admin')
85 @HasPermissionAllDecorator('hg.admin')
88 def new(self, format='html'):
86 def new(self, format='html'):
89 """GET /admin/settings/new: Form to create a new item"""
87 """GET /admin/settings/new: Form to create a new item"""
90 # url('admin_new_setting')
88 # url('admin_new_setting')
91
89
92 @HasPermissionAllDecorator('hg.admin')
90 @HasPermissionAllDecorator('hg.admin')
93 def update(self, setting_id):
91 def update(self, setting_id):
94 """PUT /admin/settings/setting_id: Update an existing item"""
92 """PUT /admin/settings/setting_id: Update an existing item"""
95 # Forms posted to this method should contain a hidden field:
93 # Forms posted to this method should contain a hidden field:
96 # <input type="hidden" name="_method" value="PUT" />
94 # <input type="hidden" name="_method" value="PUT" />
97 # Or using helpers:
95 # Or using helpers:
98 # h.form(url('admin_setting', setting_id=ID),
96 # h.form(url('admin_setting', setting_id=ID),
99 # method='put')
97 # method='put')
100 # url('admin_setting', setting_id=ID)
98 # url('admin_setting', setting_id=ID)
101 if setting_id == 'mapping':
99 if setting_id == 'mapping':
102 rm_obsolete = request.POST.get('destroy', False)
100 rm_obsolete = request.POST.get('destroy', False)
103 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
101 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
104 initial = ScmModel().repo_scan()
102 initial = ScmModel().repo_scan()
105 log.debug('invalidating all repositories')
103 log.debug('invalidating all repositories')
106 for repo_name in initial.keys():
104 for repo_name in initial.keys():
107 invalidate_cache('get_repo_cached_%s' % repo_name)
105 invalidate_cache('get_repo_cached_%s' % repo_name)
108
106
109 added, removed = repo2db_mapper(initial, rm_obsolete)
107 added, removed = repo2db_mapper(initial, rm_obsolete)
110
108
111 h.flash(_('Repositories successfully'
109 h.flash(_('Repositories successfully'
112 ' rescanned added: %s,removed: %s') % (added, removed)
110 ' rescanned added: %s,removed: %s') % (added, removed),
113 , category='success')
111 category='success')
114
112
115 if setting_id == 'whoosh':
113 if setting_id == 'whoosh':
116 repo_location = self.get_hg_ui_settings()['paths_root_path']
114 repo_location = self.get_hg_ui_settings()['paths_root_path']
117 full_index = request.POST.get('full_index', False)
115 full_index = request.POST.get('full_index', False)
118 run_task(tasks.whoosh_index, repo_location, full_index)
116 run_task(tasks.whoosh_index, repo_location, full_index)
119
117
120 h.flash(_('Whoosh reindex task scheduled'), category='success')
118 h.flash(_('Whoosh reindex task scheduled'), category='success')
121 if setting_id == 'global':
119 if setting_id == 'global':
122
120
123 application_form = ApplicationSettingsForm()()
121 application_form = ApplicationSettingsForm()()
124 try:
122 try:
125 form_result = application_form.to_python(dict(request.POST))
123 form_result = application_form.to_python(dict(request.POST))
126 settings_model = SettingsModel()
124 settings_model = SettingsModel()
127
125
128 try:
126 try:
129 hgsettings1 = settings_model.get('title')
127 hgsettings1 = settings_model.get('title')
130 hgsettings1.app_settings_value = form_result['rhodecode_title']
128 hgsettings1.app_settings_value = \
129 form_result['rhodecode_title']
131
130
132 hgsettings2 = settings_model.get('realm')
131 hgsettings2 = settings_model.get('realm')
133 hgsettings2.app_settings_value = form_result['rhodecode_realm']
132 hgsettings2.app_settings_value = \
133 form_result['rhodecode_realm']
134
134
135 hgsettings3 = settings_model.get('ga_code')
135 hgsettings3 = settings_model.get('ga_code')
136 hgsettings3.app_settings_value = form_result['rhodecode_ga_code']
136 hgsettings3.app_settings_value = \
137
137 form_result['rhodecode_ga_code']
138
139
138
140 self.sa.add(hgsettings1)
139 self.sa.add(hgsettings1)
141 self.sa.add(hgsettings2)
140 self.sa.add(hgsettings2)
142 self.sa.add(hgsettings3)
141 self.sa.add(hgsettings3)
143 self.sa.commit()
142 self.sa.commit()
144 set_rhodecode_config(config)
143 set_rhodecode_config(config)
145 h.flash(_('Updated application settings'),
144 h.flash(_('Updated application settings'),
146 category='success')
145 category='success')
147
146
148 except Exception:
147 except Exception:
149 log.error(traceback.format_exc())
148 log.error(traceback.format_exc())
150 h.flash(_('error occurred during updating application settings'),
149 h.flash(_('error occurred during updating '
150 'application settings'),
151 category='error')
151 category='error')
152
152
153 self.sa.rollback()
153 self.sa.rollback()
154
154
155
156 except formencode.Invalid, errors:
155 except formencode.Invalid, errors:
157 return htmlfill.render(
156 return htmlfill.render(
158 render('admin/settings/settings.html'),
157 render('admin/settings/settings.html'),
159 defaults=errors.value,
158 defaults=errors.value,
160 errors=errors.error_dict or {},
159 errors=errors.error_dict or {},
161 prefix_error=False,
160 prefix_error=False,
162 encoding="UTF-8")
161 encoding="UTF-8")
163
162
164 if setting_id == 'mercurial':
163 if setting_id == 'mercurial':
165 application_form = ApplicationUiSettingsForm()()
164 application_form = ApplicationUiSettingsForm()()
166 try:
165 try:
167 form_result = application_form.to_python(dict(request.POST))
166 form_result = application_form.to_python(dict(request.POST))
168
167
169 try:
168 try:
170
169
171 hgsettings1 = self.sa.query(RhodeCodeUi)\
170 hgsettings1 = self.sa.query(RhodeCodeUi)\
172 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
171 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
173 hgsettings1.ui_value = form_result['web_push_ssl']
172 hgsettings1.ui_value = form_result['web_push_ssl']
174
173
175 hgsettings2 = self.sa.query(RhodeCodeUi)\
174 hgsettings2 = self.sa.query(RhodeCodeUi)\
176 .filter(RhodeCodeUi.ui_key == '/').one()
175 .filter(RhodeCodeUi.ui_key == '/').one()
177 hgsettings2.ui_value = form_result['paths_root_path']
176 hgsettings2.ui_value = form_result['paths_root_path']
178
177
179
180 #HOOKS
178 #HOOKS
181 hgsettings3 = self.sa.query(RhodeCodeUi)\
179 hgsettings3 = self.sa.query(RhodeCodeUi)\
182 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
180 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
183 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
181 hgsettings3.ui_active = \
182 bool(form_result['hooks_changegroup_update'])
184
183
185 hgsettings4 = self.sa.query(RhodeCodeUi)\
184 hgsettings4 = self.sa.query(RhodeCodeUi)\
186 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
185 .filter(RhodeCodeUi.ui_key ==
187 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
186 'changegroup.repo_size').one()
187 hgsettings4.ui_active = \
188 bool(form_result['hooks_changegroup_repo_size'])
188
189
189 hgsettings5 = self.sa.query(RhodeCodeUi)\
190 hgsettings5 = self.sa.query(RhodeCodeUi)\
190 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
191 .filter(RhodeCodeUi.ui_key ==
191 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
192 'pretxnchangegroup.push_logger').one()
193 hgsettings5.ui_active = \
194 bool(form_result['hooks_pretxnchangegroup'
195 '_push_logger'])
192
196
193 hgsettings6 = self.sa.query(RhodeCodeUi)\
197 hgsettings6 = self.sa.query(RhodeCodeUi)\
194 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
198 .filter(RhodeCodeUi.ui_key ==
195 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
199 'preoutgoing.pull_logger').one()
196
200 hgsettings6.ui_active = \
201 bool(form_result['hooks_preoutgoing_pull_logger'])
197
202
198 self.sa.add(hgsettings1)
203 self.sa.add(hgsettings1)
199 self.sa.add(hgsettings2)
204 self.sa.add(hgsettings2)
200 self.sa.add(hgsettings3)
205 self.sa.add(hgsettings3)
201 self.sa.add(hgsettings4)
206 self.sa.add(hgsettings4)
202 self.sa.add(hgsettings5)
207 self.sa.add(hgsettings5)
203 self.sa.add(hgsettings6)
208 self.sa.add(hgsettings6)
204 self.sa.commit()
209 self.sa.commit()
205
210
206 h.flash(_('Updated mercurial settings'),
211 h.flash(_('Updated mercurial settings'),
207 category='success')
212 category='success')
208
213
209 except:
214 except:
210 log.error(traceback.format_exc())
215 log.error(traceback.format_exc())
211 h.flash(_('error occurred during updating application settings'),
216 h.flash(_('error occurred during updating '
212 category='error')
217 'application settings'), category='error')
213
218
214 self.sa.rollback()
219 self.sa.rollback()
215
220
216
217 except formencode.Invalid, errors:
221 except formencode.Invalid, errors:
218 return htmlfill.render(
222 return htmlfill.render(
219 render('admin/settings/settings.html'),
223 render('admin/settings/settings.html'),
220 defaults=errors.value,
224 defaults=errors.value,
221 errors=errors.error_dict or {},
225 errors=errors.error_dict or {},
222 prefix_error=False,
226 prefix_error=False,
223 encoding="UTF-8")
227 encoding="UTF-8")
224
228
225
226
227 return redirect(url('admin_settings'))
229 return redirect(url('admin_settings'))
228
230
229 @HasPermissionAllDecorator('hg.admin')
231 @HasPermissionAllDecorator('hg.admin')
230 def delete(self, setting_id):
232 def delete(self, setting_id):
231 """DELETE /admin/settings/setting_id: Delete an existing item"""
233 """DELETE /admin/settings/setting_id: Delete an existing item"""
232 # Forms posted to this method should contain a hidden field:
234 # Forms posted to this method should contain a hidden field:
233 # <input type="hidden" name="_method" value="DELETE" />
235 # <input type="hidden" name="_method" value="DELETE" />
234 # Or using helpers:
236 # Or using helpers:
235 # h.form(url('admin_setting', setting_id=ID),
237 # h.form(url('admin_setting', setting_id=ID),
236 # method='delete')
238 # method='delete')
237 # url('admin_setting', setting_id=ID)
239 # url('admin_setting', setting_id=ID)
238
240
239 @HasPermissionAllDecorator('hg.admin')
241 @HasPermissionAllDecorator('hg.admin')
240 def show(self, setting_id, format='html'):
242 def show(self, setting_id, format='html'):
241 """GET /admin/settings/setting_id: Show a specific item"""
243 """
244 GET /admin/settings/setting_id: Show a specific item"""
242 # url('admin_setting', setting_id=ID)
245 # url('admin_setting', setting_id=ID)
243
246
244 @HasPermissionAllDecorator('hg.admin')
247 @HasPermissionAllDecorator('hg.admin')
245 def edit(self, setting_id, format='html'):
248 def edit(self, setting_id, format='html'):
246 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
249 """
250 GET /admin/settings/setting_id/edit: Form to
251 edit an existing item"""
247 # url('admin_edit_setting', setting_id=ID)
252 # url('admin_edit_setting', setting_id=ID)
248
253
249 @NotAnonymous()
254 @NotAnonymous()
250 def my_account(self):
255 def my_account(self):
251 """
256 """
252 GET /_admin/my_account Displays info about my account
257 GET /_admin/my_account Displays info about my account
253 """
258 """
254 # url('admin_settings_my_account')
259 # url('admin_settings_my_account')
255
260
256 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
261 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
257 all_repos = [r.repo_name for r in self.sa.query(Repository)\
262 all_repos = [r.repo_name for r in self.sa.query(Repository)\
258 .filter(Repository.user_id == c.user.user_id)\
263 .filter(Repository.user_id == c.user.user_id)\
259 .order_by(func.lower(Repository.repo_name)).all()]
264 .order_by(func.lower(Repository.repo_name)).all()]
260 c.user_repos = ScmModel().get_repos(all_repos)
265 c.user_repos = ScmModel().get_repos(all_repos)
261
266
262 if c.user.username == 'default':
267 if c.user.username == 'default':
263 h.flash(_("You can't edit this user since it's"
268 h.flash(_("You can't edit this user since it's"
264 " crucial for entire application"), category='warning')
269 " crucial for entire application"), category='warning')
265 return redirect(url('users'))
270 return redirect(url('users'))
266
271
267 defaults = c.user.get_dict()
272 defaults = c.user.get_dict()
268 return htmlfill.render(
273 return htmlfill.render(
269 render('admin/users/user_edit_my_account.html'),
274 render('admin/users/user_edit_my_account.html'),
270 defaults=defaults,
275 defaults=defaults,
271 encoding="UTF-8",
276 encoding="UTF-8",
272 force_defaults=False
277 force_defaults=False
273 )
278 )
274
279
275 def my_account_update(self):
280 def my_account_update(self):
276 """PUT /_admin/my_account_update: Update an existing item"""
281 """PUT /_admin/my_account_update: Update an existing item"""
277 # Forms posted to this method should contain a hidden field:
282 # Forms posted to this method should contain a hidden field:
278 # <input type="hidden" name="_method" value="PUT" />
283 # <input type="hidden" name="_method" value="PUT" />
279 # Or using helpers:
284 # Or using helpers:
280 # h.form(url('admin_settings_my_account_update'),
285 # h.form(url('admin_settings_my_account_update'),
281 # method='put')
286 # method='put')
282 # url('admin_settings_my_account_update', id=ID)
287 # url('admin_settings_my_account_update', id=ID)
283 user_model = UserModel()
288 user_model = UserModel()
284 uid = self.rhodecode_user.user_id
289 uid = self.rhodecode_user.user_id
285 _form = UserForm(edit=True, old_data={'user_id':uid,
290 _form = UserForm(edit=True,
291 old_data={'user_id': uid,
286 'email':self.rhodecode_user.email})()
292 'email': self.rhodecode_user.email})()
287 form_result = {}
293 form_result = {}
288 try:
294 try:
289 form_result = _form.to_python(dict(request.POST))
295 form_result = _form.to_python(dict(request.POST))
290 user_model.update_my_account(uid, form_result)
296 user_model.update_my_account(uid, form_result)
291 h.flash(_('Your account was updated successfully'),
297 h.flash(_('Your account was updated successfully'),
292 category='success')
298 category='success')
293
299
294 except formencode.Invalid, errors:
300 except formencode.Invalid, errors:
295 c.user = user_model.get(self.rhodecode_user.user_id, cache=False)
301 c.user = user_model.get(self.rhodecode_user.user_id, cache=False)
296 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
302 c.user = UserModel().get(self.rhodecode_user.user_id, cache=False)
297 all_repos = self.sa.query(Repository)\
303 all_repos = self.sa.query(Repository)\
298 .filter(Repository.user_id == c.user.user_id)\
304 .filter(Repository.user_id == c.user.user_id)\
299 .order_by(func.lower(Repository.repo_name))\
305 .order_by(func.lower(Repository.repo_name))\
300 .all()
306 .all()
301 c.user_repos = ScmModel().get_repos(all_repos)
307 c.user_repos = ScmModel().get_repos(all_repos)
302
308
303 return htmlfill.render(
309 return htmlfill.render(
304 render('admin/users/user_edit_my_account.html'),
310 render('admin/users/user_edit_my_account.html'),
305 defaults=errors.value,
311 defaults=errors.value,
306 errors=errors.error_dict or {},
312 errors=errors.error_dict or {},
307 prefix_error=False,
313 prefix_error=False,
308 encoding="UTF-8")
314 encoding="UTF-8")
309 except Exception:
315 except Exception:
310 log.error(traceback.format_exc())
316 log.error(traceback.format_exc())
311 h.flash(_('error occurred during update of user %s') \
317 h.flash(_('error occurred during update of user %s') \
312 % form_result.get('username'), category='error')
318 % form_result.get('username'), category='error')
313
319
314 return redirect(url('my_account'))
320 return redirect(url('my_account'))
315
321
316 @NotAnonymous()
322 @NotAnonymous()
317 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
323 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
318 def create_repository(self):
324 def create_repository(self):
319 """GET /_admin/create_repository: Form to create a new item"""
325 """GET /_admin/create_repository: Form to create a new item"""
320
326
321 c.repo_groups = [('', '')]
327 c.repo_groups = [('', '')]
322 parents_link = lambda k:h.literal('&raquo;'.join(
328 parents_link = lambda k: h.literal('&raquo;'.join(
323 map(lambda k:k.group_name,
329 map(lambda k: k.group_name,
324 k.parents + [k])
330 k.parents + [k])
325 )
331 )
326 )
332 )
327
333
328 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
334 c.repo_groups.extend([(x.group_id, parents_link(x)) for \
329 x in self.sa.query(Group).all()])
335 x in self.sa.query(Group).all()])
330 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
336 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
331
337
332 new_repo = request.GET.get('repo', '')
338 new_repo = request.GET.get('repo', '')
333 c.new_repo = repo_name_slug(new_repo)
339 c.new_repo = repo_name_slug(new_repo)
334
340
335 return render('admin/repos/repo_add_create_repository.html')
341 return render('admin/repos/repo_add_create_repository.html')
336
342
337 def get_hg_ui_settings(self):
343 def get_hg_ui_settings(self):
338 ret = self.sa.query(RhodeCodeUi).all()
344 ret = self.sa.query(RhodeCodeUi).all()
339
345
340 if not ret:
346 if not ret:
341 raise Exception('Could not get application ui settings !')
347 raise Exception('Could not get application ui settings !')
342 settings = {}
348 settings = {}
343 for each in ret:
349 for each in ret:
344 k = each.ui_key
350 k = each.ui_key
345 v = each.ui_value
351 v = each.ui_value
346 if k == '/':
352 if k == '/':
347 k = 'root_path'
353 k = 'root_path'
348
354
349 if k.find('.') != -1:
355 if k.find('.') != -1:
350 k = k.replace('.', '_')
356 k = k.replace('.', '_')
351
357
352 if each.ui_section == 'hooks':
358 if each.ui_section == 'hooks':
353 v = each.ui_active
359 v = each.ui_active
354
360
355 settings[each.ui_section + '_' + k] = v
361 settings[each.ui_section + '_' + k] = v
356
362
357 return settings
363 return settings
@@ -1,174 +1,176
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-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, session, tmpl_context as c, url, config
31 from pylons import request, session, tmpl_context as c, url, config
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
35 from rhodecode.lib.exceptions import DefaultUserException, \
36 UserOwnsReposException
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
38 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
39
40
40 from rhodecode.model.db import User
41 from rhodecode.model.db import User
41 from rhodecode.model.forms import UserForm
42 from rhodecode.model.forms import UserForm
42 from rhodecode.model.user import UserModel
43 from rhodecode.model.user import UserModel
43
44
44 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
45
46
47
46 class UsersController(BaseController):
48 class UsersController(BaseController):
47 """REST Controller styled on the Atom Publishing Protocol"""
49 """REST Controller styled on the Atom Publishing Protocol"""
48 # To properly map this controller, ensure your config/routing.py
50 # To properly map this controller, ensure your config/routing.py
49 # file has a resource setup:
51 # file has a resource setup:
50 # map.resource('user', 'users')
52 # map.resource('user', 'users')
51
53
52 @LoginRequired()
54 @LoginRequired()
53 @HasPermissionAllDecorator('hg.admin')
55 @HasPermissionAllDecorator('hg.admin')
54 def __before__(self):
56 def __before__(self):
55 c.admin_user = session.get('admin_user')
57 c.admin_user = session.get('admin_user')
56 c.admin_username = session.get('admin_username')
58 c.admin_username = session.get('admin_username')
57 super(UsersController, self).__before__()
59 super(UsersController, self).__before__()
58 c.available_permissions = config['available_permissions']
60 c.available_permissions = config['available_permissions']
59
61
60 def index(self, format='html'):
62 def index(self, format='html'):
61 """GET /users: All items in the collection"""
63 """GET /users: All items in the collection"""
62 # url('users')
64 # url('users')
63
65
64 c.users_list = self.sa.query(User).all()
66 c.users_list = self.sa.query(User).all()
65 return render('admin/users/users.html')
67 return render('admin/users/users.html')
66
68
67 def create(self):
69 def create(self):
68 """POST /users: Create a new item"""
70 """POST /users: Create a new item"""
69 # url('users')
71 # url('users')
70
72
71 user_model = UserModel()
73 user_model = UserModel()
72 login_form = UserForm()()
74 login_form = UserForm()()
73 try:
75 try:
74 form_result = login_form.to_python(dict(request.POST))
76 form_result = login_form.to_python(dict(request.POST))
75 user_model.create(form_result)
77 user_model.create(form_result)
76 h.flash(_('created user %s') % form_result['username'],
78 h.flash(_('created user %s') % form_result['username'],
77 category='success')
79 category='success')
78 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
80 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
79 except formencode.Invalid, errors:
81 except formencode.Invalid, errors:
80 return htmlfill.render(
82 return htmlfill.render(
81 render('admin/users/user_add.html'),
83 render('admin/users/user_add.html'),
82 defaults=errors.value,
84 defaults=errors.value,
83 errors=errors.error_dict or {},
85 errors=errors.error_dict or {},
84 prefix_error=False,
86 prefix_error=False,
85 encoding="UTF-8")
87 encoding="UTF-8")
86 except Exception:
88 except Exception:
87 log.error(traceback.format_exc())
89 log.error(traceback.format_exc())
88 h.flash(_('error occurred during creation of user %s') \
90 h.flash(_('error occurred during creation of user %s') \
89 % request.POST.get('username'), category='error')
91 % request.POST.get('username'), category='error')
90 return redirect(url('users'))
92 return redirect(url('users'))
91
93
92 def new(self, format='html'):
94 def new(self, format='html'):
93 """GET /users/new: Form to create a new item"""
95 """GET /users/new: Form to create a new item"""
94 # url('new_user')
96 # url('new_user')
95 return render('admin/users/user_add.html')
97 return render('admin/users/user_add.html')
96
98
97 def update(self, id):
99 def update(self, id):
98 """PUT /users/id: Update an existing item"""
100 """PUT /users/id: Update an existing item"""
99 # Forms posted to this method should contain a hidden field:
101 # Forms posted to this method should contain a hidden field:
100 # <input type="hidden" name="_method" value="PUT" />
102 # <input type="hidden" name="_method" value="PUT" />
101 # Or using helpers:
103 # Or using helpers:
102 # h.form(url('user', id=ID),
104 # h.form(url('user', id=ID),
103 # method='put')
105 # method='put')
104 # url('user', id=ID)
106 # url('user', id=ID)
105 user_model = UserModel()
107 user_model = UserModel()
106 c.user = user_model.get(id)
108 c.user = user_model.get(id)
107
109
108 _form = UserForm(edit=True, old_data={'user_id':id,
110 _form = UserForm(edit=True, old_data={'user_id': id,
109 'email':c.user.email})()
111 'email': c.user.email})()
110 form_result = {}
112 form_result = {}
111 try:
113 try:
112 form_result = _form.to_python(dict(request.POST))
114 form_result = _form.to_python(dict(request.POST))
113 user_model.update(id, form_result)
115 user_model.update(id, form_result)
114 h.flash(_('User updated succesfully'), category='success')
116 h.flash(_('User updated succesfully'), category='success')
115
117
116 except formencode.Invalid, errors:
118 except formencode.Invalid, errors:
117 return htmlfill.render(
119 return htmlfill.render(
118 render('admin/users/user_edit.html'),
120 render('admin/users/user_edit.html'),
119 defaults=errors.value,
121 defaults=errors.value,
120 errors=errors.error_dict or {},
122 errors=errors.error_dict or {},
121 prefix_error=False,
123 prefix_error=False,
122 encoding="UTF-8")
124 encoding="UTF-8")
123 except Exception:
125 except Exception:
124 log.error(traceback.format_exc())
126 log.error(traceback.format_exc())
125 h.flash(_('error occurred during update of user %s') \
127 h.flash(_('error occurred during update of user %s') \
126 % form_result.get('username'), category='error')
128 % form_result.get('username'), category='error')
127
129
128 return redirect(url('users'))
130 return redirect(url('users'))
129
131
130 def delete(self, id):
132 def delete(self, id):
131 """DELETE /users/id: Delete an existing item"""
133 """DELETE /users/id: Delete an existing item"""
132 # Forms posted to this method should contain a hidden field:
134 # Forms posted to this method should contain a hidden field:
133 # <input type="hidden" name="_method" value="DELETE" />
135 # <input type="hidden" name="_method" value="DELETE" />
134 # Or using helpers:
136 # Or using helpers:
135 # h.form(url('user', id=ID),
137 # h.form(url('user', id=ID),
136 # method='delete')
138 # method='delete')
137 # url('user', id=ID)
139 # url('user', id=ID)
138 user_model = UserModel()
140 user_model = UserModel()
139 try:
141 try:
140 user_model.delete(id)
142 user_model.delete(id)
141 h.flash(_('successfully deleted user'), category='success')
143 h.flash(_('successfully deleted user'), category='success')
142 except (UserOwnsReposException, DefaultUserException), e:
144 except (UserOwnsReposException, DefaultUserException), e:
143 h.flash(str(e), category='warning')
145 h.flash(str(e), category='warning')
144 except Exception:
146 except Exception:
145 h.flash(_('An error occurred during deletion of user'),
147 h.flash(_('An error occurred during deletion of user'),
146 category='error')
148 category='error')
147 return redirect(url('users'))
149 return redirect(url('users'))
148
150
149 def show(self, id, format='html'):
151 def show(self, id, format='html'):
150 """GET /users/id: Show a specific item"""
152 """GET /users/id: Show a specific item"""
151 # url('user', id=ID)
153 # url('user', id=ID)
152
154
153
154 def edit(self, id, format='html'):
155 def edit(self, id, format='html'):
155 """GET /users/id/edit: Form to edit an existing item"""
156 """GET /users/id/edit: Form to edit an existing item"""
156 # url('edit_user', id=ID)
157 # url('edit_user', id=ID)
157 user_model = UserModel()
158 user_model = UserModel()
158 c.user = user_model.get(id)
159 c.user = user_model.get(id)
159 if not c.user:
160 if not c.user:
160 return redirect(url('users'))
161 return redirect(url('users'))
161 if c.user.username == 'default':
162 if c.user.username == 'default':
162 h.flash(_("You can't edit this user"), category='warning')
163 h.flash(_("You can't edit this user"), category='warning')
163 return redirect(url('users'))
164 return redirect(url('users'))
164 c.user.permissions = {}
165 c.user.permissions = {}
165 c.granted_permissions = user_model.fill_perms(c.user).permissions['global']
166 c.granted_permissions = user_model.fill_perms(c.user)\
167 .permissions['global']
166
168
167 defaults = c.user.get_dict()
169 defaults = c.user.get_dict()
168
170
169 return htmlfill.render(
171 return htmlfill.render(
170 render('admin/users/user_edit.html'),
172 render('admin/users/user_edit.html'),
171 defaults=defaults,
173 defaults=defaults,
172 encoding="UTF-8",
174 encoding="UTF-8",
173 force_defaults=False
175 force_defaults=False
174 )
176 )
@@ -1,182 +1,180
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users_groups
3 rhodecode.controllers.admin.users_groups
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Users Groups crud controller for pylons
6 Users Groups crud controller for pylons
7
7
8 :created_on: Jan 25, 2011
8 :created_on: Jan 25, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, session, tmpl_context as c, url, config
31 from pylons import request, session, tmpl_context as c, url, config
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
36 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
36 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
38 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
39
38
40 from rhodecode.model.db import User, UsersGroup
39 from rhodecode.model.db import User, UsersGroup
41 from rhodecode.model.forms import UserForm, UsersGroupForm
40 from rhodecode.model.forms import UserForm, UsersGroupForm
42 from rhodecode.model.user import UserModel
41 from rhodecode.model.user import UserModel
43 from rhodecode.model.users_group import UsersGroupModel
42 from rhodecode.model.users_group import UsersGroupModel
44
43
45 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
46
45
46
47 class UsersGroupsController(BaseController):
47 class UsersGroupsController(BaseController):
48 """REST Controller styled on the Atom Publishing Protocol"""
48 """REST Controller styled on the Atom Publishing Protocol"""
49 # To properly map this controller, ensure your config/routing.py
49 # To properly map this controller, ensure your config/routing.py
50 # file has a resource setup:
50 # file has a resource setup:
51 # map.resource('users_group', 'users_groups')
51 # map.resource('users_group', 'users_groups')
52
52
53 @LoginRequired()
53 @LoginRequired()
54 @HasPermissionAllDecorator('hg.admin')
54 @HasPermissionAllDecorator('hg.admin')
55 def __before__(self):
55 def __before__(self):
56 c.admin_user = session.get('admin_user')
56 c.admin_user = session.get('admin_user')
57 c.admin_username = session.get('admin_username')
57 c.admin_username = session.get('admin_username')
58 super(UsersGroupsController, self).__before__()
58 super(UsersGroupsController, self).__before__()
59 c.available_permissions = config['available_permissions']
59 c.available_permissions = config['available_permissions']
60
60
61 def index(self, format='html'):
61 def index(self, format='html'):
62 """GET /users_groups: All items in the collection"""
62 """GET /users_groups: All items in the collection"""
63 # url('users_groups')
63 # url('users_groups')
64 c.users_groups_list = self.sa.query(UsersGroup).all()
64 c.users_groups_list = self.sa.query(UsersGroup).all()
65 return render('admin/users_groups/users_groups.html')
65 return render('admin/users_groups/users_groups.html')
66
66
67 def create(self):
67 def create(self):
68 """POST /users_groups: Create a new item"""
68 """POST /users_groups: Create a new item"""
69 # url('users_groups')
69 # url('users_groups')
70 users_group_model = UsersGroupModel()
70 users_group_model = UsersGroupModel()
71 users_group_form = UsersGroupForm()()
71 users_group_form = UsersGroupForm()()
72 try:
72 try:
73 form_result = users_group_form.to_python(dict(request.POST))
73 form_result = users_group_form.to_python(dict(request.POST))
74 users_group_model.create(form_result)
74 users_group_model.create(form_result)
75 h.flash(_('created users group %s') % form_result['users_group_name'],
75 h.flash(_('created users group %s') \
76 category='success')
76 % form_result['users_group_name'], category='success')
77 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
77 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
78 except formencode.Invalid, errors:
78 except formencode.Invalid, errors:
79 return htmlfill.render(
79 return htmlfill.render(
80 render('admin/users_groups/users_group_add.html'),
80 render('admin/users_groups/users_group_add.html'),
81 defaults=errors.value,
81 defaults=errors.value,
82 errors=errors.error_dict or {},
82 errors=errors.error_dict or {},
83 prefix_error=False,
83 prefix_error=False,
84 encoding="UTF-8")
84 encoding="UTF-8")
85 except Exception:
85 except Exception:
86 log.error(traceback.format_exc())
86 log.error(traceback.format_exc())
87 h.flash(_('error occurred during creation of users group %s') \
87 h.flash(_('error occurred during creation of users group %s') \
88 % request.POST.get('users_group_name'), category='error')
88 % request.POST.get('users_group_name'), category='error')
89
89
90 return redirect(url('users_groups'))
90 return redirect(url('users_groups'))
91
91
92 def new(self, format='html'):
92 def new(self, format='html'):
93 """GET /users_groups/new: Form to create a new item"""
93 """GET /users_groups/new: Form to create a new item"""
94 # url('new_users_group')
94 # url('new_users_group')
95 return render('admin/users_groups/users_group_add.html')
95 return render('admin/users_groups/users_group_add.html')
96
96
97 def update(self, id):
97 def update(self, id):
98 """PUT /users_groups/id: Update an existing item"""
98 """PUT /users_groups/id: Update an existing item"""
99 # Forms posted to this method should contain a hidden field:
99 # Forms posted to this method should contain a hidden field:
100 # <input type="hidden" name="_method" value="PUT" />
100 # <input type="hidden" name="_method" value="PUT" />
101 # Or using helpers:
101 # Or using helpers:
102 # h.form(url('users_group', id=ID),
102 # h.form(url('users_group', id=ID),
103 # method='put')
103 # method='put')
104 # url('users_group', id=ID)
104 # url('users_group', id=ID)
105
105
106
107 users_group_model = UsersGroupModel()
106 users_group_model = UsersGroupModel()
108 c.users_group = users_group_model.get(id)
107 c.users_group = users_group_model.get(id)
109 c.group_members = [(x.user_id, x.user.username) for x in
108 c.group_members = [(x.user_id, x.user.username) for x in
110 c.users_group.members]
109 c.users_group.members]
111
110
112 c.available_members = [(x.user_id, x.username) for x in
111 c.available_members = [(x.user_id, x.username) for x in
113 self.sa.query(User).all()]
112 self.sa.query(User).all()]
114 users_group_form = UsersGroupForm(edit=True,
113 users_group_form = UsersGroupForm(edit=True,
115 old_data=c.users_group.get_dict(),
114 old_data=c.users_group.get_dict(),
116 available_members=[str(x[0]) for x
115 available_members=[str(x[0]) for x
117 in c.available_members])()
116 in c.available_members])()
118
117
119 try:
118 try:
120 form_result = users_group_form.to_python(request.POST)
119 form_result = users_group_form.to_python(request.POST)
121 users_group_model.update(id, form_result)
120 users_group_model.update(id, form_result)
122 h.flash(_('updated users group %s') % form_result['users_group_name'],
121 h.flash(_('updated users group %s') \
122 % form_result['users_group_name'],
123 category='success')
123 category='success')
124 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
124 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
125 except formencode.Invalid, errors:
125 except formencode.Invalid, errors:
126 return htmlfill.render(
126 return htmlfill.render(
127 render('admin/users_groups/users_group_edit.html'),
127 render('admin/users_groups/users_group_edit.html'),
128 defaults=errors.value,
128 defaults=errors.value,
129 errors=errors.error_dict or {},
129 errors=errors.error_dict or {},
130 prefix_error=False,
130 prefix_error=False,
131 encoding="UTF-8")
131 encoding="UTF-8")
132 except Exception:
132 except Exception:
133 log.error(traceback.format_exc())
133 log.error(traceback.format_exc())
134 h.flash(_('error occurred during update of users group %s') \
134 h.flash(_('error occurred during update of users group %s') \
135 % request.POST.get('users_group_name'), category='error')
135 % request.POST.get('users_group_name'), category='error')
136
136
137 return redirect(url('users_groups'))
137 return redirect(url('users_groups'))
138
138
139
140
141 def delete(self, id):
139 def delete(self, id):
142 """DELETE /users_groups/id: Delete an existing item"""
140 """DELETE /users_groups/id: Delete an existing item"""
143 # Forms posted to this method should contain a hidden field:
141 # Forms posted to this method should contain a hidden field:
144 # <input type="hidden" name="_method" value="DELETE" />
142 # <input type="hidden" name="_method" value="DELETE" />
145 # Or using helpers:
143 # Or using helpers:
146 # h.form(url('users_group', id=ID),
144 # h.form(url('users_group', id=ID),
147 # method='delete')
145 # method='delete')
148 # url('users_group', id=ID)
146 # url('users_group', id=ID)
149 users_group_model = UsersGroupModel()
147 users_group_model = UsersGroupModel()
150 try:
148 try:
151 users_group_model.delete(id)
149 users_group_model.delete(id)
152 h.flash(_('successfully deleted users group'), category='success')
150 h.flash(_('successfully deleted users group'), category='success')
153 except Exception:
151 except Exception:
154 h.flash(_('An error occurred during deletion of users group'),
152 h.flash(_('An error occurred during deletion of users group'),
155 category='error')
153 category='error')
156 return redirect(url('users_groups'))
154 return redirect(url('users_groups'))
157
155
158 def show(self, id, format='html'):
156 def show(self, id, format='html'):
159 """GET /users_groups/id: Show a specific item"""
157 """GET /users_groups/id: Show a specific item"""
160 # url('users_group', id=ID)
158 # url('users_group', id=ID)
161
159
162 def edit(self, id, format='html'):
160 def edit(self, id, format='html'):
163 """GET /users_groups/id/edit: Form to edit an existing item"""
161 """GET /users_groups/id/edit: Form to edit an existing item"""
164 # url('edit_users_group', id=ID)
162 # url('edit_users_group', id=ID)
165
163
166 c.users_group = self.sa.query(UsersGroup).get(id)
164 c.users_group = self.sa.query(UsersGroup).get(id)
167 if not c.users_group:
165 if not c.users_group:
168 return redirect(url('users_groups'))
166 return redirect(url('users_groups'))
169
167
170 c.users_group.permissions = {}
168 c.users_group.permissions = {}
171 c.group_members = [(x.user_id, x.user.username) for x in
169 c.group_members = [(x.user_id, x.user.username) for x in
172 c.users_group.members]
170 c.users_group.members]
173 c.available_members = [(x.user_id, x.username) for x in
171 c.available_members = [(x.user_id, x.username) for x in
174 self.sa.query(User).all()]
172 self.sa.query(User).all()]
175 defaults = c.users_group.get_dict()
173 defaults = c.users_group.get_dict()
176
174
177 return htmlfill.render(
175 return htmlfill.render(
178 render('admin/users_groups/users_group_edit.html'),
176 render('admin/users_groups/users_group_edit.html'),
179 defaults=defaults,
177 defaults=defaults,
180 encoding="UTF-8",
178 encoding="UTF-8",
181 force_defaults=False
179 force_defaults=False
182 )
180 )
@@ -1,334 +1,335
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.files
3 rhodecode.controllers.files
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Files controller for RhodeCode
6 Files controller for RhodeCode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import mimetypes
28 import mimetypes
29 import rhodecode.lib.helpers as h
29 import rhodecode.lib.helpers as h
30
30
31 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34
34
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.base import BaseRepoController, render
36 from rhodecode.lib.base import BaseRepoController, render
37 from rhodecode.lib.utils import EmptyChangeset
37 from rhodecode.lib.utils import EmptyChangeset
38 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.repo import RepoModel
39
39
40 from vcs.backends import ARCHIVE_SPECS
40 from vcs.backends import ARCHIVE_SPECS
41 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
41 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
42 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
42 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
43 from vcs.nodes import FileNode, NodeKind
43 from vcs.nodes import FileNode, NodeKind
44 from vcs.utils import diffs as differ
44 from vcs.utils import diffs as differ
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class FilesController(BaseRepoController):
49 class FilesController(BaseRepoController):
50
50
51 @LoginRequired()
51 @LoginRequired()
52 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
52 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
53 'repository.admin')
53 'repository.admin')
54 def __before__(self):
54 def __before__(self):
55 super(FilesController, self).__before__()
55 super(FilesController, self).__before__()
56 c.cut_off_limit = self.cut_off_limit
56 c.cut_off_limit = self.cut_off_limit
57
57
58 def __get_cs_or_redirect(self, rev, repo_name):
58 def __get_cs_or_redirect(self, rev, repo_name):
59 """
59 """
60 Safe way to get changeset if error occur it redirects to tip with
60 Safe way to get changeset if error occur it redirects to tip with
61 proper message
61 proper message
62
62
63 :param rev: revision to fetch
63 :param rev: revision to fetch
64 :param repo_name: repo name to redirect after
64 :param repo_name: repo name to redirect after
65 """
65 """
66
66
67 try:
67 try:
68 return c.rhodecode_repo.get_changeset(rev)
68 return c.rhodecode_repo.get_changeset(rev)
69 except EmptyRepositoryError, e:
69 except EmptyRepositoryError, e:
70 h.flash(_('There are no files yet'), category='warning')
70 h.flash(_('There are no files yet'), category='warning')
71 redirect(h.url('summary_home', repo_name=repo_name))
71 redirect(h.url('summary_home', repo_name=repo_name))
72
72
73 except RepositoryError, e:
73 except RepositoryError, e:
74 h.flash(str(e), category='warning')
74 h.flash(str(e), category='warning')
75 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
75 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
76
76
77 def __get_filenode_or_redirect(self, repo_name, cs, path):
77 def __get_filenode_or_redirect(self, repo_name, cs, path):
78 """
78 """
79 Returns file_node, if error occurs or given path is directory,
79 Returns file_node, if error occurs or given path is directory,
80 it'll redirect to top level path
80 it'll redirect to top level path
81
81
82 :param repo_name: repo_name
82 :param repo_name: repo_name
83 :param cs: given changeset
83 :param cs: given changeset
84 :param path: path to lookup
84 :param path: path to lookup
85 """
85 """
86
86
87 try:
87 try:
88 file_node = cs.get_node(path)
88 file_node = cs.get_node(path)
89 if file_node.is_dir():
89 if file_node.is_dir():
90 raise RepositoryError('given path is a directory')
90 raise RepositoryError('given path is a directory')
91 except RepositoryError, e:
91 except RepositoryError, e:
92 h.flash(str(e), category='warning')
92 h.flash(str(e), category='warning')
93 redirect(h.url('files_home', repo_name=repo_name,
93 redirect(h.url('files_home', repo_name=repo_name,
94 revision=cs.raw_id))
94 revision=cs.raw_id))
95
95
96 return file_node
96 return file_node
97
97
98 def index(self, repo_name, revision, f_path):
98 def index(self, repo_name, revision, f_path):
99 #reditect to given revision from form if given
99 #reditect to given revision from form if given
100 post_revision = request.POST.get('at_rev', None)
100 post_revision = request.POST.get('at_rev', None)
101 if post_revision:
101 if post_revision:
102 cs = self.__get_cs_or_redirect(post_revision, repo_name)
102 cs = self.__get_cs_or_redirect(post_revision, repo_name)
103 redirect(url('files_home', repo_name=c.repo_name,
103 redirect(url('files_home', repo_name=c.repo_name,
104 revision=cs.raw_id, f_path=f_path))
104 revision=cs.raw_id, f_path=f_path))
105
105
106 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
106 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
107 c.branch = request.GET.get('branch', None)
107 c.branch = request.GET.get('branch', None)
108 c.f_path = f_path
108 c.f_path = f_path
109
109
110 cur_rev = c.changeset.revision
110 cur_rev = c.changeset.revision
111
111
112 #prev link
112 #prev link
113 try:
113 try:
114 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
114 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
115 c.url_prev = url('files_home', repo_name=c.repo_name,
115 c.url_prev = url('files_home', repo_name=c.repo_name,
116 revision=prev_rev.raw_id, f_path=f_path)
116 revision=prev_rev.raw_id, f_path=f_path)
117 if c.branch:
117 if c.branch:
118 c.url_prev += '?branch=%s' % c.branch
118 c.url_prev += '?branch=%s' % c.branch
119 except (ChangesetDoesNotExistError, VCSError):
119 except (ChangesetDoesNotExistError, VCSError):
120 c.url_prev = '#'
120 c.url_prev = '#'
121
121
122 #next link
122 #next link
123 try:
123 try:
124 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
124 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
125 c.url_next = url('files_home', repo_name=c.repo_name,
125 c.url_next = url('files_home', repo_name=c.repo_name,
126 revision=next_rev.raw_id, f_path=f_path)
126 revision=next_rev.raw_id, f_path=f_path)
127 if c.branch:
127 if c.branch:
128 c.url_next += '?branch=%s' % c.branch
128 c.url_next += '?branch=%s' % c.branch
129 except (ChangesetDoesNotExistError, VCSError):
129 except (ChangesetDoesNotExistError, VCSError):
130 c.url_next = '#'
130 c.url_next = '#'
131
131
132 #files or dirs
132 #files or dirs
133 try:
133 try:
134 c.files_list = c.changeset.get_node(f_path)
134 c.files_list = c.changeset.get_node(f_path)
135
135
136 if c.files_list.is_file():
136 if c.files_list.is_file():
137 c.file_history = self._get_node_history(c.changeset, f_path)
137 c.file_history = self._get_node_history(c.changeset, f_path)
138 else:
138 else:
139 c.file_history = []
139 c.file_history = []
140 except RepositoryError, e:
140 except RepositoryError, e:
141 h.flash(str(e), category='warning')
141 h.flash(str(e), category='warning')
142 redirect(h.url('files_home', repo_name=repo_name,
142 redirect(h.url('files_home', repo_name=repo_name,
143 revision=revision))
143 revision=revision))
144
144
145 return render('files/files.html')
145 return render('files/files.html')
146
146
147 def rawfile(self, repo_name, revision, f_path):
147 def rawfile(self, repo_name, revision, f_path):
148 cs = self.__get_cs_or_redirect(revision, repo_name)
148 cs = self.__get_cs_or_redirect(revision, repo_name)
149 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
149 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
150
150
151 response.content_disposition = 'attachment; filename=%s' % \
151 response.content_disposition = 'attachment; filename=%s' % \
152 f_path.split(os.sep)[-1].encode('utf8', 'replace')
152 f_path.split(os.sep)[-1].encode('utf8', 'replace')
153
153
154 response.content_type = file_node.mimetype
154 response.content_type = file_node.mimetype
155 return file_node.content
155 return file_node.content
156
156
157 def raw(self, repo_name, revision, f_path):
157 def raw(self, repo_name, revision, f_path):
158 cs = self.__get_cs_or_redirect(revision, repo_name)
158 cs = self.__get_cs_or_redirect(revision, repo_name)
159 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
159 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
160
160
161 raw_mimetype_mapping = {
161 raw_mimetype_mapping = {
162 # map original mimetype to a mimetype used for "show as raw"
162 # map original mimetype to a mimetype used for "show as raw"
163 # you can also provide a content-disposition to override the
163 # you can also provide a content-disposition to override the
164 # default "attachment" disposition.
164 # default "attachment" disposition.
165 # orig_type: (new_type, new_dispo)
165 # orig_type: (new_type, new_dispo)
166
166
167 # show images inline:
167 # show images inline:
168 'image/x-icon': ('image/x-icon', 'inline'),
168 'image/x-icon': ('image/x-icon', 'inline'),
169 'image/png': ('image/png', 'inline'),
169 'image/png': ('image/png', 'inline'),
170 'image/gif': ('image/gif', 'inline'),
170 'image/gif': ('image/gif', 'inline'),
171 'image/jpeg': ('image/jpeg', 'inline'),
171 'image/jpeg': ('image/jpeg', 'inline'),
172 'image/svg+xml': ('image/svg+xml', 'inline'),
172 'image/svg+xml': ('image/svg+xml', 'inline'),
173 }
173 }
174
174
175 mimetype = file_node.mimetype
175 mimetype = file_node.mimetype
176 try:
176 try:
177 mimetype, dispo = raw_mimetype_mapping[mimetype]
177 mimetype, dispo = raw_mimetype_mapping[mimetype]
178 except KeyError:
178 except KeyError:
179 # we don't know anything special about this, handle it safely
179 # we don't know anything special about this, handle it safely
180 if file_node.is_binary:
180 if file_node.is_binary:
181 # do same as download raw for binary files
181 # do same as download raw for binary files
182 mimetype, dispo = 'application/octet-stream', 'attachment'
182 mimetype, dispo = 'application/octet-stream', 'attachment'
183 else:
183 else:
184 # do not just use the original mimetype, but force text/plain,
184 # do not just use the original mimetype, but force text/plain,
185 # otherwise it would serve text/html and that might be unsafe.
185 # otherwise it would serve text/html and that might be unsafe.
186 # Note: underlying vcs library fakes text/plain mimetype if the
186 # Note: underlying vcs library fakes text/plain mimetype if the
187 # mimetype can not be determined and it thinks it is not binary.
187 # mimetype can not be determined and it thinks it is not
188 # This might lead to erroneous text display in some cases, but
188 # binary.This might lead to erroneous text display in some
189 # helps in other cases, like with text files without extension.
189 # cases, but helps in other cases, like with text files
190 # without extension.
190 mimetype, dispo = 'text/plain', 'inline'
191 mimetype, dispo = 'text/plain', 'inline'
191
192
192 if dispo == 'attachment':
193 if dispo == 'attachment':
193 dispo = 'attachment; filename=%s' % \
194 dispo = 'attachment; filename=%s' % \
194 f_path.split(os.sep)[-1].encode('utf8', 'replace')
195 f_path.split(os.sep)[-1].encode('utf8', 'replace')
195
196
196 response.content_disposition = dispo
197 response.content_disposition = dispo
197 response.content_type = mimetype
198 response.content_type = mimetype
198 return file_node.content
199 return file_node.content
199
200
200 def annotate(self, repo_name, revision, f_path):
201 def annotate(self, repo_name, revision, f_path):
201 c.cs = self.__get_cs_or_redirect(revision, repo_name)
202 c.cs = self.__get_cs_or_redirect(revision, repo_name)
202 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
203 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
203
204
204 c.file_history = self._get_node_history(c.cs, f_path)
205 c.file_history = self._get_node_history(c.cs, f_path)
205 c.f_path = f_path
206 c.f_path = f_path
206 return render('files/files_annotate.html')
207 return render('files/files_annotate.html')
207
208
208 def archivefile(self, repo_name, fname):
209 def archivefile(self, repo_name, fname):
209
210
210 fileformat = None
211 fileformat = None
211 revision = None
212 revision = None
212 ext = None
213 ext = None
213
214
214 for a_type, ext_data in ARCHIVE_SPECS.items():
215 for a_type, ext_data in ARCHIVE_SPECS.items():
215 archive_spec = fname.split(ext_data[1])
216 archive_spec = fname.split(ext_data[1])
216 if len(archive_spec) == 2 and archive_spec[1] == '':
217 if len(archive_spec) == 2 and archive_spec[1] == '':
217 fileformat = a_type or ext_data[1]
218 fileformat = a_type or ext_data[1]
218 revision = archive_spec[0]
219 revision = archive_spec[0]
219 ext = ext_data[1]
220 ext = ext_data[1]
220
221
221 try:
222 try:
222 dbrepo = RepoModel().get_by_repo_name(repo_name)
223 dbrepo = RepoModel().get_by_repo_name(repo_name)
223 if dbrepo.enable_downloads is False:
224 if dbrepo.enable_downloads is False:
224 return _('downloads disabled')
225 return _('downloads disabled')
225
226
226 cs = c.rhodecode_repo.get_changeset(revision)
227 cs = c.rhodecode_repo.get_changeset(revision)
227 content_type = ARCHIVE_SPECS[fileformat][0]
228 content_type = ARCHIVE_SPECS[fileformat][0]
228 except ChangesetDoesNotExistError:
229 except ChangesetDoesNotExistError:
229 return _('Unknown revision %s') % revision
230 return _('Unknown revision %s') % revision
230 except EmptyRepositoryError:
231 except EmptyRepositoryError:
231 return _('Empty repository')
232 return _('Empty repository')
232 except (ImproperArchiveTypeError, KeyError):
233 except (ImproperArchiveTypeError, KeyError):
233 return _('Unknown archive type')
234 return _('Unknown archive type')
234
235
235 response.content_type = content_type
236 response.content_type = content_type
236 response.content_disposition = 'attachment; filename=%s-%s%s' \
237 response.content_disposition = 'attachment; filename=%s-%s%s' \
237 % (repo_name, revision, ext)
238 % (repo_name, revision, ext)
238
239
239 return cs.get_chunked_archive(stream=None, kind=fileformat)
240 return cs.get_chunked_archive(stream=None, kind=fileformat)
240
241
241 def diff(self, repo_name, f_path):
242 def diff(self, repo_name, f_path):
242 diff1 = request.GET.get('diff1')
243 diff1 = request.GET.get('diff1')
243 diff2 = request.GET.get('diff2')
244 diff2 = request.GET.get('diff2')
244 c.action = request.GET.get('diff')
245 c.action = request.GET.get('diff')
245 c.no_changes = diff1 == diff2
246 c.no_changes = diff1 == diff2
246 c.f_path = f_path
247 c.f_path = f_path
247
248
248 try:
249 try:
249 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
250 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
250 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
251 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
251 node1 = c.changeset_1.get_node(f_path)
252 node1 = c.changeset_1.get_node(f_path)
252 else:
253 else:
253 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
254 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
254 node1 = FileNode('.', '', changeset=c.changeset_1)
255 node1 = FileNode('.', '', changeset=c.changeset_1)
255
256
256 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
257 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
257 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
258 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
258 node2 = c.changeset_2.get_node(f_path)
259 node2 = c.changeset_2.get_node(f_path)
259 else:
260 else:
260 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
261 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
261 node2 = FileNode('.', '', changeset=c.changeset_2)
262 node2 = FileNode('.', '', changeset=c.changeset_2)
262 except RepositoryError:
263 except RepositoryError:
263 return redirect(url('files_home',
264 return redirect(url('files_home',
264 repo_name=c.repo_name, f_path=f_path))
265 repo_name=c.repo_name, f_path=f_path))
265
266
266 if c.action == 'download':
267 if c.action == 'download':
267 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
268 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
268 format='gitdiff')
269 format='gitdiff')
269
270
270 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
271 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
271 response.content_type = 'text/plain'
272 response.content_type = 'text/plain'
272 response.content_disposition = 'attachment; filename=%s' \
273 response.content_disposition = 'attachment; filename=%s' \
273 % diff_name
274 % diff_name
274 return diff.raw_diff()
275 return diff.raw_diff()
275
276
276 elif c.action == 'raw':
277 elif c.action == 'raw':
277 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
278 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
278 format='gitdiff')
279 format='gitdiff')
279 response.content_type = 'text/plain'
280 response.content_type = 'text/plain'
280 return diff.raw_diff()
281 return diff.raw_diff()
281
282
282 elif c.action == 'diff':
283 elif c.action == 'diff':
283
284
284 if node1.is_binary or node2.is_binary:
285 if node1.is_binary or node2.is_binary:
285 c.cur_diff = _('Binary file')
286 c.cur_diff = _('Binary file')
286 elif node1.size > self.cut_off_limit or \
287 elif node1.size > self.cut_off_limit or \
287 node2.size > self.cut_off_limit:
288 node2.size > self.cut_off_limit:
288 c.cur_diff = _('Diff is too big to display')
289 c.cur_diff = _('Diff is too big to display')
289 else:
290 else:
290 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
291 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
291 format='gitdiff')
292 format='gitdiff')
292 c.cur_diff = diff.as_html()
293 c.cur_diff = diff.as_html()
293 else:
294 else:
294
295
295 #default option
296 #default option
296 if node1.is_binary or node2.is_binary:
297 if node1.is_binary or node2.is_binary:
297 c.cur_diff = _('Binary file')
298 c.cur_diff = _('Binary file')
298 elif node1.size > self.cut_off_limit or \
299 elif node1.size > self.cut_off_limit or \
299 node2.size > self.cut_off_limit:
300 node2.size > self.cut_off_limit:
300 c.cur_diff = _('Diff is too big to display')
301 c.cur_diff = _('Diff is too big to display')
301 else:
302 else:
302 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
303 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
303 format='gitdiff')
304 format='gitdiff')
304 c.cur_diff = diff.as_html()
305 c.cur_diff = diff.as_html()
305
306
306 if not c.cur_diff:
307 if not c.cur_diff:
307 c.no_changes = True
308 c.no_changes = True
308 return render('files/file_diff.html')
309 return render('files/file_diff.html')
309
310
310 def _get_node_history(self, cs, f_path):
311 def _get_node_history(self, cs, f_path):
311 changesets = cs.get_file_history(f_path)
312 changesets = cs.get_file_history(f_path)
312 hist_l = []
313 hist_l = []
313
314
314 changesets_group = ([], _("Changesets"))
315 changesets_group = ([], _("Changesets"))
315 branches_group = ([], _("Branches"))
316 branches_group = ([], _("Branches"))
316 tags_group = ([], _("Tags"))
317 tags_group = ([], _("Tags"))
317
318
318 for chs in changesets:
319 for chs in changesets:
319 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
320 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
320 changesets_group[0].append((chs.raw_id, n_desc,))
321 changesets_group[0].append((chs.raw_id, n_desc,))
321
322
322 hist_l.append(changesets_group)
323 hist_l.append(changesets_group)
323
324
324 for name, chs in c.rhodecode_repo.branches.items():
325 for name, chs in c.rhodecode_repo.branches.items():
325 #chs = chs.split(':')[-1]
326 #chs = chs.split(':')[-1]
326 branches_group[0].append((chs, name),)
327 branches_group[0].append((chs, name),)
327 hist_l.append(branches_group)
328 hist_l.append(branches_group)
328
329
329 for name, chs in c.rhodecode_repo.tags.items():
330 for name, chs in c.rhodecode_repo.tags.items():
330 #chs = chs.split(':')[-1]
331 #chs = chs.split(':')[-1]
331 tags_group[0].append((chs, name),)
332 tags_group[0].append((chs, name),)
332 hist_l.append(tags_group)
333 hist_l.append(tags_group)
333
334
334 return hist_l
335 return hist_l
General Comments 0
You need to be logged in to leave comments. Login now