Show More
@@ -1,140 +1,139 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.auth_settings |
|
15 | kallithea.controllers.admin.auth_settings | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | pluggable authentication controller for Kallithea |
|
18 | pluggable authentication controller for Kallithea | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Nov 26, 2010 |
|
22 | :created_on: Nov 26, 2010 | |
23 | :author: akesterson |
|
23 | :author: akesterson | |
24 | """ |
|
24 | """ | |
25 |
|
25 | |||
26 | import pprint |
|
26 | import pprint | |
27 | import logging |
|
27 | import logging | |
28 | import formencode.htmlfill |
|
28 | import formencode.htmlfill | |
29 | import traceback |
|
29 | import traceback | |
30 |
|
30 | |||
31 | from pylons import request, tmpl_context as c, url |
|
31 | from pylons import request, tmpl_context as c, url | |
32 | from pylons.controllers.util import redirect |
|
32 | from pylons.controllers.util import redirect | |
33 | from pylons.i18n.translation import _ |
|
33 | from pylons.i18n.translation import _ | |
34 |
|
34 | |||
35 | from kallithea.lib import helpers as h |
|
35 | from kallithea.lib import helpers as h | |
36 | from kallithea.lib.compat import formatted_json |
|
36 | from kallithea.lib.compat import formatted_json | |
37 | from kallithea.lib.base import BaseController, render |
|
37 | from kallithea.lib.base import BaseController, render | |
38 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator |
|
38 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator | |
39 | from kallithea.lib import auth_modules |
|
39 | from kallithea.lib import auth_modules | |
40 | from kallithea.model.forms import AuthSettingsForm |
|
40 | from kallithea.model.forms import AuthSettingsForm | |
41 | from kallithea.model.db import Setting |
|
41 | from kallithea.model.db import Setting | |
42 | from kallithea.model.meta import Session |
|
42 | from kallithea.model.meta import Session | |
43 |
|
43 | |||
44 | log = logging.getLogger(__name__) |
|
44 | log = logging.getLogger(__name__) | |
45 |
|
45 | |||
46 |
|
46 | |||
47 | class AuthSettingsController(BaseController): |
|
47 | class AuthSettingsController(BaseController): | |
48 |
|
48 | |||
49 | @LoginRequired() |
|
49 | @LoginRequired() | |
50 | @HasPermissionAllDecorator('hg.admin') |
|
50 | @HasPermissionAllDecorator('hg.admin') | |
51 | def __before__(self): |
|
51 | def __before__(self): | |
52 | super(AuthSettingsController, self).__before__() |
|
52 | super(AuthSettingsController, self).__before__() | |
53 |
|
53 | |||
54 | def __load_defaults(self): |
|
54 | def __load_defaults(self): | |
55 | c.available_plugins = [ |
|
55 | c.available_plugins = [ | |
56 | 'kallithea.lib.auth_modules.auth_internal', |
|
56 | 'kallithea.lib.auth_modules.auth_internal', | |
57 | 'kallithea.lib.auth_modules.auth_container', |
|
57 | 'kallithea.lib.auth_modules.auth_container', | |
58 | 'kallithea.lib.auth_modules.auth_ldap', |
|
58 | 'kallithea.lib.auth_modules.auth_ldap', | |
59 | 'kallithea.lib.auth_modules.auth_crowd', |
|
59 | 'kallithea.lib.auth_modules.auth_crowd', | |
60 | 'kallithea.lib.auth_modules.auth_pam' |
|
60 | 'kallithea.lib.auth_modules.auth_pam' | |
61 | ] |
|
61 | ] | |
62 | c.enabled_plugins = Setting.get_auth_plugins() |
|
62 | c.enabled_plugins = Setting.get_auth_plugins() | |
63 |
|
63 | |||
64 | def index(self, defaults=None, errors=None, prefix_error=False): |
|
64 | def index(self, defaults=None, errors=None, prefix_error=False): | |
65 | self.__load_defaults() |
|
65 | self.__load_defaults() | |
66 | _defaults = {} |
|
66 | _defaults = {} | |
67 | # default plugins loaded |
|
67 | # default plugins loaded | |
68 | formglobals = { |
|
68 | formglobals = { | |
69 | "auth_plugins": ["kallithea.lib.auth_modules.auth_internal"] |
|
69 | "auth_plugins": ["kallithea.lib.auth_modules.auth_internal"] | |
70 | } |
|
70 | } | |
71 | formglobals.update(Setting.get_auth_settings()) |
|
71 | formglobals.update(Setting.get_auth_settings()) | |
72 | formglobals["plugin_settings"] = {} |
|
72 | formglobals["plugin_settings"] = {} | |
73 | formglobals["auth_plugins_shortnames"] = {} |
|
73 | formglobals["auth_plugins_shortnames"] = {} | |
74 | _defaults["auth_plugins"] = formglobals["auth_plugins"] |
|
74 | _defaults["auth_plugins"] = formglobals["auth_plugins"] | |
75 |
|
75 | |||
76 | for module in formglobals["auth_plugins"]: |
|
76 | for module in formglobals["auth_plugins"]: | |
77 | plugin = auth_modules.loadplugin(module) |
|
77 | plugin = auth_modules.loadplugin(module) | |
78 | plugin_name = plugin.name |
|
78 | plugin_name = plugin.name | |
79 | formglobals["auth_plugins_shortnames"][module] = plugin_name |
|
79 | formglobals["auth_plugins_shortnames"][module] = plugin_name | |
80 | formglobals["plugin_settings"][module] = plugin.plugin_settings() |
|
80 | formglobals["plugin_settings"][module] = plugin.plugin_settings() | |
81 | for v in formglobals["plugin_settings"][module]: |
|
81 | for v in formglobals["plugin_settings"][module]: | |
82 | fullname = ("auth_" + plugin_name + "_" + v["name"]) |
|
82 | fullname = ("auth_" + plugin_name + "_" + v["name"]) | |
83 | if "default" in v: |
|
83 | if "default" in v: | |
84 | _defaults[fullname] = v["default"] |
|
84 | _defaults[fullname] = v["default"] | |
85 | # Current values will be the default on the form, if there are any |
|
85 | # Current values will be the default on the form, if there are any | |
86 | setting = Setting.get_by_name(fullname) |
|
86 | setting = Setting.get_by_name(fullname) | |
87 | if setting: |
|
87 | if setting: | |
88 | _defaults[fullname] = setting.app_settings_value |
|
88 | _defaults[fullname] = setting.app_settings_value | |
89 | # we want to show , separated list of enabled plugins |
|
89 | # we want to show , separated list of enabled plugins | |
90 | _defaults['auth_plugins'] = ','.join(_defaults['auth_plugins']) |
|
90 | _defaults['auth_plugins'] = ','.join(_defaults['auth_plugins']) | |
91 | if defaults: |
|
91 | if defaults: | |
92 | _defaults.update(defaults) |
|
92 | _defaults.update(defaults) | |
93 |
|
93 | |||
94 | formglobals["defaults"] = _defaults |
|
94 | formglobals["defaults"] = _defaults | |
95 | # set template context variables |
|
95 | # set template context variables | |
96 | for k, v in formglobals.iteritems(): |
|
96 | for k, v in formglobals.iteritems(): | |
97 | setattr(c, k, v) |
|
97 | setattr(c, k, v) | |
98 |
|
98 | |||
99 | log.debug(pprint.pformat(formglobals, indent=4)) |
|
99 | log.debug(pprint.pformat(formglobals, indent=4)) | |
100 | log.debug(formatted_json(defaults)) |
|
100 | log.debug(formatted_json(defaults)) | |
101 | return formencode.htmlfill.render( |
|
101 | return formencode.htmlfill.render( | |
102 | render('admin/auth/auth_settings.html'), |
|
102 | render('admin/auth/auth_settings.html'), | |
103 | defaults=_defaults, |
|
103 | defaults=_defaults, | |
104 | errors=errors, |
|
104 | errors=errors, | |
105 | prefix_error=prefix_error, |
|
105 | prefix_error=prefix_error, | |
106 | encoding="UTF-8", |
|
106 | encoding="UTF-8", | |
107 |
force_defaults= |
|
107 | force_defaults=False) | |
108 | ) |
|
|||
109 |
|
108 | |||
110 | def auth_settings(self): |
|
109 | def auth_settings(self): | |
111 | """POST create and store auth settings""" |
|
110 | """POST create and store auth settings""" | |
112 | self.__load_defaults() |
|
111 | self.__load_defaults() | |
113 | _form = AuthSettingsForm(c.enabled_plugins)() |
|
112 | _form = AuthSettingsForm(c.enabled_plugins)() | |
114 | log.debug("POST Result: %s" % formatted_json(dict(request.POST))) |
|
113 | log.debug("POST Result: %s" % formatted_json(dict(request.POST))) | |
115 |
|
114 | |||
116 | try: |
|
115 | try: | |
117 | form_result = _form.to_python(dict(request.POST)) |
|
116 | form_result = _form.to_python(dict(request.POST)) | |
118 | for k, v in form_result.items(): |
|
117 | for k, v in form_result.items(): | |
119 | if k == 'auth_plugins': |
|
118 | if k == 'auth_plugins': | |
120 | # we want to store it comma separated inside our settings |
|
119 | # we want to store it comma separated inside our settings | |
121 | v = ','.join(v) |
|
120 | v = ','.join(v) | |
122 | log.debug("%s = %s" % (k, str(v))) |
|
121 | log.debug("%s = %s" % (k, str(v))) | |
123 | setting = Setting.create_or_update(k, v) |
|
122 | setting = Setting.create_or_update(k, v) | |
124 | Session().add(setting) |
|
123 | Session().add(setting) | |
125 | Session().commit() |
|
124 | Session().commit() | |
126 | h.flash(_('Auth settings updated successfully'), |
|
125 | h.flash(_('Auth settings updated successfully'), | |
127 | category='success') |
|
126 | category='success') | |
128 | except formencode.Invalid, errors: |
|
127 | except formencode.Invalid, errors: | |
129 | log.error(traceback.format_exc()) |
|
128 | log.error(traceback.format_exc()) | |
130 | e = errors.error_dict or {} |
|
129 | e = errors.error_dict or {} | |
131 | return self.index( |
|
130 | return self.index( | |
132 | defaults=errors.value, |
|
131 | defaults=errors.value, | |
133 | errors=e, |
|
132 | errors=e, | |
134 | prefix_error=False) |
|
133 | prefix_error=False) | |
135 | except Exception: |
|
134 | except Exception: | |
136 | log.error(traceback.format_exc()) |
|
135 | log.error(traceback.format_exc()) | |
137 | h.flash(_('error occurred during update of auth settings'), |
|
136 | h.flash(_('error occurred during update of auth settings'), | |
138 | category='error') |
|
137 | category='error') | |
139 |
|
138 | |||
140 | return redirect(url('auth_home')) |
|
139 | return redirect(url('auth_home')) |
@@ -1,131 +1,132 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.defaults |
|
15 | kallithea.controllers.admin.defaults | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | default settings controller for Kallithea |
|
18 | default settings controller for Kallithea | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Apr 27, 2010 |
|
22 | :created_on: Apr 27, 2010 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 | import logging |
|
28 | import logging | |
29 | import traceback |
|
29 | import traceback | |
30 | import formencode |
|
30 | import formencode | |
31 | from formencode import htmlfill |
|
31 | from formencode import htmlfill | |
32 |
|
32 | |||
33 | from pylons import request, tmpl_context as c, url |
|
33 | from pylons import request, tmpl_context as c, url | |
34 | from pylons.controllers.util import redirect |
|
34 | from pylons.controllers.util import redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 |
|
36 | |||
37 | from kallithea.lib import helpers as h |
|
37 | from kallithea.lib import helpers as h | |
38 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator |
|
38 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator | |
39 | from kallithea.lib.base import BaseController, render |
|
39 | from kallithea.lib.base import BaseController, render | |
40 | from kallithea.model.forms import DefaultsForm |
|
40 | from kallithea.model.forms import DefaultsForm | |
41 | from kallithea.model.meta import Session |
|
41 | from kallithea.model.meta import Session | |
42 | from kallithea import BACKENDS |
|
42 | from kallithea import BACKENDS | |
43 | from kallithea.model.db import Setting |
|
43 | from kallithea.model.db import Setting | |
44 |
|
44 | |||
45 | log = logging.getLogger(__name__) |
|
45 | log = logging.getLogger(__name__) | |
46 |
|
46 | |||
47 |
|
47 | |||
48 | class DefaultsController(BaseController): |
|
48 | class DefaultsController(BaseController): | |
49 | """REST Controller styled on the Atom Publishing Protocol""" |
|
49 | """REST Controller styled on the Atom Publishing Protocol""" | |
50 | # To properly map this controller, ensure your config/routing.py |
|
50 | # To properly map this controller, ensure your config/routing.py | |
51 | # file has a resource setup: |
|
51 | # file has a resource setup: | |
52 | # map.resource('default', 'defaults') |
|
52 | # map.resource('default', 'defaults') | |
53 |
|
53 | |||
54 | @LoginRequired() |
|
54 | @LoginRequired() | |
55 | @HasPermissionAllDecorator('hg.admin') |
|
55 | @HasPermissionAllDecorator('hg.admin') | |
56 | def __before__(self): |
|
56 | def __before__(self): | |
57 | super(DefaultsController, self).__before__() |
|
57 | super(DefaultsController, self).__before__() | |
58 |
|
58 | |||
59 | def index(self, format='html'): |
|
59 | def index(self, format='html'): | |
60 | """GET /defaults: All items in the collection""" |
|
60 | """GET /defaults: All items in the collection""" | |
61 | # url('defaults') |
|
61 | # url('defaults') | |
62 | c.backends = BACKENDS.keys() |
|
62 | c.backends = BACKENDS.keys() | |
63 | defaults = Setting.get_default_repo_settings() |
|
63 | defaults = Setting.get_default_repo_settings() | |
64 |
|
64 | |||
65 | return htmlfill.render( |
|
65 | return htmlfill.render( | |
66 | render('admin/defaults/defaults.html'), |
|
66 | render('admin/defaults/defaults.html'), | |
67 | defaults=defaults, |
|
67 | defaults=defaults, | |
68 | encoding="UTF-8", |
|
68 | encoding="UTF-8", | |
69 | force_defaults=False |
|
69 | force_defaults=False | |
70 | ) |
|
70 | ) | |
71 |
|
71 | |||
72 | def create(self): |
|
72 | def create(self): | |
73 | """POST /defaults: Create a new item""" |
|
73 | """POST /defaults: Create a new item""" | |
74 | # url('defaults') |
|
74 | # url('defaults') | |
75 |
|
75 | |||
76 | def new(self, format='html'): |
|
76 | def new(self, format='html'): | |
77 | """GET /defaults/new: Form to create a new item""" |
|
77 | """GET /defaults/new: Form to create a new item""" | |
78 | # url('new_default') |
|
78 | # url('new_default') | |
79 |
|
79 | |||
80 | def update(self, id): |
|
80 | def update(self, id): | |
81 | """PUT /defaults/id: Update an existing item""" |
|
81 | """PUT /defaults/id: Update an existing item""" | |
82 | # Forms posted to this method should contain a hidden field: |
|
82 | # Forms posted to this method should contain a hidden field: | |
83 | # <input type="hidden" name="_method" value="PUT" /> |
|
83 | # <input type="hidden" name="_method" value="PUT" /> | |
84 | # Or using helpers: |
|
84 | # Or using helpers: | |
85 | # h.form(url('default', id=ID), |
|
85 | # h.form(url('default', id=ID), | |
86 | # method='put') |
|
86 | # method='put') | |
87 | # url('default', id=ID) |
|
87 | # url('default', id=ID) | |
88 |
|
88 | |||
89 | _form = DefaultsForm()() |
|
89 | _form = DefaultsForm()() | |
90 |
|
90 | |||
91 | try: |
|
91 | try: | |
92 | form_result = _form.to_python(dict(request.POST)) |
|
92 | form_result = _form.to_python(dict(request.POST)) | |
93 | for k, v in form_result.iteritems(): |
|
93 | for k, v in form_result.iteritems(): | |
94 | setting = Setting.create_or_update(k, v) |
|
94 | setting = Setting.create_or_update(k, v) | |
95 | Session().add(setting) |
|
95 | Session().add(setting) | |
96 | Session().commit() |
|
96 | Session().commit() | |
97 | h.flash(_('Default settings updated successfully'), |
|
97 | h.flash(_('Default settings updated successfully'), | |
98 | category='success') |
|
98 | category='success') | |
99 |
|
99 | |||
100 | except formencode.Invalid, errors: |
|
100 | except formencode.Invalid, errors: | |
101 | defaults = errors.value |
|
101 | defaults = errors.value | |
102 |
|
102 | |||
103 | return htmlfill.render( |
|
103 | return htmlfill.render( | |
104 | render('admin/defaults/defaults.html'), |
|
104 | render('admin/defaults/defaults.html'), | |
105 | defaults=defaults, |
|
105 | defaults=defaults, | |
106 | errors=errors.error_dict or {}, |
|
106 | errors=errors.error_dict or {}, | |
107 | prefix_error=False, |
|
107 | prefix_error=False, | |
108 |
encoding="UTF-8" |
|
108 | encoding="UTF-8", | |
|
109 | force_defaults=False) | |||
109 | except Exception: |
|
110 | except Exception: | |
110 | log.error(traceback.format_exc()) |
|
111 | log.error(traceback.format_exc()) | |
111 | h.flash(_('Error occurred during update of defaults'), |
|
112 | h.flash(_('Error occurred during update of defaults'), | |
112 | category='error') |
|
113 | category='error') | |
113 |
|
114 | |||
114 | return redirect(url('defaults')) |
|
115 | return redirect(url('defaults')) | |
115 |
|
116 | |||
116 | def delete(self, id): |
|
117 | def delete(self, id): | |
117 | """DELETE /defaults/id: Delete an existing item""" |
|
118 | """DELETE /defaults/id: Delete an existing item""" | |
118 | # Forms posted to this method should contain a hidden field: |
|
119 | # Forms posted to this method should contain a hidden field: | |
119 | # <input type="hidden" name="_method" value="DELETE" /> |
|
120 | # <input type="hidden" name="_method" value="DELETE" /> | |
120 | # Or using helpers: |
|
121 | # Or using helpers: | |
121 | # h.form(url('default', id=ID), |
|
122 | # h.form(url('default', id=ID), | |
122 | # method='delete') |
|
123 | # method='delete') | |
123 | # url('default', id=ID) |
|
124 | # url('default', id=ID) | |
124 |
|
125 | |||
125 | def show(self, id, format='html'): |
|
126 | def show(self, id, format='html'): | |
126 | """GET /defaults/id: Show a specific item""" |
|
127 | """GET /defaults/id: Show a specific item""" | |
127 | # url('default', id=ID) |
|
128 | # url('default', id=ID) | |
128 |
|
129 | |||
129 | def edit(self, id, format='html'): |
|
130 | def edit(self, id, format='html'): | |
130 | """GET /defaults/id/edit: Form to edit an existing item""" |
|
131 | """GET /defaults/id/edit: Form to edit an existing item""" | |
131 | # url('edit_default', id=ID) |
|
132 | # url('edit_default', id=ID) |
@@ -1,293 +1,293 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.gist |
|
15 | kallithea.controllers.admin.gist | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | gist controller for Kallithea |
|
18 | gist controller for Kallithea | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: May 9, 2013 |
|
22 | :created_on: May 9, 2013 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 | import time |
|
28 | import time | |
29 | import logging |
|
29 | import logging | |
30 | import traceback |
|
30 | import traceback | |
31 | import formencode.htmlfill |
|
31 | import formencode.htmlfill | |
32 |
|
32 | |||
33 | from pylons import request, response, tmpl_context as c, url |
|
33 | from pylons import request, response, tmpl_context as c, url | |
34 | from pylons.controllers.util import redirect |
|
34 | from pylons.controllers.util import redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 |
|
36 | |||
37 | from kallithea.model.forms import GistForm |
|
37 | from kallithea.model.forms import GistForm | |
38 | from kallithea.model.gist import GistModel |
|
38 | from kallithea.model.gist import GistModel | |
39 | from kallithea.model.meta import Session |
|
39 | from kallithea.model.meta import Session | |
40 | from kallithea.model.db import Gist, User |
|
40 | from kallithea.model.db import Gist, User | |
41 | from kallithea.lib import helpers as h |
|
41 | from kallithea.lib import helpers as h | |
42 | from kallithea.lib.base import BaseController, render |
|
42 | from kallithea.lib.base import BaseController, render | |
43 | from kallithea.lib.auth import LoginRequired, NotAnonymous |
|
43 | from kallithea.lib.auth import LoginRequired, NotAnonymous | |
44 | from kallithea.lib.utils import jsonify |
|
44 | from kallithea.lib.utils import jsonify | |
45 | from kallithea.lib.utils2 import safe_int, time_to_datetime |
|
45 | from kallithea.lib.utils2 import safe_int, time_to_datetime | |
46 | from kallithea.lib.helpers import Page |
|
46 | from kallithea.lib.helpers import Page | |
47 | from webob.exc import HTTPNotFound, HTTPForbidden |
|
47 | from webob.exc import HTTPNotFound, HTTPForbidden | |
48 | from sqlalchemy.sql.expression import or_ |
|
48 | from sqlalchemy.sql.expression import or_ | |
49 | from kallithea.lib.vcs.exceptions import VCSError, NodeNotChangedError |
|
49 | from kallithea.lib.vcs.exceptions import VCSError, NodeNotChangedError | |
50 |
|
50 | |||
51 | log = logging.getLogger(__name__) |
|
51 | log = logging.getLogger(__name__) | |
52 |
|
52 | |||
53 |
|
53 | |||
54 | class GistsController(BaseController): |
|
54 | class GistsController(BaseController): | |
55 | """REST Controller styled on the Atom Publishing Protocol""" |
|
55 | """REST Controller styled on the Atom Publishing Protocol""" | |
56 |
|
56 | |||
57 | def __load_defaults(self, extra_values=None): |
|
57 | def __load_defaults(self, extra_values=None): | |
58 | c.lifetime_values = [ |
|
58 | c.lifetime_values = [ | |
59 | (str(-1), _('forever')), |
|
59 | (str(-1), _('forever')), | |
60 | (str(5), _('5 minutes')), |
|
60 | (str(5), _('5 minutes')), | |
61 | (str(60), _('1 hour')), |
|
61 | (str(60), _('1 hour')), | |
62 | (str(60 * 24), _('1 day')), |
|
62 | (str(60 * 24), _('1 day')), | |
63 | (str(60 * 24 * 30), _('1 month')), |
|
63 | (str(60 * 24 * 30), _('1 month')), | |
64 | ] |
|
64 | ] | |
65 | if extra_values: |
|
65 | if extra_values: | |
66 | c.lifetime_values.append(extra_values) |
|
66 | c.lifetime_values.append(extra_values) | |
67 | c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] |
|
67 | c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] | |
68 |
|
68 | |||
69 | @LoginRequired() |
|
69 | @LoginRequired() | |
70 | def index(self): |
|
70 | def index(self): | |
71 | """GET /admin/gists: All items in the collection""" |
|
71 | """GET /admin/gists: All items in the collection""" | |
72 | # url('gists') |
|
72 | # url('gists') | |
73 | not_default_user = c.authuser.username != User.DEFAULT_USER |
|
73 | not_default_user = c.authuser.username != User.DEFAULT_USER | |
74 | c.show_private = request.GET.get('private') and not_default_user |
|
74 | c.show_private = request.GET.get('private') and not_default_user | |
75 | c.show_public = request.GET.get('public') and not_default_user |
|
75 | c.show_public = request.GET.get('public') and not_default_user | |
76 |
|
76 | |||
77 | gists = Gist().query()\ |
|
77 | gists = Gist().query()\ | |
78 | .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\ |
|
78 | .filter(or_(Gist.gist_expires == -1, Gist.gist_expires >= time.time()))\ | |
79 | .order_by(Gist.created_on.desc()) |
|
79 | .order_by(Gist.created_on.desc()) | |
80 |
|
80 | |||
81 | # MY private |
|
81 | # MY private | |
82 | if c.show_private and not c.show_public: |
|
82 | if c.show_private and not c.show_public: | |
83 | gists = gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\ |
|
83 | gists = gists.filter(Gist.gist_type == Gist.GIST_PRIVATE)\ | |
84 | .filter(Gist.gist_owner == c.authuser.user_id) |
|
84 | .filter(Gist.gist_owner == c.authuser.user_id) | |
85 | # MY public |
|
85 | # MY public | |
86 | elif c.show_public and not c.show_private: |
|
86 | elif c.show_public and not c.show_private: | |
87 | gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\ |
|
87 | gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC)\ | |
88 | .filter(Gist.gist_owner == c.authuser.user_id) |
|
88 | .filter(Gist.gist_owner == c.authuser.user_id) | |
89 |
|
89 | |||
90 | # MY public+private |
|
90 | # MY public+private | |
91 | elif c.show_private and c.show_public: |
|
91 | elif c.show_private and c.show_public: | |
92 | gists = gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC, |
|
92 | gists = gists.filter(or_(Gist.gist_type == Gist.GIST_PUBLIC, | |
93 | Gist.gist_type == Gist.GIST_PRIVATE))\ |
|
93 | Gist.gist_type == Gist.GIST_PRIVATE))\ | |
94 | .filter(Gist.gist_owner == c.authuser.user_id) |
|
94 | .filter(Gist.gist_owner == c.authuser.user_id) | |
95 |
|
95 | |||
96 | # default show ALL public gists |
|
96 | # default show ALL public gists | |
97 | if not c.show_public and not c.show_private: |
|
97 | if not c.show_public and not c.show_private: | |
98 | gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC) |
|
98 | gists = gists.filter(Gist.gist_type == Gist.GIST_PUBLIC) | |
99 |
|
99 | |||
100 | c.gists = gists |
|
100 | c.gists = gists | |
101 | p = safe_int(request.GET.get('page', 1), 1) |
|
101 | p = safe_int(request.GET.get('page', 1), 1) | |
102 | c.gists_pager = Page(c.gists, page=p, items_per_page=10) |
|
102 | c.gists_pager = Page(c.gists, page=p, items_per_page=10) | |
103 | return render('admin/gists/index.html') |
|
103 | return render('admin/gists/index.html') | |
104 |
|
104 | |||
105 | @LoginRequired() |
|
105 | @LoginRequired() | |
106 | @NotAnonymous() |
|
106 | @NotAnonymous() | |
107 | def create(self): |
|
107 | def create(self): | |
108 | """POST /admin/gists: Create a new item""" |
|
108 | """POST /admin/gists: Create a new item""" | |
109 | # url('gists') |
|
109 | # url('gists') | |
110 | self.__load_defaults() |
|
110 | self.__load_defaults() | |
111 | gist_form = GistForm([x[0] for x in c.lifetime_values])() |
|
111 | gist_form = GistForm([x[0] for x in c.lifetime_values])() | |
112 | try: |
|
112 | try: | |
113 | form_result = gist_form.to_python(dict(request.POST)) |
|
113 | form_result = gist_form.to_python(dict(request.POST)) | |
114 | #TODO: multiple files support, from the form |
|
114 | #TODO: multiple files support, from the form | |
115 | filename = form_result['filename'] or Gist.DEFAULT_FILENAME |
|
115 | filename = form_result['filename'] or Gist.DEFAULT_FILENAME | |
116 | nodes = { |
|
116 | nodes = { | |
117 | filename: { |
|
117 | filename: { | |
118 | 'content': form_result['content'], |
|
118 | 'content': form_result['content'], | |
119 | 'lexer': form_result['mimetype'] # None is autodetect |
|
119 | 'lexer': form_result['mimetype'] # None is autodetect | |
120 | } |
|
120 | } | |
121 | } |
|
121 | } | |
122 | _public = form_result['public'] |
|
122 | _public = form_result['public'] | |
123 | gist_type = Gist.GIST_PUBLIC if _public else Gist.GIST_PRIVATE |
|
123 | gist_type = Gist.GIST_PUBLIC if _public else Gist.GIST_PRIVATE | |
124 | gist = GistModel().create( |
|
124 | gist = GistModel().create( | |
125 | description=form_result['description'], |
|
125 | description=form_result['description'], | |
126 | owner=c.authuser.user_id, |
|
126 | owner=c.authuser.user_id, | |
127 | gist_mapping=nodes, |
|
127 | gist_mapping=nodes, | |
128 | gist_type=gist_type, |
|
128 | gist_type=gist_type, | |
129 | lifetime=form_result['lifetime'] |
|
129 | lifetime=form_result['lifetime'] | |
130 | ) |
|
130 | ) | |
131 | Session().commit() |
|
131 | Session().commit() | |
132 | new_gist_id = gist.gist_access_id |
|
132 | new_gist_id = gist.gist_access_id | |
133 | except formencode.Invalid, errors: |
|
133 | except formencode.Invalid, errors: | |
134 | defaults = errors.value |
|
134 | defaults = errors.value | |
135 |
|
135 | |||
136 | return formencode.htmlfill.render( |
|
136 | return formencode.htmlfill.render( | |
137 | render('admin/gists/new.html'), |
|
137 | render('admin/gists/new.html'), | |
138 | defaults=defaults, |
|
138 | defaults=defaults, | |
139 | errors=errors.error_dict or {}, |
|
139 | errors=errors.error_dict or {}, | |
140 | prefix_error=False, |
|
140 | prefix_error=False, | |
141 | encoding="UTF-8" |
|
141 | encoding="UTF-8", | |
142 | ) |
|
142 | force_defaults=False) | |
143 |
|
143 | |||
144 | except Exception, e: |
|
144 | except Exception, e: | |
145 | log.error(traceback.format_exc()) |
|
145 | log.error(traceback.format_exc()) | |
146 | h.flash(_('Error occurred during gist creation'), category='error') |
|
146 | h.flash(_('Error occurred during gist creation'), category='error') | |
147 | return redirect(url('new_gist')) |
|
147 | return redirect(url('new_gist')) | |
148 | return redirect(url('gist', gist_id=new_gist_id)) |
|
148 | return redirect(url('gist', gist_id=new_gist_id)) | |
149 |
|
149 | |||
150 | @LoginRequired() |
|
150 | @LoginRequired() | |
151 | @NotAnonymous() |
|
151 | @NotAnonymous() | |
152 | def new(self, format='html'): |
|
152 | def new(self, format='html'): | |
153 | """GET /admin/gists/new: Form to create a new item""" |
|
153 | """GET /admin/gists/new: Form to create a new item""" | |
154 | # url('new_gist') |
|
154 | # url('new_gist') | |
155 | self.__load_defaults() |
|
155 | self.__load_defaults() | |
156 | return render('admin/gists/new.html') |
|
156 | return render('admin/gists/new.html') | |
157 |
|
157 | |||
158 | @LoginRequired() |
|
158 | @LoginRequired() | |
159 | @NotAnonymous() |
|
159 | @NotAnonymous() | |
160 | def update(self, gist_id): |
|
160 | def update(self, gist_id): | |
161 | """PUT /admin/gists/gist_id: Update an existing item""" |
|
161 | """PUT /admin/gists/gist_id: Update an existing item""" | |
162 | # Forms posted to this method should contain a hidden field: |
|
162 | # Forms posted to this method should contain a hidden field: | |
163 | # <input type="hidden" name="_method" value="PUT" /> |
|
163 | # <input type="hidden" name="_method" value="PUT" /> | |
164 | # Or using helpers: |
|
164 | # Or using helpers: | |
165 | # h.form(url('gist', gist_id=ID), |
|
165 | # h.form(url('gist', gist_id=ID), | |
166 | # method='put') |
|
166 | # method='put') | |
167 | # url('gist', gist_id=ID) |
|
167 | # url('gist', gist_id=ID) | |
168 |
|
168 | |||
169 | @LoginRequired() |
|
169 | @LoginRequired() | |
170 | @NotAnonymous() |
|
170 | @NotAnonymous() | |
171 | def delete(self, gist_id): |
|
171 | def delete(self, gist_id): | |
172 | """DELETE /admin/gists/gist_id: Delete an existing item""" |
|
172 | """DELETE /admin/gists/gist_id: Delete an existing item""" | |
173 | # Forms posted to this method should contain a hidden field: |
|
173 | # Forms posted to this method should contain a hidden field: | |
174 | # <input type="hidden" name="_method" value="DELETE" /> |
|
174 | # <input type="hidden" name="_method" value="DELETE" /> | |
175 | # Or using helpers: |
|
175 | # Or using helpers: | |
176 | # h.form(url('gist', gist_id=ID), |
|
176 | # h.form(url('gist', gist_id=ID), | |
177 | # method='delete') |
|
177 | # method='delete') | |
178 | # url('gist', gist_id=ID) |
|
178 | # url('gist', gist_id=ID) | |
179 | gist = GistModel().get_gist(gist_id) |
|
179 | gist = GistModel().get_gist(gist_id) | |
180 | owner = gist.gist_owner == c.authuser.user_id |
|
180 | owner = gist.gist_owner == c.authuser.user_id | |
181 | if h.HasPermissionAny('hg.admin')() or owner: |
|
181 | if h.HasPermissionAny('hg.admin')() or owner: | |
182 | GistModel().delete(gist) |
|
182 | GistModel().delete(gist) | |
183 | Session().commit() |
|
183 | Session().commit() | |
184 | h.flash(_('Deleted gist %s') % gist.gist_access_id, category='success') |
|
184 | h.flash(_('Deleted gist %s') % gist.gist_access_id, category='success') | |
185 | else: |
|
185 | else: | |
186 | raise HTTPForbidden() |
|
186 | raise HTTPForbidden() | |
187 |
|
187 | |||
188 | return redirect(url('gists')) |
|
188 | return redirect(url('gists')) | |
189 |
|
189 | |||
190 | @LoginRequired() |
|
190 | @LoginRequired() | |
191 | def show(self, gist_id, revision='tip', format='html', f_path=None): |
|
191 | def show(self, gist_id, revision='tip', format='html', f_path=None): | |
192 | """GET /admin/gists/gist_id: Show a specific item""" |
|
192 | """GET /admin/gists/gist_id: Show a specific item""" | |
193 | # url('gist', gist_id=ID) |
|
193 | # url('gist', gist_id=ID) | |
194 | c.gist = Gist.get_or_404(gist_id) |
|
194 | c.gist = Gist.get_or_404(gist_id) | |
195 |
|
195 | |||
196 | #check if this gist is not expired |
|
196 | #check if this gist is not expired | |
197 | if c.gist.gist_expires != -1: |
|
197 | if c.gist.gist_expires != -1: | |
198 | if time.time() > c.gist.gist_expires: |
|
198 | if time.time() > c.gist.gist_expires: | |
199 | log.error('Gist expired at %s' % |
|
199 | log.error('Gist expired at %s' % | |
200 | (time_to_datetime(c.gist.gist_expires))) |
|
200 | (time_to_datetime(c.gist.gist_expires))) | |
201 | raise HTTPNotFound() |
|
201 | raise HTTPNotFound() | |
202 | try: |
|
202 | try: | |
203 | c.file_changeset, c.files = GistModel().get_gist_files(gist_id, |
|
203 | c.file_changeset, c.files = GistModel().get_gist_files(gist_id, | |
204 | revision=revision) |
|
204 | revision=revision) | |
205 | except VCSError: |
|
205 | except VCSError: | |
206 | log.error(traceback.format_exc()) |
|
206 | log.error(traceback.format_exc()) | |
207 | raise HTTPNotFound() |
|
207 | raise HTTPNotFound() | |
208 | if format == 'raw': |
|
208 | if format == 'raw': | |
209 | content = '\n\n'.join([f.content for f in c.files if (f_path is None or f.path == f_path)]) |
|
209 | content = '\n\n'.join([f.content for f in c.files if (f_path is None or f.path == f_path)]) | |
210 | response.content_type = 'text/plain' |
|
210 | response.content_type = 'text/plain' | |
211 | return content |
|
211 | return content | |
212 | return render('admin/gists/show.html') |
|
212 | return render('admin/gists/show.html') | |
213 |
|
213 | |||
214 | @LoginRequired() |
|
214 | @LoginRequired() | |
215 | @NotAnonymous() |
|
215 | @NotAnonymous() | |
216 | def edit(self, gist_id, format='html'): |
|
216 | def edit(self, gist_id, format='html'): | |
217 | """GET /admin/gists/gist_id/edit: Form to edit an existing item""" |
|
217 | """GET /admin/gists/gist_id/edit: Form to edit an existing item""" | |
218 | # url('edit_gist', gist_id=ID) |
|
218 | # url('edit_gist', gist_id=ID) | |
219 | c.gist = Gist.get_or_404(gist_id) |
|
219 | c.gist = Gist.get_or_404(gist_id) | |
220 |
|
220 | |||
221 | #check if this gist is not expired |
|
221 | #check if this gist is not expired | |
222 | if c.gist.gist_expires != -1: |
|
222 | if c.gist.gist_expires != -1: | |
223 | if time.time() > c.gist.gist_expires: |
|
223 | if time.time() > c.gist.gist_expires: | |
224 | log.error('Gist expired at %s' % |
|
224 | log.error('Gist expired at %s' % | |
225 | (time_to_datetime(c.gist.gist_expires))) |
|
225 | (time_to_datetime(c.gist.gist_expires))) | |
226 | raise HTTPNotFound() |
|
226 | raise HTTPNotFound() | |
227 | try: |
|
227 | try: | |
228 | c.file_changeset, c.files = GistModel().get_gist_files(gist_id) |
|
228 | c.file_changeset, c.files = GistModel().get_gist_files(gist_id) | |
229 | except VCSError: |
|
229 | except VCSError: | |
230 | log.error(traceback.format_exc()) |
|
230 | log.error(traceback.format_exc()) | |
231 | raise HTTPNotFound() |
|
231 | raise HTTPNotFound() | |
232 |
|
232 | |||
233 | self.__load_defaults(extra_values=('0', _('unmodified'))) |
|
233 | self.__load_defaults(extra_values=('0', _('unmodified'))) | |
234 | rendered = render('admin/gists/edit.html') |
|
234 | rendered = render('admin/gists/edit.html') | |
235 |
|
235 | |||
236 | if request.POST: |
|
236 | if request.POST: | |
237 | rpost = request.POST |
|
237 | rpost = request.POST | |
238 | nodes = {} |
|
238 | nodes = {} | |
239 | for org_filename, filename, mimetype, content in zip( |
|
239 | for org_filename, filename, mimetype, content in zip( | |
240 | rpost.getall('org_files'), |
|
240 | rpost.getall('org_files'), | |
241 | rpost.getall('files'), |
|
241 | rpost.getall('files'), | |
242 | rpost.getall('mimetypes'), |
|
242 | rpost.getall('mimetypes'), | |
243 | rpost.getall('contents')): |
|
243 | rpost.getall('contents')): | |
244 |
|
244 | |||
245 | nodes[org_filename] = { |
|
245 | nodes[org_filename] = { | |
246 | 'org_filename': org_filename, |
|
246 | 'org_filename': org_filename, | |
247 | 'filename': filename, |
|
247 | 'filename': filename, | |
248 | 'content': content, |
|
248 | 'content': content, | |
249 | 'lexer': mimetype, |
|
249 | 'lexer': mimetype, | |
250 | } |
|
250 | } | |
251 | try: |
|
251 | try: | |
252 | GistModel().update( |
|
252 | GistModel().update( | |
253 | gist=c.gist, |
|
253 | gist=c.gist, | |
254 | description=rpost['description'], |
|
254 | description=rpost['description'], | |
255 | owner=c.gist.owner, |
|
255 | owner=c.gist.owner, | |
256 | gist_mapping=nodes, |
|
256 | gist_mapping=nodes, | |
257 | gist_type=c.gist.gist_type, |
|
257 | gist_type=c.gist.gist_type, | |
258 | lifetime=rpost['lifetime'] |
|
258 | lifetime=rpost['lifetime'] | |
259 | ) |
|
259 | ) | |
260 |
|
260 | |||
261 | Session().commit() |
|
261 | Session().commit() | |
262 | h.flash(_('Successfully updated gist content'), category='success') |
|
262 | h.flash(_('Successfully updated gist content'), category='success') | |
263 | except NodeNotChangedError: |
|
263 | except NodeNotChangedError: | |
264 | # raised if nothing was changed in repo itself. We anyway then |
|
264 | # raised if nothing was changed in repo itself. We anyway then | |
265 | # store only DB stuff for gist |
|
265 | # store only DB stuff for gist | |
266 | Session().commit() |
|
266 | Session().commit() | |
267 | h.flash(_('Successfully updated gist data'), category='success') |
|
267 | h.flash(_('Successfully updated gist data'), category='success') | |
268 | except Exception: |
|
268 | except Exception: | |
269 | log.error(traceback.format_exc()) |
|
269 | log.error(traceback.format_exc()) | |
270 | h.flash(_('Error occurred during update of gist %s') % gist_id, |
|
270 | h.flash(_('Error occurred during update of gist %s') % gist_id, | |
271 | category='error') |
|
271 | category='error') | |
272 |
|
272 | |||
273 | return redirect(url('gist', gist_id=gist_id)) |
|
273 | return redirect(url('gist', gist_id=gist_id)) | |
274 |
|
274 | |||
275 | return rendered |
|
275 | return rendered | |
276 |
|
276 | |||
277 | @LoginRequired() |
|
277 | @LoginRequired() | |
278 | @NotAnonymous() |
|
278 | @NotAnonymous() | |
279 | @jsonify |
|
279 | @jsonify | |
280 | def check_revision(self, gist_id): |
|
280 | def check_revision(self, gist_id): | |
281 | c.gist = Gist.get_or_404(gist_id) |
|
281 | c.gist = Gist.get_or_404(gist_id) | |
282 | last_rev = c.gist.scm_instance.get_changeset() |
|
282 | last_rev = c.gist.scm_instance.get_changeset() | |
283 | success = True |
|
283 | success = True | |
284 | revision = request.POST.get('revision') |
|
284 | revision = request.POST.get('revision') | |
285 |
|
285 | |||
286 | ##TODO: maybe move this to model ? |
|
286 | ##TODO: maybe move this to model ? | |
287 | if revision != last_rev.raw_id: |
|
287 | if revision != last_rev.raw_id: | |
288 | log.error('Last revision %s is different than submitted %s' |
|
288 | log.error('Last revision %s is different than submitted %s' | |
289 | % (revision, last_rev)) |
|
289 | % (revision, last_rev)) | |
290 | # our gist has newer version than we |
|
290 | # our gist has newer version than we | |
291 | success = False |
|
291 | success = False | |
292 |
|
292 | |||
293 | return {'success': success} |
|
293 | return {'success': success} |
@@ -1,271 +1,272 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.my_account |
|
15 | kallithea.controllers.admin.my_account | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | my account controller for Kallithea admin |
|
18 | my account controller for Kallithea admin | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: August 20, 2013 |
|
22 | :created_on: August 20, 2013 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 | import logging |
|
28 | import logging | |
29 | import traceback |
|
29 | import traceback | |
30 | import formencode |
|
30 | import formencode | |
31 |
|
31 | |||
32 | from sqlalchemy import func |
|
32 | from sqlalchemy import func | |
33 | from formencode import htmlfill |
|
33 | from formencode import htmlfill | |
34 | from pylons import request, tmpl_context as c, url |
|
34 | from pylons import request, tmpl_context as c, url | |
35 | from pylons.controllers.util import redirect |
|
35 | from pylons.controllers.util import redirect | |
36 | from pylons.i18n.translation import _ |
|
36 | from pylons.i18n.translation import _ | |
37 |
|
37 | |||
38 | from kallithea import EXTERN_TYPE_INTERNAL |
|
38 | from kallithea import EXTERN_TYPE_INTERNAL | |
39 | from kallithea.lib import helpers as h |
|
39 | from kallithea.lib import helpers as h | |
40 | from kallithea.lib.auth import LoginRequired, NotAnonymous, AuthUser |
|
40 | from kallithea.lib.auth import LoginRequired, NotAnonymous, AuthUser | |
41 | from kallithea.lib.base import BaseController, render |
|
41 | from kallithea.lib.base import BaseController, render | |
42 | from kallithea.lib.utils2 import generate_api_key, safe_int |
|
42 | from kallithea.lib.utils2 import generate_api_key, safe_int | |
43 | from kallithea.lib.compat import json |
|
43 | from kallithea.lib.compat import json | |
44 | from kallithea.model.db import Repository, \ |
|
44 | from kallithea.model.db import Repository, \ | |
45 | UserEmailMap, UserApiKeys, User, UserFollowing |
|
45 | UserEmailMap, UserApiKeys, User, UserFollowing | |
46 | from kallithea.model.forms import UserForm, PasswordChangeForm |
|
46 | from kallithea.model.forms import UserForm, PasswordChangeForm | |
47 | from kallithea.model.user import UserModel |
|
47 | from kallithea.model.user import UserModel | |
48 | from kallithea.model.repo import RepoModel |
|
48 | from kallithea.model.repo import RepoModel | |
49 | from kallithea.model.api_key import ApiKeyModel |
|
49 | from kallithea.model.api_key import ApiKeyModel | |
50 | from kallithea.model.meta import Session |
|
50 | from kallithea.model.meta import Session | |
51 |
|
51 | |||
52 | log = logging.getLogger(__name__) |
|
52 | log = logging.getLogger(__name__) | |
53 |
|
53 | |||
54 |
|
54 | |||
55 | class MyAccountController(BaseController): |
|
55 | class MyAccountController(BaseController): | |
56 | """REST Controller styled on the Atom Publishing Protocol""" |
|
56 | """REST Controller styled on the Atom Publishing Protocol""" | |
57 | # To properly map this controller, ensure your config/routing.py |
|
57 | # To properly map this controller, ensure your config/routing.py | |
58 | # file has a resource setup: |
|
58 | # file has a resource setup: | |
59 | # map.resource('setting', 'settings', controller='admin/settings', |
|
59 | # map.resource('setting', 'settings', controller='admin/settings', | |
60 | # path_prefix='/admin', name_prefix='admin_') |
|
60 | # path_prefix='/admin', name_prefix='admin_') | |
61 |
|
61 | |||
62 | @LoginRequired() |
|
62 | @LoginRequired() | |
63 | @NotAnonymous() |
|
63 | @NotAnonymous() | |
64 | def __before__(self): |
|
64 | def __before__(self): | |
65 | super(MyAccountController, self).__before__() |
|
65 | super(MyAccountController, self).__before__() | |
66 |
|
66 | |||
67 | def __load_data(self): |
|
67 | def __load_data(self): | |
68 | c.user = User.get(self.authuser.user_id) |
|
68 | c.user = User.get(self.authuser.user_id) | |
69 | if c.user.username == User.DEFAULT_USER: |
|
69 | if c.user.username == User.DEFAULT_USER: | |
70 | h.flash(_("You can't edit this user since it's" |
|
70 | h.flash(_("You can't edit this user since it's" | |
71 | " crucial for entire application"), category='warning') |
|
71 | " crucial for entire application"), category='warning') | |
72 | return redirect(url('users')) |
|
72 | return redirect(url('users')) | |
73 | c.EXTERN_TYPE_INTERNAL = EXTERN_TYPE_INTERNAL |
|
73 | c.EXTERN_TYPE_INTERNAL = EXTERN_TYPE_INTERNAL | |
74 |
|
74 | |||
75 | def _load_my_repos_data(self, watched=False): |
|
75 | def _load_my_repos_data(self, watched=False): | |
76 | if watched: |
|
76 | if watched: | |
77 | admin = False |
|
77 | admin = False | |
78 | repos_list = [x.follows_repository for x in |
|
78 | repos_list = [x.follows_repository for x in | |
79 | Session().query(UserFollowing).filter( |
|
79 | Session().query(UserFollowing).filter( | |
80 | UserFollowing.user_id == |
|
80 | UserFollowing.user_id == | |
81 | self.authuser.user_id).all()] |
|
81 | self.authuser.user_id).all()] | |
82 | else: |
|
82 | else: | |
83 | admin = True |
|
83 | admin = True | |
84 | repos_list = Session().query(Repository)\ |
|
84 | repos_list = Session().query(Repository)\ | |
85 | .filter(Repository.user_id == |
|
85 | .filter(Repository.user_id == | |
86 | self.authuser.user_id)\ |
|
86 | self.authuser.user_id)\ | |
87 | .order_by(func.lower(Repository.repo_name)).all() |
|
87 | .order_by(func.lower(Repository.repo_name)).all() | |
88 |
|
88 | |||
89 | repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list, |
|
89 | repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list, | |
90 | admin=admin) |
|
90 | admin=admin) | |
91 | #json used to render the grid |
|
91 | #json used to render the grid | |
92 | return json.dumps(repos_data) |
|
92 | return json.dumps(repos_data) | |
93 |
|
93 | |||
94 | def my_account(self): |
|
94 | def my_account(self): | |
95 | """ |
|
95 | """ | |
96 | GET /_admin/my_account Displays info about my account |
|
96 | GET /_admin/my_account Displays info about my account | |
97 | """ |
|
97 | """ | |
98 | # url('my_account') |
|
98 | # url('my_account') | |
99 | c.active = 'profile' |
|
99 | c.active = 'profile' | |
100 | self.__load_data() |
|
100 | self.__load_data() | |
101 | c.perm_user = AuthUser(user_id=self.authuser.user_id, |
|
101 | c.perm_user = AuthUser(user_id=self.authuser.user_id, | |
102 | ip_addr=self.ip_addr) |
|
102 | ip_addr=self.ip_addr) | |
103 | c.extern_type = c.user.extern_type |
|
103 | c.extern_type = c.user.extern_type | |
104 | c.extern_name = c.user.extern_name |
|
104 | c.extern_name = c.user.extern_name | |
105 |
|
105 | |||
106 | defaults = c.user.get_dict() |
|
106 | defaults = c.user.get_dict() | |
107 | update = False |
|
107 | update = False | |
108 | if request.POST: |
|
108 | if request.POST: | |
109 | _form = UserForm(edit=True, |
|
109 | _form = UserForm(edit=True, | |
110 | old_data={'user_id': self.authuser.user_id, |
|
110 | old_data={'user_id': self.authuser.user_id, | |
111 | 'email': self.authuser.email})() |
|
111 | 'email': self.authuser.email})() | |
112 | form_result = {} |
|
112 | form_result = {} | |
113 | try: |
|
113 | try: | |
114 | post_data = dict(request.POST) |
|
114 | post_data = dict(request.POST) | |
115 | post_data['new_password'] = '' |
|
115 | post_data['new_password'] = '' | |
116 | post_data['password_confirmation'] = '' |
|
116 | post_data['password_confirmation'] = '' | |
117 | form_result = _form.to_python(post_data) |
|
117 | form_result = _form.to_python(post_data) | |
118 | # skip updating those attrs for my account |
|
118 | # skip updating those attrs for my account | |
119 | skip_attrs = ['admin', 'active', 'extern_type', 'extern_name', |
|
119 | skip_attrs = ['admin', 'active', 'extern_type', 'extern_name', | |
120 | 'new_password', 'password_confirmation'] |
|
120 | 'new_password', 'password_confirmation'] | |
121 | #TODO: plugin should define if username can be updated |
|
121 | #TODO: plugin should define if username can be updated | |
122 | if c.extern_type != EXTERN_TYPE_INTERNAL: |
|
122 | if c.extern_type != EXTERN_TYPE_INTERNAL: | |
123 | # forbid updating username for external accounts |
|
123 | # forbid updating username for external accounts | |
124 | skip_attrs.append('username') |
|
124 | skip_attrs.append('username') | |
125 |
|
125 | |||
126 | UserModel().update(self.authuser.user_id, form_result, |
|
126 | UserModel().update(self.authuser.user_id, form_result, | |
127 | skip_attrs=skip_attrs) |
|
127 | skip_attrs=skip_attrs) | |
128 | h.flash(_('Your account was updated successfully'), |
|
128 | h.flash(_('Your account was updated successfully'), | |
129 | category='success') |
|
129 | category='success') | |
130 | Session().commit() |
|
130 | Session().commit() | |
131 | update = True |
|
131 | update = True | |
132 |
|
132 | |||
133 | except formencode.Invalid, errors: |
|
133 | except formencode.Invalid, errors: | |
134 | return htmlfill.render( |
|
134 | return htmlfill.render( | |
135 | render('admin/my_account/my_account.html'), |
|
135 | render('admin/my_account/my_account.html'), | |
136 | defaults=errors.value, |
|
136 | defaults=errors.value, | |
137 | errors=errors.error_dict or {}, |
|
137 | errors=errors.error_dict or {}, | |
138 | prefix_error=False, |
|
138 | prefix_error=False, | |
139 |
encoding="UTF-8" |
|
139 | encoding="UTF-8", | |
|
140 | force_defaults=False) | |||
140 | except Exception: |
|
141 | except Exception: | |
141 | log.error(traceback.format_exc()) |
|
142 | log.error(traceback.format_exc()) | |
142 | h.flash(_('Error occurred during update of user %s') \ |
|
143 | h.flash(_('Error occurred during update of user %s') \ | |
143 | % form_result.get('username'), category='error') |
|
144 | % form_result.get('username'), category='error') | |
144 | if update: |
|
145 | if update: | |
145 | return redirect('my_account') |
|
146 | return redirect('my_account') | |
146 | return htmlfill.render( |
|
147 | return htmlfill.render( | |
147 | render('admin/my_account/my_account.html'), |
|
148 | render('admin/my_account/my_account.html'), | |
148 | defaults=defaults, |
|
149 | defaults=defaults, | |
149 | encoding="UTF-8", |
|
150 | encoding="UTF-8", | |
150 | force_defaults=False |
|
151 | force_defaults=False) | |
151 | ) |
|
|||
152 |
|
152 | |||
153 | def my_account_password(self): |
|
153 | def my_account_password(self): | |
154 | c.active = 'password' |
|
154 | c.active = 'password' | |
155 | self.__load_data() |
|
155 | self.__load_data() | |
156 | if request.POST: |
|
156 | if request.POST: | |
157 | _form = PasswordChangeForm(self.authuser.username)() |
|
157 | _form = PasswordChangeForm(self.authuser.username)() | |
158 | try: |
|
158 | try: | |
159 | form_result = _form.to_python(request.POST) |
|
159 | form_result = _form.to_python(request.POST) | |
160 | UserModel().update(self.authuser.user_id, form_result) |
|
160 | UserModel().update(self.authuser.user_id, form_result) | |
161 | Session().commit() |
|
161 | Session().commit() | |
162 | h.flash(_("Successfully updated password"), category='success') |
|
162 | h.flash(_("Successfully updated password"), category='success') | |
163 | except formencode.Invalid as errors: |
|
163 | except formencode.Invalid as errors: | |
164 | return htmlfill.render( |
|
164 | return htmlfill.render( | |
165 | render('admin/my_account/my_account.html'), |
|
165 | render('admin/my_account/my_account.html'), | |
166 | defaults=errors.value, |
|
166 | defaults=errors.value, | |
167 | errors=errors.error_dict or {}, |
|
167 | errors=errors.error_dict or {}, | |
168 | prefix_error=False, |
|
168 | prefix_error=False, | |
169 |
encoding="UTF-8" |
|
169 | encoding="UTF-8", | |
|
170 | force_defaults=False) | |||
170 | except Exception: |
|
171 | except Exception: | |
171 | log.error(traceback.format_exc()) |
|
172 | log.error(traceback.format_exc()) | |
172 | h.flash(_('Error occurred during update of user password'), |
|
173 | h.flash(_('Error occurred during update of user password'), | |
173 | category='error') |
|
174 | category='error') | |
174 | return render('admin/my_account/my_account.html') |
|
175 | return render('admin/my_account/my_account.html') | |
175 |
|
176 | |||
176 | def my_account_repos(self): |
|
177 | def my_account_repos(self): | |
177 | c.active = 'repos' |
|
178 | c.active = 'repos' | |
178 | self.__load_data() |
|
179 | self.__load_data() | |
179 |
|
180 | |||
180 | #json used to render the grid |
|
181 | #json used to render the grid | |
181 | c.data = self._load_my_repos_data() |
|
182 | c.data = self._load_my_repos_data() | |
182 | return render('admin/my_account/my_account.html') |
|
183 | return render('admin/my_account/my_account.html') | |
183 |
|
184 | |||
184 | def my_account_watched(self): |
|
185 | def my_account_watched(self): | |
185 | c.active = 'watched' |
|
186 | c.active = 'watched' | |
186 | self.__load_data() |
|
187 | self.__load_data() | |
187 |
|
188 | |||
188 | #json used to render the grid |
|
189 | #json used to render the grid | |
189 | c.data = self._load_my_repos_data(watched=True) |
|
190 | c.data = self._load_my_repos_data(watched=True) | |
190 | return render('admin/my_account/my_account.html') |
|
191 | return render('admin/my_account/my_account.html') | |
191 |
|
192 | |||
192 | def my_account_perms(self): |
|
193 | def my_account_perms(self): | |
193 | c.active = 'perms' |
|
194 | c.active = 'perms' | |
194 | self.__load_data() |
|
195 | self.__load_data() | |
195 | c.perm_user = AuthUser(user_id=self.authuser.user_id, |
|
196 | c.perm_user = AuthUser(user_id=self.authuser.user_id, | |
196 | ip_addr=self.ip_addr) |
|
197 | ip_addr=self.ip_addr) | |
197 |
|
198 | |||
198 | return render('admin/my_account/my_account.html') |
|
199 | return render('admin/my_account/my_account.html') | |
199 |
|
200 | |||
200 | def my_account_emails(self): |
|
201 | def my_account_emails(self): | |
201 | c.active = 'emails' |
|
202 | c.active = 'emails' | |
202 | self.__load_data() |
|
203 | self.__load_data() | |
203 |
|
204 | |||
204 | c.user_email_map = UserEmailMap.query()\ |
|
205 | c.user_email_map = UserEmailMap.query()\ | |
205 | .filter(UserEmailMap.user == c.user).all() |
|
206 | .filter(UserEmailMap.user == c.user).all() | |
206 | return render('admin/my_account/my_account.html') |
|
207 | return render('admin/my_account/my_account.html') | |
207 |
|
208 | |||
208 | def my_account_emails_add(self): |
|
209 | def my_account_emails_add(self): | |
209 | email = request.POST.get('new_email') |
|
210 | email = request.POST.get('new_email') | |
210 |
|
211 | |||
211 | try: |
|
212 | try: | |
212 | UserModel().add_extra_email(self.authuser.user_id, email) |
|
213 | UserModel().add_extra_email(self.authuser.user_id, email) | |
213 | Session().commit() |
|
214 | Session().commit() | |
214 | h.flash(_("Added email %s to user") % email, category='success') |
|
215 | h.flash(_("Added email %s to user") % email, category='success') | |
215 | except formencode.Invalid, error: |
|
216 | except formencode.Invalid, error: | |
216 | msg = error.error_dict['email'] |
|
217 | msg = error.error_dict['email'] | |
217 | h.flash(msg, category='error') |
|
218 | h.flash(msg, category='error') | |
218 | except Exception: |
|
219 | except Exception: | |
219 | log.error(traceback.format_exc()) |
|
220 | log.error(traceback.format_exc()) | |
220 | h.flash(_('An error occurred during email saving'), |
|
221 | h.flash(_('An error occurred during email saving'), | |
221 | category='error') |
|
222 | category='error') | |
222 | return redirect(url('my_account_emails')) |
|
223 | return redirect(url('my_account_emails')) | |
223 |
|
224 | |||
224 | def my_account_emails_delete(self): |
|
225 | def my_account_emails_delete(self): | |
225 | email_id = request.POST.get('del_email_id') |
|
226 | email_id = request.POST.get('del_email_id') | |
226 | user_model = UserModel() |
|
227 | user_model = UserModel() | |
227 | user_model.delete_extra_email(self.authuser.user_id, email_id) |
|
228 | user_model.delete_extra_email(self.authuser.user_id, email_id) | |
228 | Session().commit() |
|
229 | Session().commit() | |
229 | h.flash(_("Removed email from user"), category='success') |
|
230 | h.flash(_("Removed email from user"), category='success') | |
230 | return redirect(url('my_account_emails')) |
|
231 | return redirect(url('my_account_emails')) | |
231 |
|
232 | |||
232 | def my_account_api_keys(self): |
|
233 | def my_account_api_keys(self): | |
233 | c.active = 'api_keys' |
|
234 | c.active = 'api_keys' | |
234 | self.__load_data() |
|
235 | self.__load_data() | |
235 | show_expired = True |
|
236 | show_expired = True | |
236 | c.lifetime_values = [ |
|
237 | c.lifetime_values = [ | |
237 | (str(-1), _('forever')), |
|
238 | (str(-1), _('forever')), | |
238 | (str(5), _('5 minutes')), |
|
239 | (str(5), _('5 minutes')), | |
239 | (str(60), _('1 hour')), |
|
240 | (str(60), _('1 hour')), | |
240 | (str(60 * 24), _('1 day')), |
|
241 | (str(60 * 24), _('1 day')), | |
241 | (str(60 * 24 * 30), _('1 month')), |
|
242 | (str(60 * 24 * 30), _('1 month')), | |
242 | ] |
|
243 | ] | |
243 | c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] |
|
244 | c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] | |
244 | c.user_api_keys = ApiKeyModel().get_api_keys(self.authuser.user_id, |
|
245 | c.user_api_keys = ApiKeyModel().get_api_keys(self.authuser.user_id, | |
245 | show_expired=show_expired) |
|
246 | show_expired=show_expired) | |
246 | return render('admin/my_account/my_account.html') |
|
247 | return render('admin/my_account/my_account.html') | |
247 |
|
248 | |||
248 | def my_account_api_keys_add(self): |
|
249 | def my_account_api_keys_add(self): | |
249 | lifetime = safe_int(request.POST.get('lifetime'), -1) |
|
250 | lifetime = safe_int(request.POST.get('lifetime'), -1) | |
250 | description = request.POST.get('description') |
|
251 | description = request.POST.get('description') | |
251 | ApiKeyModel().create(self.authuser.user_id, description, lifetime) |
|
252 | ApiKeyModel().create(self.authuser.user_id, description, lifetime) | |
252 | Session().commit() |
|
253 | Session().commit() | |
253 | h.flash(_("Api key successfully created"), category='success') |
|
254 | h.flash(_("Api key successfully created"), category='success') | |
254 | return redirect(url('my_account_api_keys')) |
|
255 | return redirect(url('my_account_api_keys')) | |
255 |
|
256 | |||
256 | def my_account_api_keys_delete(self): |
|
257 | def my_account_api_keys_delete(self): | |
257 | api_key = request.POST.get('del_api_key') |
|
258 | api_key = request.POST.get('del_api_key') | |
258 | user_id = self.authuser.user_id |
|
259 | user_id = self.authuser.user_id | |
259 | if request.POST.get('del_api_key_builtin'): |
|
260 | if request.POST.get('del_api_key_builtin'): | |
260 | user = User.get(user_id) |
|
261 | user = User.get(user_id) | |
261 | if user: |
|
262 | if user: | |
262 | user.api_key = generate_api_key(user.username) |
|
263 | user.api_key = generate_api_key(user.username) | |
263 | Session().add(user) |
|
264 | Session().add(user) | |
264 | Session().commit() |
|
265 | Session().commit() | |
265 | h.flash(_("Api key successfully reset"), category='success') |
|
266 | h.flash(_("Api key successfully reset"), category='success') | |
266 | elif api_key: |
|
267 | elif api_key: | |
267 | ApiKeyModel().delete(api_key, self.authuser.user_id) |
|
268 | ApiKeyModel().delete(api_key, self.authuser.user_id) | |
268 | Session().commit() |
|
269 | Session().commit() | |
269 | h.flash(_("Api key successfully deleted"), category='success') |
|
270 | h.flash(_("Api key successfully deleted"), category='success') | |
270 |
|
271 | |||
271 | return redirect(url('my_account_api_keys')) |
|
272 | return redirect(url('my_account_api_keys')) |
@@ -1,196 +1,197 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.permissions |
|
15 | kallithea.controllers.admin.permissions | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | permissions controller for Kallithea |
|
18 | permissions controller for Kallithea | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Apr 27, 2010 |
|
22 | :created_on: Apr 27, 2010 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 |
|
28 | |||
29 | import logging |
|
29 | import logging | |
30 | import traceback |
|
30 | import traceback | |
31 | import formencode |
|
31 | import formencode | |
32 | from formencode import htmlfill |
|
32 | from formencode import htmlfill | |
33 |
|
33 | |||
34 | from pylons import request, tmpl_context as c, url |
|
34 | from pylons import request, tmpl_context as c, url | |
35 | from pylons.controllers.util import redirect |
|
35 | from pylons.controllers.util import redirect | |
36 | from pylons.i18n.translation import _ |
|
36 | from pylons.i18n.translation import _ | |
37 |
|
37 | |||
38 | from kallithea.lib import helpers as h |
|
38 | from kallithea.lib import helpers as h | |
39 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator,\ |
|
39 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator,\ | |
40 | AuthUser |
|
40 | AuthUser | |
41 | from kallithea.lib.base import BaseController, render |
|
41 | from kallithea.lib.base import BaseController, render | |
42 | from kallithea.model.forms import DefaultPermissionsForm |
|
42 | from kallithea.model.forms import DefaultPermissionsForm | |
43 | from kallithea.model.permission import PermissionModel |
|
43 | from kallithea.model.permission import PermissionModel | |
44 | from kallithea.model.db import User, UserIpMap |
|
44 | from kallithea.model.db import User, UserIpMap | |
45 | from kallithea.model.meta import Session |
|
45 | from kallithea.model.meta import Session | |
46 |
|
46 | |||
47 | log = logging.getLogger(__name__) |
|
47 | log = logging.getLogger(__name__) | |
48 |
|
48 | |||
49 |
|
49 | |||
50 | class PermissionsController(BaseController): |
|
50 | class PermissionsController(BaseController): | |
51 | """REST Controller styled on the Atom Publishing Protocol""" |
|
51 | """REST Controller styled on the Atom Publishing Protocol""" | |
52 | # To properly map this controller, ensure your config/routing.py |
|
52 | # To properly map this controller, ensure your config/routing.py | |
53 | # file has a resource setup: |
|
53 | # file has a resource setup: | |
54 | # map.resource('permission', 'permissions') |
|
54 | # map.resource('permission', 'permissions') | |
55 |
|
55 | |||
56 | @LoginRequired() |
|
56 | @LoginRequired() | |
57 | @HasPermissionAllDecorator('hg.admin') |
|
57 | @HasPermissionAllDecorator('hg.admin') | |
58 | def __before__(self): |
|
58 | def __before__(self): | |
59 | super(PermissionsController, self).__before__() |
|
59 | super(PermissionsController, self).__before__() | |
60 |
|
60 | |||
61 | def __load_data(self): |
|
61 | def __load_data(self): | |
62 | c.repo_perms_choices = [('repository.none', _('None'),), |
|
62 | c.repo_perms_choices = [('repository.none', _('None'),), | |
63 | ('repository.read', _('Read'),), |
|
63 | ('repository.read', _('Read'),), | |
64 | ('repository.write', _('Write'),), |
|
64 | ('repository.write', _('Write'),), | |
65 | ('repository.admin', _('Admin'),)] |
|
65 | ('repository.admin', _('Admin'),)] | |
66 | c.group_perms_choices = [('group.none', _('None'),), |
|
66 | c.group_perms_choices = [('group.none', _('None'),), | |
67 | ('group.read', _('Read'),), |
|
67 | ('group.read', _('Read'),), | |
68 | ('group.write', _('Write'),), |
|
68 | ('group.write', _('Write'),), | |
69 | ('group.admin', _('Admin'),)] |
|
69 | ('group.admin', _('Admin'),)] | |
70 | c.user_group_perms_choices = [('usergroup.none', _('None'),), |
|
70 | c.user_group_perms_choices = [('usergroup.none', _('None'),), | |
71 | ('usergroup.read', _('Read'),), |
|
71 | ('usergroup.read', _('Read'),), | |
72 | ('usergroup.write', _('Write'),), |
|
72 | ('usergroup.write', _('Write'),), | |
73 | ('usergroup.admin', _('Admin'),)] |
|
73 | ('usergroup.admin', _('Admin'),)] | |
74 | c.register_choices = [ |
|
74 | c.register_choices = [ | |
75 | ('hg.register.none', |
|
75 | ('hg.register.none', | |
76 | _('Disabled')), |
|
76 | _('Disabled')), | |
77 | ('hg.register.manual_activate', |
|
77 | ('hg.register.manual_activate', | |
78 | _('Allowed with manual account activation')), |
|
78 | _('Allowed with manual account activation')), | |
79 | ('hg.register.auto_activate', |
|
79 | ('hg.register.auto_activate', | |
80 | _('Allowed with automatic account activation')), ] |
|
80 | _('Allowed with automatic account activation')), ] | |
81 |
|
81 | |||
82 | c.extern_activate_choices = [ |
|
82 | c.extern_activate_choices = [ | |
83 | ('hg.extern_activate.manual', _('Manual activation of external account')), |
|
83 | ('hg.extern_activate.manual', _('Manual activation of external account')), | |
84 | ('hg.extern_activate.auto', _('Automatic activation of external account')), |
|
84 | ('hg.extern_activate.auto', _('Automatic activation of external account')), | |
85 | ] |
|
85 | ] | |
86 |
|
86 | |||
87 | c.repo_create_choices = [('hg.create.none', _('Disabled')), |
|
87 | c.repo_create_choices = [('hg.create.none', _('Disabled')), | |
88 | ('hg.create.repository', _('Enabled'))] |
|
88 | ('hg.create.repository', _('Enabled'))] | |
89 |
|
89 | |||
90 | c.repo_create_on_write_choices = [ |
|
90 | c.repo_create_on_write_choices = [ | |
91 | ('hg.create.write_on_repogroup.true', _('Enabled')), |
|
91 | ('hg.create.write_on_repogroup.true', _('Enabled')), | |
92 | ('hg.create.write_on_repogroup.false', _('Disabled')), |
|
92 | ('hg.create.write_on_repogroup.false', _('Disabled')), | |
93 | ] |
|
93 | ] | |
94 |
|
94 | |||
95 | c.user_group_create_choices = [('hg.usergroup.create.false', _('Disabled')), |
|
95 | c.user_group_create_choices = [('hg.usergroup.create.false', _('Disabled')), | |
96 | ('hg.usergroup.create.true', _('Enabled'))] |
|
96 | ('hg.usergroup.create.true', _('Enabled'))] | |
97 |
|
97 | |||
98 | c.repo_group_create_choices = [('hg.repogroup.create.false', _('Disabled')), |
|
98 | c.repo_group_create_choices = [('hg.repogroup.create.false', _('Disabled')), | |
99 | ('hg.repogroup.create.true', _('Enabled'))] |
|
99 | ('hg.repogroup.create.true', _('Enabled'))] | |
100 |
|
100 | |||
101 | c.fork_choices = [('hg.fork.none', _('Disabled')), |
|
101 | c.fork_choices = [('hg.fork.none', _('Disabled')), | |
102 | ('hg.fork.repository', _('Enabled'))] |
|
102 | ('hg.fork.repository', _('Enabled'))] | |
103 |
|
103 | |||
104 | def permission_globals(self): |
|
104 | def permission_globals(self): | |
105 | c.active = 'globals' |
|
105 | c.active = 'globals' | |
106 | self.__load_data() |
|
106 | self.__load_data() | |
107 | if request.POST: |
|
107 | if request.POST: | |
108 | _form = DefaultPermissionsForm( |
|
108 | _form = DefaultPermissionsForm( | |
109 | [x[0] for x in c.repo_perms_choices], |
|
109 | [x[0] for x in c.repo_perms_choices], | |
110 | [x[0] for x in c.group_perms_choices], |
|
110 | [x[0] for x in c.group_perms_choices], | |
111 | [x[0] for x in c.user_group_perms_choices], |
|
111 | [x[0] for x in c.user_group_perms_choices], | |
112 | [x[0] for x in c.repo_create_choices], |
|
112 | [x[0] for x in c.repo_create_choices], | |
113 | [x[0] for x in c.repo_create_on_write_choices], |
|
113 | [x[0] for x in c.repo_create_on_write_choices], | |
114 | [x[0] for x in c.repo_group_create_choices], |
|
114 | [x[0] for x in c.repo_group_create_choices], | |
115 | [x[0] for x in c.user_group_create_choices], |
|
115 | [x[0] for x in c.user_group_create_choices], | |
116 | [x[0] for x in c.fork_choices], |
|
116 | [x[0] for x in c.fork_choices], | |
117 | [x[0] for x in c.register_choices], |
|
117 | [x[0] for x in c.register_choices], | |
118 | [x[0] for x in c.extern_activate_choices])() |
|
118 | [x[0] for x in c.extern_activate_choices])() | |
119 |
|
119 | |||
120 | try: |
|
120 | try: | |
121 | form_result = _form.to_python(dict(request.POST)) |
|
121 | form_result = _form.to_python(dict(request.POST)) | |
122 | form_result.update({'perm_user_name': 'default'}) |
|
122 | form_result.update({'perm_user_name': 'default'}) | |
123 | PermissionModel().update(form_result) |
|
123 | PermissionModel().update(form_result) | |
124 | Session().commit() |
|
124 | Session().commit() | |
125 | h.flash(_('Global permissions updated successfully'), |
|
125 | h.flash(_('Global permissions updated successfully'), | |
126 | category='success') |
|
126 | category='success') | |
127 |
|
127 | |||
128 | except formencode.Invalid, errors: |
|
128 | except formencode.Invalid, errors: | |
129 | defaults = errors.value |
|
129 | defaults = errors.value | |
130 |
|
130 | |||
131 | return htmlfill.render( |
|
131 | return htmlfill.render( | |
132 | render('admin/permissions/permissions.html'), |
|
132 | render('admin/permissions/permissions.html'), | |
133 | defaults=defaults, |
|
133 | defaults=defaults, | |
134 | errors=errors.error_dict or {}, |
|
134 | errors=errors.error_dict or {}, | |
135 | prefix_error=False, |
|
135 | prefix_error=False, | |
136 |
encoding="UTF-8" |
|
136 | encoding="UTF-8", | |
|
137 | force_defaults=False) | |||
137 | except Exception: |
|
138 | except Exception: | |
138 | log.error(traceback.format_exc()) |
|
139 | log.error(traceback.format_exc()) | |
139 | h.flash(_('Error occurred during update of permissions'), |
|
140 | h.flash(_('Error occurred during update of permissions'), | |
140 | category='error') |
|
141 | category='error') | |
141 |
|
142 | |||
142 | return redirect(url('admin_permissions')) |
|
143 | return redirect(url('admin_permissions')) | |
143 |
|
144 | |||
144 | c.user = User.get_default_user() |
|
145 | c.user = User.get_default_user() | |
145 | defaults = {'anonymous': c.user.active} |
|
146 | defaults = {'anonymous': c.user.active} | |
146 |
|
147 | |||
147 | for p in c.user.user_perms: |
|
148 | for p in c.user.user_perms: | |
148 | if p.permission.permission_name.startswith('repository.'): |
|
149 | if p.permission.permission_name.startswith('repository.'): | |
149 | defaults['default_repo_perm'] = p.permission.permission_name |
|
150 | defaults['default_repo_perm'] = p.permission.permission_name | |
150 |
|
151 | |||
151 | if p.permission.permission_name.startswith('group.'): |
|
152 | if p.permission.permission_name.startswith('group.'): | |
152 | defaults['default_group_perm'] = p.permission.permission_name |
|
153 | defaults['default_group_perm'] = p.permission.permission_name | |
153 |
|
154 | |||
154 | if p.permission.permission_name.startswith('usergroup.'): |
|
155 | if p.permission.permission_name.startswith('usergroup.'): | |
155 | defaults['default_user_group_perm'] = p.permission.permission_name |
|
156 | defaults['default_user_group_perm'] = p.permission.permission_name | |
156 |
|
157 | |||
157 | if p.permission.permission_name.startswith('hg.create.write_on_repogroup'): |
|
158 | if p.permission.permission_name.startswith('hg.create.write_on_repogroup'): | |
158 | defaults['create_on_write'] = p.permission.permission_name |
|
159 | defaults['create_on_write'] = p.permission.permission_name | |
159 |
|
160 | |||
160 | elif p.permission.permission_name.startswith('hg.create.'): |
|
161 | elif p.permission.permission_name.startswith('hg.create.'): | |
161 | defaults['default_repo_create'] = p.permission.permission_name |
|
162 | defaults['default_repo_create'] = p.permission.permission_name | |
162 |
|
163 | |||
163 | if p.permission.permission_name.startswith('hg.repogroup.'): |
|
164 | if p.permission.permission_name.startswith('hg.repogroup.'): | |
164 | defaults['default_repo_group_create'] = p.permission.permission_name |
|
165 | defaults['default_repo_group_create'] = p.permission.permission_name | |
165 |
|
166 | |||
166 | if p.permission.permission_name.startswith('hg.usergroup.'): |
|
167 | if p.permission.permission_name.startswith('hg.usergroup.'): | |
167 | defaults['default_user_group_create'] = p.permission.permission_name |
|
168 | defaults['default_user_group_create'] = p.permission.permission_name | |
168 |
|
169 | |||
169 | if p.permission.permission_name.startswith('hg.register.'): |
|
170 | if p.permission.permission_name.startswith('hg.register.'): | |
170 | defaults['default_register'] = p.permission.permission_name |
|
171 | defaults['default_register'] = p.permission.permission_name | |
171 |
|
172 | |||
172 | if p.permission.permission_name.startswith('hg.extern_activate.'): |
|
173 | if p.permission.permission_name.startswith('hg.extern_activate.'): | |
173 | defaults['default_extern_activate'] = p.permission.permission_name |
|
174 | defaults['default_extern_activate'] = p.permission.permission_name | |
174 |
|
175 | |||
175 | if p.permission.permission_name.startswith('hg.fork.'): |
|
176 | if p.permission.permission_name.startswith('hg.fork.'): | |
176 | defaults['default_fork'] = p.permission.permission_name |
|
177 | defaults['default_fork'] = p.permission.permission_name | |
177 |
|
178 | |||
178 | return htmlfill.render( |
|
179 | return htmlfill.render( | |
179 | render('admin/permissions/permissions.html'), |
|
180 | render('admin/permissions/permissions.html'), | |
180 | defaults=defaults, |
|
181 | defaults=defaults, | |
181 | encoding="UTF-8", |
|
182 | encoding="UTF-8", | |
182 | force_defaults=False) |
|
183 | force_defaults=False) | |
183 |
|
184 | |||
184 | def permission_ips(self): |
|
185 | def permission_ips(self): | |
185 | c.active = 'ips' |
|
186 | c.active = 'ips' | |
186 | c.user = User.get_default_user() |
|
187 | c.user = User.get_default_user() | |
187 | c.user_ip_map = UserIpMap.query()\ |
|
188 | c.user_ip_map = UserIpMap.query()\ | |
188 | .filter(UserIpMap.user == c.user).all() |
|
189 | .filter(UserIpMap.user == c.user).all() | |
189 |
|
190 | |||
190 | return render('admin/permissions/permissions.html') |
|
191 | return render('admin/permissions/permissions.html') | |
191 |
|
192 | |||
192 | def permission_perms(self): |
|
193 | def permission_perms(self): | |
193 | c.active = 'perms' |
|
194 | c.active = 'perms' | |
194 | c.user = User.get_default_user() |
|
195 | c.user = User.get_default_user() | |
195 | c.perm_user = c.user.AuthUser |
|
196 | c.perm_user = c.user.AuthUser | |
196 | return render('admin/permissions/permissions.html') |
|
197 | return render('admin/permissions/permissions.html') |
@@ -1,472 +1,474 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.repo_groups |
|
15 | kallithea.controllers.admin.repo_groups | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | Repository groups controller for Kallithea |
|
18 | Repository groups controller for Kallithea | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Mar 23, 2010 |
|
22 | :created_on: Mar 23, 2010 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 | import logging |
|
28 | import logging | |
29 | import traceback |
|
29 | import traceback | |
30 | import formencode |
|
30 | import formencode | |
31 | import itertools |
|
31 | import itertools | |
32 |
|
32 | |||
33 | from formencode import htmlfill |
|
33 | from formencode import htmlfill | |
34 |
|
34 | |||
35 | from pylons import request, tmpl_context as c, url |
|
35 | from pylons import request, tmpl_context as c, url | |
36 | from pylons.controllers.util import abort, redirect |
|
36 | from pylons.controllers.util import abort, redirect | |
37 | from pylons.i18n.translation import _, ungettext |
|
37 | from pylons.i18n.translation import _, ungettext | |
38 |
|
38 | |||
39 | import kallithea |
|
39 | import kallithea | |
40 | from kallithea.lib import helpers as h |
|
40 | from kallithea.lib import helpers as h | |
41 | from kallithea.lib.compat import json |
|
41 | from kallithea.lib.compat import json | |
42 | from kallithea.lib.auth import LoginRequired, HasPermissionAnyDecorator,\ |
|
42 | from kallithea.lib.auth import LoginRequired, HasPermissionAnyDecorator,\ | |
43 | HasRepoGroupPermissionAnyDecorator, HasRepoGroupPermissionAll,\ |
|
43 | HasRepoGroupPermissionAnyDecorator, HasRepoGroupPermissionAll,\ | |
44 | HasPermissionAll |
|
44 | HasPermissionAll | |
45 | from kallithea.lib.base import BaseController, render |
|
45 | from kallithea.lib.base import BaseController, render | |
46 | from kallithea.model.db import RepoGroup, Repository |
|
46 | from kallithea.model.db import RepoGroup, Repository | |
47 | from kallithea.model.scm import RepoGroupList |
|
47 | from kallithea.model.scm import RepoGroupList | |
48 | from kallithea.model.repo_group import RepoGroupModel |
|
48 | from kallithea.model.repo_group import RepoGroupModel | |
49 | from kallithea.model.forms import RepoGroupForm, RepoGroupPermsForm |
|
49 | from kallithea.model.forms import RepoGroupForm, RepoGroupPermsForm | |
50 | from kallithea.model.meta import Session |
|
50 | from kallithea.model.meta import Session | |
51 | from kallithea.model.repo import RepoModel |
|
51 | from kallithea.model.repo import RepoModel | |
52 | from webob.exc import HTTPInternalServerError, HTTPNotFound |
|
52 | from webob.exc import HTTPInternalServerError, HTTPNotFound | |
53 | from kallithea.lib.utils2 import safe_int |
|
53 | from kallithea.lib.utils2 import safe_int | |
54 | from sqlalchemy.sql.expression import func |
|
54 | from sqlalchemy.sql.expression import func | |
55 |
|
55 | |||
56 |
|
56 | |||
57 | log = logging.getLogger(__name__) |
|
57 | log = logging.getLogger(__name__) | |
58 |
|
58 | |||
59 |
|
59 | |||
60 | class RepoGroupsController(BaseController): |
|
60 | class RepoGroupsController(BaseController): | |
61 | """REST Controller styled on the Atom Publishing Protocol""" |
|
61 | """REST Controller styled on the Atom Publishing Protocol""" | |
62 |
|
62 | |||
63 | @LoginRequired() |
|
63 | @LoginRequired() | |
64 | def __before__(self): |
|
64 | def __before__(self): | |
65 | super(RepoGroupsController, self).__before__() |
|
65 | super(RepoGroupsController, self).__before__() | |
66 |
|
66 | |||
67 | def __load_defaults(self, allow_empty_group=False, exclude_group_ids=[]): |
|
67 | def __load_defaults(self, allow_empty_group=False, exclude_group_ids=[]): | |
68 | if HasPermissionAll('hg.admin')('group edit'): |
|
68 | if HasPermissionAll('hg.admin')('group edit'): | |
69 | #we're global admin, we're ok and we can create TOP level groups |
|
69 | #we're global admin, we're ok and we can create TOP level groups | |
70 | allow_empty_group = True |
|
70 | allow_empty_group = True | |
71 |
|
71 | |||
72 | #override the choices for this form, we need to filter choices |
|
72 | #override the choices for this form, we need to filter choices | |
73 | #and display only those we have ADMIN right |
|
73 | #and display only those we have ADMIN right | |
74 | groups_with_admin_rights = RepoGroupList(RepoGroup.query().all(), |
|
74 | groups_with_admin_rights = RepoGroupList(RepoGroup.query().all(), | |
75 | perm_set=['group.admin']) |
|
75 | perm_set=['group.admin']) | |
76 | c.repo_groups = RepoGroup.groups_choices(groups=groups_with_admin_rights, |
|
76 | c.repo_groups = RepoGroup.groups_choices(groups=groups_with_admin_rights, | |
77 | show_empty_group=allow_empty_group) |
|
77 | show_empty_group=allow_empty_group) | |
78 | # exclude filtered ids |
|
78 | # exclude filtered ids | |
79 | c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids, |
|
79 | c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids, | |
80 | c.repo_groups) |
|
80 | c.repo_groups) | |
81 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) |
|
81 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) | |
82 | repo_model = RepoModel() |
|
82 | repo_model = RepoModel() | |
83 | c.users_array = repo_model.get_users_js() |
|
83 | c.users_array = repo_model.get_users_js() | |
84 | c.user_groups_array = repo_model.get_user_groups_js() |
|
84 | c.user_groups_array = repo_model.get_user_groups_js() | |
85 |
|
85 | |||
86 | def __load_data(self, group_id): |
|
86 | def __load_data(self, group_id): | |
87 | """ |
|
87 | """ | |
88 | Load defaults settings for edit, and update |
|
88 | Load defaults settings for edit, and update | |
89 |
|
89 | |||
90 | :param group_id: |
|
90 | :param group_id: | |
91 | """ |
|
91 | """ | |
92 | repo_group = RepoGroup.get_or_404(group_id) |
|
92 | repo_group = RepoGroup.get_or_404(group_id) | |
93 | data = repo_group.get_dict() |
|
93 | data = repo_group.get_dict() | |
94 | data['group_name'] = repo_group.name |
|
94 | data['group_name'] = repo_group.name | |
95 |
|
95 | |||
96 | # fill repository group users |
|
96 | # fill repository group users | |
97 | for p in repo_group.repo_group_to_perm: |
|
97 | for p in repo_group.repo_group_to_perm: | |
98 | data.update({'u_perm_%s' % p.user.username: |
|
98 | data.update({'u_perm_%s' % p.user.username: | |
99 | p.permission.permission_name}) |
|
99 | p.permission.permission_name}) | |
100 |
|
100 | |||
101 | # fill repository group groups |
|
101 | # fill repository group groups | |
102 | for p in repo_group.users_group_to_perm: |
|
102 | for p in repo_group.users_group_to_perm: | |
103 | data.update({'g_perm_%s' % p.users_group.users_group_name: |
|
103 | data.update({'g_perm_%s' % p.users_group.users_group_name: | |
104 | p.permission.permission_name}) |
|
104 | p.permission.permission_name}) | |
105 |
|
105 | |||
106 | return data |
|
106 | return data | |
107 |
|
107 | |||
108 | def _revoke_perms_on_yourself(self, form_result): |
|
108 | def _revoke_perms_on_yourself(self, form_result): | |
109 | _up = filter(lambda u: c.authuser.username == u[0], |
|
109 | _up = filter(lambda u: c.authuser.username == u[0], | |
110 | form_result['perms_updates']) |
|
110 | form_result['perms_updates']) | |
111 | _new = filter(lambda u: c.authuser.username == u[0], |
|
111 | _new = filter(lambda u: c.authuser.username == u[0], | |
112 | form_result['perms_new']) |
|
112 | form_result['perms_new']) | |
113 | if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin': |
|
113 | if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin': | |
114 | return True |
|
114 | return True | |
115 | return False |
|
115 | return False | |
116 |
|
116 | |||
117 | def index(self, format='html'): |
|
117 | def index(self, format='html'): | |
118 | """GET /repo_groups: All items in the collection""" |
|
118 | """GET /repo_groups: All items in the collection""" | |
119 | # url('repos_groups') |
|
119 | # url('repos_groups') | |
120 | _list = RepoGroup.query()\ |
|
120 | _list = RepoGroup.query()\ | |
121 | .order_by(func.lower(RepoGroup.group_name))\ |
|
121 | .order_by(func.lower(RepoGroup.group_name))\ | |
122 | .all() |
|
122 | .all() | |
123 | group_iter = RepoGroupList(_list, perm_set=['group.admin']) |
|
123 | group_iter = RepoGroupList(_list, perm_set=['group.admin']) | |
124 | repo_groups_data = [] |
|
124 | repo_groups_data = [] | |
125 | total_records = len(group_iter) |
|
125 | total_records = len(group_iter) | |
126 | _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup |
|
126 | _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup | |
127 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') |
|
127 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') | |
128 |
|
128 | |||
129 | repo_group_name = lambda repo_group_name, children_groups: ( |
|
129 | repo_group_name = lambda repo_group_name, children_groups: ( | |
130 | template.get_def("repo_group_name") |
|
130 | template.get_def("repo_group_name") | |
131 | .render(repo_group_name, children_groups, _=_, h=h, c=c) |
|
131 | .render(repo_group_name, children_groups, _=_, h=h, c=c) | |
132 | ) |
|
132 | ) | |
133 | repo_group_actions = lambda repo_group_id, repo_group_name, gr_count: ( |
|
133 | repo_group_actions = lambda repo_group_id, repo_group_name, gr_count: ( | |
134 | template.get_def("repo_group_actions") |
|
134 | template.get_def("repo_group_actions") | |
135 | .render(repo_group_id, repo_group_name, gr_count, _=_, h=h, c=c, |
|
135 | .render(repo_group_id, repo_group_name, gr_count, _=_, h=h, c=c, | |
136 | ungettext=ungettext) |
|
136 | ungettext=ungettext) | |
137 | ) |
|
137 | ) | |
138 |
|
138 | |||
139 | for repo_gr in group_iter: |
|
139 | for repo_gr in group_iter: | |
140 | children_groups = map(h.safe_unicode, |
|
140 | children_groups = map(h.safe_unicode, | |
141 | itertools.chain((g.name for g in repo_gr.parents), |
|
141 | itertools.chain((g.name for g in repo_gr.parents), | |
142 | (x.name for x in [repo_gr]))) |
|
142 | (x.name for x in [repo_gr]))) | |
143 | repo_count = repo_gr.repositories.count() |
|
143 | repo_count = repo_gr.repositories.count() | |
144 | repo_groups_data.append({ |
|
144 | repo_groups_data.append({ | |
145 | "raw_name": repo_gr.group_name, |
|
145 | "raw_name": repo_gr.group_name, | |
146 | "group_name": repo_group_name(repo_gr.group_name, children_groups), |
|
146 | "group_name": repo_group_name(repo_gr.group_name, children_groups), | |
147 | "desc": repo_gr.group_description, |
|
147 | "desc": repo_gr.group_description, | |
148 | "repos": repo_count, |
|
148 | "repos": repo_count, | |
149 | "owner": h.person(repo_gr.user), |
|
149 | "owner": h.person(repo_gr.user), | |
150 | "action": repo_group_actions(repo_gr.group_id, repo_gr.group_name, |
|
150 | "action": repo_group_actions(repo_gr.group_id, repo_gr.group_name, | |
151 | repo_count) |
|
151 | repo_count) | |
152 | }) |
|
152 | }) | |
153 |
|
153 | |||
154 | c.data = json.dumps({ |
|
154 | c.data = json.dumps({ | |
155 | "totalRecords": total_records, |
|
155 | "totalRecords": total_records, | |
156 | "startIndex": 0, |
|
156 | "startIndex": 0, | |
157 | "sort": None, |
|
157 | "sort": None, | |
158 | "dir": "asc", |
|
158 | "dir": "asc", | |
159 | "records": repo_groups_data |
|
159 | "records": repo_groups_data | |
160 | }) |
|
160 | }) | |
161 |
|
161 | |||
162 | return render('admin/repo_groups/repo_groups.html') |
|
162 | return render('admin/repo_groups/repo_groups.html') | |
163 |
|
163 | |||
164 | def create(self): |
|
164 | def create(self): | |
165 | """POST /repo_groups: Create a new item""" |
|
165 | """POST /repo_groups: Create a new item""" | |
166 | # url('repos_groups') |
|
166 | # url('repos_groups') | |
167 |
|
167 | |||
168 | self.__load_defaults() |
|
168 | self.__load_defaults() | |
169 |
|
169 | |||
170 | # permissions for can create group based on parent_id are checked |
|
170 | # permissions for can create group based on parent_id are checked | |
171 | # here in the Form |
|
171 | # here in the Form | |
172 | repo_group_form = RepoGroupForm(available_groups= |
|
172 | repo_group_form = RepoGroupForm(available_groups= | |
173 | map(lambda k: unicode(k[0]), c.repo_groups))() |
|
173 | map(lambda k: unicode(k[0]), c.repo_groups))() | |
174 | try: |
|
174 | try: | |
175 | form_result = repo_group_form.to_python(dict(request.POST)) |
|
175 | form_result = repo_group_form.to_python(dict(request.POST)) | |
176 | RepoGroupModel().create( |
|
176 | RepoGroupModel().create( | |
177 | group_name=form_result['group_name'], |
|
177 | group_name=form_result['group_name'], | |
178 | group_description=form_result['group_description'], |
|
178 | group_description=form_result['group_description'], | |
179 | parent=form_result['group_parent_id'], |
|
179 | parent=form_result['group_parent_id'], | |
180 | owner=self.authuser.user_id, |
|
180 | owner=self.authuser.user_id, | |
181 | copy_permissions=form_result['group_copy_permissions'] |
|
181 | copy_permissions=form_result['group_copy_permissions'] | |
182 | ) |
|
182 | ) | |
183 | Session().commit() |
|
183 | Session().commit() | |
184 | h.flash(_('Created repository group %s') \ |
|
184 | h.flash(_('Created repository group %s') \ | |
185 | % form_result['group_name'], category='success') |
|
185 | % form_result['group_name'], category='success') | |
186 | #TODO: in futureaction_logger(, '', '', '', self.sa) |
|
186 | #TODO: in futureaction_logger(, '', '', '', self.sa) | |
187 | except formencode.Invalid, errors: |
|
187 | except formencode.Invalid, errors: | |
188 | return htmlfill.render( |
|
188 | return htmlfill.render( | |
189 | render('admin/repo_groups/repo_group_add.html'), |
|
189 | render('admin/repo_groups/repo_group_add.html'), | |
190 | defaults=errors.value, |
|
190 | defaults=errors.value, | |
191 | errors=errors.error_dict or {}, |
|
191 | errors=errors.error_dict or {}, | |
192 | prefix_error=False, |
|
192 | prefix_error=False, | |
193 |
encoding="UTF-8" |
|
193 | encoding="UTF-8", | |
|
194 | force_defaults=False) | |||
194 | except Exception: |
|
195 | except Exception: | |
195 | log.error(traceback.format_exc()) |
|
196 | log.error(traceback.format_exc()) | |
196 | h.flash(_('Error occurred during creation of repository group %s') \ |
|
197 | h.flash(_('Error occurred during creation of repository group %s') \ | |
197 | % request.POST.get('group_name'), category='error') |
|
198 | % request.POST.get('group_name'), category='error') | |
198 | parent_group_id = form_result['group_parent_id'] |
|
199 | parent_group_id = form_result['group_parent_id'] | |
199 | #TODO: maybe we should get back to the main view, not the admin one |
|
200 | #TODO: maybe we should get back to the main view, not the admin one | |
200 | return redirect(url('repos_groups', parent_group=parent_group_id)) |
|
201 | return redirect(url('repos_groups', parent_group=parent_group_id)) | |
201 |
|
202 | |||
202 | def new(self): |
|
203 | def new(self): | |
203 | """GET /repo_groups/new: Form to create a new item""" |
|
204 | """GET /repo_groups/new: Form to create a new item""" | |
204 | # url('new_repos_group') |
|
205 | # url('new_repos_group') | |
205 | if HasPermissionAll('hg.admin')('group create'): |
|
206 | if HasPermissionAll('hg.admin')('group create'): | |
206 | #we're global admin, we're ok and we can create TOP level groups |
|
207 | #we're global admin, we're ok and we can create TOP level groups | |
207 | pass |
|
208 | pass | |
208 | else: |
|
209 | else: | |
209 | # we pass in parent group into creation form, thus we know |
|
210 | # we pass in parent group into creation form, thus we know | |
210 | # what would be the group, we can check perms here ! |
|
211 | # what would be the group, we can check perms here ! | |
211 | group_id = safe_int(request.GET.get('parent_group')) |
|
212 | group_id = safe_int(request.GET.get('parent_group')) | |
212 | group = RepoGroup.get(group_id) if group_id else None |
|
213 | group = RepoGroup.get(group_id) if group_id else None | |
213 | group_name = group.group_name if group else None |
|
214 | group_name = group.group_name if group else None | |
214 | if HasRepoGroupPermissionAll('group.admin')(group_name, 'group create'): |
|
215 | if HasRepoGroupPermissionAll('group.admin')(group_name, 'group create'): | |
215 | pass |
|
216 | pass | |
216 | else: |
|
217 | else: | |
217 | return abort(403) |
|
218 | return abort(403) | |
218 |
|
219 | |||
219 | self.__load_defaults() |
|
220 | self.__load_defaults() | |
220 | return render('admin/repo_groups/repo_group_add.html') |
|
221 | return render('admin/repo_groups/repo_group_add.html') | |
221 |
|
222 | |||
222 | @HasRepoGroupPermissionAnyDecorator('group.admin') |
|
223 | @HasRepoGroupPermissionAnyDecorator('group.admin') | |
223 | def update(self, group_name): |
|
224 | def update(self, group_name): | |
224 | """PUT /repo_groups/group_name: Update an existing item""" |
|
225 | """PUT /repo_groups/group_name: Update an existing item""" | |
225 | # Forms posted to this method should contain a hidden field: |
|
226 | # Forms posted to this method should contain a hidden field: | |
226 | # <input type="hidden" name="_method" value="PUT" /> |
|
227 | # <input type="hidden" name="_method" value="PUT" /> | |
227 | # Or using helpers: |
|
228 | # Or using helpers: | |
228 | # h.form(url('repos_group', group_name=GROUP_NAME), |
|
229 | # h.form(url('repos_group', group_name=GROUP_NAME), | |
229 | # method='put') |
|
230 | # method='put') | |
230 | # url('repos_group', group_name=GROUP_NAME) |
|
231 | # url('repos_group', group_name=GROUP_NAME) | |
231 |
|
232 | |||
232 | c.repo_group = RepoGroupModel()._get_repo_group(group_name) |
|
233 | c.repo_group = RepoGroupModel()._get_repo_group(group_name) | |
233 | if HasPermissionAll('hg.admin')('group edit'): |
|
234 | if HasPermissionAll('hg.admin')('group edit'): | |
234 | #we're global admin, we're ok and we can create TOP level groups |
|
235 | #we're global admin, we're ok and we can create TOP level groups | |
235 | allow_empty_group = True |
|
236 | allow_empty_group = True | |
236 | elif not c.repo_group.parent_group: |
|
237 | elif not c.repo_group.parent_group: | |
237 | allow_empty_group = True |
|
238 | allow_empty_group = True | |
238 | else: |
|
239 | else: | |
239 | allow_empty_group = False |
|
240 | allow_empty_group = False | |
240 | self.__load_defaults(allow_empty_group=allow_empty_group, |
|
241 | self.__load_defaults(allow_empty_group=allow_empty_group, | |
241 | exclude_group_ids=[c.repo_group.group_id]) |
|
242 | exclude_group_ids=[c.repo_group.group_id]) | |
242 |
|
243 | |||
243 | repo_group_form = RepoGroupForm( |
|
244 | repo_group_form = RepoGroupForm( | |
244 | edit=True, |
|
245 | edit=True, | |
245 | old_data=c.repo_group.get_dict(), |
|
246 | old_data=c.repo_group.get_dict(), | |
246 | available_groups=c.repo_groups_choices, |
|
247 | available_groups=c.repo_groups_choices, | |
247 | can_create_in_root=allow_empty_group, |
|
248 | can_create_in_root=allow_empty_group, | |
248 | )() |
|
249 | )() | |
249 | try: |
|
250 | try: | |
250 | form_result = repo_group_form.to_python(dict(request.POST)) |
|
251 | form_result = repo_group_form.to_python(dict(request.POST)) | |
251 |
|
252 | |||
252 | new_gr = RepoGroupModel().update(group_name, form_result) |
|
253 | new_gr = RepoGroupModel().update(group_name, form_result) | |
253 | Session().commit() |
|
254 | Session().commit() | |
254 | h.flash(_('Updated repository group %s') \ |
|
255 | h.flash(_('Updated repository group %s') \ | |
255 | % form_result['group_name'], category='success') |
|
256 | % form_result['group_name'], category='success') | |
256 | # we now have new name ! |
|
257 | # we now have new name ! | |
257 | group_name = new_gr.group_name |
|
258 | group_name = new_gr.group_name | |
258 | #TODO: in future action_logger(, '', '', '', self.sa) |
|
259 | #TODO: in future action_logger(, '', '', '', self.sa) | |
259 | except formencode.Invalid, errors: |
|
260 | except formencode.Invalid, errors: | |
260 |
|
261 | |||
261 | return htmlfill.render( |
|
262 | return htmlfill.render( | |
262 | render('admin/repo_groups/repo_group_edit.html'), |
|
263 | render('admin/repo_groups/repo_group_edit.html'), | |
263 | defaults=errors.value, |
|
264 | defaults=errors.value, | |
264 | errors=errors.error_dict or {}, |
|
265 | errors=errors.error_dict or {}, | |
265 | prefix_error=False, |
|
266 | prefix_error=False, | |
266 |
encoding="UTF-8" |
|
267 | encoding="UTF-8", | |
|
268 | force_defaults=False) | |||
267 | except Exception: |
|
269 | except Exception: | |
268 | log.error(traceback.format_exc()) |
|
270 | log.error(traceback.format_exc()) | |
269 | h.flash(_('Error occurred during update of repository group %s') \ |
|
271 | h.flash(_('Error occurred during update of repository group %s') \ | |
270 | % request.POST.get('group_name'), category='error') |
|
272 | % request.POST.get('group_name'), category='error') | |
271 |
|
273 | |||
272 | return redirect(url('edit_repo_group', group_name=group_name)) |
|
274 | return redirect(url('edit_repo_group', group_name=group_name)) | |
273 |
|
275 | |||
274 | @HasRepoGroupPermissionAnyDecorator('group.admin') |
|
276 | @HasRepoGroupPermissionAnyDecorator('group.admin') | |
275 | def delete(self, group_name): |
|
277 | def delete(self, group_name): | |
276 | """DELETE /repo_groups/group_name: Delete an existing item""" |
|
278 | """DELETE /repo_groups/group_name: Delete an existing item""" | |
277 | # Forms posted to this method should contain a hidden field: |
|
279 | # Forms posted to this method should contain a hidden field: | |
278 | # <input type="hidden" name="_method" value="DELETE" /> |
|
280 | # <input type="hidden" name="_method" value="DELETE" /> | |
279 | # Or using helpers: |
|
281 | # Or using helpers: | |
280 | # h.form(url('repos_group', group_name=GROUP_NAME), |
|
282 | # h.form(url('repos_group', group_name=GROUP_NAME), | |
281 | # method='delete') |
|
283 | # method='delete') | |
282 | # url('repos_group', group_name=GROUP_NAME) |
|
284 | # url('repos_group', group_name=GROUP_NAME) | |
283 |
|
285 | |||
284 | gr = c.repo_group = RepoGroupModel()._get_repo_group(group_name) |
|
286 | gr = c.repo_group = RepoGroupModel()._get_repo_group(group_name) | |
285 | repos = gr.repositories.all() |
|
287 | repos = gr.repositories.all() | |
286 | if repos: |
|
288 | if repos: | |
287 | h.flash(_('This group contains %s repositories and cannot be ' |
|
289 | h.flash(_('This group contains %s repositories and cannot be ' | |
288 | 'deleted') % len(repos), category='warning') |
|
290 | 'deleted') % len(repos), category='warning') | |
289 | return redirect(url('repos_groups')) |
|
291 | return redirect(url('repos_groups')) | |
290 |
|
292 | |||
291 | children = gr.children.all() |
|
293 | children = gr.children.all() | |
292 | if children: |
|
294 | if children: | |
293 | h.flash(_('This group contains %s subgroups and cannot be deleted' |
|
295 | h.flash(_('This group contains %s subgroups and cannot be deleted' | |
294 | % (len(children))), category='warning') |
|
296 | % (len(children))), category='warning') | |
295 | return redirect(url('repos_groups')) |
|
297 | return redirect(url('repos_groups')) | |
296 |
|
298 | |||
297 | try: |
|
299 | try: | |
298 | RepoGroupModel().delete(group_name) |
|
300 | RepoGroupModel().delete(group_name) | |
299 | Session().commit() |
|
301 | Session().commit() | |
300 | h.flash(_('Removed repository group %s') % group_name, |
|
302 | h.flash(_('Removed repository group %s') % group_name, | |
301 | category='success') |
|
303 | category='success') | |
302 | #TODO: in future action_logger(, '', '', '', self.sa) |
|
304 | #TODO: in future action_logger(, '', '', '', self.sa) | |
303 | except Exception: |
|
305 | except Exception: | |
304 | log.error(traceback.format_exc()) |
|
306 | log.error(traceback.format_exc()) | |
305 | h.flash(_('Error occurred during deletion of repository group %s') |
|
307 | h.flash(_('Error occurred during deletion of repository group %s') | |
306 | % group_name, category='error') |
|
308 | % group_name, category='error') | |
307 |
|
309 | |||
308 | if gr.parent_group: |
|
310 | if gr.parent_group: | |
309 | return redirect(url('repos_group_home', group_name=gr.parent_group.group_name)) |
|
311 | return redirect(url('repos_group_home', group_name=gr.parent_group.group_name)) | |
310 | return redirect(url('repos_groups')) |
|
312 | return redirect(url('repos_groups')) | |
311 |
|
313 | |||
312 | def show_by_name(self, group_name): |
|
314 | def show_by_name(self, group_name): | |
313 | """ |
|
315 | """ | |
314 | This is a proxy that does a lookup group_name -> id, and shows |
|
316 | This is a proxy that does a lookup group_name -> id, and shows | |
315 | the group by id view instead |
|
317 | the group by id view instead | |
316 | """ |
|
318 | """ | |
317 | group_name = group_name.rstrip('/') |
|
319 | group_name = group_name.rstrip('/') | |
318 | id_ = RepoGroup.get_by_group_name(group_name) |
|
320 | id_ = RepoGroup.get_by_group_name(group_name) | |
319 | if id_: |
|
321 | if id_: | |
320 | return self.show(group_name) |
|
322 | return self.show(group_name) | |
321 | raise HTTPNotFound |
|
323 | raise HTTPNotFound | |
322 |
|
324 | |||
323 | @HasRepoGroupPermissionAnyDecorator('group.read', 'group.write', |
|
325 | @HasRepoGroupPermissionAnyDecorator('group.read', 'group.write', | |
324 | 'group.admin') |
|
326 | 'group.admin') | |
325 | def show(self, group_name): |
|
327 | def show(self, group_name): | |
326 | """GET /repo_groups/group_name: Show a specific item""" |
|
328 | """GET /repo_groups/group_name: Show a specific item""" | |
327 | # url('repos_group', group_name=GROUP_NAME) |
|
329 | # url('repos_group', group_name=GROUP_NAME) | |
328 | c.active = 'settings' |
|
330 | c.active = 'settings' | |
329 |
|
331 | |||
330 | c.group = c.repo_group = RepoGroupModel()._get_repo_group(group_name) |
|
332 | c.group = c.repo_group = RepoGroupModel()._get_repo_group(group_name) | |
331 | c.group_repos = c.group.repositories.all() |
|
333 | c.group_repos = c.group.repositories.all() | |
332 |
|
334 | |||
333 | #overwrite our cached list with current filter |
|
335 | #overwrite our cached list with current filter | |
334 | c.repo_cnt = 0 |
|
336 | c.repo_cnt = 0 | |
335 |
|
337 | |||
336 | groups = RepoGroup.query().order_by(RepoGroup.group_name)\ |
|
338 | groups = RepoGroup.query().order_by(RepoGroup.group_name)\ | |
337 | .filter(RepoGroup.group_parent_id == c.group.group_id).all() |
|
339 | .filter(RepoGroup.group_parent_id == c.group.group_id).all() | |
338 | c.groups = self.scm_model.get_repo_groups(groups) |
|
340 | c.groups = self.scm_model.get_repo_groups(groups) | |
339 |
|
341 | |||
340 | c.repos_list = Repository.query()\ |
|
342 | c.repos_list = Repository.query()\ | |
341 | .filter(Repository.group_id == c.group.group_id)\ |
|
343 | .filter(Repository.group_id == c.group.group_id)\ | |
342 | .order_by(func.lower(Repository.repo_name))\ |
|
344 | .order_by(func.lower(Repository.repo_name))\ | |
343 | .all() |
|
345 | .all() | |
344 |
|
346 | |||
345 | repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list, |
|
347 | repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list, | |
346 | admin=False) |
|
348 | admin=False) | |
347 | #json used to render the grid |
|
349 | #json used to render the grid | |
348 | c.data = json.dumps(repos_data) |
|
350 | c.data = json.dumps(repos_data) | |
349 |
|
351 | |||
350 | return render('admin/repo_groups/repo_group_show.html') |
|
352 | return render('admin/repo_groups/repo_group_show.html') | |
351 |
|
353 | |||
352 | @HasRepoGroupPermissionAnyDecorator('group.admin') |
|
354 | @HasRepoGroupPermissionAnyDecorator('group.admin') | |
353 | def edit(self, group_name): |
|
355 | def edit(self, group_name): | |
354 | """GET /repo_groups/group_name/edit: Form to edit an existing item""" |
|
356 | """GET /repo_groups/group_name/edit: Form to edit an existing item""" | |
355 | # url('edit_repo_group', group_name=GROUP_NAME) |
|
357 | # url('edit_repo_group', group_name=GROUP_NAME) | |
356 | c.active = 'settings' |
|
358 | c.active = 'settings' | |
357 |
|
359 | |||
358 | c.repo_group = RepoGroupModel()._get_repo_group(group_name) |
|
360 | c.repo_group = RepoGroupModel()._get_repo_group(group_name) | |
359 | #we can only allow moving empty group if it's already a top-level |
|
361 | #we can only allow moving empty group if it's already a top-level | |
360 | #group, ie has no parents, or we're admin |
|
362 | #group, ie has no parents, or we're admin | |
361 | if HasPermissionAll('hg.admin')('group edit'): |
|
363 | if HasPermissionAll('hg.admin')('group edit'): | |
362 | #we're global admin, we're ok and we can create TOP level groups |
|
364 | #we're global admin, we're ok and we can create TOP level groups | |
363 | allow_empty_group = True |
|
365 | allow_empty_group = True | |
364 | elif not c.repo_group.parent_group: |
|
366 | elif not c.repo_group.parent_group: | |
365 | allow_empty_group = True |
|
367 | allow_empty_group = True | |
366 | else: |
|
368 | else: | |
367 | allow_empty_group = False |
|
369 | allow_empty_group = False | |
368 |
|
370 | |||
369 | self.__load_defaults(allow_empty_group=allow_empty_group, |
|
371 | self.__load_defaults(allow_empty_group=allow_empty_group, | |
370 | exclude_group_ids=[c.repo_group.group_id]) |
|
372 | exclude_group_ids=[c.repo_group.group_id]) | |
371 | defaults = self.__load_data(c.repo_group.group_id) |
|
373 | defaults = self.__load_data(c.repo_group.group_id) | |
372 |
|
374 | |||
373 | return htmlfill.render( |
|
375 | return htmlfill.render( | |
374 | render('admin/repo_groups/repo_group_edit.html'), |
|
376 | render('admin/repo_groups/repo_group_edit.html'), | |
375 | defaults=defaults, |
|
377 | defaults=defaults, | |
376 | encoding="UTF-8", |
|
378 | encoding="UTF-8", | |
377 | force_defaults=False |
|
379 | force_defaults=False | |
378 | ) |
|
380 | ) | |
379 |
|
381 | |||
380 | @HasRepoGroupPermissionAnyDecorator('group.admin') |
|
382 | @HasRepoGroupPermissionAnyDecorator('group.admin') | |
381 | def edit_repo_group_advanced(self, group_name): |
|
383 | def edit_repo_group_advanced(self, group_name): | |
382 | """GET /repo_groups/group_name/edit: Form to edit an existing item""" |
|
384 | """GET /repo_groups/group_name/edit: Form to edit an existing item""" | |
383 | # url('edit_repo_group', group_name=GROUP_NAME) |
|
385 | # url('edit_repo_group', group_name=GROUP_NAME) | |
384 | c.active = 'advanced' |
|
386 | c.active = 'advanced' | |
385 | c.repo_group = RepoGroupModel()._get_repo_group(group_name) |
|
387 | c.repo_group = RepoGroupModel()._get_repo_group(group_name) | |
386 |
|
388 | |||
387 | return render('admin/repo_groups/repo_group_edit.html') |
|
389 | return render('admin/repo_groups/repo_group_edit.html') | |
388 |
|
390 | |||
389 | @HasRepoGroupPermissionAnyDecorator('group.admin') |
|
391 | @HasRepoGroupPermissionAnyDecorator('group.admin') | |
390 | def edit_repo_group_perms(self, group_name): |
|
392 | def edit_repo_group_perms(self, group_name): | |
391 | """GET /repo_groups/group_name/edit: Form to edit an existing item""" |
|
393 | """GET /repo_groups/group_name/edit: Form to edit an existing item""" | |
392 | # url('edit_repo_group', group_name=GROUP_NAME) |
|
394 | # url('edit_repo_group', group_name=GROUP_NAME) | |
393 | c.active = 'perms' |
|
395 | c.active = 'perms' | |
394 | c.repo_group = RepoGroupModel()._get_repo_group(group_name) |
|
396 | c.repo_group = RepoGroupModel()._get_repo_group(group_name) | |
395 | self.__load_defaults() |
|
397 | self.__load_defaults() | |
396 | defaults = self.__load_data(c.repo_group.group_id) |
|
398 | defaults = self.__load_data(c.repo_group.group_id) | |
397 |
|
399 | |||
398 | return htmlfill.render( |
|
400 | return htmlfill.render( | |
399 | render('admin/repo_groups/repo_group_edit.html'), |
|
401 | render('admin/repo_groups/repo_group_edit.html'), | |
400 | defaults=defaults, |
|
402 | defaults=defaults, | |
401 | encoding="UTF-8", |
|
403 | encoding="UTF-8", | |
402 | force_defaults=False |
|
404 | force_defaults=False | |
403 | ) |
|
405 | ) | |
404 |
|
406 | |||
405 | @HasRepoGroupPermissionAnyDecorator('group.admin') |
|
407 | @HasRepoGroupPermissionAnyDecorator('group.admin') | |
406 | def update_perms(self, group_name): |
|
408 | def update_perms(self, group_name): | |
407 | """ |
|
409 | """ | |
408 | Update permissions for given repository group |
|
410 | Update permissions for given repository group | |
409 |
|
411 | |||
410 | :param group_name: |
|
412 | :param group_name: | |
411 | """ |
|
413 | """ | |
412 |
|
414 | |||
413 | c.repo_group = RepoGroupModel()._get_repo_group(group_name) |
|
415 | c.repo_group = RepoGroupModel()._get_repo_group(group_name) | |
414 | valid_recursive_choices = ['none', 'repos', 'groups', 'all'] |
|
416 | valid_recursive_choices = ['none', 'repos', 'groups', 'all'] | |
415 | form_result = RepoGroupPermsForm(valid_recursive_choices)().to_python(request.POST) |
|
417 | form_result = RepoGroupPermsForm(valid_recursive_choices)().to_python(request.POST) | |
416 | if not c.authuser.is_admin: |
|
418 | if not c.authuser.is_admin: | |
417 | if self._revoke_perms_on_yourself(form_result): |
|
419 | if self._revoke_perms_on_yourself(form_result): | |
418 | msg = _('Cannot revoke permission for yourself as admin') |
|
420 | msg = _('Cannot revoke permission for yourself as admin') | |
419 | h.flash(msg, category='warning') |
|
421 | h.flash(msg, category='warning') | |
420 | return redirect(url('edit_repo_group_perms', group_name=group_name)) |
|
422 | return redirect(url('edit_repo_group_perms', group_name=group_name)) | |
421 | recursive = form_result['recursive'] |
|
423 | recursive = form_result['recursive'] | |
422 | # iterate over all members(if in recursive mode) of this groups and |
|
424 | # iterate over all members(if in recursive mode) of this groups and | |
423 | # set the permissions ! |
|
425 | # set the permissions ! | |
424 | # this can be potentially heavy operation |
|
426 | # this can be potentially heavy operation | |
425 | RepoGroupModel()._update_permissions(c.repo_group, |
|
427 | RepoGroupModel()._update_permissions(c.repo_group, | |
426 | form_result['perms_new'], |
|
428 | form_result['perms_new'], | |
427 | form_result['perms_updates'], |
|
429 | form_result['perms_updates'], | |
428 | recursive) |
|
430 | recursive) | |
429 | #TODO: implement this |
|
431 | #TODO: implement this | |
430 | #action_logger(self.authuser, 'admin_changed_repo_permissions', |
|
432 | #action_logger(self.authuser, 'admin_changed_repo_permissions', | |
431 | # repo_name, self.ip_addr, self.sa) |
|
433 | # repo_name, self.ip_addr, self.sa) | |
432 | Session().commit() |
|
434 | Session().commit() | |
433 | h.flash(_('Repository Group permissions updated'), category='success') |
|
435 | h.flash(_('Repository Group permissions updated'), category='success') | |
434 | return redirect(url('edit_repo_group_perms', group_name=group_name)) |
|
436 | return redirect(url('edit_repo_group_perms', group_name=group_name)) | |
435 |
|
437 | |||
436 | @HasRepoGroupPermissionAnyDecorator('group.admin') |
|
438 | @HasRepoGroupPermissionAnyDecorator('group.admin') | |
437 | def delete_perms(self, group_name): |
|
439 | def delete_perms(self, group_name): | |
438 | """ |
|
440 | """ | |
439 | DELETE an existing repository group permission user |
|
441 | DELETE an existing repository group permission user | |
440 |
|
442 | |||
441 | :param group_name: |
|
443 | :param group_name: | |
442 | """ |
|
444 | """ | |
443 | try: |
|
445 | try: | |
444 | obj_type = request.POST.get('obj_type') |
|
446 | obj_type = request.POST.get('obj_type') | |
445 | obj_id = None |
|
447 | obj_id = None | |
446 | if obj_type == 'user': |
|
448 | if obj_type == 'user': | |
447 | obj_id = safe_int(request.POST.get('user_id')) |
|
449 | obj_id = safe_int(request.POST.get('user_id')) | |
448 | elif obj_type == 'user_group': |
|
450 | elif obj_type == 'user_group': | |
449 | obj_id = safe_int(request.POST.get('user_group_id')) |
|
451 | obj_id = safe_int(request.POST.get('user_group_id')) | |
450 |
|
452 | |||
451 | if not c.authuser.is_admin: |
|
453 | if not c.authuser.is_admin: | |
452 | if obj_type == 'user' and c.authuser.user_id == obj_id: |
|
454 | if obj_type == 'user' and c.authuser.user_id == obj_id: | |
453 | msg = _('Cannot revoke permission for yourself as admin') |
|
455 | msg = _('Cannot revoke permission for yourself as admin') | |
454 | h.flash(msg, category='warning') |
|
456 | h.flash(msg, category='warning') | |
455 | raise Exception('revoke admin permission on self') |
|
457 | raise Exception('revoke admin permission on self') | |
456 | recursive = request.POST.get('recursive', 'none') |
|
458 | recursive = request.POST.get('recursive', 'none') | |
457 | if obj_type == 'user': |
|
459 | if obj_type == 'user': | |
458 | RepoGroupModel().delete_permission(repo_group=group_name, |
|
460 | RepoGroupModel().delete_permission(repo_group=group_name, | |
459 | obj=obj_id, obj_type='user', |
|
461 | obj=obj_id, obj_type='user', | |
460 | recursive=recursive) |
|
462 | recursive=recursive) | |
461 | elif obj_type == 'user_group': |
|
463 | elif obj_type == 'user_group': | |
462 | RepoGroupModel().delete_permission(repo_group=group_name, |
|
464 | RepoGroupModel().delete_permission(repo_group=group_name, | |
463 | obj=obj_id, |
|
465 | obj=obj_id, | |
464 | obj_type='user_group', |
|
466 | obj_type='user_group', | |
465 | recursive=recursive) |
|
467 | recursive=recursive) | |
466 |
|
468 | |||
467 | Session().commit() |
|
469 | Session().commit() | |
468 | except Exception: |
|
470 | except Exception: | |
469 | log.error(traceback.format_exc()) |
|
471 | log.error(traceback.format_exc()) | |
470 | h.flash(_('An error occurred during revoking of permission'), |
|
472 | h.flash(_('An error occurred during revoking of permission'), | |
471 | category='error') |
|
473 | category='error') | |
472 | raise HTTPInternalServerError() |
|
474 | raise HTTPInternalServerError() |
@@ -1,679 +1,681 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.repos |
|
15 | kallithea.controllers.admin.repos | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | Repositories controller for Kallithea |
|
18 | Repositories controller for Kallithea | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Apr 7, 2010 |
|
22 | :created_on: Apr 7, 2010 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 | import logging |
|
28 | import logging | |
29 | import traceback |
|
29 | import traceback | |
30 | import formencode |
|
30 | import formencode | |
31 | from formencode import htmlfill |
|
31 | from formencode import htmlfill | |
32 | from webob.exc import HTTPInternalServerError, HTTPForbidden, HTTPNotFound |
|
32 | from webob.exc import HTTPInternalServerError, HTTPForbidden, HTTPNotFound | |
33 | from pylons import request, tmpl_context as c, url |
|
33 | from pylons import request, tmpl_context as c, url | |
34 | from pylons.controllers.util import redirect |
|
34 | from pylons.controllers.util import redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 | from sqlalchemy.sql.expression import func |
|
36 | from sqlalchemy.sql.expression import func | |
37 |
|
37 | |||
38 | from kallithea.lib import helpers as h |
|
38 | from kallithea.lib import helpers as h | |
39 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator, \ |
|
39 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator, \ | |
40 | HasRepoPermissionAllDecorator, NotAnonymous,HasPermissionAny, \ |
|
40 | HasRepoPermissionAllDecorator, NotAnonymous,HasPermissionAny, \ | |
41 | HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator |
|
41 | HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator | |
42 | from kallithea.lib.base import BaseRepoController, render |
|
42 | from kallithea.lib.base import BaseRepoController, render | |
43 | from kallithea.lib.utils import action_logger, repo_name_slug, jsonify |
|
43 | from kallithea.lib.utils import action_logger, repo_name_slug, jsonify | |
44 | from kallithea.lib.helpers import get_token |
|
44 | from kallithea.lib.helpers import get_token | |
45 | from kallithea.lib.vcs import RepositoryError |
|
45 | from kallithea.lib.vcs import RepositoryError | |
46 | from kallithea.model.meta import Session |
|
46 | from kallithea.model.meta import Session | |
47 | from kallithea.model.db import User, Repository, UserFollowing, RepoGroup,\ |
|
47 | from kallithea.model.db import User, Repository, UserFollowing, RepoGroup,\ | |
48 | Setting, RepositoryField |
|
48 | Setting, RepositoryField | |
49 | from kallithea.model.forms import RepoForm, RepoFieldForm, RepoPermsForm |
|
49 | from kallithea.model.forms import RepoForm, RepoFieldForm, RepoPermsForm | |
50 | from kallithea.model.scm import ScmModel, RepoGroupList, RepoList |
|
50 | from kallithea.model.scm import ScmModel, RepoGroupList, RepoList | |
51 | from kallithea.model.repo import RepoModel |
|
51 | from kallithea.model.repo import RepoModel | |
52 | from kallithea.lib.compat import json |
|
52 | from kallithea.lib.compat import json | |
53 | from kallithea.lib.exceptions import AttachedForksError |
|
53 | from kallithea.lib.exceptions import AttachedForksError | |
54 | from kallithea.lib.utils2 import safe_int |
|
54 | from kallithea.lib.utils2 import safe_int | |
55 |
|
55 | |||
56 | log = logging.getLogger(__name__) |
|
56 | log = logging.getLogger(__name__) | |
57 |
|
57 | |||
58 |
|
58 | |||
59 | class ReposController(BaseRepoController): |
|
59 | class ReposController(BaseRepoController): | |
60 | """ |
|
60 | """ | |
61 | REST Controller styled on the Atom Publishing Protocol""" |
|
61 | REST Controller styled on the Atom Publishing Protocol""" | |
62 | # To properly map this controller, ensure your config/routing.py |
|
62 | # To properly map this controller, ensure your config/routing.py | |
63 | # file has a resource setup: |
|
63 | # file has a resource setup: | |
64 | # map.resource('repo', 'repos') |
|
64 | # map.resource('repo', 'repos') | |
65 |
|
65 | |||
66 | @LoginRequired() |
|
66 | @LoginRequired() | |
67 | def __before__(self): |
|
67 | def __before__(self): | |
68 | super(ReposController, self).__before__() |
|
68 | super(ReposController, self).__before__() | |
69 |
|
69 | |||
70 | def _load_repo(self, repo_name): |
|
70 | def _load_repo(self, repo_name): | |
71 | repo_obj = Repository.get_by_repo_name(repo_name) |
|
71 | repo_obj = Repository.get_by_repo_name(repo_name) | |
72 |
|
72 | |||
73 | if repo_obj is None: |
|
73 | if repo_obj is None: | |
74 | h.not_mapped_error(repo_name) |
|
74 | h.not_mapped_error(repo_name) | |
75 | return redirect(url('repos')) |
|
75 | return redirect(url('repos')) | |
76 |
|
76 | |||
77 | return repo_obj |
|
77 | return repo_obj | |
78 |
|
78 | |||
79 | def __load_defaults(self, repo=None): |
|
79 | def __load_defaults(self, repo=None): | |
80 | acl_groups = RepoGroupList(RepoGroup.query().all(), |
|
80 | acl_groups = RepoGroupList(RepoGroup.query().all(), | |
81 | perm_set=['group.write', 'group.admin']) |
|
81 | perm_set=['group.write', 'group.admin']) | |
82 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) |
|
82 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) | |
83 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) |
|
83 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) | |
84 |
|
84 | |||
85 | # in case someone no longer have a group.write access to a repository |
|
85 | # in case someone no longer have a group.write access to a repository | |
86 | # pre fill the list with this entry, we don't care if this is the same |
|
86 | # pre fill the list with this entry, we don't care if this is the same | |
87 | # but it will allow saving repo data properly. |
|
87 | # but it will allow saving repo data properly. | |
88 |
|
88 | |||
89 | repo_group = None |
|
89 | repo_group = None | |
90 | if repo: |
|
90 | if repo: | |
91 | repo_group = repo.group |
|
91 | repo_group = repo.group | |
92 | if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices: |
|
92 | if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices: | |
93 | c.repo_groups_choices.append(unicode(repo_group.group_id)) |
|
93 | c.repo_groups_choices.append(unicode(repo_group.group_id)) | |
94 | c.repo_groups.append(RepoGroup._generate_choice(repo_group)) |
|
94 | c.repo_groups.append(RepoGroup._generate_choice(repo_group)) | |
95 |
|
95 | |||
96 | choices, c.landing_revs = ScmModel().get_repo_landing_revs() |
|
96 | choices, c.landing_revs = ScmModel().get_repo_landing_revs() | |
97 | c.landing_revs_choices = choices |
|
97 | c.landing_revs_choices = choices | |
98 |
|
98 | |||
99 | def __load_data(self, repo_name=None): |
|
99 | def __load_data(self, repo_name=None): | |
100 | """ |
|
100 | """ | |
101 | Load defaults settings for edit, and update |
|
101 | Load defaults settings for edit, and update | |
102 |
|
102 | |||
103 | :param repo_name: |
|
103 | :param repo_name: | |
104 | """ |
|
104 | """ | |
105 | c.repo_info = self._load_repo(repo_name) |
|
105 | c.repo_info = self._load_repo(repo_name) | |
106 | self.__load_defaults(c.repo_info) |
|
106 | self.__load_defaults(c.repo_info) | |
107 |
|
107 | |||
108 | ##override defaults for exact repo info here git/hg etc |
|
108 | ##override defaults for exact repo info here git/hg etc | |
109 | choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info) |
|
109 | choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info) | |
110 | c.landing_revs_choices = choices |
|
110 | c.landing_revs_choices = choices | |
111 | defaults = RepoModel()._get_defaults(repo_name) |
|
111 | defaults = RepoModel()._get_defaults(repo_name) | |
112 |
|
112 | |||
113 | return defaults |
|
113 | return defaults | |
114 |
|
114 | |||
115 | def index(self, format='html'): |
|
115 | def index(self, format='html'): | |
116 | """GET /repos: All items in the collection""" |
|
116 | """GET /repos: All items in the collection""" | |
117 | # url('repos') |
|
117 | # url('repos') | |
118 | _list = Repository.query()\ |
|
118 | _list = Repository.query()\ | |
119 | .order_by(func.lower(Repository.repo_name))\ |
|
119 | .order_by(func.lower(Repository.repo_name))\ | |
120 | .all() |
|
120 | .all() | |
121 |
|
121 | |||
122 | c.repos_list = RepoList(_list, perm_set=['repository.admin']) |
|
122 | c.repos_list = RepoList(_list, perm_set=['repository.admin']) | |
123 | repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list, |
|
123 | repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list, | |
124 | admin=True, |
|
124 | admin=True, | |
125 | super_user_actions=True) |
|
125 | super_user_actions=True) | |
126 | #json used to render the grid |
|
126 | #json used to render the grid | |
127 | c.data = json.dumps(repos_data) |
|
127 | c.data = json.dumps(repos_data) | |
128 |
|
128 | |||
129 | return render('admin/repos/repos.html') |
|
129 | return render('admin/repos/repos.html') | |
130 |
|
130 | |||
131 | @NotAnonymous() |
|
131 | @NotAnonymous() | |
132 | def create(self): |
|
132 | def create(self): | |
133 | """ |
|
133 | """ | |
134 | POST /repos: Create a new item""" |
|
134 | POST /repos: Create a new item""" | |
135 | # url('repos') |
|
135 | # url('repos') | |
136 |
|
136 | |||
137 | self.__load_defaults() |
|
137 | self.__load_defaults() | |
138 | form_result = {} |
|
138 | form_result = {} | |
139 | task_id = None |
|
139 | task_id = None | |
140 | try: |
|
140 | try: | |
141 | # CanWriteToGroup validators checks permissions of this POST |
|
141 | # CanWriteToGroup validators checks permissions of this POST | |
142 | form_result = RepoForm(repo_groups=c.repo_groups_choices, |
|
142 | form_result = RepoForm(repo_groups=c.repo_groups_choices, | |
143 | landing_revs=c.landing_revs_choices)()\ |
|
143 | landing_revs=c.landing_revs_choices)()\ | |
144 | .to_python(dict(request.POST)) |
|
144 | .to_python(dict(request.POST)) | |
145 |
|
145 | |||
146 | # create is done sometimes async on celery, db transaction |
|
146 | # create is done sometimes async on celery, db transaction | |
147 | # management is handled there. |
|
147 | # management is handled there. | |
148 | task = RepoModel().create(form_result, self.authuser.user_id) |
|
148 | task = RepoModel().create(form_result, self.authuser.user_id) | |
149 | from celery.result import BaseAsyncResult |
|
149 | from celery.result import BaseAsyncResult | |
150 | if isinstance(task, BaseAsyncResult): |
|
150 | if isinstance(task, BaseAsyncResult): | |
151 | task_id = task.task_id |
|
151 | task_id = task.task_id | |
152 | except formencode.Invalid, errors: |
|
152 | except formencode.Invalid, errors: | |
153 | return htmlfill.render( |
|
153 | return htmlfill.render( | |
154 | render('admin/repos/repo_add.html'), |
|
154 | render('admin/repos/repo_add.html'), | |
155 | defaults=errors.value, |
|
155 | defaults=errors.value, | |
156 | errors=errors.error_dict or {}, |
|
156 | errors=errors.error_dict or {}, | |
157 | prefix_error=False, |
|
157 | prefix_error=False, | |
|
158 | force_defaults=False, | |||
158 | encoding="UTF-8") |
|
159 | encoding="UTF-8") | |
159 |
|
160 | |||
160 | except Exception: |
|
161 | except Exception: | |
161 | log.error(traceback.format_exc()) |
|
162 | log.error(traceback.format_exc()) | |
162 | msg = (_('Error creating repository %s') |
|
163 | msg = (_('Error creating repository %s') | |
163 | % form_result.get('repo_name')) |
|
164 | % form_result.get('repo_name')) | |
164 | h.flash(msg, category='error') |
|
165 | h.flash(msg, category='error') | |
165 | return redirect(url('home')) |
|
166 | return redirect(url('home')) | |
166 |
|
167 | |||
167 | return redirect(h.url('repo_creating_home', |
|
168 | return redirect(h.url('repo_creating_home', | |
168 | repo_name=form_result['repo_name_full'], |
|
169 | repo_name=form_result['repo_name_full'], | |
169 | task_id=task_id)) |
|
170 | task_id=task_id)) | |
170 |
|
171 | |||
171 | @NotAnonymous() |
|
172 | @NotAnonymous() | |
172 | def create_repository(self): |
|
173 | def create_repository(self): | |
173 | """GET /_admin/create_repository: Form to create a new item""" |
|
174 | """GET /_admin/create_repository: Form to create a new item""" | |
174 | new_repo = request.GET.get('repo', '') |
|
175 | new_repo = request.GET.get('repo', '') | |
175 | parent_group = request.GET.get('parent_group') |
|
176 | parent_group = request.GET.get('parent_group') | |
176 | if not HasPermissionAny('hg.admin', 'hg.create.repository')(): |
|
177 | if not HasPermissionAny('hg.admin', 'hg.create.repository')(): | |
177 | #you're not super admin nor have global create permissions, |
|
178 | #you're not super admin nor have global create permissions, | |
178 | #but maybe you have at least write permission to a parent group ? |
|
179 | #but maybe you have at least write permission to a parent group ? | |
179 | _gr = RepoGroup.get(parent_group) |
|
180 | _gr = RepoGroup.get(parent_group) | |
180 | gr_name = _gr.group_name if _gr else None |
|
181 | gr_name = _gr.group_name if _gr else None | |
181 | # create repositories with write permission on group is set to true |
|
182 | # create repositories with write permission on group is set to true | |
182 | create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')() |
|
183 | create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')() | |
183 | group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name) |
|
184 | group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name) | |
184 | group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name) |
|
185 | group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name) | |
185 | if not (group_admin or (group_write and create_on_write)): |
|
186 | if not (group_admin or (group_write and create_on_write)): | |
186 | raise HTTPForbidden |
|
187 | raise HTTPForbidden | |
187 |
|
188 | |||
188 | acl_groups = RepoGroupList(RepoGroup.query().all(), |
|
189 | acl_groups = RepoGroupList(RepoGroup.query().all(), | |
189 | perm_set=['group.write', 'group.admin']) |
|
190 | perm_set=['group.write', 'group.admin']) | |
190 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) |
|
191 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) | |
191 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) |
|
192 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) | |
192 | choices, c.landing_revs = ScmModel().get_repo_landing_revs() |
|
193 | choices, c.landing_revs = ScmModel().get_repo_landing_revs() | |
193 |
|
194 | |||
194 | c.new_repo = repo_name_slug(new_repo) |
|
195 | c.new_repo = repo_name_slug(new_repo) | |
195 |
|
196 | |||
196 | ## apply the defaults from defaults page |
|
197 | ## apply the defaults from defaults page | |
197 | defaults = Setting.get_default_repo_settings(strip_prefix=True) |
|
198 | defaults = Setting.get_default_repo_settings(strip_prefix=True) | |
198 | if parent_group: |
|
199 | if parent_group: | |
199 | defaults.update({'repo_group': parent_group}) |
|
200 | defaults.update({'repo_group': parent_group}) | |
200 |
|
201 | |||
201 | return htmlfill.render( |
|
202 | return htmlfill.render( | |
202 | render('admin/repos/repo_add.html'), |
|
203 | render('admin/repos/repo_add.html'), | |
203 | defaults=defaults, |
|
204 | defaults=defaults, | |
204 | errors={}, |
|
205 | errors={}, | |
205 | prefix_error=False, |
|
206 | prefix_error=False, | |
206 | encoding="UTF-8" |
|
207 | encoding="UTF-8", | |
207 | ) |
|
208 | force_defaults=False) | |
208 |
|
209 | |||
209 | @LoginRequired() |
|
210 | @LoginRequired() | |
210 | @NotAnonymous() |
|
211 | @NotAnonymous() | |
211 | def repo_creating(self, repo_name): |
|
212 | def repo_creating(self, repo_name): | |
212 | c.repo = repo_name |
|
213 | c.repo = repo_name | |
213 | c.task_id = request.GET.get('task_id') |
|
214 | c.task_id = request.GET.get('task_id') | |
214 | if not c.repo: |
|
215 | if not c.repo: | |
215 | raise HTTPNotFound() |
|
216 | raise HTTPNotFound() | |
216 | return render('admin/repos/repo_creating.html') |
|
217 | return render('admin/repos/repo_creating.html') | |
217 |
|
218 | |||
218 | @LoginRequired() |
|
219 | @LoginRequired() | |
219 | @NotAnonymous() |
|
220 | @NotAnonymous() | |
220 | @jsonify |
|
221 | @jsonify | |
221 | def repo_check(self, repo_name): |
|
222 | def repo_check(self, repo_name): | |
222 | c.repo = repo_name |
|
223 | c.repo = repo_name | |
223 | task_id = request.GET.get('task_id') |
|
224 | task_id = request.GET.get('task_id') | |
224 |
|
225 | |||
225 | if task_id and task_id not in ['None']: |
|
226 | if task_id and task_id not in ['None']: | |
226 | from kallithea import CELERY_ON |
|
227 | from kallithea import CELERY_ON | |
227 | from celery.result import AsyncResult |
|
228 | from celery.result import AsyncResult | |
228 | if CELERY_ON: |
|
229 | if CELERY_ON: | |
229 | task = AsyncResult(task_id) |
|
230 | task = AsyncResult(task_id) | |
230 | if task.failed(): |
|
231 | if task.failed(): | |
231 | raise HTTPInternalServerError(task.traceback) |
|
232 | raise HTTPInternalServerError(task.traceback) | |
232 |
|
233 | |||
233 | repo = Repository.get_by_repo_name(repo_name) |
|
234 | repo = Repository.get_by_repo_name(repo_name) | |
234 | if repo and repo.repo_state == Repository.STATE_CREATED: |
|
235 | if repo and repo.repo_state == Repository.STATE_CREATED: | |
235 | if repo.clone_uri: |
|
236 | if repo.clone_uri: | |
236 | clone_uri = repo.clone_uri_hidden |
|
237 | clone_uri = repo.clone_uri_hidden | |
237 | h.flash(_('Created repository %s from %s') |
|
238 | h.flash(_('Created repository %s from %s') | |
238 | % (repo.repo_name, clone_uri), category='success') |
|
239 | % (repo.repo_name, clone_uri), category='success') | |
239 | else: |
|
240 | else: | |
240 | repo_url = h.link_to(repo.repo_name, |
|
241 | repo_url = h.link_to(repo.repo_name, | |
241 | h.url('summary_home', |
|
242 | h.url('summary_home', | |
242 | repo_name=repo.repo_name)) |
|
243 | repo_name=repo.repo_name)) | |
243 | fork = repo.fork |
|
244 | fork = repo.fork | |
244 | if fork: |
|
245 | if fork: | |
245 | fork_name = fork.repo_name |
|
246 | fork_name = fork.repo_name | |
246 | h.flash(h.literal(_('Forked repository %s as %s') |
|
247 | h.flash(h.literal(_('Forked repository %s as %s') | |
247 | % (fork_name, repo_url)), category='success') |
|
248 | % (fork_name, repo_url)), category='success') | |
248 | else: |
|
249 | else: | |
249 | h.flash(h.literal(_('Created repository %s') % repo_url), |
|
250 | h.flash(h.literal(_('Created repository %s') % repo_url), | |
250 | category='success') |
|
251 | category='success') | |
251 | return {'result': True} |
|
252 | return {'result': True} | |
252 | return {'result': False} |
|
253 | return {'result': False} | |
253 |
|
254 | |||
254 | @HasRepoPermissionAllDecorator('repository.admin') |
|
255 | @HasRepoPermissionAllDecorator('repository.admin') | |
255 | def update(self, repo_name): |
|
256 | def update(self, repo_name): | |
256 | """ |
|
257 | """ | |
257 | PUT /repos/repo_name: Update an existing item""" |
|
258 | PUT /repos/repo_name: Update an existing item""" | |
258 | # Forms posted to this method should contain a hidden field: |
|
259 | # Forms posted to this method should contain a hidden field: | |
259 | # <input type="hidden" name="_method" value="PUT" /> |
|
260 | # <input type="hidden" name="_method" value="PUT" /> | |
260 | # Or using helpers: |
|
261 | # Or using helpers: | |
261 | # h.form(url('repo', repo_name=ID), |
|
262 | # h.form(url('repo', repo_name=ID), | |
262 | # method='put') |
|
263 | # method='put') | |
263 | # url('repo', repo_name=ID) |
|
264 | # url('repo', repo_name=ID) | |
264 | c.repo_info = self._load_repo(repo_name) |
|
265 | c.repo_info = self._load_repo(repo_name) | |
265 | c.active = 'settings' |
|
266 | c.active = 'settings' | |
266 | c.repo_fields = RepositoryField.query()\ |
|
267 | c.repo_fields = RepositoryField.query()\ | |
267 | .filter(RepositoryField.repository == c.repo_info).all() |
|
268 | .filter(RepositoryField.repository == c.repo_info).all() | |
268 | self.__load_defaults(c.repo_info) |
|
269 | self.__load_defaults(c.repo_info) | |
269 |
|
270 | |||
270 | repo_model = RepoModel() |
|
271 | repo_model = RepoModel() | |
271 | changed_name = repo_name |
|
272 | changed_name = repo_name | |
272 | #override the choices with extracted revisions ! |
|
273 | #override the choices with extracted revisions ! | |
273 | choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name) |
|
274 | choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name) | |
274 | c.landing_revs_choices = choices |
|
275 | c.landing_revs_choices = choices | |
275 | repo = Repository.get_by_repo_name(repo_name) |
|
276 | repo = Repository.get_by_repo_name(repo_name) | |
276 | old_data = { |
|
277 | old_data = { | |
277 | 'repo_name': repo_name, |
|
278 | 'repo_name': repo_name, | |
278 | 'repo_group': repo.group.get_dict() if repo.group else {}, |
|
279 | 'repo_group': repo.group.get_dict() if repo.group else {}, | |
279 | 'repo_type': repo.repo_type, |
|
280 | 'repo_type': repo.repo_type, | |
280 | } |
|
281 | } | |
281 | _form = RepoForm(edit=True, old_data=old_data, |
|
282 | _form = RepoForm(edit=True, old_data=old_data, | |
282 | repo_groups=c.repo_groups_choices, |
|
283 | repo_groups=c.repo_groups_choices, | |
283 | landing_revs=c.landing_revs_choices)() |
|
284 | landing_revs=c.landing_revs_choices)() | |
284 |
|
285 | |||
285 | try: |
|
286 | try: | |
286 | form_result = _form.to_python(dict(request.POST)) |
|
287 | form_result = _form.to_python(dict(request.POST)) | |
287 | repo = repo_model.update(repo_name, **form_result) |
|
288 | repo = repo_model.update(repo_name, **form_result) | |
288 | ScmModel().mark_for_invalidation(repo_name) |
|
289 | ScmModel().mark_for_invalidation(repo_name) | |
289 | h.flash(_('Repository %s updated successfully') % repo_name, |
|
290 | h.flash(_('Repository %s updated successfully') % repo_name, | |
290 | category='success') |
|
291 | category='success') | |
291 | changed_name = repo.repo_name |
|
292 | changed_name = repo.repo_name | |
292 | action_logger(self.authuser, 'admin_updated_repo', |
|
293 | action_logger(self.authuser, 'admin_updated_repo', | |
293 | changed_name, self.ip_addr, self.sa) |
|
294 | changed_name, self.ip_addr, self.sa) | |
294 | Session().commit() |
|
295 | Session().commit() | |
295 | except formencode.Invalid, errors: |
|
296 | except formencode.Invalid, errors: | |
296 | defaults = self.__load_data(repo_name) |
|
297 | defaults = self.__load_data(repo_name) | |
297 | defaults.update(errors.value) |
|
298 | defaults.update(errors.value) | |
298 | return htmlfill.render( |
|
299 | return htmlfill.render( | |
299 | render('admin/repos/repo_edit.html'), |
|
300 | render('admin/repos/repo_edit.html'), | |
300 | defaults=defaults, |
|
301 | defaults=defaults, | |
301 | errors=errors.error_dict or {}, |
|
302 | errors=errors.error_dict or {}, | |
302 | prefix_error=False, |
|
303 | prefix_error=False, | |
303 |
encoding="UTF-8" |
|
304 | encoding="UTF-8", | |
|
305 | force_defaults=False) | |||
304 |
|
306 | |||
305 | except Exception: |
|
307 | except Exception: | |
306 | log.error(traceback.format_exc()) |
|
308 | log.error(traceback.format_exc()) | |
307 | h.flash(_('Error occurred during update of repository %s') \ |
|
309 | h.flash(_('Error occurred during update of repository %s') \ | |
308 | % repo_name, category='error') |
|
310 | % repo_name, category='error') | |
309 | return redirect(url('edit_repo', repo_name=changed_name)) |
|
311 | return redirect(url('edit_repo', repo_name=changed_name)) | |
310 |
|
312 | |||
311 | @HasRepoPermissionAllDecorator('repository.admin') |
|
313 | @HasRepoPermissionAllDecorator('repository.admin') | |
312 | def delete(self, repo_name): |
|
314 | def delete(self, repo_name): | |
313 | """ |
|
315 | """ | |
314 | DELETE /repos/repo_name: Delete an existing item""" |
|
316 | DELETE /repos/repo_name: Delete an existing item""" | |
315 | # Forms posted to this method should contain a hidden field: |
|
317 | # Forms posted to this method should contain a hidden field: | |
316 | # <input type="hidden" name="_method" value="DELETE" /> |
|
318 | # <input type="hidden" name="_method" value="DELETE" /> | |
317 | # Or using helpers: |
|
319 | # Or using helpers: | |
318 | # h.form(url('repo', repo_name=ID), |
|
320 | # h.form(url('repo', repo_name=ID), | |
319 | # method='delete') |
|
321 | # method='delete') | |
320 | # url('repo', repo_name=ID) |
|
322 | # url('repo', repo_name=ID) | |
321 |
|
323 | |||
322 | repo_model = RepoModel() |
|
324 | repo_model = RepoModel() | |
323 | repo = repo_model.get_by_repo_name(repo_name) |
|
325 | repo = repo_model.get_by_repo_name(repo_name) | |
324 | if not repo: |
|
326 | if not repo: | |
325 | h.not_mapped_error(repo_name) |
|
327 | h.not_mapped_error(repo_name) | |
326 | return redirect(url('repos')) |
|
328 | return redirect(url('repos')) | |
327 | try: |
|
329 | try: | |
328 | _forks = repo.forks.count() |
|
330 | _forks = repo.forks.count() | |
329 | handle_forks = None |
|
331 | handle_forks = None | |
330 | if _forks and request.POST.get('forks'): |
|
332 | if _forks and request.POST.get('forks'): | |
331 | do = request.POST['forks'] |
|
333 | do = request.POST['forks'] | |
332 | if do == 'detach_forks': |
|
334 | if do == 'detach_forks': | |
333 | handle_forks = 'detach' |
|
335 | handle_forks = 'detach' | |
334 | h.flash(_('Detached %s forks') % _forks, category='success') |
|
336 | h.flash(_('Detached %s forks') % _forks, category='success') | |
335 | elif do == 'delete_forks': |
|
337 | elif do == 'delete_forks': | |
336 | handle_forks = 'delete' |
|
338 | handle_forks = 'delete' | |
337 | h.flash(_('Deleted %s forks') % _forks, category='success') |
|
339 | h.flash(_('Deleted %s forks') % _forks, category='success') | |
338 | repo_model.delete(repo, forks=handle_forks) |
|
340 | repo_model.delete(repo, forks=handle_forks) | |
339 | action_logger(self.authuser, 'admin_deleted_repo', |
|
341 | action_logger(self.authuser, 'admin_deleted_repo', | |
340 | repo_name, self.ip_addr, self.sa) |
|
342 | repo_name, self.ip_addr, self.sa) | |
341 | ScmModel().mark_for_invalidation(repo_name) |
|
343 | ScmModel().mark_for_invalidation(repo_name) | |
342 | h.flash(_('Deleted repository %s') % repo_name, category='success') |
|
344 | h.flash(_('Deleted repository %s') % repo_name, category='success') | |
343 | Session().commit() |
|
345 | Session().commit() | |
344 | except AttachedForksError: |
|
346 | except AttachedForksError: | |
345 | h.flash(_('Cannot delete %s it still contains attached forks') |
|
347 | h.flash(_('Cannot delete %s it still contains attached forks') | |
346 | % repo_name, category='warning') |
|
348 | % repo_name, category='warning') | |
347 |
|
349 | |||
348 | except Exception: |
|
350 | except Exception: | |
349 | log.error(traceback.format_exc()) |
|
351 | log.error(traceback.format_exc()) | |
350 | h.flash(_('An error occurred during deletion of %s') % repo_name, |
|
352 | h.flash(_('An error occurred during deletion of %s') % repo_name, | |
351 | category='error') |
|
353 | category='error') | |
352 |
|
354 | |||
353 | if repo.group: |
|
355 | if repo.group: | |
354 | return redirect(url('repos_group_home', group_name=repo.group.group_name)) |
|
356 | return redirect(url('repos_group_home', group_name=repo.group.group_name)) | |
355 | return redirect(url('repos')) |
|
357 | return redirect(url('repos')) | |
356 |
|
358 | |||
357 | @HasPermissionAllDecorator('hg.admin') |
|
359 | @HasPermissionAllDecorator('hg.admin') | |
358 | def show(self, repo_name, format='html'): |
|
360 | def show(self, repo_name, format='html'): | |
359 | """GET /repos/repo_name: Show a specific item""" |
|
361 | """GET /repos/repo_name: Show a specific item""" | |
360 | # url('repo', repo_name=ID) |
|
362 | # url('repo', repo_name=ID) | |
361 |
|
363 | |||
362 | @HasRepoPermissionAllDecorator('repository.admin') |
|
364 | @HasRepoPermissionAllDecorator('repository.admin') | |
363 | def edit(self, repo_name): |
|
365 | def edit(self, repo_name): | |
364 | """GET /repo_name/settings: Form to edit an existing item""" |
|
366 | """GET /repo_name/settings: Form to edit an existing item""" | |
365 | # url('edit_repo', repo_name=ID) |
|
367 | # url('edit_repo', repo_name=ID) | |
366 | defaults = self.__load_data(repo_name) |
|
368 | defaults = self.__load_data(repo_name) | |
367 | if 'clone_uri' in defaults: |
|
369 | if 'clone_uri' in defaults: | |
368 | del defaults['clone_uri'] |
|
370 | del defaults['clone_uri'] | |
369 |
|
371 | |||
370 | c.repo_fields = RepositoryField.query()\ |
|
372 | c.repo_fields = RepositoryField.query()\ | |
371 | .filter(RepositoryField.repository == c.repo_info).all() |
|
373 | .filter(RepositoryField.repository == c.repo_info).all() | |
372 | c.active = 'settings' |
|
374 | c.active = 'settings' | |
373 | return htmlfill.render( |
|
375 | return htmlfill.render( | |
374 | render('admin/repos/repo_edit.html'), |
|
376 | render('admin/repos/repo_edit.html'), | |
375 | defaults=defaults, |
|
377 | defaults=defaults, | |
376 | encoding="UTF-8", |
|
378 | encoding="UTF-8", | |
377 | force_defaults=False) |
|
379 | force_defaults=False) | |
378 |
|
380 | |||
379 | @HasRepoPermissionAllDecorator('repository.admin') |
|
381 | @HasRepoPermissionAllDecorator('repository.admin') | |
380 | def edit_permissions(self, repo_name): |
|
382 | def edit_permissions(self, repo_name): | |
381 | """GET /repo_name/settings: Form to edit an existing item""" |
|
383 | """GET /repo_name/settings: Form to edit an existing item""" | |
382 | # url('edit_repo', repo_name=ID) |
|
384 | # url('edit_repo', repo_name=ID) | |
383 | c.repo_info = self._load_repo(repo_name) |
|
385 | c.repo_info = self._load_repo(repo_name) | |
384 | repo_model = RepoModel() |
|
386 | repo_model = RepoModel() | |
385 | c.users_array = repo_model.get_users_js() |
|
387 | c.users_array = repo_model.get_users_js() | |
386 | c.user_groups_array = repo_model.get_user_groups_js() |
|
388 | c.user_groups_array = repo_model.get_user_groups_js() | |
387 | c.active = 'permissions' |
|
389 | c.active = 'permissions' | |
388 | defaults = RepoModel()._get_defaults(repo_name) |
|
390 | defaults = RepoModel()._get_defaults(repo_name) | |
389 |
|
391 | |||
390 | return htmlfill.render( |
|
392 | return htmlfill.render( | |
391 | render('admin/repos/repo_edit.html'), |
|
393 | render('admin/repos/repo_edit.html'), | |
392 | defaults=defaults, |
|
394 | defaults=defaults, | |
393 | encoding="UTF-8", |
|
395 | encoding="UTF-8", | |
394 | force_defaults=False) |
|
396 | force_defaults=False) | |
395 |
|
397 | |||
396 | def edit_permissions_update(self, repo_name): |
|
398 | def edit_permissions_update(self, repo_name): | |
397 | form = RepoPermsForm()().to_python(request.POST) |
|
399 | form = RepoPermsForm()().to_python(request.POST) | |
398 | RepoModel()._update_permissions(repo_name, form['perms_new'], |
|
400 | RepoModel()._update_permissions(repo_name, form['perms_new'], | |
399 | form['perms_updates']) |
|
401 | form['perms_updates']) | |
400 | #TODO: implement this |
|
402 | #TODO: implement this | |
401 | #action_logger(self.authuser, 'admin_changed_repo_permissions', |
|
403 | #action_logger(self.authuser, 'admin_changed_repo_permissions', | |
402 | # repo_name, self.ip_addr, self.sa) |
|
404 | # repo_name, self.ip_addr, self.sa) | |
403 | Session().commit() |
|
405 | Session().commit() | |
404 | h.flash(_('Repository permissions updated'), category='success') |
|
406 | h.flash(_('Repository permissions updated'), category='success') | |
405 | return redirect(url('edit_repo_perms', repo_name=repo_name)) |
|
407 | return redirect(url('edit_repo_perms', repo_name=repo_name)) | |
406 |
|
408 | |||
407 | def edit_permissions_revoke(self, repo_name): |
|
409 | def edit_permissions_revoke(self, repo_name): | |
408 | try: |
|
410 | try: | |
409 | obj_type = request.POST.get('obj_type') |
|
411 | obj_type = request.POST.get('obj_type') | |
410 | obj_id = None |
|
412 | obj_id = None | |
411 | if obj_type == 'user': |
|
413 | if obj_type == 'user': | |
412 | obj_id = safe_int(request.POST.get('user_id')) |
|
414 | obj_id = safe_int(request.POST.get('user_id')) | |
413 | elif obj_type == 'user_group': |
|
415 | elif obj_type == 'user_group': | |
414 | obj_id = safe_int(request.POST.get('user_group_id')) |
|
416 | obj_id = safe_int(request.POST.get('user_group_id')) | |
415 |
|
417 | |||
416 | if obj_type == 'user': |
|
418 | if obj_type == 'user': | |
417 | RepoModel().revoke_user_permission(repo=repo_name, user=obj_id) |
|
419 | RepoModel().revoke_user_permission(repo=repo_name, user=obj_id) | |
418 | elif obj_type == 'user_group': |
|
420 | elif obj_type == 'user_group': | |
419 | RepoModel().revoke_user_group_permission( |
|
421 | RepoModel().revoke_user_group_permission( | |
420 | repo=repo_name, group_name=obj_id |
|
422 | repo=repo_name, group_name=obj_id | |
421 | ) |
|
423 | ) | |
422 | #TODO: implement this |
|
424 | #TODO: implement this | |
423 | #action_logger(self.authuser, 'admin_revoked_repo_permissions', |
|
425 | #action_logger(self.authuser, 'admin_revoked_repo_permissions', | |
424 | # repo_name, self.ip_addr, self.sa) |
|
426 | # repo_name, self.ip_addr, self.sa) | |
425 | Session().commit() |
|
427 | Session().commit() | |
426 | except Exception: |
|
428 | except Exception: | |
427 | log.error(traceback.format_exc()) |
|
429 | log.error(traceback.format_exc()) | |
428 | h.flash(_('An error occurred during revoking of permission'), |
|
430 | h.flash(_('An error occurred during revoking of permission'), | |
429 | category='error') |
|
431 | category='error') | |
430 | raise HTTPInternalServerError() |
|
432 | raise HTTPInternalServerError() | |
431 |
|
433 | |||
432 | @HasRepoPermissionAllDecorator('repository.admin') |
|
434 | @HasRepoPermissionAllDecorator('repository.admin') | |
433 | def edit_fields(self, repo_name): |
|
435 | def edit_fields(self, repo_name): | |
434 | """GET /repo_name/settings: Form to edit an existing item""" |
|
436 | """GET /repo_name/settings: Form to edit an existing item""" | |
435 | # url('edit_repo', repo_name=ID) |
|
437 | # url('edit_repo', repo_name=ID) | |
436 | c.repo_info = self._load_repo(repo_name) |
|
438 | c.repo_info = self._load_repo(repo_name) | |
437 | c.repo_fields = RepositoryField.query()\ |
|
439 | c.repo_fields = RepositoryField.query()\ | |
438 | .filter(RepositoryField.repository == c.repo_info).all() |
|
440 | .filter(RepositoryField.repository == c.repo_info).all() | |
439 | c.active = 'fields' |
|
441 | c.active = 'fields' | |
440 | if request.POST: |
|
442 | if request.POST: | |
441 |
|
443 | |||
442 | return redirect(url('repo_edit_fields')) |
|
444 | return redirect(url('repo_edit_fields')) | |
443 | return render('admin/repos/repo_edit.html') |
|
445 | return render('admin/repos/repo_edit.html') | |
444 |
|
446 | |||
445 | @HasRepoPermissionAllDecorator('repository.admin') |
|
447 | @HasRepoPermissionAllDecorator('repository.admin') | |
446 | def create_repo_field(self, repo_name): |
|
448 | def create_repo_field(self, repo_name): | |
447 | try: |
|
449 | try: | |
448 | form_result = RepoFieldForm()().to_python(dict(request.POST)) |
|
450 | form_result = RepoFieldForm()().to_python(dict(request.POST)) | |
449 | new_field = RepositoryField() |
|
451 | new_field = RepositoryField() | |
450 | new_field.repository = Repository.get_by_repo_name(repo_name) |
|
452 | new_field.repository = Repository.get_by_repo_name(repo_name) | |
451 | new_field.field_key = form_result['new_field_key'] |
|
453 | new_field.field_key = form_result['new_field_key'] | |
452 | new_field.field_type = form_result['new_field_type'] # python type |
|
454 | new_field.field_type = form_result['new_field_type'] # python type | |
453 | new_field.field_value = form_result['new_field_value'] # set initial blank value |
|
455 | new_field.field_value = form_result['new_field_value'] # set initial blank value | |
454 | new_field.field_desc = form_result['new_field_desc'] |
|
456 | new_field.field_desc = form_result['new_field_desc'] | |
455 | new_field.field_label = form_result['new_field_label'] |
|
457 | new_field.field_label = form_result['new_field_label'] | |
456 | Session().add(new_field) |
|
458 | Session().add(new_field) | |
457 | Session().commit() |
|
459 | Session().commit() | |
458 | except Exception, e: |
|
460 | except Exception, e: | |
459 | log.error(traceback.format_exc()) |
|
461 | log.error(traceback.format_exc()) | |
460 | msg = _('An error occurred during creation of field') |
|
462 | msg = _('An error occurred during creation of field') | |
461 | if isinstance(e, formencode.Invalid): |
|
463 | if isinstance(e, formencode.Invalid): | |
462 | msg += ". " + e.msg |
|
464 | msg += ". " + e.msg | |
463 | h.flash(msg, category='error') |
|
465 | h.flash(msg, category='error') | |
464 | return redirect(url('edit_repo_fields', repo_name=repo_name)) |
|
466 | return redirect(url('edit_repo_fields', repo_name=repo_name)) | |
465 |
|
467 | |||
466 | @HasRepoPermissionAllDecorator('repository.admin') |
|
468 | @HasRepoPermissionAllDecorator('repository.admin') | |
467 | def delete_repo_field(self, repo_name, field_id): |
|
469 | def delete_repo_field(self, repo_name, field_id): | |
468 | field = RepositoryField.get_or_404(field_id) |
|
470 | field = RepositoryField.get_or_404(field_id) | |
469 | try: |
|
471 | try: | |
470 | Session().delete(field) |
|
472 | Session().delete(field) | |
471 | Session().commit() |
|
473 | Session().commit() | |
472 | except Exception, e: |
|
474 | except Exception, e: | |
473 | log.error(traceback.format_exc()) |
|
475 | log.error(traceback.format_exc()) | |
474 | msg = _('An error occurred during removal of field') |
|
476 | msg = _('An error occurred during removal of field') | |
475 | h.flash(msg, category='error') |
|
477 | h.flash(msg, category='error') | |
476 | return redirect(url('edit_repo_fields', repo_name=repo_name)) |
|
478 | return redirect(url('edit_repo_fields', repo_name=repo_name)) | |
477 |
|
479 | |||
478 | @HasRepoPermissionAllDecorator('repository.admin') |
|
480 | @HasRepoPermissionAllDecorator('repository.admin') | |
479 | def edit_advanced(self, repo_name): |
|
481 | def edit_advanced(self, repo_name): | |
480 | """GET /repo_name/settings: Form to edit an existing item""" |
|
482 | """GET /repo_name/settings: Form to edit an existing item""" | |
481 | # url('edit_repo', repo_name=ID) |
|
483 | # url('edit_repo', repo_name=ID) | |
482 | c.repo_info = self._load_repo(repo_name) |
|
484 | c.repo_info = self._load_repo(repo_name) | |
483 | c.default_user_id = User.get_default_user().user_id |
|
485 | c.default_user_id = User.get_default_user().user_id | |
484 | c.in_public_journal = UserFollowing.query()\ |
|
486 | c.in_public_journal = UserFollowing.query()\ | |
485 | .filter(UserFollowing.user_id == c.default_user_id)\ |
|
487 | .filter(UserFollowing.user_id == c.default_user_id)\ | |
486 | .filter(UserFollowing.follows_repository == c.repo_info).scalar() |
|
488 | .filter(UserFollowing.follows_repository == c.repo_info).scalar() | |
487 |
|
489 | |||
488 | _repos = Repository.query().order_by(Repository.repo_name).all() |
|
490 | _repos = Repository.query().order_by(Repository.repo_name).all() | |
489 | read_access_repos = RepoList(_repos) |
|
491 | read_access_repos = RepoList(_repos) | |
490 | c.repos_list = [(None, _('-- Not a fork --'))] |
|
492 | c.repos_list = [(None, _('-- Not a fork --'))] | |
491 | c.repos_list += [(x.repo_id, x.repo_name) |
|
493 | c.repos_list += [(x.repo_id, x.repo_name) | |
492 | for x in read_access_repos |
|
494 | for x in read_access_repos | |
493 | if x.repo_id != c.repo_info.repo_id] |
|
495 | if x.repo_id != c.repo_info.repo_id] | |
494 |
|
496 | |||
495 | defaults = { |
|
497 | defaults = { | |
496 | 'id_fork_of': c.repo_info.fork.repo_id if c.repo_info.fork else '' |
|
498 | 'id_fork_of': c.repo_info.fork.repo_id if c.repo_info.fork else '' | |
497 | } |
|
499 | } | |
498 |
|
500 | |||
499 | c.active = 'advanced' |
|
501 | c.active = 'advanced' | |
500 | if request.POST: |
|
502 | if request.POST: | |
501 | return redirect(url('repo_edit_advanced')) |
|
503 | return redirect(url('repo_edit_advanced')) | |
502 | return htmlfill.render( |
|
504 | return htmlfill.render( | |
503 | render('admin/repos/repo_edit.html'), |
|
505 | render('admin/repos/repo_edit.html'), | |
504 | defaults=defaults, |
|
506 | defaults=defaults, | |
505 | encoding="UTF-8", |
|
507 | encoding="UTF-8", | |
506 | force_defaults=False) |
|
508 | force_defaults=False) | |
507 |
|
509 | |||
508 | @HasRepoPermissionAllDecorator('repository.admin') |
|
510 | @HasRepoPermissionAllDecorator('repository.admin') | |
509 | def edit_advanced_journal(self, repo_name): |
|
511 | def edit_advanced_journal(self, repo_name): | |
510 | """ |
|
512 | """ | |
511 | Sets this repository to be visible in public journal, |
|
513 | Sets this repository to be visible in public journal, | |
512 | in other words asking default user to follow this repo |
|
514 | in other words asking default user to follow this repo | |
513 |
|
515 | |||
514 | :param repo_name: |
|
516 | :param repo_name: | |
515 | """ |
|
517 | """ | |
516 |
|
518 | |||
517 | cur_token = request.POST.get('auth_token') |
|
519 | cur_token = request.POST.get('auth_token') | |
518 | token = get_token() |
|
520 | token = get_token() | |
519 | if cur_token == token: |
|
521 | if cur_token == token: | |
520 | try: |
|
522 | try: | |
521 | repo_id = Repository.get_by_repo_name(repo_name).repo_id |
|
523 | repo_id = Repository.get_by_repo_name(repo_name).repo_id | |
522 | user_id = User.get_default_user().user_id |
|
524 | user_id = User.get_default_user().user_id | |
523 | self.scm_model.toggle_following_repo(repo_id, user_id) |
|
525 | self.scm_model.toggle_following_repo(repo_id, user_id) | |
524 | h.flash(_('Updated repository visibility in public journal'), |
|
526 | h.flash(_('Updated repository visibility in public journal'), | |
525 | category='success') |
|
527 | category='success') | |
526 | Session().commit() |
|
528 | Session().commit() | |
527 | except Exception: |
|
529 | except Exception: | |
528 | h.flash(_('An error occurred during setting this' |
|
530 | h.flash(_('An error occurred during setting this' | |
529 | ' repository in public journal'), |
|
531 | ' repository in public journal'), | |
530 | category='error') |
|
532 | category='error') | |
531 |
|
533 | |||
532 | else: |
|
534 | else: | |
533 | h.flash(_('Token mismatch'), category='error') |
|
535 | h.flash(_('Token mismatch'), category='error') | |
534 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) |
|
536 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) | |
535 |
|
537 | |||
536 |
|
538 | |||
537 | @HasRepoPermissionAllDecorator('repository.admin') |
|
539 | @HasRepoPermissionAllDecorator('repository.admin') | |
538 | def edit_advanced_fork(self, repo_name): |
|
540 | def edit_advanced_fork(self, repo_name): | |
539 | """ |
|
541 | """ | |
540 | Mark given repository as a fork of another |
|
542 | Mark given repository as a fork of another | |
541 |
|
543 | |||
542 | :param repo_name: |
|
544 | :param repo_name: | |
543 | """ |
|
545 | """ | |
544 | try: |
|
546 | try: | |
545 | fork_id = request.POST.get('id_fork_of') |
|
547 | fork_id = request.POST.get('id_fork_of') | |
546 | repo = ScmModel().mark_as_fork(repo_name, fork_id, |
|
548 | repo = ScmModel().mark_as_fork(repo_name, fork_id, | |
547 | self.authuser.username) |
|
549 | self.authuser.username) | |
548 | fork = repo.fork.repo_name if repo.fork else _('Nothing') |
|
550 | fork = repo.fork.repo_name if repo.fork else _('Nothing') | |
549 | Session().commit() |
|
551 | Session().commit() | |
550 | h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork), |
|
552 | h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork), | |
551 | category='success') |
|
553 | category='success') | |
552 | except RepositoryError, e: |
|
554 | except RepositoryError, e: | |
553 | log.error(traceback.format_exc()) |
|
555 | log.error(traceback.format_exc()) | |
554 | h.flash(str(e), category='error') |
|
556 | h.flash(str(e), category='error') | |
555 | except Exception, e: |
|
557 | except Exception, e: | |
556 | log.error(traceback.format_exc()) |
|
558 | log.error(traceback.format_exc()) | |
557 | h.flash(_('An error occurred during this operation'), |
|
559 | h.flash(_('An error occurred during this operation'), | |
558 | category='error') |
|
560 | category='error') | |
559 |
|
561 | |||
560 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) |
|
562 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) | |
561 |
|
563 | |||
562 | @HasRepoPermissionAllDecorator('repository.admin') |
|
564 | @HasRepoPermissionAllDecorator('repository.admin') | |
563 | def edit_advanced_locking(self, repo_name): |
|
565 | def edit_advanced_locking(self, repo_name): | |
564 | """ |
|
566 | """ | |
565 | Unlock repository when it is locked ! |
|
567 | Unlock repository when it is locked ! | |
566 |
|
568 | |||
567 | :param repo_name: |
|
569 | :param repo_name: | |
568 | """ |
|
570 | """ | |
569 | try: |
|
571 | try: | |
570 | repo = Repository.get_by_repo_name(repo_name) |
|
572 | repo = Repository.get_by_repo_name(repo_name) | |
571 | if request.POST.get('set_lock'): |
|
573 | if request.POST.get('set_lock'): | |
572 | Repository.lock(repo, c.authuser.user_id) |
|
574 | Repository.lock(repo, c.authuser.user_id) | |
573 | h.flash(_('Locked repository'), category='success') |
|
575 | h.flash(_('Locked repository'), category='success') | |
574 | elif request.POST.get('set_unlock'): |
|
576 | elif request.POST.get('set_unlock'): | |
575 | Repository.unlock(repo) |
|
577 | Repository.unlock(repo) | |
576 | h.flash(_('Unlocked repository'), category='success') |
|
578 | h.flash(_('Unlocked repository'), category='success') | |
577 | except Exception, e: |
|
579 | except Exception, e: | |
578 | log.error(traceback.format_exc()) |
|
580 | log.error(traceback.format_exc()) | |
579 | h.flash(_('An error occurred during unlocking'), |
|
581 | h.flash(_('An error occurred during unlocking'), | |
580 | category='error') |
|
582 | category='error') | |
581 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) |
|
583 | return redirect(url('edit_repo_advanced', repo_name=repo_name)) | |
582 |
|
584 | |||
583 | @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') |
|
585 | @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') | |
584 | def toggle_locking(self, repo_name): |
|
586 | def toggle_locking(self, repo_name): | |
585 | """ |
|
587 | """ | |
586 | Toggle locking of repository by simple GET call to url |
|
588 | Toggle locking of repository by simple GET call to url | |
587 |
|
589 | |||
588 | :param repo_name: |
|
590 | :param repo_name: | |
589 | """ |
|
591 | """ | |
590 |
|
592 | |||
591 | try: |
|
593 | try: | |
592 | repo = Repository.get_by_repo_name(repo_name) |
|
594 | repo = Repository.get_by_repo_name(repo_name) | |
593 |
|
595 | |||
594 | if repo.enable_locking: |
|
596 | if repo.enable_locking: | |
595 | if repo.locked[0]: |
|
597 | if repo.locked[0]: | |
596 | Repository.unlock(repo) |
|
598 | Repository.unlock(repo) | |
597 | action = _('Unlocked') |
|
599 | action = _('Unlocked') | |
598 | else: |
|
600 | else: | |
599 | Repository.lock(repo, c.authuser.user_id) |
|
601 | Repository.lock(repo, c.authuser.user_id) | |
600 | action = _('Locked') |
|
602 | action = _('Locked') | |
601 |
|
603 | |||
602 | h.flash(_('Repository has been %s') % action, |
|
604 | h.flash(_('Repository has been %s') % action, | |
603 | category='success') |
|
605 | category='success') | |
604 | except Exception, e: |
|
606 | except Exception, e: | |
605 | log.error(traceback.format_exc()) |
|
607 | log.error(traceback.format_exc()) | |
606 | h.flash(_('An error occurred during unlocking'), |
|
608 | h.flash(_('An error occurred during unlocking'), | |
607 | category='error') |
|
609 | category='error') | |
608 | return redirect(url('summary_home', repo_name=repo_name)) |
|
610 | return redirect(url('summary_home', repo_name=repo_name)) | |
609 |
|
611 | |||
610 | @HasRepoPermissionAllDecorator('repository.admin') |
|
612 | @HasRepoPermissionAllDecorator('repository.admin') | |
611 | def edit_caches(self, repo_name): |
|
613 | def edit_caches(self, repo_name): | |
612 | """GET /repo_name/settings: Form to edit an existing item""" |
|
614 | """GET /repo_name/settings: Form to edit an existing item""" | |
613 | # url('edit_repo', repo_name=ID) |
|
615 | # url('edit_repo', repo_name=ID) | |
614 | c.repo_info = self._load_repo(repo_name) |
|
616 | c.repo_info = self._load_repo(repo_name) | |
615 | c.active = 'caches' |
|
617 | c.active = 'caches' | |
616 | if request.POST: |
|
618 | if request.POST: | |
617 | try: |
|
619 | try: | |
618 | ScmModel().mark_for_invalidation(repo_name, delete=True) |
|
620 | ScmModel().mark_for_invalidation(repo_name, delete=True) | |
619 | Session().commit() |
|
621 | Session().commit() | |
620 | h.flash(_('Cache invalidation successful'), |
|
622 | h.flash(_('Cache invalidation successful'), | |
621 | category='success') |
|
623 | category='success') | |
622 | except Exception, e: |
|
624 | except Exception, e: | |
623 | log.error(traceback.format_exc()) |
|
625 | log.error(traceback.format_exc()) | |
624 | h.flash(_('An error occurred during cache invalidation'), |
|
626 | h.flash(_('An error occurred during cache invalidation'), | |
625 | category='error') |
|
627 | category='error') | |
626 |
|
628 | |||
627 | return redirect(url('edit_repo_caches', repo_name=c.repo_name)) |
|
629 | return redirect(url('edit_repo_caches', repo_name=c.repo_name)) | |
628 | return render('admin/repos/repo_edit.html') |
|
630 | return render('admin/repos/repo_edit.html') | |
629 |
|
631 | |||
630 | @HasRepoPermissionAllDecorator('repository.admin') |
|
632 | @HasRepoPermissionAllDecorator('repository.admin') | |
631 | def edit_remote(self, repo_name): |
|
633 | def edit_remote(self, repo_name): | |
632 | """GET /repo_name/settings: Form to edit an existing item""" |
|
634 | """GET /repo_name/settings: Form to edit an existing item""" | |
633 | # url('edit_repo', repo_name=ID) |
|
635 | # url('edit_repo', repo_name=ID) | |
634 | c.repo_info = self._load_repo(repo_name) |
|
636 | c.repo_info = self._load_repo(repo_name) | |
635 | c.active = 'remote' |
|
637 | c.active = 'remote' | |
636 | if request.POST: |
|
638 | if request.POST: | |
637 | try: |
|
639 | try: | |
638 | ScmModel().pull_changes(repo_name, self.authuser.username) |
|
640 | ScmModel().pull_changes(repo_name, self.authuser.username) | |
639 | h.flash(_('Pulled from remote location'), category='success') |
|
641 | h.flash(_('Pulled from remote location'), category='success') | |
640 | except Exception, e: |
|
642 | except Exception, e: | |
641 | log.error(traceback.format_exc()) |
|
643 | log.error(traceback.format_exc()) | |
642 | h.flash(_('An error occurred during pull from remote location'), |
|
644 | h.flash(_('An error occurred during pull from remote location'), | |
643 | category='error') |
|
645 | category='error') | |
644 | return redirect(url('edit_repo_remote', repo_name=c.repo_name)) |
|
646 | return redirect(url('edit_repo_remote', repo_name=c.repo_name)) | |
645 | return render('admin/repos/repo_edit.html') |
|
647 | return render('admin/repos/repo_edit.html') | |
646 |
|
648 | |||
647 | @HasRepoPermissionAllDecorator('repository.admin') |
|
649 | @HasRepoPermissionAllDecorator('repository.admin') | |
648 | def edit_statistics(self, repo_name): |
|
650 | def edit_statistics(self, repo_name): | |
649 | """GET /repo_name/settings: Form to edit an existing item""" |
|
651 | """GET /repo_name/settings: Form to edit an existing item""" | |
650 | # url('edit_repo', repo_name=ID) |
|
652 | # url('edit_repo', repo_name=ID) | |
651 | c.repo_info = self._load_repo(repo_name) |
|
653 | c.repo_info = self._load_repo(repo_name) | |
652 | repo = c.repo_info.scm_instance |
|
654 | repo = c.repo_info.scm_instance | |
653 |
|
655 | |||
654 | if c.repo_info.stats: |
|
656 | if c.repo_info.stats: | |
655 | # this is on what revision we ended up so we add +1 for count |
|
657 | # this is on what revision we ended up so we add +1 for count | |
656 | last_rev = c.repo_info.stats.stat_on_revision + 1 |
|
658 | last_rev = c.repo_info.stats.stat_on_revision + 1 | |
657 | else: |
|
659 | else: | |
658 | last_rev = 0 |
|
660 | last_rev = 0 | |
659 | c.stats_revision = last_rev |
|
661 | c.stats_revision = last_rev | |
660 |
|
662 | |||
661 | c.repo_last_rev = repo.count() if repo.revisions else 0 |
|
663 | c.repo_last_rev = repo.count() if repo.revisions else 0 | |
662 |
|
664 | |||
663 | if last_rev == 0 or c.repo_last_rev == 0: |
|
665 | if last_rev == 0 or c.repo_last_rev == 0: | |
664 | c.stats_percentage = 0 |
|
666 | c.stats_percentage = 0 | |
665 | else: |
|
667 | else: | |
666 | c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100) |
|
668 | c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100) | |
667 |
|
669 | |||
668 | c.active = 'statistics' |
|
670 | c.active = 'statistics' | |
669 | if request.POST: |
|
671 | if request.POST: | |
670 | try: |
|
672 | try: | |
671 | RepoModel().delete_stats(repo_name) |
|
673 | RepoModel().delete_stats(repo_name) | |
672 | Session().commit() |
|
674 | Session().commit() | |
673 | except Exception, e: |
|
675 | except Exception, e: | |
674 | log.error(traceback.format_exc()) |
|
676 | log.error(traceback.format_exc()) | |
675 | h.flash(_('An error occurred during deletion of repository stats'), |
|
677 | h.flash(_('An error occurred during deletion of repository stats'), | |
676 | category='error') |
|
678 | category='error') | |
677 | return redirect(url('edit_repo_statistics', repo_name=c.repo_name)) |
|
679 | return redirect(url('edit_repo_statistics', repo_name=c.repo_name)) | |
678 |
|
680 | |||
679 | return render('admin/repos/repo_edit.html') |
|
681 | return render('admin/repos/repo_edit.html') |
@@ -1,524 +1,525 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.settings |
|
15 | kallithea.controllers.admin.settings | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | settings controller for Kallithea admin |
|
18 | settings controller for Kallithea admin | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Jul 14, 2010 |
|
22 | :created_on: Jul 14, 2010 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 | import logging |
|
28 | import logging | |
29 | import traceback |
|
29 | import traceback | |
30 | import formencode |
|
30 | import formencode | |
31 |
|
31 | |||
32 | from formencode import htmlfill |
|
32 | from formencode import htmlfill | |
33 | from pylons import request, tmpl_context as c, url, config |
|
33 | from pylons import request, tmpl_context as c, url, config | |
34 | from pylons.controllers.util import redirect |
|
34 | from pylons.controllers.util import redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 |
|
36 | |||
37 | from kallithea.lib import helpers as h |
|
37 | from kallithea.lib import helpers as h | |
38 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator |
|
38 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator | |
39 | from kallithea.lib.base import BaseController, render |
|
39 | from kallithea.lib.base import BaseController, render | |
40 | from kallithea.lib.celerylib import tasks, run_task |
|
40 | from kallithea.lib.celerylib import tasks, run_task | |
41 | from kallithea.lib.exceptions import HgsubversionImportError |
|
41 | from kallithea.lib.exceptions import HgsubversionImportError | |
42 | from kallithea.lib.utils import repo2db_mapper, set_app_settings |
|
42 | from kallithea.lib.utils import repo2db_mapper, set_app_settings | |
43 | from kallithea.model.db import Ui, Repository, Setting |
|
43 | from kallithea.model.db import Ui, Repository, Setting | |
44 | from kallithea.model.forms import ApplicationSettingsForm, \ |
|
44 | from kallithea.model.forms import ApplicationSettingsForm, \ | |
45 | ApplicationUiSettingsForm, ApplicationVisualisationForm |
|
45 | ApplicationUiSettingsForm, ApplicationVisualisationForm | |
46 | from kallithea.model.scm import ScmModel |
|
46 | from kallithea.model.scm import ScmModel | |
47 | from kallithea.model.notification import EmailNotificationModel |
|
47 | from kallithea.model.notification import EmailNotificationModel | |
48 | from kallithea.model.meta import Session |
|
48 | from kallithea.model.meta import Session | |
49 | from kallithea.lib.utils2 import str2bool, safe_unicode |
|
49 | from kallithea.lib.utils2 import str2bool, safe_unicode | |
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 | @LoginRequired() |
|
60 | @LoginRequired() | |
61 | def __before__(self): |
|
61 | def __before__(self): | |
62 | super(SettingsController, self).__before__() |
|
62 | super(SettingsController, self).__before__() | |
63 |
|
63 | |||
64 | def _get_hg_ui_settings(self): |
|
64 | def _get_hg_ui_settings(self): | |
65 | ret = Ui.query().all() |
|
65 | ret = Ui.query().all() | |
66 |
|
66 | |||
67 | if not ret: |
|
67 | if not ret: | |
68 | raise Exception('Could not get application ui settings !') |
|
68 | raise Exception('Could not get application ui settings !') | |
69 | settings = {} |
|
69 | settings = {} | |
70 | for each in ret: |
|
70 | for each in ret: | |
71 | k = each.ui_key |
|
71 | k = each.ui_key | |
72 | v = each.ui_value |
|
72 | v = each.ui_value | |
73 | if k == '/': |
|
73 | if k == '/': | |
74 | k = 'root_path' |
|
74 | k = 'root_path' | |
75 |
|
75 | |||
76 | if k == 'push_ssl': |
|
76 | if k == 'push_ssl': | |
77 | v = str2bool(v) |
|
77 | v = str2bool(v) | |
78 |
|
78 | |||
79 | if k.find('.') != -1: |
|
79 | if k.find('.') != -1: | |
80 | k = k.replace('.', '_') |
|
80 | k = k.replace('.', '_') | |
81 |
|
81 | |||
82 | if each.ui_section in ['hooks', 'extensions']: |
|
82 | if each.ui_section in ['hooks', 'extensions']: | |
83 | v = each.ui_active |
|
83 | v = each.ui_active | |
84 |
|
84 | |||
85 | settings[each.ui_section + '_' + k] = v |
|
85 | settings[each.ui_section + '_' + k] = v | |
86 | return settings |
|
86 | return settings | |
87 |
|
87 | |||
88 | @HasPermissionAllDecorator('hg.admin') |
|
88 | @HasPermissionAllDecorator('hg.admin') | |
89 | def settings_vcs(self): |
|
89 | def settings_vcs(self): | |
90 | """GET /admin/settings: All items in the collection""" |
|
90 | """GET /admin/settings: All items in the collection""" | |
91 | # url('admin_settings') |
|
91 | # url('admin_settings') | |
92 | c.active = 'vcs' |
|
92 | c.active = 'vcs' | |
93 | if request.POST: |
|
93 | if request.POST: | |
94 | application_form = ApplicationUiSettingsForm()() |
|
94 | application_form = ApplicationUiSettingsForm()() | |
95 | try: |
|
95 | try: | |
96 | form_result = application_form.to_python(dict(request.POST)) |
|
96 | form_result = application_form.to_python(dict(request.POST)) | |
97 | except formencode.Invalid, errors: |
|
97 | except formencode.Invalid, errors: | |
98 | return htmlfill.render( |
|
98 | return htmlfill.render( | |
99 | render('admin/settings/settings.html'), |
|
99 | render('admin/settings/settings.html'), | |
100 | defaults=errors.value, |
|
100 | defaults=errors.value, | |
101 | errors=errors.error_dict or {}, |
|
101 | errors=errors.error_dict or {}, | |
102 | prefix_error=False, |
|
102 | prefix_error=False, | |
103 | encoding="UTF-8" |
|
103 | encoding="UTF-8", | |
104 | ) |
|
104 | force_defaults=False) | |
105 |
|
105 | |||
106 | try: |
|
106 | try: | |
107 | sett = Ui.get_by_key('push_ssl') |
|
107 | sett = Ui.get_by_key('push_ssl') | |
108 | sett.ui_value = form_result['web_push_ssl'] |
|
108 | sett.ui_value = form_result['web_push_ssl'] | |
109 | Session().add(sett) |
|
109 | Session().add(sett) | |
110 | if c.visual.allow_repo_location_change: |
|
110 | if c.visual.allow_repo_location_change: | |
111 | sett = Ui.get_by_key('/') |
|
111 | sett = Ui.get_by_key('/') | |
112 | sett.ui_value = form_result['paths_root_path'] |
|
112 | sett.ui_value = form_result['paths_root_path'] | |
113 | Session().add(sett) |
|
113 | Session().add(sett) | |
114 |
|
114 | |||
115 | #HOOKS |
|
115 | #HOOKS | |
116 | sett = Ui.get_by_key(Ui.HOOK_UPDATE) |
|
116 | sett = Ui.get_by_key(Ui.HOOK_UPDATE) | |
117 | sett.ui_active = form_result['hooks_changegroup_update'] |
|
117 | sett.ui_active = form_result['hooks_changegroup_update'] | |
118 | Session().add(sett) |
|
118 | Session().add(sett) | |
119 |
|
119 | |||
120 | sett = Ui.get_by_key(Ui.HOOK_REPO_SIZE) |
|
120 | sett = Ui.get_by_key(Ui.HOOK_REPO_SIZE) | |
121 | sett.ui_active = form_result['hooks_changegroup_repo_size'] |
|
121 | sett.ui_active = form_result['hooks_changegroup_repo_size'] | |
122 | Session().add(sett) |
|
122 | Session().add(sett) | |
123 |
|
123 | |||
124 | sett = Ui.get_by_key(Ui.HOOK_PUSH) |
|
124 | sett = Ui.get_by_key(Ui.HOOK_PUSH) | |
125 | sett.ui_active = form_result['hooks_changegroup_push_logger'] |
|
125 | sett.ui_active = form_result['hooks_changegroup_push_logger'] | |
126 | Session().add(sett) |
|
126 | Session().add(sett) | |
127 |
|
127 | |||
128 | sett = Ui.get_by_key(Ui.HOOK_PULL) |
|
128 | sett = Ui.get_by_key(Ui.HOOK_PULL) | |
129 | sett.ui_active = form_result['hooks_outgoing_pull_logger'] |
|
129 | sett.ui_active = form_result['hooks_outgoing_pull_logger'] | |
130 |
|
130 | |||
131 | Session().add(sett) |
|
131 | Session().add(sett) | |
132 |
|
132 | |||
133 | ## EXTENSIONS |
|
133 | ## EXTENSIONS | |
134 | sett = Ui.get_by_key('largefiles') |
|
134 | sett = Ui.get_by_key('largefiles') | |
135 | if not sett: |
|
135 | if not sett: | |
136 | #make one if it's not there ! |
|
136 | #make one if it's not there ! | |
137 | sett = Ui() |
|
137 | sett = Ui() | |
138 | sett.ui_key = 'largefiles' |
|
138 | sett.ui_key = 'largefiles' | |
139 | sett.ui_section = 'extensions' |
|
139 | sett.ui_section = 'extensions' | |
140 | sett.ui_active = form_result['extensions_largefiles'] |
|
140 | sett.ui_active = form_result['extensions_largefiles'] | |
141 | Session().add(sett) |
|
141 | Session().add(sett) | |
142 |
|
142 | |||
143 | sett = Ui.get_by_key('hgsubversion') |
|
143 | sett = Ui.get_by_key('hgsubversion') | |
144 | if not sett: |
|
144 | if not sett: | |
145 | #make one if it's not there ! |
|
145 | #make one if it's not there ! | |
146 | sett = Ui() |
|
146 | sett = Ui() | |
147 | sett.ui_key = 'hgsubversion' |
|
147 | sett.ui_key = 'hgsubversion' | |
148 | sett.ui_section = 'extensions' |
|
148 | sett.ui_section = 'extensions' | |
149 |
|
149 | |||
150 | sett.ui_active = form_result['extensions_hgsubversion'] |
|
150 | sett.ui_active = form_result['extensions_hgsubversion'] | |
151 | if sett.ui_active: |
|
151 | if sett.ui_active: | |
152 | try: |
|
152 | try: | |
153 | import hgsubversion # pragma: no cover |
|
153 | import hgsubversion # pragma: no cover | |
154 | except ImportError: |
|
154 | except ImportError: | |
155 | raise HgsubversionImportError |
|
155 | raise HgsubversionImportError | |
156 | Session().add(sett) |
|
156 | Session().add(sett) | |
157 |
|
157 | |||
158 | # sett = Ui.get_by_key('hggit') |
|
158 | # sett = Ui.get_by_key('hggit') | |
159 | # if not sett: |
|
159 | # if not sett: | |
160 | # #make one if it's not there ! |
|
160 | # #make one if it's not there ! | |
161 | # sett = Ui() |
|
161 | # sett = Ui() | |
162 | # sett.ui_key = 'hggit' |
|
162 | # sett.ui_key = 'hggit' | |
163 | # sett.ui_section = 'extensions' |
|
163 | # sett.ui_section = 'extensions' | |
164 | # |
|
164 | # | |
165 | # sett.ui_active = form_result['extensions_hggit'] |
|
165 | # sett.ui_active = form_result['extensions_hggit'] | |
166 | # Session().add(sett) |
|
166 | # Session().add(sett) | |
167 |
|
167 | |||
168 | Session().commit() |
|
168 | Session().commit() | |
169 |
|
169 | |||
170 | h.flash(_('Updated VCS settings'), category='success') |
|
170 | h.flash(_('Updated VCS settings'), category='success') | |
171 |
|
171 | |||
172 | except HgsubversionImportError: |
|
172 | except HgsubversionImportError: | |
173 | log.error(traceback.format_exc()) |
|
173 | log.error(traceback.format_exc()) | |
174 | h.flash(_('Unable to activate hgsubversion support. ' |
|
174 | h.flash(_('Unable to activate hgsubversion support. ' | |
175 | 'The "hgsubversion" library is missing'), |
|
175 | 'The "hgsubversion" library is missing'), | |
176 | category='error') |
|
176 | category='error') | |
177 |
|
177 | |||
178 | except Exception: |
|
178 | except Exception: | |
179 | log.error(traceback.format_exc()) |
|
179 | log.error(traceback.format_exc()) | |
180 | h.flash(_('Error occurred during updating ' |
|
180 | h.flash(_('Error occurred during updating ' | |
181 | 'application settings'), category='error') |
|
181 | 'application settings'), category='error') | |
182 |
|
182 | |||
183 | defaults = Setting.get_app_settings() |
|
183 | defaults = Setting.get_app_settings() | |
184 | defaults.update(self._get_hg_ui_settings()) |
|
184 | defaults.update(self._get_hg_ui_settings()) | |
185 |
|
185 | |||
186 | return htmlfill.render( |
|
186 | return htmlfill.render( | |
187 | render('admin/settings/settings.html'), |
|
187 | render('admin/settings/settings.html'), | |
188 | defaults=defaults, |
|
188 | defaults=defaults, | |
189 | encoding="UTF-8", |
|
189 | encoding="UTF-8", | |
190 | force_defaults=False) |
|
190 | force_defaults=False) | |
191 |
|
191 | |||
192 | @HasPermissionAllDecorator('hg.admin') |
|
192 | @HasPermissionAllDecorator('hg.admin') | |
193 | def settings_mapping(self): |
|
193 | def settings_mapping(self): | |
194 | """GET /admin/settings/mapping: All items in the collection""" |
|
194 | """GET /admin/settings/mapping: All items in the collection""" | |
195 | # url('admin_settings_mapping') |
|
195 | # url('admin_settings_mapping') | |
196 | c.active = 'mapping' |
|
196 | c.active = 'mapping' | |
197 | if request.POST: |
|
197 | if request.POST: | |
198 | rm_obsolete = request.POST.get('destroy', False) |
|
198 | rm_obsolete = request.POST.get('destroy', False) | |
199 | install_git_hooks = request.POST.get('hooks', False) |
|
199 | install_git_hooks = request.POST.get('hooks', False) | |
200 | invalidate_cache = request.POST.get('invalidate', False) |
|
200 | invalidate_cache = request.POST.get('invalidate', False) | |
201 | log.debug('rescanning repo location with destroy obsolete=%s and ' |
|
201 | log.debug('rescanning repo location with destroy obsolete=%s and ' | |
202 | 'install git hooks=%s' % (rm_obsolete,install_git_hooks)) |
|
202 | 'install git hooks=%s' % (rm_obsolete,install_git_hooks)) | |
203 |
|
203 | |||
204 | if invalidate_cache: |
|
204 | if invalidate_cache: | |
205 | log.debug('invalidating all repositories cache') |
|
205 | log.debug('invalidating all repositories cache') | |
206 | for repo in Repository.get_all(): |
|
206 | for repo in Repository.get_all(): | |
207 | ScmModel().mark_for_invalidation(repo.repo_name, delete=True) |
|
207 | ScmModel().mark_for_invalidation(repo.repo_name, delete=True) | |
208 |
|
208 | |||
209 | filesystem_repos = ScmModel().repo_scan() |
|
209 | filesystem_repos = ScmModel().repo_scan() | |
210 | added, removed = repo2db_mapper(filesystem_repos, rm_obsolete, |
|
210 | added, removed = repo2db_mapper(filesystem_repos, rm_obsolete, | |
211 | install_git_hook=install_git_hooks, |
|
211 | install_git_hook=install_git_hooks, | |
212 | user=c.authuser.username) |
|
212 | user=c.authuser.username) | |
213 | h.flash(h.literal(_('Repositories successfully rescanned. Added: %s. Removed: %s.') % |
|
213 | h.flash(h.literal(_('Repositories successfully rescanned. Added: %s. Removed: %s.') % | |
214 | (', '.join(h.link_to(safe_unicode(repo_name), h.url('summary_home', repo_name=repo_name)) |
|
214 | (', '.join(h.link_to(safe_unicode(repo_name), h.url('summary_home', repo_name=repo_name)) | |
215 | for repo_name in added) or '-', |
|
215 | for repo_name in added) or '-', | |
216 | ', '.join(h.escape(safe_unicode(repo_name)) for repo_name in removed) or '-')), |
|
216 | ', '.join(h.escape(safe_unicode(repo_name)) for repo_name in removed) or '-')), | |
217 | category='success') |
|
217 | category='success') | |
218 | return redirect(url('admin_settings_mapping')) |
|
218 | return redirect(url('admin_settings_mapping')) | |
219 |
|
219 | |||
220 | defaults = Setting.get_app_settings() |
|
220 | defaults = Setting.get_app_settings() | |
221 | defaults.update(self._get_hg_ui_settings()) |
|
221 | defaults.update(self._get_hg_ui_settings()) | |
222 |
|
222 | |||
223 | return htmlfill.render( |
|
223 | return htmlfill.render( | |
224 | render('admin/settings/settings.html'), |
|
224 | render('admin/settings/settings.html'), | |
225 | defaults=defaults, |
|
225 | defaults=defaults, | |
226 | encoding="UTF-8", |
|
226 | encoding="UTF-8", | |
227 | force_defaults=False) |
|
227 | force_defaults=False) | |
228 |
|
228 | |||
229 | @HasPermissionAllDecorator('hg.admin') |
|
229 | @HasPermissionAllDecorator('hg.admin') | |
230 | def settings_global(self): |
|
230 | def settings_global(self): | |
231 | """GET /admin/settings/global: All items in the collection""" |
|
231 | """GET /admin/settings/global: All items in the collection""" | |
232 | # url('admin_settings_global') |
|
232 | # url('admin_settings_global') | |
233 | c.active = 'global' |
|
233 | c.active = 'global' | |
234 | if request.POST: |
|
234 | if request.POST: | |
235 | application_form = ApplicationSettingsForm()() |
|
235 | application_form = ApplicationSettingsForm()() | |
236 | try: |
|
236 | try: | |
237 | form_result = application_form.to_python(dict(request.POST)) |
|
237 | form_result = application_form.to_python(dict(request.POST)) | |
238 | except formencode.Invalid, errors: |
|
238 | except formencode.Invalid, errors: | |
239 | return htmlfill.render( |
|
239 | return htmlfill.render( | |
240 | render('admin/settings/settings.html'), |
|
240 | render('admin/settings/settings.html'), | |
241 | defaults=errors.value, |
|
241 | defaults=errors.value, | |
242 | errors=errors.error_dict or {}, |
|
242 | errors=errors.error_dict or {}, | |
243 | prefix_error=False, |
|
243 | prefix_error=False, | |
244 |
encoding="UTF-8" |
|
244 | encoding="UTF-8", | |
|
245 | force_defaults=False) | |||
245 |
|
246 | |||
246 | try: |
|
247 | try: | |
247 | sett1 = Setting.create_or_update('title', |
|
248 | sett1 = Setting.create_or_update('title', | |
248 | form_result['title']) |
|
249 | form_result['title']) | |
249 | Session().add(sett1) |
|
250 | Session().add(sett1) | |
250 |
|
251 | |||
251 | sett2 = Setting.create_or_update('realm', |
|
252 | sett2 = Setting.create_or_update('realm', | |
252 | form_result['realm']) |
|
253 | form_result['realm']) | |
253 | Session().add(sett2) |
|
254 | Session().add(sett2) | |
254 |
|
255 | |||
255 | sett3 = Setting.create_or_update('ga_code', |
|
256 | sett3 = Setting.create_or_update('ga_code', | |
256 | form_result['ga_code']) |
|
257 | form_result['ga_code']) | |
257 | Session().add(sett3) |
|
258 | Session().add(sett3) | |
258 |
|
259 | |||
259 | sett4 = Setting.create_or_update('captcha_public_key', |
|
260 | sett4 = Setting.create_or_update('captcha_public_key', | |
260 | form_result['captcha_public_key']) |
|
261 | form_result['captcha_public_key']) | |
261 | Session().add(sett4) |
|
262 | Session().add(sett4) | |
262 |
|
263 | |||
263 | sett5 = Setting.create_or_update('captcha_private_key', |
|
264 | sett5 = Setting.create_or_update('captcha_private_key', | |
264 | form_result['captcha_private_key']) |
|
265 | form_result['captcha_private_key']) | |
265 | Session().add(sett5) |
|
266 | Session().add(sett5) | |
266 |
|
267 | |||
267 | Session().commit() |
|
268 | Session().commit() | |
268 | set_app_settings(config) |
|
269 | set_app_settings(config) | |
269 | h.flash(_('Updated application settings'), category='success') |
|
270 | h.flash(_('Updated application settings'), category='success') | |
270 |
|
271 | |||
271 | except Exception: |
|
272 | except Exception: | |
272 | log.error(traceback.format_exc()) |
|
273 | log.error(traceback.format_exc()) | |
273 | h.flash(_('Error occurred during updating ' |
|
274 | h.flash(_('Error occurred during updating ' | |
274 | 'application settings'), |
|
275 | 'application settings'), | |
275 | category='error') |
|
276 | category='error') | |
276 |
|
277 | |||
277 | return redirect(url('admin_settings_global')) |
|
278 | return redirect(url('admin_settings_global')) | |
278 |
|
279 | |||
279 | defaults = Setting.get_app_settings() |
|
280 | defaults = Setting.get_app_settings() | |
280 | defaults.update(self._get_hg_ui_settings()) |
|
281 | defaults.update(self._get_hg_ui_settings()) | |
281 |
|
282 | |||
282 | return htmlfill.render( |
|
283 | return htmlfill.render( | |
283 | render('admin/settings/settings.html'), |
|
284 | render('admin/settings/settings.html'), | |
284 | defaults=defaults, |
|
285 | defaults=defaults, | |
285 | encoding="UTF-8", |
|
286 | encoding="UTF-8", | |
286 | force_defaults=False) |
|
287 | force_defaults=False) | |
287 |
|
288 | |||
288 | @HasPermissionAllDecorator('hg.admin') |
|
289 | @HasPermissionAllDecorator('hg.admin') | |
289 | def settings_visual(self): |
|
290 | def settings_visual(self): | |
290 | """GET /admin/settings/visual: All items in the collection""" |
|
291 | """GET /admin/settings/visual: All items in the collection""" | |
291 | # url('admin_settings_visual') |
|
292 | # url('admin_settings_visual') | |
292 | c.active = 'visual' |
|
293 | c.active = 'visual' | |
293 | if request.POST: |
|
294 | if request.POST: | |
294 | application_form = ApplicationVisualisationForm()() |
|
295 | application_form = ApplicationVisualisationForm()() | |
295 | try: |
|
296 | try: | |
296 | form_result = application_form.to_python(dict(request.POST)) |
|
297 | form_result = application_form.to_python(dict(request.POST)) | |
297 | except formencode.Invalid, errors: |
|
298 | except formencode.Invalid, errors: | |
298 | return htmlfill.render( |
|
299 | return htmlfill.render( | |
299 | render('admin/settings/settings.html'), |
|
300 | render('admin/settings/settings.html'), | |
300 | defaults=errors.value, |
|
301 | defaults=errors.value, | |
301 | errors=errors.error_dict or {}, |
|
302 | errors=errors.error_dict or {}, | |
302 | prefix_error=False, |
|
303 | prefix_error=False, | |
303 | encoding="UTF-8" |
|
304 | encoding="UTF-8", | |
304 | ) |
|
305 | force_defaults=False) | |
305 |
|
306 | |||
306 | try: |
|
307 | try: | |
307 | settings = [ |
|
308 | settings = [ | |
308 | ('show_public_icon', 'show_public_icon', 'bool'), |
|
309 | ('show_public_icon', 'show_public_icon', 'bool'), | |
309 | ('show_private_icon', 'show_private_icon', 'bool'), |
|
310 | ('show_private_icon', 'show_private_icon', 'bool'), | |
310 | ('stylify_metatags', 'stylify_metatags', 'bool'), |
|
311 | ('stylify_metatags', 'stylify_metatags', 'bool'), | |
311 | ('repository_fields', 'repository_fields', 'bool'), |
|
312 | ('repository_fields', 'repository_fields', 'bool'), | |
312 | ('dashboard_items', 'dashboard_items', 'int'), |
|
313 | ('dashboard_items', 'dashboard_items', 'int'), | |
313 | ('admin_grid_items', 'admin_grid_items', 'int'), |
|
314 | ('admin_grid_items', 'admin_grid_items', 'int'), | |
314 | ('show_version', 'show_version', 'bool'), |
|
315 | ('show_version', 'show_version', 'bool'), | |
315 | ('use_gravatar', 'use_gravatar', 'bool'), |
|
316 | ('use_gravatar', 'use_gravatar', 'bool'), | |
316 | ('gravatar_url', 'gravatar_url', 'unicode'), |
|
317 | ('gravatar_url', 'gravatar_url', 'unicode'), | |
317 | ('clone_uri_tmpl', 'clone_uri_tmpl', 'unicode'), |
|
318 | ('clone_uri_tmpl', 'clone_uri_tmpl', 'unicode'), | |
318 | ] |
|
319 | ] | |
319 | for setting, form_key, type_ in settings: |
|
320 | for setting, form_key, type_ in settings: | |
320 | sett = Setting.create_or_update(setting, |
|
321 | sett = Setting.create_or_update(setting, | |
321 | form_result[form_key], type_) |
|
322 | form_result[form_key], type_) | |
322 | Session().add(sett) |
|
323 | Session().add(sett) | |
323 |
|
324 | |||
324 | Session().commit() |
|
325 | Session().commit() | |
325 | set_app_settings(config) |
|
326 | set_app_settings(config) | |
326 | h.flash(_('Updated visualisation settings'), |
|
327 | h.flash(_('Updated visualisation settings'), | |
327 | category='success') |
|
328 | category='success') | |
328 |
|
329 | |||
329 | except Exception: |
|
330 | except Exception: | |
330 | log.error(traceback.format_exc()) |
|
331 | log.error(traceback.format_exc()) | |
331 | h.flash(_('Error occurred during updating ' |
|
332 | h.flash(_('Error occurred during updating ' | |
332 | 'visualisation settings'), |
|
333 | 'visualisation settings'), | |
333 | category='error') |
|
334 | category='error') | |
334 |
|
335 | |||
335 | return redirect(url('admin_settings_visual')) |
|
336 | return redirect(url('admin_settings_visual')) | |
336 |
|
337 | |||
337 | defaults = Setting.get_app_settings() |
|
338 | defaults = Setting.get_app_settings() | |
338 | defaults.update(self._get_hg_ui_settings()) |
|
339 | defaults.update(self._get_hg_ui_settings()) | |
339 |
|
340 | |||
340 | return htmlfill.render( |
|
341 | return htmlfill.render( | |
341 | render('admin/settings/settings.html'), |
|
342 | render('admin/settings/settings.html'), | |
342 | defaults=defaults, |
|
343 | defaults=defaults, | |
343 | encoding="UTF-8", |
|
344 | encoding="UTF-8", | |
344 | force_defaults=False) |
|
345 | force_defaults=False) | |
345 |
|
346 | |||
346 | @HasPermissionAllDecorator('hg.admin') |
|
347 | @HasPermissionAllDecorator('hg.admin') | |
347 | def settings_email(self): |
|
348 | def settings_email(self): | |
348 | """GET /admin/settings/email: All items in the collection""" |
|
349 | """GET /admin/settings/email: All items in the collection""" | |
349 | # url('admin_settings_email') |
|
350 | # url('admin_settings_email') | |
350 | c.active = 'email' |
|
351 | c.active = 'email' | |
351 | if request.POST: |
|
352 | if request.POST: | |
352 | test_email = request.POST.get('test_email') |
|
353 | test_email = request.POST.get('test_email') | |
353 | test_email_subj = 'Kallithea test email' |
|
354 | test_email_subj = 'Kallithea test email' | |
354 | test_body = ('Kallithea Email test, ' |
|
355 | test_body = ('Kallithea Email test, ' | |
355 | 'Kallithea version: %s' % c.kallithea_version) |
|
356 | 'Kallithea version: %s' % c.kallithea_version) | |
356 | if not test_email: |
|
357 | if not test_email: | |
357 | h.flash(_('Please enter email address'), category='error') |
|
358 | h.flash(_('Please enter email address'), category='error') | |
358 | return redirect(url('admin_settings_email')) |
|
359 | return redirect(url('admin_settings_email')) | |
359 |
|
360 | |||
360 | test_email_txt_body = EmailNotificationModel()\ |
|
361 | test_email_txt_body = EmailNotificationModel()\ | |
361 | .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT, |
|
362 | .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT, | |
362 | 'txt', body=test_body) |
|
363 | 'txt', body=test_body) | |
363 | test_email_html_body = EmailNotificationModel()\ |
|
364 | test_email_html_body = EmailNotificationModel()\ | |
364 | .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT, |
|
365 | .get_email_tmpl(EmailNotificationModel.TYPE_DEFAULT, | |
365 | 'html', body=test_body) |
|
366 | 'html', body=test_body) | |
366 |
|
367 | |||
367 | recipients = [test_email] if test_email else None |
|
368 | recipients = [test_email] if test_email else None | |
368 |
|
369 | |||
369 | run_task(tasks.send_email, recipients, test_email_subj, |
|
370 | run_task(tasks.send_email, recipients, test_email_subj, | |
370 | test_email_txt_body, test_email_html_body) |
|
371 | test_email_txt_body, test_email_html_body) | |
371 |
|
372 | |||
372 | h.flash(_('Send email task created'), category='success') |
|
373 | h.flash(_('Send email task created'), category='success') | |
373 | return redirect(url('admin_settings_email')) |
|
374 | return redirect(url('admin_settings_email')) | |
374 |
|
375 | |||
375 | defaults = Setting.get_app_settings() |
|
376 | defaults = Setting.get_app_settings() | |
376 | defaults.update(self._get_hg_ui_settings()) |
|
377 | defaults.update(self._get_hg_ui_settings()) | |
377 |
|
378 | |||
378 | import kallithea |
|
379 | import kallithea | |
379 | c.ini = kallithea.CONFIG |
|
380 | c.ini = kallithea.CONFIG | |
380 |
|
381 | |||
381 | return htmlfill.render( |
|
382 | return htmlfill.render( | |
382 | render('admin/settings/settings.html'), |
|
383 | render('admin/settings/settings.html'), | |
383 | defaults=defaults, |
|
384 | defaults=defaults, | |
384 | encoding="UTF-8", |
|
385 | encoding="UTF-8", | |
385 | force_defaults=False) |
|
386 | force_defaults=False) | |
386 |
|
387 | |||
387 | @HasPermissionAllDecorator('hg.admin') |
|
388 | @HasPermissionAllDecorator('hg.admin') | |
388 | def settings_hooks(self): |
|
389 | def settings_hooks(self): | |
389 | """GET /admin/settings/hooks: All items in the collection""" |
|
390 | """GET /admin/settings/hooks: All items in the collection""" | |
390 | # url('admin_settings_hooks') |
|
391 | # url('admin_settings_hooks') | |
391 | c.active = 'hooks' |
|
392 | c.active = 'hooks' | |
392 | if request.POST: |
|
393 | if request.POST: | |
393 | if c.visual.allow_custom_hooks_settings: |
|
394 | if c.visual.allow_custom_hooks_settings: | |
394 | ui_key = request.POST.get('new_hook_ui_key') |
|
395 | ui_key = request.POST.get('new_hook_ui_key') | |
395 | ui_value = request.POST.get('new_hook_ui_value') |
|
396 | ui_value = request.POST.get('new_hook_ui_value') | |
396 |
|
397 | |||
397 | hook_id = request.POST.get('hook_id') |
|
398 | hook_id = request.POST.get('hook_id') | |
398 |
|
399 | |||
399 | try: |
|
400 | try: | |
400 | ui_key = ui_key and ui_key.strip() |
|
401 | ui_key = ui_key and ui_key.strip() | |
401 | if ui_value and ui_key: |
|
402 | if ui_value and ui_key: | |
402 | Ui.create_or_update_hook(ui_key, ui_value) |
|
403 | Ui.create_or_update_hook(ui_key, ui_value) | |
403 | h.flash(_('Added new hook'), category='success') |
|
404 | h.flash(_('Added new hook'), category='success') | |
404 | elif hook_id: |
|
405 | elif hook_id: | |
405 | Ui.delete(hook_id) |
|
406 | Ui.delete(hook_id) | |
406 | Session().commit() |
|
407 | Session().commit() | |
407 |
|
408 | |||
408 | # check for edits |
|
409 | # check for edits | |
409 | update = False |
|
410 | update = False | |
410 | _d = request.POST.dict_of_lists() |
|
411 | _d = request.POST.dict_of_lists() | |
411 | for k, v in zip(_d.get('hook_ui_key', []), |
|
412 | for k, v in zip(_d.get('hook_ui_key', []), | |
412 | _d.get('hook_ui_value_new', [])): |
|
413 | _d.get('hook_ui_value_new', [])): | |
413 | Ui.create_or_update_hook(k, v) |
|
414 | Ui.create_or_update_hook(k, v) | |
414 | update = True |
|
415 | update = True | |
415 |
|
416 | |||
416 | if update: |
|
417 | if update: | |
417 | h.flash(_('Updated hooks'), category='success') |
|
418 | h.flash(_('Updated hooks'), category='success') | |
418 | Session().commit() |
|
419 | Session().commit() | |
419 | except Exception: |
|
420 | except Exception: | |
420 | log.error(traceback.format_exc()) |
|
421 | log.error(traceback.format_exc()) | |
421 | h.flash(_('Error occurred during hook creation'), |
|
422 | h.flash(_('Error occurred during hook creation'), | |
422 | category='error') |
|
423 | category='error') | |
423 |
|
424 | |||
424 | return redirect(url('admin_settings_hooks')) |
|
425 | return redirect(url('admin_settings_hooks')) | |
425 |
|
426 | |||
426 | defaults = Setting.get_app_settings() |
|
427 | defaults = Setting.get_app_settings() | |
427 | defaults.update(self._get_hg_ui_settings()) |
|
428 | defaults.update(self._get_hg_ui_settings()) | |
428 |
|
429 | |||
429 | c.hooks = Ui.get_builtin_hooks() |
|
430 | c.hooks = Ui.get_builtin_hooks() | |
430 | c.custom_hooks = Ui.get_custom_hooks() |
|
431 | c.custom_hooks = Ui.get_custom_hooks() | |
431 |
|
432 | |||
432 | return htmlfill.render( |
|
433 | return htmlfill.render( | |
433 | render('admin/settings/settings.html'), |
|
434 | render('admin/settings/settings.html'), | |
434 | defaults=defaults, |
|
435 | defaults=defaults, | |
435 | encoding="UTF-8", |
|
436 | encoding="UTF-8", | |
436 | force_defaults=False) |
|
437 | force_defaults=False) | |
437 |
|
438 | |||
438 | @HasPermissionAllDecorator('hg.admin') |
|
439 | @HasPermissionAllDecorator('hg.admin') | |
439 | def settings_search(self): |
|
440 | def settings_search(self): | |
440 | """GET /admin/settings/search: All items in the collection""" |
|
441 | """GET /admin/settings/search: All items in the collection""" | |
441 | # url('admin_settings_search') |
|
442 | # url('admin_settings_search') | |
442 | c.active = 'search' |
|
443 | c.active = 'search' | |
443 | if request.POST: |
|
444 | if request.POST: | |
444 | repo_location = self._get_hg_ui_settings()['paths_root_path'] |
|
445 | repo_location = self._get_hg_ui_settings()['paths_root_path'] | |
445 | full_index = request.POST.get('full_index', False) |
|
446 | full_index = request.POST.get('full_index', False) | |
446 | run_task(tasks.whoosh_index, repo_location, full_index) |
|
447 | run_task(tasks.whoosh_index, repo_location, full_index) | |
447 | h.flash(_('Whoosh reindex task scheduled'), category='success') |
|
448 | h.flash(_('Whoosh reindex task scheduled'), category='success') | |
448 | return redirect(url('admin_settings_search')) |
|
449 | return redirect(url('admin_settings_search')) | |
449 |
|
450 | |||
450 | defaults = Setting.get_app_settings() |
|
451 | defaults = Setting.get_app_settings() | |
451 | defaults.update(self._get_hg_ui_settings()) |
|
452 | defaults.update(self._get_hg_ui_settings()) | |
452 |
|
453 | |||
453 | return htmlfill.render( |
|
454 | return htmlfill.render( | |
454 | render('admin/settings/settings.html'), |
|
455 | render('admin/settings/settings.html'), | |
455 | defaults=defaults, |
|
456 | defaults=defaults, | |
456 | encoding="UTF-8", |
|
457 | encoding="UTF-8", | |
457 | force_defaults=False) |
|
458 | force_defaults=False) | |
458 |
|
459 | |||
459 | @HasPermissionAllDecorator('hg.admin') |
|
460 | @HasPermissionAllDecorator('hg.admin') | |
460 | def settings_system(self): |
|
461 | def settings_system(self): | |
461 | """GET /admin/settings/system: All items in the collection""" |
|
462 | """GET /admin/settings/system: All items in the collection""" | |
462 | # url('admin_settings_system') |
|
463 | # url('admin_settings_system') | |
463 | c.active = 'system' |
|
464 | c.active = 'system' | |
464 |
|
465 | |||
465 | defaults = Setting.get_app_settings() |
|
466 | defaults = Setting.get_app_settings() | |
466 | defaults.update(self._get_hg_ui_settings()) |
|
467 | defaults.update(self._get_hg_ui_settings()) | |
467 |
|
468 | |||
468 | import kallithea |
|
469 | import kallithea | |
469 | c.ini = kallithea.CONFIG |
|
470 | c.ini = kallithea.CONFIG | |
470 | c.update_url = defaults.get('update_url') |
|
471 | c.update_url = defaults.get('update_url') | |
471 | server_info = Setting.get_server_info() |
|
472 | server_info = Setting.get_server_info() | |
472 | for key, val in server_info.iteritems(): |
|
473 | for key, val in server_info.iteritems(): | |
473 | setattr(c, key, val) |
|
474 | setattr(c, key, val) | |
474 |
|
475 | |||
475 | return htmlfill.render( |
|
476 | return htmlfill.render( | |
476 | render('admin/settings/settings.html'), |
|
477 | render('admin/settings/settings.html'), | |
477 | defaults=defaults, |
|
478 | defaults=defaults, | |
478 | encoding="UTF-8", |
|
479 | encoding="UTF-8", | |
479 | force_defaults=False) |
|
480 | force_defaults=False) | |
480 |
|
481 | |||
481 | @HasPermissionAllDecorator('hg.admin') |
|
482 | @HasPermissionAllDecorator('hg.admin') | |
482 | def settings_system_update(self): |
|
483 | def settings_system_update(self): | |
483 | """GET /admin/settings/system/updates: All items in the collection""" |
|
484 | """GET /admin/settings/system/updates: All items in the collection""" | |
484 | # url('admin_settings_system_update') |
|
485 | # url('admin_settings_system_update') | |
485 | import json |
|
486 | import json | |
486 | import urllib2 |
|
487 | import urllib2 | |
487 | from kallithea.lib.verlib import NormalizedVersion |
|
488 | from kallithea.lib.verlib import NormalizedVersion | |
488 | from kallithea import __version__ |
|
489 | from kallithea import __version__ | |
489 |
|
490 | |||
490 | defaults = Setting.get_app_settings() |
|
491 | defaults = Setting.get_app_settings() | |
491 | defaults.update(self._get_hg_ui_settings()) |
|
492 | defaults.update(self._get_hg_ui_settings()) | |
492 | _update_url = defaults.get('update_url', '') |
|
493 | _update_url = defaults.get('update_url', '') | |
493 | _update_url = "" # FIXME: disabled |
|
494 | _update_url = "" # FIXME: disabled | |
494 |
|
495 | |||
495 | _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s) |
|
496 | _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s) | |
496 | try: |
|
497 | try: | |
497 | import kallithea |
|
498 | import kallithea | |
498 | ver = kallithea.__version__ |
|
499 | ver = kallithea.__version__ | |
499 | log.debug('Checking for upgrade on `%s` server' % _update_url) |
|
500 | log.debug('Checking for upgrade on `%s` server' % _update_url) | |
500 | opener = urllib2.build_opener() |
|
501 | opener = urllib2.build_opener() | |
501 | opener.addheaders = [('User-agent', 'Kallithea-SCM/%s' % ver)] |
|
502 | opener.addheaders = [('User-agent', 'Kallithea-SCM/%s' % ver)] | |
502 | response = opener.open(_update_url) |
|
503 | response = opener.open(_update_url) | |
503 | response_data = response.read() |
|
504 | response_data = response.read() | |
504 | data = json.loads(response_data) |
|
505 | data = json.loads(response_data) | |
505 | except urllib2.URLError, e: |
|
506 | except urllib2.URLError, e: | |
506 | log.error(traceback.format_exc()) |
|
507 | log.error(traceback.format_exc()) | |
507 | return _err('Failed to contact upgrade server: %r' % e) |
|
508 | return _err('Failed to contact upgrade server: %r' % e) | |
508 | except ValueError, e: |
|
509 | except ValueError, e: | |
509 | log.error(traceback.format_exc()) |
|
510 | log.error(traceback.format_exc()) | |
510 | return _err('Bad data sent from update server') |
|
511 | return _err('Bad data sent from update server') | |
511 |
|
512 | |||
512 | latest = data['versions'][0] |
|
513 | latest = data['versions'][0] | |
513 |
|
514 | |||
514 | c.update_url = _update_url |
|
515 | c.update_url = _update_url | |
515 | c.latest_data = latest |
|
516 | c.latest_data = latest | |
516 | c.latest_ver = latest['version'] |
|
517 | c.latest_ver = latest['version'] | |
517 | c.cur_ver = __version__ |
|
518 | c.cur_ver = __version__ | |
518 | c.should_upgrade = False |
|
519 | c.should_upgrade = False | |
519 |
|
520 | |||
520 | if NormalizedVersion(c.latest_ver) > NormalizedVersion(c.cur_ver): |
|
521 | if NormalizedVersion(c.latest_ver) > NormalizedVersion(c.cur_ver): | |
521 | c.should_upgrade = True |
|
522 | c.should_upgrade = True | |
522 | c.important_notices = latest['general'] |
|
523 | c.important_notices = latest['general'] | |
523 |
|
524 | |||
524 | return render('admin/settings/settings_system_update.html'), |
|
525 | return render('admin/settings/settings_system_update.html'), |
@@ -1,464 +1,466 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.users_groups |
|
15 | kallithea.controllers.admin.users_groups | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | User Groups crud controller for pylons |
|
18 | User Groups crud controller for pylons | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Jan 25, 2011 |
|
22 | :created_on: Jan 25, 2011 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 | import logging |
|
28 | import logging | |
29 | import traceback |
|
29 | import traceback | |
30 | import formencode |
|
30 | import formencode | |
31 |
|
31 | |||
32 | from formencode import htmlfill |
|
32 | from formencode import htmlfill | |
33 | from pylons import request, tmpl_context as c, url, config |
|
33 | from pylons import request, tmpl_context as c, url, config | |
34 | from pylons.controllers.util import redirect |
|
34 | from pylons.controllers.util import redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 |
|
36 | |||
37 | from sqlalchemy.orm import joinedload |
|
37 | from sqlalchemy.orm import joinedload | |
38 | from sqlalchemy.sql.expression import func |
|
38 | from sqlalchemy.sql.expression import func | |
39 | from webob.exc import HTTPInternalServerError |
|
39 | from webob.exc import HTTPInternalServerError | |
40 |
|
40 | |||
41 | import kallithea |
|
41 | import kallithea | |
42 | from kallithea.lib import helpers as h |
|
42 | from kallithea.lib import helpers as h | |
43 | from kallithea.lib.exceptions import UserGroupsAssignedException,\ |
|
43 | from kallithea.lib.exceptions import UserGroupsAssignedException,\ | |
44 | RepoGroupAssignmentError |
|
44 | RepoGroupAssignmentError | |
45 | from kallithea.lib.utils2 import safe_unicode, safe_int |
|
45 | from kallithea.lib.utils2 import safe_unicode, safe_int | |
46 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator,\ |
|
46 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator,\ | |
47 | HasUserGroupPermissionAnyDecorator, HasPermissionAnyDecorator |
|
47 | HasUserGroupPermissionAnyDecorator, HasPermissionAnyDecorator | |
48 | from kallithea.lib.base import BaseController, render |
|
48 | from kallithea.lib.base import BaseController, render | |
49 | from kallithea.model.scm import UserGroupList |
|
49 | from kallithea.model.scm import UserGroupList | |
50 | from kallithea.model.user_group import UserGroupModel |
|
50 | from kallithea.model.user_group import UserGroupModel | |
51 | from kallithea.model.repo import RepoModel |
|
51 | from kallithea.model.repo import RepoModel | |
52 | from kallithea.model.db import User, UserGroup, UserGroupToPerm,\ |
|
52 | from kallithea.model.db import User, UserGroup, UserGroupToPerm,\ | |
53 | UserGroupRepoToPerm, UserGroupRepoGroupToPerm |
|
53 | UserGroupRepoToPerm, UserGroupRepoGroupToPerm | |
54 | from kallithea.model.forms import UserGroupForm, UserGroupPermsForm,\ |
|
54 | from kallithea.model.forms import UserGroupForm, UserGroupPermsForm,\ | |
55 | CustomDefaultPermissionsForm |
|
55 | CustomDefaultPermissionsForm | |
56 | from kallithea.model.meta import Session |
|
56 | from kallithea.model.meta import Session | |
57 | from kallithea.lib.utils import action_logger |
|
57 | from kallithea.lib.utils import action_logger | |
58 | from kallithea.lib.compat import json |
|
58 | from kallithea.lib.compat import json | |
59 |
|
59 | |||
60 | log = logging.getLogger(__name__) |
|
60 | log = logging.getLogger(__name__) | |
61 |
|
61 | |||
62 |
|
62 | |||
63 | class UserGroupsController(BaseController): |
|
63 | class UserGroupsController(BaseController): | |
64 | """REST Controller styled on the Atom Publishing Protocol""" |
|
64 | """REST Controller styled on the Atom Publishing Protocol""" | |
65 |
|
65 | |||
66 | @LoginRequired() |
|
66 | @LoginRequired() | |
67 | def __before__(self): |
|
67 | def __before__(self): | |
68 | super(UserGroupsController, self).__before__() |
|
68 | super(UserGroupsController, self).__before__() | |
69 | c.available_permissions = config['available_permissions'] |
|
69 | c.available_permissions = config['available_permissions'] | |
70 |
|
70 | |||
71 | def __load_data(self, user_group_id): |
|
71 | def __load_data(self, user_group_id): | |
72 | c.group_members_obj = sorted((x.user for x in c.user_group.members), |
|
72 | c.group_members_obj = sorted((x.user for x in c.user_group.members), | |
73 | key=lambda u: u.username.lower()) |
|
73 | key=lambda u: u.username.lower()) | |
74 |
|
74 | |||
75 | c.group_members = [(x.user_id, x.username) for x in c.group_members_obj] |
|
75 | c.group_members = [(x.user_id, x.username) for x in c.group_members_obj] | |
76 | c.available_members = sorted(((x.user_id, x.username) for x in |
|
76 | c.available_members = sorted(((x.user_id, x.username) for x in | |
77 | User.query().all()), |
|
77 | User.query().all()), | |
78 | key=lambda u: u[1].lower()) |
|
78 | key=lambda u: u[1].lower()) | |
79 |
|
79 | |||
80 | def __load_defaults(self, user_group_id): |
|
80 | def __load_defaults(self, user_group_id): | |
81 | """ |
|
81 | """ | |
82 | Load defaults settings for edit, and update |
|
82 | Load defaults settings for edit, and update | |
83 |
|
83 | |||
84 | :param user_group_id: |
|
84 | :param user_group_id: | |
85 | """ |
|
85 | """ | |
86 | user_group = UserGroup.get_or_404(user_group_id) |
|
86 | user_group = UserGroup.get_or_404(user_group_id) | |
87 | data = user_group.get_dict() |
|
87 | data = user_group.get_dict() | |
88 | return data |
|
88 | return data | |
89 |
|
89 | |||
90 | def index(self, format='html'): |
|
90 | def index(self, format='html'): | |
91 | """GET /users_groups: All items in the collection""" |
|
91 | """GET /users_groups: All items in the collection""" | |
92 | # url('users_groups') |
|
92 | # url('users_groups') | |
93 | _list = UserGroup.query()\ |
|
93 | _list = UserGroup.query()\ | |
94 | .order_by(func.lower(UserGroup.users_group_name))\ |
|
94 | .order_by(func.lower(UserGroup.users_group_name))\ | |
95 | .all() |
|
95 | .all() | |
96 | group_iter = UserGroupList(_list, perm_set=['usergroup.admin']) |
|
96 | group_iter = UserGroupList(_list, perm_set=['usergroup.admin']) | |
97 | user_groups_data = [] |
|
97 | user_groups_data = [] | |
98 | total_records = len(group_iter) |
|
98 | total_records = len(group_iter) | |
99 | _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup |
|
99 | _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup | |
100 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') |
|
100 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') | |
101 |
|
101 | |||
102 | user_group_name = lambda user_group_id, user_group_name: ( |
|
102 | user_group_name = lambda user_group_id, user_group_name: ( | |
103 | template.get_def("user_group_name") |
|
103 | template.get_def("user_group_name") | |
104 | .render(user_group_id, user_group_name, _=_, h=h, c=c) |
|
104 | .render(user_group_id, user_group_name, _=_, h=h, c=c) | |
105 | ) |
|
105 | ) | |
106 | user_group_actions = lambda user_group_id, user_group_name: ( |
|
106 | user_group_actions = lambda user_group_id, user_group_name: ( | |
107 | template.get_def("user_group_actions") |
|
107 | template.get_def("user_group_actions") | |
108 | .render(user_group_id, user_group_name, _=_, h=h, c=c) |
|
108 | .render(user_group_id, user_group_name, _=_, h=h, c=c) | |
109 | ) |
|
109 | ) | |
110 | for user_gr in group_iter: |
|
110 | for user_gr in group_iter: | |
111 |
|
111 | |||
112 | user_groups_data.append({ |
|
112 | user_groups_data.append({ | |
113 | "raw_name": user_gr.users_group_name, |
|
113 | "raw_name": user_gr.users_group_name, | |
114 | "group_name": user_group_name(user_gr.users_group_id, |
|
114 | "group_name": user_group_name(user_gr.users_group_id, | |
115 | user_gr.users_group_name), |
|
115 | user_gr.users_group_name), | |
116 | "desc": user_gr.user_group_description, |
|
116 | "desc": user_gr.user_group_description, | |
117 | "members": len(user_gr.members), |
|
117 | "members": len(user_gr.members), | |
118 | "active": h.boolicon(user_gr.users_group_active), |
|
118 | "active": h.boolicon(user_gr.users_group_active), | |
119 | "owner": h.person(user_gr.user.username), |
|
119 | "owner": h.person(user_gr.user.username), | |
120 | "action": user_group_actions(user_gr.users_group_id, user_gr.users_group_name) |
|
120 | "action": user_group_actions(user_gr.users_group_id, user_gr.users_group_name) | |
121 | }) |
|
121 | }) | |
122 |
|
122 | |||
123 | c.data = json.dumps({ |
|
123 | c.data = json.dumps({ | |
124 | "totalRecords": total_records, |
|
124 | "totalRecords": total_records, | |
125 | "startIndex": 0, |
|
125 | "startIndex": 0, | |
126 | "sort": None, |
|
126 | "sort": None, | |
127 | "dir": "asc", |
|
127 | "dir": "asc", | |
128 | "records": user_groups_data |
|
128 | "records": user_groups_data | |
129 | }) |
|
129 | }) | |
130 |
|
130 | |||
131 | return render('admin/user_groups/user_groups.html') |
|
131 | return render('admin/user_groups/user_groups.html') | |
132 |
|
132 | |||
133 | @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true') |
|
133 | @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true') | |
134 | def create(self): |
|
134 | def create(self): | |
135 | """POST /users_groups: Create a new item""" |
|
135 | """POST /users_groups: Create a new item""" | |
136 | # url('users_groups') |
|
136 | # url('users_groups') | |
137 |
|
137 | |||
138 | users_group_form = UserGroupForm()() |
|
138 | users_group_form = UserGroupForm()() | |
139 | try: |
|
139 | try: | |
140 | form_result = users_group_form.to_python(dict(request.POST)) |
|
140 | form_result = users_group_form.to_python(dict(request.POST)) | |
141 | ug = UserGroupModel().create(name=form_result['users_group_name'], |
|
141 | ug = UserGroupModel().create(name=form_result['users_group_name'], | |
142 | description=form_result['user_group_description'], |
|
142 | description=form_result['user_group_description'], | |
143 | owner=self.authuser.user_id, |
|
143 | owner=self.authuser.user_id, | |
144 | active=form_result['users_group_active']) |
|
144 | active=form_result['users_group_active']) | |
145 |
|
145 | |||
146 | gr = form_result['users_group_name'] |
|
146 | gr = form_result['users_group_name'] | |
147 | action_logger(self.authuser, |
|
147 | action_logger(self.authuser, | |
148 | 'admin_created_users_group:%s' % gr, |
|
148 | 'admin_created_users_group:%s' % gr, | |
149 | None, self.ip_addr, self.sa) |
|
149 | None, self.ip_addr, self.sa) | |
150 | h.flash(h.literal(_('Created user group %s') % h.link_to(h.escape(gr), url('edit_users_group', id=ug.users_group_id))), |
|
150 | h.flash(h.literal(_('Created user group %s') % h.link_to(h.escape(gr), url('edit_users_group', id=ug.users_group_id))), | |
151 | category='success') |
|
151 | category='success') | |
152 | Session().commit() |
|
152 | Session().commit() | |
153 | except formencode.Invalid, errors: |
|
153 | except formencode.Invalid, errors: | |
154 | return htmlfill.render( |
|
154 | return htmlfill.render( | |
155 | render('admin/user_groups/user_group_add.html'), |
|
155 | render('admin/user_groups/user_group_add.html'), | |
156 | defaults=errors.value, |
|
156 | defaults=errors.value, | |
157 | errors=errors.error_dict or {}, |
|
157 | errors=errors.error_dict or {}, | |
158 | prefix_error=False, |
|
158 | prefix_error=False, | |
159 |
encoding="UTF-8" |
|
159 | encoding="UTF-8", | |
|
160 | force_defaults=False) | |||
160 | except Exception: |
|
161 | except Exception: | |
161 | log.error(traceback.format_exc()) |
|
162 | log.error(traceback.format_exc()) | |
162 | h.flash(_('Error occurred during creation of user group %s') \ |
|
163 | h.flash(_('Error occurred during creation of user group %s') \ | |
163 | % request.POST.get('users_group_name'), category='error') |
|
164 | % request.POST.get('users_group_name'), category='error') | |
164 |
|
165 | |||
165 | return redirect(url('users_groups')) |
|
166 | return redirect(url('users_groups')) | |
166 |
|
167 | |||
167 | @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true') |
|
168 | @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true') | |
168 | def new(self, format='html'): |
|
169 | def new(self, format='html'): | |
169 | """GET /user_groups/new: Form to create a new item""" |
|
170 | """GET /user_groups/new: Form to create a new item""" | |
170 | # url('new_users_group') |
|
171 | # url('new_users_group') | |
171 | return render('admin/user_groups/user_group_add.html') |
|
172 | return render('admin/user_groups/user_group_add.html') | |
172 |
|
173 | |||
173 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
174 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
174 | def update(self, id): |
|
175 | def update(self, id): | |
175 | """PUT /user_groups/id: Update an existing item""" |
|
176 | """PUT /user_groups/id: Update an existing item""" | |
176 | # Forms posted to this method should contain a hidden field: |
|
177 | # Forms posted to this method should contain a hidden field: | |
177 | # <input type="hidden" name="_method" value="PUT" /> |
|
178 | # <input type="hidden" name="_method" value="PUT" /> | |
178 | # Or using helpers: |
|
179 | # Or using helpers: | |
179 | # h.form(url('users_group', id=ID), |
|
180 | # h.form(url('users_group', id=ID), | |
180 | # method='put') |
|
181 | # method='put') | |
181 | # url('users_group', id=ID) |
|
182 | # url('users_group', id=ID) | |
182 |
|
183 | |||
183 | c.user_group = UserGroup.get_or_404(id) |
|
184 | c.user_group = UserGroup.get_or_404(id) | |
184 | c.active = 'settings' |
|
185 | c.active = 'settings' | |
185 | self.__load_data(id) |
|
186 | self.__load_data(id) | |
186 |
|
187 | |||
187 | available_members = [safe_unicode(x[0]) for x in c.available_members] |
|
188 | available_members = [safe_unicode(x[0]) for x in c.available_members] | |
188 |
|
189 | |||
189 | users_group_form = UserGroupForm(edit=True, |
|
190 | users_group_form = UserGroupForm(edit=True, | |
190 | old_data=c.user_group.get_dict(), |
|
191 | old_data=c.user_group.get_dict(), | |
191 | available_members=available_members)() |
|
192 | available_members=available_members)() | |
192 |
|
193 | |||
193 | try: |
|
194 | try: | |
194 | form_result = users_group_form.to_python(request.POST) |
|
195 | form_result = users_group_form.to_python(request.POST) | |
195 | UserGroupModel().update(c.user_group, form_result) |
|
196 | UserGroupModel().update(c.user_group, form_result) | |
196 | gr = form_result['users_group_name'] |
|
197 | gr = form_result['users_group_name'] | |
197 | action_logger(self.authuser, |
|
198 | action_logger(self.authuser, | |
198 | 'admin_updated_users_group:%s' % gr, |
|
199 | 'admin_updated_users_group:%s' % gr, | |
199 | None, self.ip_addr, self.sa) |
|
200 | None, self.ip_addr, self.sa) | |
200 | h.flash(_('Updated user group %s') % gr, category='success') |
|
201 | h.flash(_('Updated user group %s') % gr, category='success') | |
201 | Session().commit() |
|
202 | Session().commit() | |
202 | except formencode.Invalid, errors: |
|
203 | except formencode.Invalid, errors: | |
203 | ug_model = UserGroupModel() |
|
204 | ug_model = UserGroupModel() | |
204 | defaults = errors.value |
|
205 | defaults = errors.value | |
205 | e = errors.error_dict or {} |
|
206 | e = errors.error_dict or {} | |
206 | defaults.update({ |
|
207 | defaults.update({ | |
207 | 'create_repo_perm': ug_model.has_perm(id, |
|
208 | 'create_repo_perm': ug_model.has_perm(id, | |
208 | 'hg.create.repository'), |
|
209 | 'hg.create.repository'), | |
209 | 'fork_repo_perm': ug_model.has_perm(id, |
|
210 | 'fork_repo_perm': ug_model.has_perm(id, | |
210 | 'hg.fork.repository'), |
|
211 | 'hg.fork.repository'), | |
211 | '_method': 'put' |
|
212 | '_method': 'put' | |
212 | }) |
|
213 | }) | |
213 |
|
214 | |||
214 | return htmlfill.render( |
|
215 | return htmlfill.render( | |
215 | render('admin/user_groups/user_group_edit.html'), |
|
216 | render('admin/user_groups/user_group_edit.html'), | |
216 | defaults=defaults, |
|
217 | defaults=defaults, | |
217 | errors=e, |
|
218 | errors=e, | |
218 | prefix_error=False, |
|
219 | prefix_error=False, | |
219 |
encoding="UTF-8" |
|
220 | encoding="UTF-8", | |
|
221 | force_defaults=False) | |||
220 | except Exception: |
|
222 | except Exception: | |
221 | log.error(traceback.format_exc()) |
|
223 | log.error(traceback.format_exc()) | |
222 | h.flash(_('Error occurred during update of user group %s') \ |
|
224 | h.flash(_('Error occurred during update of user group %s') \ | |
223 | % request.POST.get('users_group_name'), category='error') |
|
225 | % request.POST.get('users_group_name'), category='error') | |
224 |
|
226 | |||
225 | return redirect(url('edit_users_group', id=id)) |
|
227 | return redirect(url('edit_users_group', id=id)) | |
226 |
|
228 | |||
227 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
229 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
228 | def delete(self, id): |
|
230 | def delete(self, id): | |
229 | """DELETE /user_groups/id: Delete an existing item""" |
|
231 | """DELETE /user_groups/id: Delete an existing item""" | |
230 | # Forms posted to this method should contain a hidden field: |
|
232 | # Forms posted to this method should contain a hidden field: | |
231 | # <input type="hidden" name="_method" value="DELETE" /> |
|
233 | # <input type="hidden" name="_method" value="DELETE" /> | |
232 | # Or using helpers: |
|
234 | # Or using helpers: | |
233 | # h.form(url('users_group', id=ID), |
|
235 | # h.form(url('users_group', id=ID), | |
234 | # method='delete') |
|
236 | # method='delete') | |
235 | # url('users_group', id=ID) |
|
237 | # url('users_group', id=ID) | |
236 | usr_gr = UserGroup.get_or_404(id) |
|
238 | usr_gr = UserGroup.get_or_404(id) | |
237 | try: |
|
239 | try: | |
238 | UserGroupModel().delete(usr_gr) |
|
240 | UserGroupModel().delete(usr_gr) | |
239 | Session().commit() |
|
241 | Session().commit() | |
240 | h.flash(_('Successfully deleted user group'), category='success') |
|
242 | h.flash(_('Successfully deleted user group'), category='success') | |
241 | except UserGroupsAssignedException, e: |
|
243 | except UserGroupsAssignedException, e: | |
242 | h.flash(e, category='error') |
|
244 | h.flash(e, category='error') | |
243 | except Exception: |
|
245 | except Exception: | |
244 | log.error(traceback.format_exc()) |
|
246 | log.error(traceback.format_exc()) | |
245 | h.flash(_('An error occurred during deletion of user group'), |
|
247 | h.flash(_('An error occurred during deletion of user group'), | |
246 | category='error') |
|
248 | category='error') | |
247 | return redirect(url('users_groups')) |
|
249 | return redirect(url('users_groups')) | |
248 |
|
250 | |||
249 | def show(self, id, format='html'): |
|
251 | def show(self, id, format='html'): | |
250 | """GET /user_groups/id: Show a specific item""" |
|
252 | """GET /user_groups/id: Show a specific item""" | |
251 | # url('users_group', id=ID) |
|
253 | # url('users_group', id=ID) | |
252 |
|
254 | |||
253 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
255 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
254 | def edit(self, id, format='html'): |
|
256 | def edit(self, id, format='html'): | |
255 | """GET /user_groups/id/edit: Form to edit an existing item""" |
|
257 | """GET /user_groups/id/edit: Form to edit an existing item""" | |
256 | # url('edit_users_group', id=ID) |
|
258 | # url('edit_users_group', id=ID) | |
257 |
|
259 | |||
258 | c.user_group = UserGroup.get_or_404(id) |
|
260 | c.user_group = UserGroup.get_or_404(id) | |
259 | c.active = 'settings' |
|
261 | c.active = 'settings' | |
260 | self.__load_data(id) |
|
262 | self.__load_data(id) | |
261 |
|
263 | |||
262 | defaults = self.__load_defaults(id) |
|
264 | defaults = self.__load_defaults(id) | |
263 |
|
265 | |||
264 | return htmlfill.render( |
|
266 | return htmlfill.render( | |
265 | render('admin/user_groups/user_group_edit.html'), |
|
267 | render('admin/user_groups/user_group_edit.html'), | |
266 | defaults=defaults, |
|
268 | defaults=defaults, | |
267 | encoding="UTF-8", |
|
269 | encoding="UTF-8", | |
268 | force_defaults=False |
|
270 | force_defaults=False | |
269 | ) |
|
271 | ) | |
270 |
|
272 | |||
271 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
273 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
272 | def edit_perms(self, id): |
|
274 | def edit_perms(self, id): | |
273 | c.user_group = UserGroup.get_or_404(id) |
|
275 | c.user_group = UserGroup.get_or_404(id) | |
274 | c.active = 'perms' |
|
276 | c.active = 'perms' | |
275 |
|
277 | |||
276 | repo_model = RepoModel() |
|
278 | repo_model = RepoModel() | |
277 | c.users_array = repo_model.get_users_js() |
|
279 | c.users_array = repo_model.get_users_js() | |
278 | c.user_groups_array = repo_model.get_user_groups_js() |
|
280 | c.user_groups_array = repo_model.get_user_groups_js() | |
279 |
|
281 | |||
280 | defaults = {} |
|
282 | defaults = {} | |
281 | # fill user group users |
|
283 | # fill user group users | |
282 | for p in c.user_group.user_user_group_to_perm: |
|
284 | for p in c.user_group.user_user_group_to_perm: | |
283 | defaults.update({'u_perm_%s' % p.user.username: |
|
285 | defaults.update({'u_perm_%s' % p.user.username: | |
284 | p.permission.permission_name}) |
|
286 | p.permission.permission_name}) | |
285 |
|
287 | |||
286 | for p in c.user_group.user_group_user_group_to_perm: |
|
288 | for p in c.user_group.user_group_user_group_to_perm: | |
287 | defaults.update({'g_perm_%s' % p.user_group.users_group_name: |
|
289 | defaults.update({'g_perm_%s' % p.user_group.users_group_name: | |
288 | p.permission.permission_name}) |
|
290 | p.permission.permission_name}) | |
289 |
|
291 | |||
290 | return htmlfill.render( |
|
292 | return htmlfill.render( | |
291 | render('admin/user_groups/user_group_edit.html'), |
|
293 | render('admin/user_groups/user_group_edit.html'), | |
292 | defaults=defaults, |
|
294 | defaults=defaults, | |
293 | encoding="UTF-8", |
|
295 | encoding="UTF-8", | |
294 | force_defaults=False |
|
296 | force_defaults=False | |
295 | ) |
|
297 | ) | |
296 |
|
298 | |||
297 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
299 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
298 | def update_perms(self, id): |
|
300 | def update_perms(self, id): | |
299 | """ |
|
301 | """ | |
300 | grant permission for given usergroup |
|
302 | grant permission for given usergroup | |
301 |
|
303 | |||
302 | :param id: |
|
304 | :param id: | |
303 | """ |
|
305 | """ | |
304 | user_group = UserGroup.get_or_404(id) |
|
306 | user_group = UserGroup.get_or_404(id) | |
305 | form = UserGroupPermsForm()().to_python(request.POST) |
|
307 | form = UserGroupPermsForm()().to_python(request.POST) | |
306 |
|
308 | |||
307 | # set the permissions ! |
|
309 | # set the permissions ! | |
308 | try: |
|
310 | try: | |
309 | UserGroupModel()._update_permissions(user_group, form['perms_new'], |
|
311 | UserGroupModel()._update_permissions(user_group, form['perms_new'], | |
310 | form['perms_updates']) |
|
312 | form['perms_updates']) | |
311 | except RepoGroupAssignmentError: |
|
313 | except RepoGroupAssignmentError: | |
312 | h.flash(_('Target group cannot be the same'), category='error') |
|
314 | h.flash(_('Target group cannot be the same'), category='error') | |
313 | return redirect(url('edit_user_group_perms', id=id)) |
|
315 | return redirect(url('edit_user_group_perms', id=id)) | |
314 | #TODO: implement this |
|
316 | #TODO: implement this | |
315 | #action_logger(self.authuser, 'admin_changed_repo_permissions', |
|
317 | #action_logger(self.authuser, 'admin_changed_repo_permissions', | |
316 | # repo_name, self.ip_addr, self.sa) |
|
318 | # repo_name, self.ip_addr, self.sa) | |
317 | Session().commit() |
|
319 | Session().commit() | |
318 | h.flash(_('User Group permissions updated'), category='success') |
|
320 | h.flash(_('User Group permissions updated'), category='success') | |
319 | return redirect(url('edit_user_group_perms', id=id)) |
|
321 | return redirect(url('edit_user_group_perms', id=id)) | |
320 |
|
322 | |||
321 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
323 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
322 | def delete_perms(self, id): |
|
324 | def delete_perms(self, id): | |
323 | """ |
|
325 | """ | |
324 | DELETE an existing repository group permission user |
|
326 | DELETE an existing repository group permission user | |
325 |
|
327 | |||
326 | :param group_name: |
|
328 | :param group_name: | |
327 | """ |
|
329 | """ | |
328 | try: |
|
330 | try: | |
329 | obj_type = request.POST.get('obj_type') |
|
331 | obj_type = request.POST.get('obj_type') | |
330 | obj_id = None |
|
332 | obj_id = None | |
331 | if obj_type == 'user': |
|
333 | if obj_type == 'user': | |
332 | obj_id = safe_int(request.POST.get('user_id')) |
|
334 | obj_id = safe_int(request.POST.get('user_id')) | |
333 | elif obj_type == 'user_group': |
|
335 | elif obj_type == 'user_group': | |
334 | obj_id = safe_int(request.POST.get('user_group_id')) |
|
336 | obj_id = safe_int(request.POST.get('user_group_id')) | |
335 |
|
337 | |||
336 | if not c.authuser.is_admin: |
|
338 | if not c.authuser.is_admin: | |
337 | if obj_type == 'user' and c.authuser.user_id == obj_id: |
|
339 | if obj_type == 'user' and c.authuser.user_id == obj_id: | |
338 | msg = _('Cannot revoke permission for yourself as admin') |
|
340 | msg = _('Cannot revoke permission for yourself as admin') | |
339 | h.flash(msg, category='warning') |
|
341 | h.flash(msg, category='warning') | |
340 | raise Exception('revoke admin permission on self') |
|
342 | raise Exception('revoke admin permission on self') | |
341 | if obj_type == 'user': |
|
343 | if obj_type == 'user': | |
342 | UserGroupModel().revoke_user_permission(user_group=id, |
|
344 | UserGroupModel().revoke_user_permission(user_group=id, | |
343 | user=obj_id) |
|
345 | user=obj_id) | |
344 | elif obj_type == 'user_group': |
|
346 | elif obj_type == 'user_group': | |
345 | UserGroupModel().revoke_user_group_permission(target_user_group=id, |
|
347 | UserGroupModel().revoke_user_group_permission(target_user_group=id, | |
346 | user_group=obj_id) |
|
348 | user_group=obj_id) | |
347 | Session().commit() |
|
349 | Session().commit() | |
348 | except Exception: |
|
350 | except Exception: | |
349 | log.error(traceback.format_exc()) |
|
351 | log.error(traceback.format_exc()) | |
350 | h.flash(_('An error occurred during revoking of permission'), |
|
352 | h.flash(_('An error occurred during revoking of permission'), | |
351 | category='error') |
|
353 | category='error') | |
352 | raise HTTPInternalServerError() |
|
354 | raise HTTPInternalServerError() | |
353 |
|
355 | |||
354 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
356 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
355 | def edit_default_perms(self, id): |
|
357 | def edit_default_perms(self, id): | |
356 | c.user_group = UserGroup.get_or_404(id) |
|
358 | c.user_group = UserGroup.get_or_404(id) | |
357 | c.active = 'default_perms' |
|
359 | c.active = 'default_perms' | |
358 |
|
360 | |||
359 | permissions = { |
|
361 | permissions = { | |
360 | 'repositories': {}, |
|
362 | 'repositories': {}, | |
361 | 'repositories_groups': {} |
|
363 | 'repositories_groups': {} | |
362 | } |
|
364 | } | |
363 | ugroup_repo_perms = UserGroupRepoToPerm.query()\ |
|
365 | ugroup_repo_perms = UserGroupRepoToPerm.query()\ | |
364 | .options(joinedload(UserGroupRepoToPerm.permission))\ |
|
366 | .options(joinedload(UserGroupRepoToPerm.permission))\ | |
365 | .options(joinedload(UserGroupRepoToPerm.repository))\ |
|
367 | .options(joinedload(UserGroupRepoToPerm.repository))\ | |
366 | .filter(UserGroupRepoToPerm.users_group_id == id)\ |
|
368 | .filter(UserGroupRepoToPerm.users_group_id == id)\ | |
367 | .all() |
|
369 | .all() | |
368 |
|
370 | |||
369 | for gr in ugroup_repo_perms: |
|
371 | for gr in ugroup_repo_perms: | |
370 | permissions['repositories'][gr.repository.repo_name] \ |
|
372 | permissions['repositories'][gr.repository.repo_name] \ | |
371 | = gr.permission.permission_name |
|
373 | = gr.permission.permission_name | |
372 |
|
374 | |||
373 | ugroup_group_perms = UserGroupRepoGroupToPerm.query()\ |
|
375 | ugroup_group_perms = UserGroupRepoGroupToPerm.query()\ | |
374 | .options(joinedload(UserGroupRepoGroupToPerm.permission))\ |
|
376 | .options(joinedload(UserGroupRepoGroupToPerm.permission))\ | |
375 | .options(joinedload(UserGroupRepoGroupToPerm.group))\ |
|
377 | .options(joinedload(UserGroupRepoGroupToPerm.group))\ | |
376 | .filter(UserGroupRepoGroupToPerm.users_group_id == id)\ |
|
378 | .filter(UserGroupRepoGroupToPerm.users_group_id == id)\ | |
377 | .all() |
|
379 | .all() | |
378 |
|
380 | |||
379 | for gr in ugroup_group_perms: |
|
381 | for gr in ugroup_group_perms: | |
380 | permissions['repositories_groups'][gr.group.group_name] \ |
|
382 | permissions['repositories_groups'][gr.group.group_name] \ | |
381 | = gr.permission.permission_name |
|
383 | = gr.permission.permission_name | |
382 | c.permissions = permissions |
|
384 | c.permissions = permissions | |
383 |
|
385 | |||
384 | ug_model = UserGroupModel() |
|
386 | ug_model = UserGroupModel() | |
385 |
|
387 | |||
386 | defaults = c.user_group.get_dict() |
|
388 | defaults = c.user_group.get_dict() | |
387 | defaults.update({ |
|
389 | defaults.update({ | |
388 | 'create_repo_perm': ug_model.has_perm(c.user_group, |
|
390 | 'create_repo_perm': ug_model.has_perm(c.user_group, | |
389 | 'hg.create.repository'), |
|
391 | 'hg.create.repository'), | |
390 | 'create_user_group_perm': ug_model.has_perm(c.user_group, |
|
392 | 'create_user_group_perm': ug_model.has_perm(c.user_group, | |
391 | 'hg.usergroup.create.true'), |
|
393 | 'hg.usergroup.create.true'), | |
392 | 'fork_repo_perm': ug_model.has_perm(c.user_group, |
|
394 | 'fork_repo_perm': ug_model.has_perm(c.user_group, | |
393 | 'hg.fork.repository'), |
|
395 | 'hg.fork.repository'), | |
394 | }) |
|
396 | }) | |
395 |
|
397 | |||
396 | return htmlfill.render( |
|
398 | return htmlfill.render( | |
397 | render('admin/user_groups/user_group_edit.html'), |
|
399 | render('admin/user_groups/user_group_edit.html'), | |
398 | defaults=defaults, |
|
400 | defaults=defaults, | |
399 | encoding="UTF-8", |
|
401 | encoding="UTF-8", | |
400 | force_defaults=False |
|
402 | force_defaults=False | |
401 | ) |
|
403 | ) | |
402 |
|
404 | |||
403 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
405 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
404 | def update_default_perms(self, id): |
|
406 | def update_default_perms(self, id): | |
405 | """PUT /users_perm/id: Update an existing item""" |
|
407 | """PUT /users_perm/id: Update an existing item""" | |
406 | # url('users_group_perm', id=ID, method='put') |
|
408 | # url('users_group_perm', id=ID, method='put') | |
407 |
|
409 | |||
408 | user_group = UserGroup.get_or_404(id) |
|
410 | user_group = UserGroup.get_or_404(id) | |
409 |
|
411 | |||
410 | try: |
|
412 | try: | |
411 | form = CustomDefaultPermissionsForm()() |
|
413 | form = CustomDefaultPermissionsForm()() | |
412 | form_result = form.to_python(request.POST) |
|
414 | form_result = form.to_python(request.POST) | |
413 |
|
415 | |||
414 | inherit_perms = form_result['inherit_default_permissions'] |
|
416 | inherit_perms = form_result['inherit_default_permissions'] | |
415 | user_group.inherit_default_permissions = inherit_perms |
|
417 | user_group.inherit_default_permissions = inherit_perms | |
416 | Session().add(user_group) |
|
418 | Session().add(user_group) | |
417 | usergroup_model = UserGroupModel() |
|
419 | usergroup_model = UserGroupModel() | |
418 |
|
420 | |||
419 | defs = UserGroupToPerm.query()\ |
|
421 | defs = UserGroupToPerm.query()\ | |
420 | .filter(UserGroupToPerm.users_group == user_group)\ |
|
422 | .filter(UserGroupToPerm.users_group == user_group)\ | |
421 | .all() |
|
423 | .all() | |
422 | for ug in defs: |
|
424 | for ug in defs: | |
423 | Session().delete(ug) |
|
425 | Session().delete(ug) | |
424 |
|
426 | |||
425 | if form_result['create_repo_perm']: |
|
427 | if form_result['create_repo_perm']: | |
426 | usergroup_model.grant_perm(id, 'hg.create.repository') |
|
428 | usergroup_model.grant_perm(id, 'hg.create.repository') | |
427 | else: |
|
429 | else: | |
428 | usergroup_model.grant_perm(id, 'hg.create.none') |
|
430 | usergroup_model.grant_perm(id, 'hg.create.none') | |
429 | if form_result['create_user_group_perm']: |
|
431 | if form_result['create_user_group_perm']: | |
430 | usergroup_model.grant_perm(id, 'hg.usergroup.create.true') |
|
432 | usergroup_model.grant_perm(id, 'hg.usergroup.create.true') | |
431 | else: |
|
433 | else: | |
432 | usergroup_model.grant_perm(id, 'hg.usergroup.create.false') |
|
434 | usergroup_model.grant_perm(id, 'hg.usergroup.create.false') | |
433 | if form_result['fork_repo_perm']: |
|
435 | if form_result['fork_repo_perm']: | |
434 | usergroup_model.grant_perm(id, 'hg.fork.repository') |
|
436 | usergroup_model.grant_perm(id, 'hg.fork.repository') | |
435 | else: |
|
437 | else: | |
436 | usergroup_model.grant_perm(id, 'hg.fork.none') |
|
438 | usergroup_model.grant_perm(id, 'hg.fork.none') | |
437 |
|
439 | |||
438 | h.flash(_("Updated permissions"), category='success') |
|
440 | h.flash(_("Updated permissions"), category='success') | |
439 | Session().commit() |
|
441 | Session().commit() | |
440 | except Exception: |
|
442 | except Exception: | |
441 | log.error(traceback.format_exc()) |
|
443 | log.error(traceback.format_exc()) | |
442 | h.flash(_('An error occurred during permissions saving'), |
|
444 | h.flash(_('An error occurred during permissions saving'), | |
443 | category='error') |
|
445 | category='error') | |
444 |
|
446 | |||
445 | return redirect(url('edit_user_group_default_perms', id=id)) |
|
447 | return redirect(url('edit_user_group_default_perms', id=id)) | |
446 |
|
448 | |||
447 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
449 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
448 | def edit_advanced(self, id): |
|
450 | def edit_advanced(self, id): | |
449 | c.user_group = UserGroup.get_or_404(id) |
|
451 | c.user_group = UserGroup.get_or_404(id) | |
450 | c.active = 'advanced' |
|
452 | c.active = 'advanced' | |
451 | c.group_members_obj = sorted((x.user for x in c.user_group.members), |
|
453 | c.group_members_obj = sorted((x.user for x in c.user_group.members), | |
452 | key=lambda u: u.username.lower()) |
|
454 | key=lambda u: u.username.lower()) | |
453 | return render('admin/user_groups/user_group_edit.html') |
|
455 | return render('admin/user_groups/user_group_edit.html') | |
454 |
|
456 | |||
455 |
|
457 | |||
456 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') |
|
458 | @HasUserGroupPermissionAnyDecorator('usergroup.admin') | |
457 | def edit_members(self, id): |
|
459 | def edit_members(self, id): | |
458 | c.user_group = UserGroup.get_or_404(id) |
|
460 | c.user_group = UserGroup.get_or_404(id) | |
459 | c.active = 'members' |
|
461 | c.active = 'members' | |
460 | c.group_members_obj = sorted((x.user for x in c.user_group.members), |
|
462 | c.group_members_obj = sorted((x.user for x in c.user_group.members), | |
461 | key=lambda u: u.username.lower()) |
|
463 | key=lambda u: u.username.lower()) | |
462 |
|
464 | |||
463 | c.group_members = [(x.user_id, x.username) for x in c.group_members_obj] |
|
465 | c.group_members = [(x.user_id, x.username) for x in c.group_members_obj] | |
464 | return render('admin/user_groups/user_group_edit.html') |
|
466 | return render('admin/user_groups/user_group_edit.html') |
@@ -1,504 +1,506 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.admin.users |
|
15 | kallithea.controllers.admin.users | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | Users crud controller for pylons |
|
18 | Users crud controller for pylons | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Apr 4, 2010 |
|
22 | :created_on: Apr 4, 2010 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 | import logging |
|
28 | import logging | |
29 | import traceback |
|
29 | import traceback | |
30 | import formencode |
|
30 | import formencode | |
31 |
|
31 | |||
32 | from formencode import htmlfill |
|
32 | from formencode import htmlfill | |
33 | from pylons import request, tmpl_context as c, url, config |
|
33 | from pylons import request, tmpl_context as c, url, config | |
34 | from pylons.controllers.util import redirect |
|
34 | from pylons.controllers.util import redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 | from sqlalchemy.sql.expression import func |
|
36 | from sqlalchemy.sql.expression import func | |
37 |
|
37 | |||
38 | import kallithea |
|
38 | import kallithea | |
39 | from kallithea.lib.exceptions import DefaultUserException, \ |
|
39 | from kallithea.lib.exceptions import DefaultUserException, \ | |
40 | UserOwnsReposException, UserCreationError |
|
40 | UserOwnsReposException, UserCreationError | |
41 | from kallithea.lib import helpers as h |
|
41 | from kallithea.lib import helpers as h | |
42 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator, \ |
|
42 | from kallithea.lib.auth import LoginRequired, HasPermissionAllDecorator, \ | |
43 | AuthUser, generate_api_key |
|
43 | AuthUser, generate_api_key | |
44 | import kallithea.lib.auth_modules.auth_internal |
|
44 | import kallithea.lib.auth_modules.auth_internal | |
45 | from kallithea.lib import auth_modules |
|
45 | from kallithea.lib import auth_modules | |
46 | from kallithea.lib.base import BaseController, render |
|
46 | from kallithea.lib.base import BaseController, render | |
47 | from kallithea.model.api_key import ApiKeyModel |
|
47 | from kallithea.model.api_key import ApiKeyModel | |
48 |
|
48 | |||
49 | from kallithea.model.db import User, UserEmailMap, UserIpMap, UserToPerm |
|
49 | from kallithea.model.db import User, UserEmailMap, UserIpMap, UserToPerm | |
50 | from kallithea.model.forms import UserForm, CustomDefaultPermissionsForm |
|
50 | from kallithea.model.forms import UserForm, CustomDefaultPermissionsForm | |
51 | from kallithea.model.user import UserModel |
|
51 | from kallithea.model.user import UserModel | |
52 | from kallithea.model.meta import Session |
|
52 | from kallithea.model.meta import Session | |
53 | from kallithea.lib.utils import action_logger |
|
53 | from kallithea.lib.utils import action_logger | |
54 | from kallithea.lib.compat import json |
|
54 | from kallithea.lib.compat import json | |
55 | from kallithea.lib.utils2 import datetime_to_time, safe_int |
|
55 | from kallithea.lib.utils2 import datetime_to_time, safe_int | |
56 |
|
56 | |||
57 | log = logging.getLogger(__name__) |
|
57 | log = logging.getLogger(__name__) | |
58 |
|
58 | |||
59 |
|
59 | |||
60 | class UsersController(BaseController): |
|
60 | class UsersController(BaseController): | |
61 | """REST Controller styled on the Atom Publishing Protocol""" |
|
61 | """REST Controller styled on the Atom Publishing Protocol""" | |
62 |
|
62 | |||
63 | @LoginRequired() |
|
63 | @LoginRequired() | |
64 | @HasPermissionAllDecorator('hg.admin') |
|
64 | @HasPermissionAllDecorator('hg.admin') | |
65 | def __before__(self): |
|
65 | def __before__(self): | |
66 | super(UsersController, self).__before__() |
|
66 | super(UsersController, self).__before__() | |
67 | c.available_permissions = config['available_permissions'] |
|
67 | c.available_permissions = config['available_permissions'] | |
68 | c.EXTERN_TYPE_INTERNAL = kallithea.EXTERN_TYPE_INTERNAL |
|
68 | c.EXTERN_TYPE_INTERNAL = kallithea.EXTERN_TYPE_INTERNAL | |
69 |
|
69 | |||
70 | def index(self, format='html'): |
|
70 | def index(self, format='html'): | |
71 | """GET /users: All items in the collection""" |
|
71 | """GET /users: All items in the collection""" | |
72 | # url('users') |
|
72 | # url('users') | |
73 |
|
73 | |||
74 | c.users_list = User.query().order_by(User.username)\ |
|
74 | c.users_list = User.query().order_by(User.username)\ | |
75 | .filter(User.username != User.DEFAULT_USER)\ |
|
75 | .filter(User.username != User.DEFAULT_USER)\ | |
76 | .order_by(func.lower(User.username))\ |
|
76 | .order_by(func.lower(User.username))\ | |
77 | .all() |
|
77 | .all() | |
78 |
|
78 | |||
79 | users_data = [] |
|
79 | users_data = [] | |
80 | total_records = len(c.users_list) |
|
80 | total_records = len(c.users_list) | |
81 | _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup |
|
81 | _tmpl_lookup = kallithea.CONFIG['pylons.app_globals'].mako_lookup | |
82 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') |
|
82 | template = _tmpl_lookup.get_template('data_table/_dt_elements.html') | |
83 |
|
83 | |||
84 | grav_tmpl = '<div class="gravatar">%s</div>' |
|
84 | grav_tmpl = '<div class="gravatar">%s</div>' | |
85 |
|
85 | |||
86 | username = lambda user_id, username: ( |
|
86 | username = lambda user_id, username: ( | |
87 | template.get_def("user_name") |
|
87 | template.get_def("user_name") | |
88 | .render(user_id, username, _=_, h=h, c=c)) |
|
88 | .render(user_id, username, _=_, h=h, c=c)) | |
89 |
|
89 | |||
90 | user_actions = lambda user_id, username: ( |
|
90 | user_actions = lambda user_id, username: ( | |
91 | template.get_def("user_actions") |
|
91 | template.get_def("user_actions") | |
92 | .render(user_id, username, _=_, h=h, c=c)) |
|
92 | .render(user_id, username, _=_, h=h, c=c)) | |
93 |
|
93 | |||
94 | for user in c.users_list: |
|
94 | for user in c.users_list: | |
95 | users_data.append({ |
|
95 | users_data.append({ | |
96 | "gravatar": grav_tmpl % h.gravatar(user.email, size=20), |
|
96 | "gravatar": grav_tmpl % h.gravatar(user.email, size=20), | |
97 | "raw_name": user.username, |
|
97 | "raw_name": user.username, | |
98 | "username": username(user.user_id, user.username), |
|
98 | "username": username(user.user_id, user.username), | |
99 | "firstname": user.name, |
|
99 | "firstname": user.name, | |
100 | "lastname": user.lastname, |
|
100 | "lastname": user.lastname, | |
101 | "last_login": h.fmt_date(user.last_login), |
|
101 | "last_login": h.fmt_date(user.last_login), | |
102 | "last_login_raw": datetime_to_time(user.last_login), |
|
102 | "last_login_raw": datetime_to_time(user.last_login), | |
103 | "active": h.boolicon(user.active), |
|
103 | "active": h.boolicon(user.active), | |
104 | "admin": h.boolicon(user.admin), |
|
104 | "admin": h.boolicon(user.admin), | |
105 | "extern_type": user.extern_type, |
|
105 | "extern_type": user.extern_type, | |
106 | "extern_name": user.extern_name, |
|
106 | "extern_name": user.extern_name, | |
107 | "action": user_actions(user.user_id, user.username), |
|
107 | "action": user_actions(user.user_id, user.username), | |
108 | }) |
|
108 | }) | |
109 |
|
109 | |||
110 | c.data = json.dumps({ |
|
110 | c.data = json.dumps({ | |
111 | "totalRecords": total_records, |
|
111 | "totalRecords": total_records, | |
112 | "startIndex": 0, |
|
112 | "startIndex": 0, | |
113 | "sort": None, |
|
113 | "sort": None, | |
114 | "dir": "asc", |
|
114 | "dir": "asc", | |
115 | "records": users_data |
|
115 | "records": users_data | |
116 | }) |
|
116 | }) | |
117 |
|
117 | |||
118 | return render('admin/users/users.html') |
|
118 | return render('admin/users/users.html') | |
119 |
|
119 | |||
120 | def create(self): |
|
120 | def create(self): | |
121 | """POST /users: Create a new item""" |
|
121 | """POST /users: Create a new item""" | |
122 | # url('users') |
|
122 | # url('users') | |
123 | c.default_extern_type = auth_modules.auth_internal.KallitheaAuthPlugin.name |
|
123 | c.default_extern_type = auth_modules.auth_internal.KallitheaAuthPlugin.name | |
124 | user_model = UserModel() |
|
124 | user_model = UserModel() | |
125 | user_form = UserForm()() |
|
125 | user_form = UserForm()() | |
126 | try: |
|
126 | try: | |
127 | form_result = user_form.to_python(dict(request.POST)) |
|
127 | form_result = user_form.to_python(dict(request.POST)) | |
128 | user = user_model.create(form_result) |
|
128 | user = user_model.create(form_result) | |
129 | usr = form_result['username'] |
|
129 | usr = form_result['username'] | |
130 | action_logger(self.authuser, 'admin_created_user:%s' % usr, |
|
130 | action_logger(self.authuser, 'admin_created_user:%s' % usr, | |
131 | None, self.ip_addr, self.sa) |
|
131 | None, self.ip_addr, self.sa) | |
132 | h.flash(h.literal(_('Created user %s') % h.link_to(h.escape(usr), url('edit_user', id=user.user_id))), |
|
132 | h.flash(h.literal(_('Created user %s') % h.link_to(h.escape(usr), url('edit_user', id=user.user_id))), | |
133 | category='success') |
|
133 | category='success') | |
134 | Session().commit() |
|
134 | Session().commit() | |
135 | except formencode.Invalid, errors: |
|
135 | except formencode.Invalid, errors: | |
136 | return htmlfill.render( |
|
136 | return htmlfill.render( | |
137 | render('admin/users/user_add.html'), |
|
137 | render('admin/users/user_add.html'), | |
138 | defaults=errors.value, |
|
138 | defaults=errors.value, | |
139 | errors=errors.error_dict or {}, |
|
139 | errors=errors.error_dict or {}, | |
140 | prefix_error=False, |
|
140 | prefix_error=False, | |
141 |
encoding="UTF-8" |
|
141 | encoding="UTF-8", | |
|
142 | force_defaults=False) | |||
142 | except UserCreationError, e: |
|
143 | except UserCreationError, e: | |
143 | h.flash(e, 'error') |
|
144 | h.flash(e, 'error') | |
144 | except Exception: |
|
145 | except Exception: | |
145 | log.error(traceback.format_exc()) |
|
146 | log.error(traceback.format_exc()) | |
146 | h.flash(_('Error occurred during creation of user %s') \ |
|
147 | h.flash(_('Error occurred during creation of user %s') \ | |
147 | % request.POST.get('username'), category='error') |
|
148 | % request.POST.get('username'), category='error') | |
148 | return redirect(url('users')) |
|
149 | return redirect(url('users')) | |
149 |
|
150 | |||
150 | def new(self, format='html'): |
|
151 | def new(self, format='html'): | |
151 | """GET /users/new: Form to create a new item""" |
|
152 | """GET /users/new: Form to create a new item""" | |
152 | # url('new_user') |
|
153 | # url('new_user') | |
153 | c.default_extern_type = auth_modules.auth_internal.KallitheaAuthPlugin.name |
|
154 | c.default_extern_type = auth_modules.auth_internal.KallitheaAuthPlugin.name | |
154 | return render('admin/users/user_add.html') |
|
155 | return render('admin/users/user_add.html') | |
155 |
|
156 | |||
156 | def update(self, id): |
|
157 | def update(self, id): | |
157 | """PUT /users/id: Update an existing item""" |
|
158 | """PUT /users/id: Update an existing item""" | |
158 | # Forms posted to this method should contain a hidden field: |
|
159 | # Forms posted to this method should contain a hidden field: | |
159 | # <input type="hidden" name="_method" value="PUT" /> |
|
160 | # <input type="hidden" name="_method" value="PUT" /> | |
160 | # Or using helpers: |
|
161 | # Or using helpers: | |
161 | # h.form(url('update_user', id=ID), |
|
162 | # h.form(url('update_user', id=ID), | |
162 | # method='put') |
|
163 | # method='put') | |
163 | # url('user', id=ID) |
|
164 | # url('user', id=ID) | |
164 | c.active = 'profile' |
|
165 | c.active = 'profile' | |
165 | user_model = UserModel() |
|
166 | user_model = UserModel() | |
166 | c.user = user_model.get(id) |
|
167 | c.user = user_model.get(id) | |
167 | c.extern_type = c.user.extern_type |
|
168 | c.extern_type = c.user.extern_type | |
168 | c.extern_name = c.user.extern_name |
|
169 | c.extern_name = c.user.extern_name | |
169 | c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr) |
|
170 | c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr) | |
170 | _form = UserForm(edit=True, old_data={'user_id': id, |
|
171 | _form = UserForm(edit=True, old_data={'user_id': id, | |
171 | 'email': c.user.email})() |
|
172 | 'email': c.user.email})() | |
172 | form_result = {} |
|
173 | form_result = {} | |
173 | try: |
|
174 | try: | |
174 | form_result = _form.to_python(dict(request.POST)) |
|
175 | form_result = _form.to_python(dict(request.POST)) | |
175 | skip_attrs = ['extern_type', 'extern_name'] |
|
176 | skip_attrs = ['extern_type', 'extern_name'] | |
176 | #TODO: plugin should define if username can be updated |
|
177 | #TODO: plugin should define if username can be updated | |
177 | if c.extern_type != kallithea.EXTERN_TYPE_INTERNAL: |
|
178 | if c.extern_type != kallithea.EXTERN_TYPE_INTERNAL: | |
178 | # forbid updating username for external accounts |
|
179 | # forbid updating username for external accounts | |
179 | skip_attrs.append('username') |
|
180 | skip_attrs.append('username') | |
180 |
|
181 | |||
181 | user_model.update(id, form_result, skip_attrs=skip_attrs) |
|
182 | user_model.update(id, form_result, skip_attrs=skip_attrs) | |
182 | usr = form_result['username'] |
|
183 | usr = form_result['username'] | |
183 | action_logger(self.authuser, 'admin_updated_user:%s' % usr, |
|
184 | action_logger(self.authuser, 'admin_updated_user:%s' % usr, | |
184 | None, self.ip_addr, self.sa) |
|
185 | None, self.ip_addr, self.sa) | |
185 | h.flash(_('User updated successfully'), category='success') |
|
186 | h.flash(_('User updated successfully'), category='success') | |
186 | Session().commit() |
|
187 | Session().commit() | |
187 | except formencode.Invalid, errors: |
|
188 | except formencode.Invalid, errors: | |
188 | defaults = errors.value |
|
189 | defaults = errors.value | |
189 | e = errors.error_dict or {} |
|
190 | e = errors.error_dict or {} | |
190 | defaults.update({ |
|
191 | defaults.update({ | |
191 | 'create_repo_perm': user_model.has_perm(id, |
|
192 | 'create_repo_perm': user_model.has_perm(id, | |
192 | 'hg.create.repository'), |
|
193 | 'hg.create.repository'), | |
193 | 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'), |
|
194 | 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'), | |
194 | '_method': 'put' |
|
195 | '_method': 'put' | |
195 | }) |
|
196 | }) | |
196 | return htmlfill.render( |
|
197 | return htmlfill.render( | |
197 | render('admin/users/user_edit.html'), |
|
198 | render('admin/users/user_edit.html'), | |
198 | defaults=defaults, |
|
199 | defaults=defaults, | |
199 | errors=e, |
|
200 | errors=e, | |
200 | prefix_error=False, |
|
201 | prefix_error=False, | |
201 |
encoding="UTF-8" |
|
202 | encoding="UTF-8", | |
|
203 | force_defaults=False) | |||
202 | except Exception: |
|
204 | except Exception: | |
203 | log.error(traceback.format_exc()) |
|
205 | log.error(traceback.format_exc()) | |
204 | h.flash(_('Error occurred during update of user %s') \ |
|
206 | h.flash(_('Error occurred during update of user %s') \ | |
205 | % form_result.get('username'), category='error') |
|
207 | % form_result.get('username'), category='error') | |
206 | return redirect(url('edit_user', id=id)) |
|
208 | return redirect(url('edit_user', id=id)) | |
207 |
|
209 | |||
208 | def delete(self, id): |
|
210 | def delete(self, id): | |
209 | """DELETE /users/id: Delete an existing item""" |
|
211 | """DELETE /users/id: Delete an existing item""" | |
210 | # Forms posted to this method should contain a hidden field: |
|
212 | # Forms posted to this method should contain a hidden field: | |
211 | # <input type="hidden" name="_method" value="DELETE" /> |
|
213 | # <input type="hidden" name="_method" value="DELETE" /> | |
212 | # Or using helpers: |
|
214 | # Or using helpers: | |
213 | # h.form(url('delete_user', id=ID), |
|
215 | # h.form(url('delete_user', id=ID), | |
214 | # method='delete') |
|
216 | # method='delete') | |
215 | # url('user', id=ID) |
|
217 | # url('user', id=ID) | |
216 | usr = User.get_or_404(id) |
|
218 | usr = User.get_or_404(id) | |
217 | try: |
|
219 | try: | |
218 | UserModel().delete(usr) |
|
220 | UserModel().delete(usr) | |
219 | Session().commit() |
|
221 | Session().commit() | |
220 | h.flash(_('Successfully deleted user'), category='success') |
|
222 | h.flash(_('Successfully deleted user'), category='success') | |
221 | except (UserOwnsReposException, DefaultUserException), e: |
|
223 | except (UserOwnsReposException, DefaultUserException), e: | |
222 | h.flash(e, category='warning') |
|
224 | h.flash(e, category='warning') | |
223 | except Exception: |
|
225 | except Exception: | |
224 | log.error(traceback.format_exc()) |
|
226 | log.error(traceback.format_exc()) | |
225 | h.flash(_('An error occurred during deletion of user'), |
|
227 | h.flash(_('An error occurred during deletion of user'), | |
226 | category='error') |
|
228 | category='error') | |
227 | return redirect(url('users')) |
|
229 | return redirect(url('users')) | |
228 |
|
230 | |||
229 | def show(self, id, format='html'): |
|
231 | def show(self, id, format='html'): | |
230 | """GET /users/id: Show a specific item""" |
|
232 | """GET /users/id: Show a specific item""" | |
231 | # url('user', id=ID) |
|
233 | # url('user', id=ID) | |
232 | User.get_or_404(-1) |
|
234 | User.get_or_404(-1) | |
233 |
|
235 | |||
234 | def edit(self, id, format='html'): |
|
236 | def edit(self, id, format='html'): | |
235 | """GET /users/id/edit: Form to edit an existing item""" |
|
237 | """GET /users/id/edit: Form to edit an existing item""" | |
236 | # url('edit_user', id=ID) |
|
238 | # url('edit_user', id=ID) | |
237 | c.user = User.get_or_404(id) |
|
239 | c.user = User.get_or_404(id) | |
238 | if c.user.username == User.DEFAULT_USER: |
|
240 | if c.user.username == User.DEFAULT_USER: | |
239 | h.flash(_("You can't edit this user"), category='warning') |
|
241 | h.flash(_("You can't edit this user"), category='warning') | |
240 | return redirect(url('users')) |
|
242 | return redirect(url('users')) | |
241 |
|
243 | |||
242 | c.active = 'profile' |
|
244 | c.active = 'profile' | |
243 | c.extern_type = c.user.extern_type |
|
245 | c.extern_type = c.user.extern_type | |
244 | c.extern_name = c.user.extern_name |
|
246 | c.extern_name = c.user.extern_name | |
245 | c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr) |
|
247 | c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr) | |
246 |
|
248 | |||
247 | defaults = c.user.get_dict() |
|
249 | defaults = c.user.get_dict() | |
248 | return htmlfill.render( |
|
250 | return htmlfill.render( | |
249 | render('admin/users/user_edit.html'), |
|
251 | render('admin/users/user_edit.html'), | |
250 | defaults=defaults, |
|
252 | defaults=defaults, | |
251 | encoding="UTF-8", |
|
253 | encoding="UTF-8", | |
252 | force_defaults=False) |
|
254 | force_defaults=False) | |
253 |
|
255 | |||
254 | def edit_advanced(self, id): |
|
256 | def edit_advanced(self, id): | |
255 | c.user = User.get_or_404(id) |
|
257 | c.user = User.get_or_404(id) | |
256 | if c.user.username == User.DEFAULT_USER: |
|
258 | if c.user.username == User.DEFAULT_USER: | |
257 | h.flash(_("You can't edit this user"), category='warning') |
|
259 | h.flash(_("You can't edit this user"), category='warning') | |
258 | return redirect(url('users')) |
|
260 | return redirect(url('users')) | |
259 |
|
261 | |||
260 | c.active = 'advanced' |
|
262 | c.active = 'advanced' | |
261 | c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr) |
|
263 | c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr) | |
262 |
|
264 | |||
263 | umodel = UserModel() |
|
265 | umodel = UserModel() | |
264 | defaults = c.user.get_dict() |
|
266 | defaults = c.user.get_dict() | |
265 | defaults.update({ |
|
267 | defaults.update({ | |
266 | 'create_repo_perm': umodel.has_perm(c.user, 'hg.create.repository'), |
|
268 | 'create_repo_perm': umodel.has_perm(c.user, 'hg.create.repository'), | |
267 | 'create_user_group_perm': umodel.has_perm(c.user, |
|
269 | 'create_user_group_perm': umodel.has_perm(c.user, | |
268 | 'hg.usergroup.create.true'), |
|
270 | 'hg.usergroup.create.true'), | |
269 | 'fork_repo_perm': umodel.has_perm(c.user, 'hg.fork.repository'), |
|
271 | 'fork_repo_perm': umodel.has_perm(c.user, 'hg.fork.repository'), | |
270 | }) |
|
272 | }) | |
271 | return htmlfill.render( |
|
273 | return htmlfill.render( | |
272 | render('admin/users/user_edit.html'), |
|
274 | render('admin/users/user_edit.html'), | |
273 | defaults=defaults, |
|
275 | defaults=defaults, | |
274 | encoding="UTF-8", |
|
276 | encoding="UTF-8", | |
275 | force_defaults=False) |
|
277 | force_defaults=False) | |
276 |
|
278 | |||
277 | def edit_api_keys(self, id): |
|
279 | def edit_api_keys(self, id): | |
278 | c.user = User.get_or_404(id) |
|
280 | c.user = User.get_or_404(id) | |
279 | if c.user.username == User.DEFAULT_USER: |
|
281 | if c.user.username == User.DEFAULT_USER: | |
280 | h.flash(_("You can't edit this user"), category='warning') |
|
282 | h.flash(_("You can't edit this user"), category='warning') | |
281 | return redirect(url('users')) |
|
283 | return redirect(url('users')) | |
282 |
|
284 | |||
283 | c.active = 'api_keys' |
|
285 | c.active = 'api_keys' | |
284 | show_expired = True |
|
286 | show_expired = True | |
285 | c.lifetime_values = [ |
|
287 | c.lifetime_values = [ | |
286 | (str(-1), _('forever')), |
|
288 | (str(-1), _('forever')), | |
287 | (str(5), _('5 minutes')), |
|
289 | (str(5), _('5 minutes')), | |
288 | (str(60), _('1 hour')), |
|
290 | (str(60), _('1 hour')), | |
289 | (str(60 * 24), _('1 day')), |
|
291 | (str(60 * 24), _('1 day')), | |
290 | (str(60 * 24 * 30), _('1 month')), |
|
292 | (str(60 * 24 * 30), _('1 month')), | |
291 | ] |
|
293 | ] | |
292 | c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] |
|
294 | c.lifetime_options = [(c.lifetime_values, _("Lifetime"))] | |
293 | c.user_api_keys = ApiKeyModel().get_api_keys(c.user.user_id, |
|
295 | c.user_api_keys = ApiKeyModel().get_api_keys(c.user.user_id, | |
294 | show_expired=show_expired) |
|
296 | show_expired=show_expired) | |
295 | defaults = c.user.get_dict() |
|
297 | defaults = c.user.get_dict() | |
296 | return htmlfill.render( |
|
298 | return htmlfill.render( | |
297 | render('admin/users/user_edit.html'), |
|
299 | render('admin/users/user_edit.html'), | |
298 | defaults=defaults, |
|
300 | defaults=defaults, | |
299 | encoding="UTF-8", |
|
301 | encoding="UTF-8", | |
300 | force_defaults=False) |
|
302 | force_defaults=False) | |
301 |
|
303 | |||
302 | def add_api_key(self, id): |
|
304 | def add_api_key(self, id): | |
303 | c.user = User.get_or_404(id) |
|
305 | c.user = User.get_or_404(id) | |
304 | if c.user.username == User.DEFAULT_USER: |
|
306 | if c.user.username == User.DEFAULT_USER: | |
305 | h.flash(_("You can't edit this user"), category='warning') |
|
307 | h.flash(_("You can't edit this user"), category='warning') | |
306 | return redirect(url('users')) |
|
308 | return redirect(url('users')) | |
307 |
|
309 | |||
308 | lifetime = safe_int(request.POST.get('lifetime'), -1) |
|
310 | lifetime = safe_int(request.POST.get('lifetime'), -1) | |
309 | description = request.POST.get('description') |
|
311 | description = request.POST.get('description') | |
310 | ApiKeyModel().create(c.user.user_id, description, lifetime) |
|
312 | ApiKeyModel().create(c.user.user_id, description, lifetime) | |
311 | Session().commit() |
|
313 | Session().commit() | |
312 | h.flash(_("Api key successfully created"), category='success') |
|
314 | h.flash(_("Api key successfully created"), category='success') | |
313 | return redirect(url('edit_user_api_keys', id=c.user.user_id)) |
|
315 | return redirect(url('edit_user_api_keys', id=c.user.user_id)) | |
314 |
|
316 | |||
315 | def delete_api_key(self, id): |
|
317 | def delete_api_key(self, id): | |
316 | c.user = User.get_or_404(id) |
|
318 | c.user = User.get_or_404(id) | |
317 | if c.user.username == User.DEFAULT_USER: |
|
319 | if c.user.username == User.DEFAULT_USER: | |
318 | h.flash(_("You can't edit this user"), category='warning') |
|
320 | h.flash(_("You can't edit this user"), category='warning') | |
319 | return redirect(url('users')) |
|
321 | return redirect(url('users')) | |
320 |
|
322 | |||
321 | api_key = request.POST.get('del_api_key') |
|
323 | api_key = request.POST.get('del_api_key') | |
322 | if request.POST.get('del_api_key_builtin'): |
|
324 | if request.POST.get('del_api_key_builtin'): | |
323 | user = User.get(c.user.user_id) |
|
325 | user = User.get(c.user.user_id) | |
324 | if user: |
|
326 | if user: | |
325 | user.api_key = generate_api_key(user.username) |
|
327 | user.api_key = generate_api_key(user.username) | |
326 | Session().add(user) |
|
328 | Session().add(user) | |
327 | Session().commit() |
|
329 | Session().commit() | |
328 | h.flash(_("Api key successfully reset"), category='success') |
|
330 | h.flash(_("Api key successfully reset"), category='success') | |
329 | elif api_key: |
|
331 | elif api_key: | |
330 | ApiKeyModel().delete(api_key, c.user.user_id) |
|
332 | ApiKeyModel().delete(api_key, c.user.user_id) | |
331 | Session().commit() |
|
333 | Session().commit() | |
332 | h.flash(_("Api key successfully deleted"), category='success') |
|
334 | h.flash(_("Api key successfully deleted"), category='success') | |
333 |
|
335 | |||
334 | return redirect(url('edit_user_api_keys', id=c.user.user_id)) |
|
336 | return redirect(url('edit_user_api_keys', id=c.user.user_id)) | |
335 |
|
337 | |||
336 | def update_account(self, id): |
|
338 | def update_account(self, id): | |
337 | pass |
|
339 | pass | |
338 |
|
340 | |||
339 | def edit_perms(self, id): |
|
341 | def edit_perms(self, id): | |
340 | c.user = User.get_or_404(id) |
|
342 | c.user = User.get_or_404(id) | |
341 | if c.user.username == User.DEFAULT_USER: |
|
343 | if c.user.username == User.DEFAULT_USER: | |
342 | h.flash(_("You can't edit this user"), category='warning') |
|
344 | h.flash(_("You can't edit this user"), category='warning') | |
343 | return redirect(url('users')) |
|
345 | return redirect(url('users')) | |
344 |
|
346 | |||
345 | c.active = 'perms' |
|
347 | c.active = 'perms' | |
346 | c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr) |
|
348 | c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr) | |
347 |
|
349 | |||
348 | umodel = UserModel() |
|
350 | umodel = UserModel() | |
349 | defaults = c.user.get_dict() |
|
351 | defaults = c.user.get_dict() | |
350 | defaults.update({ |
|
352 | defaults.update({ | |
351 | 'create_repo_perm': umodel.has_perm(c.user, 'hg.create.repository'), |
|
353 | 'create_repo_perm': umodel.has_perm(c.user, 'hg.create.repository'), | |
352 | 'create_user_group_perm': umodel.has_perm(c.user, |
|
354 | 'create_user_group_perm': umodel.has_perm(c.user, | |
353 | 'hg.usergroup.create.true'), |
|
355 | 'hg.usergroup.create.true'), | |
354 | 'fork_repo_perm': umodel.has_perm(c.user, 'hg.fork.repository'), |
|
356 | 'fork_repo_perm': umodel.has_perm(c.user, 'hg.fork.repository'), | |
355 | }) |
|
357 | }) | |
356 | return htmlfill.render( |
|
358 | return htmlfill.render( | |
357 | render('admin/users/user_edit.html'), |
|
359 | render('admin/users/user_edit.html'), | |
358 | defaults=defaults, |
|
360 | defaults=defaults, | |
359 | encoding="UTF-8", |
|
361 | encoding="UTF-8", | |
360 | force_defaults=False) |
|
362 | force_defaults=False) | |
361 |
|
363 | |||
362 | def update_perms(self, id): |
|
364 | def update_perms(self, id): | |
363 | """PUT /users_perm/id: Update an existing item""" |
|
365 | """PUT /users_perm/id: Update an existing item""" | |
364 | # url('user_perm', id=ID, method='put') |
|
366 | # url('user_perm', id=ID, method='put') | |
365 | user = User.get_or_404(id) |
|
367 | user = User.get_or_404(id) | |
366 |
|
368 | |||
367 | try: |
|
369 | try: | |
368 | form = CustomDefaultPermissionsForm()() |
|
370 | form = CustomDefaultPermissionsForm()() | |
369 | form_result = form.to_python(request.POST) |
|
371 | form_result = form.to_python(request.POST) | |
370 |
|
372 | |||
371 | inherit_perms = form_result['inherit_default_permissions'] |
|
373 | inherit_perms = form_result['inherit_default_permissions'] | |
372 | user.inherit_default_permissions = inherit_perms |
|
374 | user.inherit_default_permissions = inherit_perms | |
373 | Session().add(user) |
|
375 | Session().add(user) | |
374 | user_model = UserModel() |
|
376 | user_model = UserModel() | |
375 |
|
377 | |||
376 | defs = UserToPerm.query()\ |
|
378 | defs = UserToPerm.query()\ | |
377 | .filter(UserToPerm.user == user)\ |
|
379 | .filter(UserToPerm.user == user)\ | |
378 | .all() |
|
380 | .all() | |
379 | for ug in defs: |
|
381 | for ug in defs: | |
380 | Session().delete(ug) |
|
382 | Session().delete(ug) | |
381 |
|
383 | |||
382 | if form_result['create_repo_perm']: |
|
384 | if form_result['create_repo_perm']: | |
383 | user_model.grant_perm(id, 'hg.create.repository') |
|
385 | user_model.grant_perm(id, 'hg.create.repository') | |
384 | else: |
|
386 | else: | |
385 | user_model.grant_perm(id, 'hg.create.none') |
|
387 | user_model.grant_perm(id, 'hg.create.none') | |
386 | if form_result['create_user_group_perm']: |
|
388 | if form_result['create_user_group_perm']: | |
387 | user_model.grant_perm(id, 'hg.usergroup.create.true') |
|
389 | user_model.grant_perm(id, 'hg.usergroup.create.true') | |
388 | else: |
|
390 | else: | |
389 | user_model.grant_perm(id, 'hg.usergroup.create.false') |
|
391 | user_model.grant_perm(id, 'hg.usergroup.create.false') | |
390 | if form_result['fork_repo_perm']: |
|
392 | if form_result['fork_repo_perm']: | |
391 | user_model.grant_perm(id, 'hg.fork.repository') |
|
393 | user_model.grant_perm(id, 'hg.fork.repository') | |
392 | else: |
|
394 | else: | |
393 | user_model.grant_perm(id, 'hg.fork.none') |
|
395 | user_model.grant_perm(id, 'hg.fork.none') | |
394 | h.flash(_("Updated permissions"), category='success') |
|
396 | h.flash(_("Updated permissions"), category='success') | |
395 | Session().commit() |
|
397 | Session().commit() | |
396 | except Exception: |
|
398 | except Exception: | |
397 | log.error(traceback.format_exc()) |
|
399 | log.error(traceback.format_exc()) | |
398 | h.flash(_('An error occurred during permissions saving'), |
|
400 | h.flash(_('An error occurred during permissions saving'), | |
399 | category='error') |
|
401 | category='error') | |
400 | return redirect(url('edit_user_perms', id=id)) |
|
402 | return redirect(url('edit_user_perms', id=id)) | |
401 |
|
403 | |||
402 | def edit_emails(self, id): |
|
404 | def edit_emails(self, id): | |
403 | c.user = User.get_or_404(id) |
|
405 | c.user = User.get_or_404(id) | |
404 | if c.user.username == User.DEFAULT_USER: |
|
406 | if c.user.username == User.DEFAULT_USER: | |
405 | h.flash(_("You can't edit this user"), category='warning') |
|
407 | h.flash(_("You can't edit this user"), category='warning') | |
406 | return redirect(url('users')) |
|
408 | return redirect(url('users')) | |
407 |
|
409 | |||
408 | c.active = 'emails' |
|
410 | c.active = 'emails' | |
409 | c.user_email_map = UserEmailMap.query()\ |
|
411 | c.user_email_map = UserEmailMap.query()\ | |
410 | .filter(UserEmailMap.user == c.user).all() |
|
412 | .filter(UserEmailMap.user == c.user).all() | |
411 |
|
413 | |||
412 | defaults = c.user.get_dict() |
|
414 | defaults = c.user.get_dict() | |
413 | return htmlfill.render( |
|
415 | return htmlfill.render( | |
414 | render('admin/users/user_edit.html'), |
|
416 | render('admin/users/user_edit.html'), | |
415 | defaults=defaults, |
|
417 | defaults=defaults, | |
416 | encoding="UTF-8", |
|
418 | encoding="UTF-8", | |
417 | force_defaults=False) |
|
419 | force_defaults=False) | |
418 |
|
420 | |||
419 | def add_email(self, id): |
|
421 | def add_email(self, id): | |
420 | """POST /user_emails:Add an existing item""" |
|
422 | """POST /user_emails:Add an existing item""" | |
421 | # url('user_emails', id=ID, method='put') |
|
423 | # url('user_emails', id=ID, method='put') | |
422 |
|
424 | |||
423 | email = request.POST.get('new_email') |
|
425 | email = request.POST.get('new_email') | |
424 | user_model = UserModel() |
|
426 | user_model = UserModel() | |
425 |
|
427 | |||
426 | try: |
|
428 | try: | |
427 | user_model.add_extra_email(id, email) |
|
429 | user_model.add_extra_email(id, email) | |
428 | Session().commit() |
|
430 | Session().commit() | |
429 | h.flash(_("Added email %s to user") % email, category='success') |
|
431 | h.flash(_("Added email %s to user") % email, category='success') | |
430 | except formencode.Invalid, error: |
|
432 | except formencode.Invalid, error: | |
431 | msg = error.error_dict['email'] |
|
433 | msg = error.error_dict['email'] | |
432 | h.flash(msg, category='error') |
|
434 | h.flash(msg, category='error') | |
433 | except Exception: |
|
435 | except Exception: | |
434 | log.error(traceback.format_exc()) |
|
436 | log.error(traceback.format_exc()) | |
435 | h.flash(_('An error occurred during email saving'), |
|
437 | h.flash(_('An error occurred during email saving'), | |
436 | category='error') |
|
438 | category='error') | |
437 | return redirect(url('edit_user_emails', id=id)) |
|
439 | return redirect(url('edit_user_emails', id=id)) | |
438 |
|
440 | |||
439 | def delete_email(self, id): |
|
441 | def delete_email(self, id): | |
440 | """DELETE /user_emails_delete/id: Delete an existing item""" |
|
442 | """DELETE /user_emails_delete/id: Delete an existing item""" | |
441 | # url('user_emails_delete', id=ID, method='delete') |
|
443 | # url('user_emails_delete', id=ID, method='delete') | |
442 | email_id = request.POST.get('del_email_id') |
|
444 | email_id = request.POST.get('del_email_id') | |
443 | user_model = UserModel() |
|
445 | user_model = UserModel() | |
444 | user_model.delete_extra_email(id, email_id) |
|
446 | user_model.delete_extra_email(id, email_id) | |
445 | Session().commit() |
|
447 | Session().commit() | |
446 | h.flash(_("Removed email from user"), category='success') |
|
448 | h.flash(_("Removed email from user"), category='success') | |
447 | return redirect(url('edit_user_emails', id=id)) |
|
449 | return redirect(url('edit_user_emails', id=id)) | |
448 |
|
450 | |||
449 | def edit_ips(self, id): |
|
451 | def edit_ips(self, id): | |
450 | c.user = User.get_or_404(id) |
|
452 | c.user = User.get_or_404(id) | |
451 | if c.user.username == User.DEFAULT_USER: |
|
453 | if c.user.username == User.DEFAULT_USER: | |
452 | h.flash(_("You can't edit this user"), category='warning') |
|
454 | h.flash(_("You can't edit this user"), category='warning') | |
453 | return redirect(url('users')) |
|
455 | return redirect(url('users')) | |
454 |
|
456 | |||
455 | c.active = 'ips' |
|
457 | c.active = 'ips' | |
456 | c.user_ip_map = UserIpMap.query()\ |
|
458 | c.user_ip_map = UserIpMap.query()\ | |
457 | .filter(UserIpMap.user == c.user).all() |
|
459 | .filter(UserIpMap.user == c.user).all() | |
458 |
|
460 | |||
459 | c.inherit_default_ips = c.user.inherit_default_permissions |
|
461 | c.inherit_default_ips = c.user.inherit_default_permissions | |
460 | c.default_user_ip_map = UserIpMap.query()\ |
|
462 | c.default_user_ip_map = UserIpMap.query()\ | |
461 | .filter(UserIpMap.user == User.get_default_user()).all() |
|
463 | .filter(UserIpMap.user == User.get_default_user()).all() | |
462 |
|
464 | |||
463 | defaults = c.user.get_dict() |
|
465 | defaults = c.user.get_dict() | |
464 | return htmlfill.render( |
|
466 | return htmlfill.render( | |
465 | render('admin/users/user_edit.html'), |
|
467 | render('admin/users/user_edit.html'), | |
466 | defaults=defaults, |
|
468 | defaults=defaults, | |
467 | encoding="UTF-8", |
|
469 | encoding="UTF-8", | |
468 | force_defaults=False) |
|
470 | force_defaults=False) | |
469 |
|
471 | |||
470 | def add_ip(self, id): |
|
472 | def add_ip(self, id): | |
471 | """POST /user_ips:Add an existing item""" |
|
473 | """POST /user_ips:Add an existing item""" | |
472 | # url('user_ips', id=ID, method='put') |
|
474 | # url('user_ips', id=ID, method='put') | |
473 |
|
475 | |||
474 | ip = request.POST.get('new_ip') |
|
476 | ip = request.POST.get('new_ip') | |
475 | user_model = UserModel() |
|
477 | user_model = UserModel() | |
476 |
|
478 | |||
477 | try: |
|
479 | try: | |
478 | user_model.add_extra_ip(id, ip) |
|
480 | user_model.add_extra_ip(id, ip) | |
479 | Session().commit() |
|
481 | Session().commit() | |
480 | h.flash(_("Added ip %s to user whitelist") % ip, category='success') |
|
482 | h.flash(_("Added ip %s to user whitelist") % ip, category='success') | |
481 | except formencode.Invalid, error: |
|
483 | except formencode.Invalid, error: | |
482 | msg = error.error_dict['ip'] |
|
484 | msg = error.error_dict['ip'] | |
483 | h.flash(msg, category='error') |
|
485 | h.flash(msg, category='error') | |
484 | except Exception: |
|
486 | except Exception: | |
485 | log.error(traceback.format_exc()) |
|
487 | log.error(traceback.format_exc()) | |
486 | h.flash(_('An error occurred during ip saving'), |
|
488 | h.flash(_('An error occurred during ip saving'), | |
487 | category='error') |
|
489 | category='error') | |
488 |
|
490 | |||
489 | if 'default_user' in request.POST: |
|
491 | if 'default_user' in request.POST: | |
490 | return redirect(url('admin_permissions_ips')) |
|
492 | return redirect(url('admin_permissions_ips')) | |
491 | return redirect(url('edit_user_ips', id=id)) |
|
493 | return redirect(url('edit_user_ips', id=id)) | |
492 |
|
494 | |||
493 | def delete_ip(self, id): |
|
495 | def delete_ip(self, id): | |
494 | """DELETE /user_ips_delete/id: Delete an existing item""" |
|
496 | """DELETE /user_ips_delete/id: Delete an existing item""" | |
495 | # url('user_ips_delete', id=ID, method='delete') |
|
497 | # url('user_ips_delete', id=ID, method='delete') | |
496 | ip_id = request.POST.get('del_ip_id') |
|
498 | ip_id = request.POST.get('del_ip_id') | |
497 | user_model = UserModel() |
|
499 | user_model = UserModel() | |
498 | user_model.delete_extra_ip(id, ip_id) |
|
500 | user_model.delete_extra_ip(id, ip_id) | |
499 | Session().commit() |
|
501 | Session().commit() | |
500 | h.flash(_("Removed ip address from user whitelist"), category='success') |
|
502 | h.flash(_("Removed ip address from user whitelist"), category='success') | |
501 |
|
503 | |||
502 | if 'default_user' in request.POST: |
|
504 | if 'default_user' in request.POST: | |
503 | return redirect(url('admin_permissions_ips')) |
|
505 | return redirect(url('admin_permissions_ips')) | |
504 | return redirect(url('edit_user_ips', id=id)) |
|
506 | return redirect(url('edit_user_ips', id=id)) |
@@ -1,192 +1,192 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.forks |
|
15 | kallithea.controllers.forks | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | forks controller for Kallithea |
|
18 | forks controller for Kallithea | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Apr 23, 2011 |
|
22 | :created_on: Apr 23, 2011 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 | import logging |
|
28 | import logging | |
29 | import formencode |
|
29 | import formencode | |
30 | import traceback |
|
30 | import traceback | |
31 | from formencode import htmlfill |
|
31 | from formencode import htmlfill | |
32 |
|
32 | |||
33 | from pylons import tmpl_context as c, request, url |
|
33 | from pylons import tmpl_context as c, request, url | |
34 | from pylons.controllers.util import redirect |
|
34 | from pylons.controllers.util import redirect | |
35 | from pylons.i18n.translation import _ |
|
35 | from pylons.i18n.translation import _ | |
36 |
|
36 | |||
37 | import kallithea.lib.helpers as h |
|
37 | import kallithea.lib.helpers as h | |
38 |
|
38 | |||
39 | from kallithea.lib.helpers import Page |
|
39 | from kallithea.lib.helpers import Page | |
40 | from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \ |
|
40 | from kallithea.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \ | |
41 | NotAnonymous, HasRepoPermissionAny, HasPermissionAnyDecorator |
|
41 | NotAnonymous, HasRepoPermissionAny, HasPermissionAnyDecorator | |
42 | from kallithea.lib.base import BaseRepoController, render |
|
42 | from kallithea.lib.base import BaseRepoController, render | |
43 | from kallithea.model.db import Repository, RepoGroup, UserFollowing, User,\ |
|
43 | from kallithea.model.db import Repository, RepoGroup, UserFollowing, User,\ | |
44 | Ui |
|
44 | Ui | |
45 | from kallithea.model.repo import RepoModel |
|
45 | from kallithea.model.repo import RepoModel | |
46 | from kallithea.model.forms import RepoForkForm |
|
46 | from kallithea.model.forms import RepoForkForm | |
47 | from kallithea.model.scm import ScmModel, RepoGroupList |
|
47 | from kallithea.model.scm import ScmModel, RepoGroupList | |
48 | from kallithea.lib.utils2 import safe_int |
|
48 | from kallithea.lib.utils2 import safe_int | |
49 |
|
49 | |||
50 | log = logging.getLogger(__name__) |
|
50 | log = logging.getLogger(__name__) | |
51 |
|
51 | |||
52 |
|
52 | |||
53 | class ForksController(BaseRepoController): |
|
53 | class ForksController(BaseRepoController): | |
54 |
|
54 | |||
55 | def __before__(self): |
|
55 | def __before__(self): | |
56 | super(ForksController, self).__before__() |
|
56 | super(ForksController, self).__before__() | |
57 |
|
57 | |||
58 | def __load_defaults(self): |
|
58 | def __load_defaults(self): | |
59 | acl_groups = RepoGroupList(RepoGroup.query().all(), |
|
59 | acl_groups = RepoGroupList(RepoGroup.query().all(), | |
60 | perm_set=['group.write', 'group.admin']) |
|
60 | perm_set=['group.write', 'group.admin']) | |
61 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) |
|
61 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) | |
62 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) |
|
62 | c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups) | |
63 | choices, c.landing_revs = ScmModel().get_repo_landing_revs() |
|
63 | choices, c.landing_revs = ScmModel().get_repo_landing_revs() | |
64 | c.landing_revs_choices = choices |
|
64 | c.landing_revs_choices = choices | |
65 | c.can_update = Ui.get_by_key(Ui.HOOK_UPDATE).ui_active |
|
65 | c.can_update = Ui.get_by_key(Ui.HOOK_UPDATE).ui_active | |
66 |
|
66 | |||
67 | def __load_data(self, repo_name=None): |
|
67 | def __load_data(self, repo_name=None): | |
68 | """ |
|
68 | """ | |
69 | Load defaults settings for edit, and update |
|
69 | Load defaults settings for edit, and update | |
70 |
|
70 | |||
71 | :param repo_name: |
|
71 | :param repo_name: | |
72 | """ |
|
72 | """ | |
73 | self.__load_defaults() |
|
73 | self.__load_defaults() | |
74 |
|
74 | |||
75 | c.repo_info = db_repo = Repository.get_by_repo_name(repo_name) |
|
75 | c.repo_info = db_repo = Repository.get_by_repo_name(repo_name) | |
76 | repo = db_repo.scm_instance |
|
76 | repo = db_repo.scm_instance | |
77 |
|
77 | |||
78 | if c.repo_info is None: |
|
78 | if c.repo_info is None: | |
79 | h.not_mapped_error(repo_name) |
|
79 | h.not_mapped_error(repo_name) | |
80 | return redirect(url('repos')) |
|
80 | return redirect(url('repos')) | |
81 |
|
81 | |||
82 | c.default_user_id = User.get_default_user().user_id |
|
82 | c.default_user_id = User.get_default_user().user_id | |
83 | c.in_public_journal = UserFollowing.query()\ |
|
83 | c.in_public_journal = UserFollowing.query()\ | |
84 | .filter(UserFollowing.user_id == c.default_user_id)\ |
|
84 | .filter(UserFollowing.user_id == c.default_user_id)\ | |
85 | .filter(UserFollowing.follows_repository == c.repo_info).scalar() |
|
85 | .filter(UserFollowing.follows_repository == c.repo_info).scalar() | |
86 |
|
86 | |||
87 | if c.repo_info.stats: |
|
87 | if c.repo_info.stats: | |
88 | last_rev = c.repo_info.stats.stat_on_revision+1 |
|
88 | last_rev = c.repo_info.stats.stat_on_revision+1 | |
89 | else: |
|
89 | else: | |
90 | last_rev = 0 |
|
90 | last_rev = 0 | |
91 | c.stats_revision = last_rev |
|
91 | c.stats_revision = last_rev | |
92 |
|
92 | |||
93 | c.repo_last_rev = repo.count() if repo.revisions else 0 |
|
93 | c.repo_last_rev = repo.count() if repo.revisions else 0 | |
94 |
|
94 | |||
95 | if last_rev == 0 or c.repo_last_rev == 0: |
|
95 | if last_rev == 0 or c.repo_last_rev == 0: | |
96 | c.stats_percentage = 0 |
|
96 | c.stats_percentage = 0 | |
97 | else: |
|
97 | else: | |
98 | c.stats_percentage = '%.2f' % ((float((last_rev)) / |
|
98 | c.stats_percentage = '%.2f' % ((float((last_rev)) / | |
99 | c.repo_last_rev) * 100) |
|
99 | c.repo_last_rev) * 100) | |
100 |
|
100 | |||
101 | defaults = RepoModel()._get_defaults(repo_name) |
|
101 | defaults = RepoModel()._get_defaults(repo_name) | |
102 | # alter the description to indicate a fork |
|
102 | # alter the description to indicate a fork | |
103 | defaults['description'] = ('fork of repository: %s \n%s' |
|
103 | defaults['description'] = ('fork of repository: %s \n%s' | |
104 | % (defaults['repo_name'], |
|
104 | % (defaults['repo_name'], | |
105 | defaults['description'])) |
|
105 | defaults['description'])) | |
106 | # add suffix to fork |
|
106 | # add suffix to fork | |
107 | defaults['repo_name'] = '%s-fork' % defaults['repo_name'] |
|
107 | defaults['repo_name'] = '%s-fork' % defaults['repo_name'] | |
108 |
|
108 | |||
109 | return defaults |
|
109 | return defaults | |
110 |
|
110 | |||
111 | @LoginRequired() |
|
111 | @LoginRequired() | |
112 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
112 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
113 | 'repository.admin') |
|
113 | 'repository.admin') | |
114 | def forks(self, repo_name): |
|
114 | def forks(self, repo_name): | |
115 | p = safe_int(request.GET.get('page', 1), 1) |
|
115 | p = safe_int(request.GET.get('page', 1), 1) | |
116 | repo_id = c.db_repo.repo_id |
|
116 | repo_id = c.db_repo.repo_id | |
117 | d = [] |
|
117 | d = [] | |
118 | for r in Repository.get_repo_forks(repo_id): |
|
118 | for r in Repository.get_repo_forks(repo_id): | |
119 | if not HasRepoPermissionAny( |
|
119 | if not HasRepoPermissionAny( | |
120 | 'repository.read', 'repository.write', 'repository.admin' |
|
120 | 'repository.read', 'repository.write', 'repository.admin' | |
121 | )(r.repo_name, 'get forks check'): |
|
121 | )(r.repo_name, 'get forks check'): | |
122 | continue |
|
122 | continue | |
123 | d.append(r) |
|
123 | d.append(r) | |
124 | c.forks_pager = Page(d, page=p, items_per_page=20) |
|
124 | c.forks_pager = Page(d, page=p, items_per_page=20) | |
125 |
|
125 | |||
126 | if request.environ.get('HTTP_X_PARTIAL_XHR'): |
|
126 | if request.environ.get('HTTP_X_PARTIAL_XHR'): | |
127 | return render('/forks/forks_data.html') |
|
127 | return render('/forks/forks_data.html') | |
128 |
|
128 | |||
129 | return render('/forks/forks.html') |
|
129 | return render('/forks/forks.html') | |
130 |
|
130 | |||
131 | @LoginRequired() |
|
131 | @LoginRequired() | |
132 | @NotAnonymous() |
|
132 | @NotAnonymous() | |
133 | @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository') |
|
133 | @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository') | |
134 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
134 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
135 | 'repository.admin') |
|
135 | 'repository.admin') | |
136 | def fork(self, repo_name): |
|
136 | def fork(self, repo_name): | |
137 | c.repo_info = Repository.get_by_repo_name(repo_name) |
|
137 | c.repo_info = Repository.get_by_repo_name(repo_name) | |
138 | if not c.repo_info: |
|
138 | if not c.repo_info: | |
139 | h.not_mapped_error(repo_name) |
|
139 | h.not_mapped_error(repo_name) | |
140 | return redirect(url('home')) |
|
140 | return redirect(url('home')) | |
141 |
|
141 | |||
142 | defaults = self.__load_data(repo_name) |
|
142 | defaults = self.__load_data(repo_name) | |
143 |
|
143 | |||
144 | return htmlfill.render( |
|
144 | return htmlfill.render( | |
145 | render('forks/fork.html'), |
|
145 | render('forks/fork.html'), | |
146 | defaults=defaults, |
|
146 | defaults=defaults, | |
147 | encoding="UTF-8", |
|
147 | encoding="UTF-8", | |
148 | force_defaults=False |
|
148 | force_defaults=False) | |
149 | ) |
|
|||
150 |
|
149 | |||
151 | @LoginRequired() |
|
150 | @LoginRequired() | |
152 | @NotAnonymous() |
|
151 | @NotAnonymous() | |
153 | @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository') |
|
152 | @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository') | |
154 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', |
|
153 | @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', | |
155 | 'repository.admin') |
|
154 | 'repository.admin') | |
156 | def fork_create(self, repo_name): |
|
155 | def fork_create(self, repo_name): | |
157 | self.__load_defaults() |
|
156 | self.__load_defaults() | |
158 | c.repo_info = Repository.get_by_repo_name(repo_name) |
|
157 | c.repo_info = Repository.get_by_repo_name(repo_name) | |
159 | _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type}, |
|
158 | _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type}, | |
160 | repo_groups=c.repo_groups_choices, |
|
159 | repo_groups=c.repo_groups_choices, | |
161 | landing_revs=c.landing_revs_choices)() |
|
160 | landing_revs=c.landing_revs_choices)() | |
162 | form_result = {} |
|
161 | form_result = {} | |
163 | task_id = None |
|
162 | task_id = None | |
164 | try: |
|
163 | try: | |
165 | form_result = _form.to_python(dict(request.POST)) |
|
164 | form_result = _form.to_python(dict(request.POST)) | |
166 |
|
165 | |||
167 | # an approximation that is better than nothing |
|
166 | # an approximation that is better than nothing | |
168 | if not Ui.get_by_key(Ui.HOOK_UPDATE).ui_active: |
|
167 | if not Ui.get_by_key(Ui.HOOK_UPDATE).ui_active: | |
169 | form_result['update_after_clone'] = False |
|
168 | form_result['update_after_clone'] = False | |
170 |
|
169 | |||
171 | # create fork is done sometimes async on celery, db transaction |
|
170 | # create fork is done sometimes async on celery, db transaction | |
172 | # management is handled there. |
|
171 | # management is handled there. | |
173 | task = RepoModel().create_fork(form_result, self.authuser.user_id) |
|
172 | task = RepoModel().create_fork(form_result, self.authuser.user_id) | |
174 | from celery.result import BaseAsyncResult |
|
173 | from celery.result import BaseAsyncResult | |
175 | if isinstance(task, BaseAsyncResult): |
|
174 | if isinstance(task, BaseAsyncResult): | |
176 | task_id = task.task_id |
|
175 | task_id = task.task_id | |
177 | except formencode.Invalid, errors: |
|
176 | except formencode.Invalid, errors: | |
178 | c.new_repo = errors.value['repo_name'] |
|
177 | c.new_repo = errors.value['repo_name'] | |
179 | return htmlfill.render( |
|
178 | return htmlfill.render( | |
180 | render('forks/fork.html'), |
|
179 | render('forks/fork.html'), | |
181 | defaults=errors.value, |
|
180 | defaults=errors.value, | |
182 | errors=errors.error_dict or {}, |
|
181 | errors=errors.error_dict or {}, | |
183 | prefix_error=False, |
|
182 | prefix_error=False, | |
184 |
encoding="UTF-8" |
|
183 | encoding="UTF-8", | |
|
184 | force_defaults=False) | |||
185 | except Exception: |
|
185 | except Exception: | |
186 | log.error(traceback.format_exc()) |
|
186 | log.error(traceback.format_exc()) | |
187 | h.flash(_('An error occurred during repository forking %s') % |
|
187 | h.flash(_('An error occurred during repository forking %s') % | |
188 | repo_name, category='error') |
|
188 | repo_name, category='error') | |
189 |
|
189 | |||
190 | return redirect(h.url('repo_creating_home', |
|
190 | return redirect(h.url('repo_creating_home', | |
191 | repo_name=form_result['repo_name_full'], |
|
191 | repo_name=form_result['repo_name_full'], | |
192 | task_id=task_id)) |
|
192 | task_id=task_id)) |
@@ -1,269 +1,272 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 | # This program is free software: you can redistribute it and/or modify |
|
2 | # This program is free software: you can redistribute it and/or modify | |
3 | # it under the terms of the GNU General Public License as published by |
|
3 | # it under the terms of the GNU General Public License as published by | |
4 | # the Free Software Foundation, either version 3 of the License, or |
|
4 | # the Free Software Foundation, either version 3 of the License, or | |
5 | # (at your option) any later version. |
|
5 | # (at your option) any later version. | |
6 | # |
|
6 | # | |
7 | # This program is distributed in the hope that it will be useful, |
|
7 | # This program is distributed in the hope that it will be useful, | |
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
8 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
9 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
10 | # GNU General Public License for more details. |
|
10 | # GNU General Public License for more details. | |
11 | # |
|
11 | # | |
12 | # You should have received a copy of the GNU General Public License |
|
12 | # You should have received a copy of the GNU General Public License | |
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
13 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
14 | """ |
|
14 | """ | |
15 | kallithea.controllers.login |
|
15 | kallithea.controllers.login | |
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
16 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
17 |
|
17 | |||
18 | Login controller for Kallithea |
|
18 | Login controller for Kallithea | |
19 |
|
19 | |||
20 | This file was forked by the Kallithea project in July 2014. |
|
20 | This file was forked by the Kallithea project in July 2014. | |
21 | Original author and date, and relevant copyright and licensing information is below: |
|
21 | Original author and date, and relevant copyright and licensing information is below: | |
22 | :created_on: Apr 22, 2010 |
|
22 | :created_on: Apr 22, 2010 | |
23 | :author: marcink |
|
23 | :author: marcink | |
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. |
|
24 | :copyright: (c) 2013 RhodeCode GmbH, and others. | |
25 | :license: GPLv3, see LICENSE.md for more details. |
|
25 | :license: GPLv3, see LICENSE.md for more details. | |
26 | """ |
|
26 | """ | |
27 |
|
27 | |||
28 |
|
28 | |||
29 | import logging |
|
29 | import logging | |
30 | import formencode |
|
30 | import formencode | |
31 | import datetime |
|
31 | import datetime | |
32 | import urlparse |
|
32 | import urlparse | |
33 |
|
33 | |||
34 | from formencode import htmlfill |
|
34 | from formencode import htmlfill | |
35 | from webob.exc import HTTPFound |
|
35 | from webob.exc import HTTPFound | |
36 | from pylons.i18n.translation import _ |
|
36 | from pylons.i18n.translation import _ | |
37 | from pylons.controllers.util import redirect |
|
37 | from pylons.controllers.util import redirect | |
38 | from pylons import request, session, tmpl_context as c, url |
|
38 | from pylons import request, session, tmpl_context as c, url | |
39 |
|
39 | |||
40 | import kallithea.lib.helpers as h |
|
40 | import kallithea.lib.helpers as h | |
41 | from kallithea.lib.auth import AuthUser, HasPermissionAnyDecorator |
|
41 | from kallithea.lib.auth import AuthUser, HasPermissionAnyDecorator | |
42 | from kallithea.lib.auth_modules import importplugin |
|
42 | from kallithea.lib.auth_modules import importplugin | |
43 | from kallithea.lib.base import BaseController, render |
|
43 | from kallithea.lib.base import BaseController, render | |
44 | from kallithea.lib.exceptions import UserCreationError |
|
44 | from kallithea.lib.exceptions import UserCreationError | |
45 | from kallithea.model.db import User, Setting |
|
45 | from kallithea.model.db import User, Setting | |
46 | from kallithea.model.forms import LoginForm, RegisterForm, PasswordResetForm |
|
46 | from kallithea.model.forms import LoginForm, RegisterForm, PasswordResetForm | |
47 | from kallithea.model.user import UserModel |
|
47 | from kallithea.model.user import UserModel | |
48 | from kallithea.model.meta import Session |
|
48 | from kallithea.model.meta import Session | |
49 |
|
49 | |||
50 |
|
50 | |||
51 | log = logging.getLogger(__name__) |
|
51 | log = logging.getLogger(__name__) | |
52 |
|
52 | |||
53 |
|
53 | |||
54 | class LoginController(BaseController): |
|
54 | class LoginController(BaseController): | |
55 |
|
55 | |||
56 | def __before__(self): |
|
56 | def __before__(self): | |
57 | super(LoginController, self).__before__() |
|
57 | super(LoginController, self).__before__() | |
58 |
|
58 | |||
59 | def _store_user_in_session(self, username, remember=False): |
|
59 | def _store_user_in_session(self, username, remember=False): | |
60 | user = User.get_by_username(username, case_insensitive=True) |
|
60 | user = User.get_by_username(username, case_insensitive=True) | |
61 | auth_user = AuthUser(user.user_id) |
|
61 | auth_user = AuthUser(user.user_id) | |
62 | auth_user.set_authenticated() |
|
62 | auth_user.set_authenticated() | |
63 | cs = auth_user.get_cookie_store() |
|
63 | cs = auth_user.get_cookie_store() | |
64 | session['authuser'] = cs |
|
64 | session['authuser'] = cs | |
65 | user.update_lastlogin() |
|
65 | user.update_lastlogin() | |
66 | Session().commit() |
|
66 | Session().commit() | |
67 |
|
67 | |||
68 | # If they want to be remembered, update the cookie |
|
68 | # If they want to be remembered, update the cookie | |
69 | if remember: |
|
69 | if remember: | |
70 | _year = (datetime.datetime.now() + |
|
70 | _year = (datetime.datetime.now() + | |
71 | datetime.timedelta(seconds=60 * 60 * 24 * 365)) |
|
71 | datetime.timedelta(seconds=60 * 60 * 24 * 365)) | |
72 | session._set_cookie_expires(_year) |
|
72 | session._set_cookie_expires(_year) | |
73 |
|
73 | |||
74 | session.save() |
|
74 | session.save() | |
75 |
|
75 | |||
76 | log.info('user %s is now authenticated and stored in ' |
|
76 | log.info('user %s is now authenticated and stored in ' | |
77 | 'session, session attrs %s' % (username, cs)) |
|
77 | 'session, session attrs %s' % (username, cs)) | |
78 |
|
78 | |||
79 | # dumps session attrs back to cookie |
|
79 | # dumps session attrs back to cookie | |
80 | session._update_cookie_out() |
|
80 | session._update_cookie_out() | |
81 | # we set new cookie |
|
81 | # we set new cookie | |
82 | headers = None |
|
82 | headers = None | |
83 | if session.request['set_cookie']: |
|
83 | if session.request['set_cookie']: | |
84 | # send set-cookie headers back to response to update cookie |
|
84 | # send set-cookie headers back to response to update cookie | |
85 | headers = [('Set-Cookie', session.request['cookie_out'])] |
|
85 | headers = [('Set-Cookie', session.request['cookie_out'])] | |
86 | return headers |
|
86 | return headers | |
87 |
|
87 | |||
88 | def _validate_came_from(self, came_from): |
|
88 | def _validate_came_from(self, came_from): | |
89 | if not came_from: |
|
89 | if not came_from: | |
90 | return came_from |
|
90 | return came_from | |
91 |
|
91 | |||
92 | parsed = urlparse.urlparse(came_from) |
|
92 | parsed = urlparse.urlparse(came_from) | |
93 | server_parsed = urlparse.urlparse(url.current()) |
|
93 | server_parsed = urlparse.urlparse(url.current()) | |
94 | allowed_schemes = ['http', 'https'] |
|
94 | allowed_schemes = ['http', 'https'] | |
95 | if parsed.scheme and parsed.scheme not in allowed_schemes: |
|
95 | if parsed.scheme and parsed.scheme not in allowed_schemes: | |
96 | log.error('Suspicious URL scheme detected %s for url %s' % |
|
96 | log.error('Suspicious URL scheme detected %s for url %s' % | |
97 | (parsed.scheme, parsed)) |
|
97 | (parsed.scheme, parsed)) | |
98 | came_from = url('home') |
|
98 | came_from = url('home') | |
99 | elif server_parsed.netloc != parsed.netloc: |
|
99 | elif server_parsed.netloc != parsed.netloc: | |
100 | log.error('Suspicious NETLOC detected %s for url %s server url ' |
|
100 | log.error('Suspicious NETLOC detected %s for url %s server url ' | |
101 | 'is: %s' % (parsed.netloc, parsed, server_parsed)) |
|
101 | 'is: %s' % (parsed.netloc, parsed, server_parsed)) | |
102 | came_from = url('home') |
|
102 | came_from = url('home') | |
103 | return came_from |
|
103 | return came_from | |
104 |
|
104 | |||
105 | def index(self): |
|
105 | def index(self): | |
106 | _default_came_from = url('home') |
|
106 | _default_came_from = url('home') | |
107 | came_from = self._validate_came_from(request.GET.get('came_from')) |
|
107 | came_from = self._validate_came_from(request.GET.get('came_from')) | |
108 | c.came_from = came_from or _default_came_from |
|
108 | c.came_from = came_from or _default_came_from | |
109 |
|
109 | |||
110 | not_default = self.authuser.username != User.DEFAULT_USER |
|
110 | not_default = self.authuser.username != User.DEFAULT_USER | |
111 | ip_allowed = self.authuser.ip_allowed |
|
111 | ip_allowed = self.authuser.ip_allowed | |
112 |
|
112 | |||
113 | # redirect if already logged in |
|
113 | # redirect if already logged in | |
114 | if self.authuser.is_authenticated and not_default and ip_allowed: |
|
114 | if self.authuser.is_authenticated and not_default and ip_allowed: | |
115 | raise HTTPFound(location=c.came_from) |
|
115 | raise HTTPFound(location=c.came_from) | |
116 |
|
116 | |||
117 | if request.POST: |
|
117 | if request.POST: | |
118 | # import Login Form validator class |
|
118 | # import Login Form validator class | |
119 | login_form = LoginForm() |
|
119 | login_form = LoginForm() | |
120 | try: |
|
120 | try: | |
121 | session.invalidate() |
|
121 | session.invalidate() | |
122 | c.form_result = login_form.to_python(dict(request.POST)) |
|
122 | c.form_result = login_form.to_python(dict(request.POST)) | |
123 | # form checks for username/password, now we're authenticated |
|
123 | # form checks for username/password, now we're authenticated | |
124 | headers = self._store_user_in_session( |
|
124 | headers = self._store_user_in_session( | |
125 | username=c.form_result['username'], |
|
125 | username=c.form_result['username'], | |
126 | remember=c.form_result['remember']) |
|
126 | remember=c.form_result['remember']) | |
127 | raise HTTPFound(location=c.came_from, headers=headers) |
|
127 | raise HTTPFound(location=c.came_from, headers=headers) | |
128 | except formencode.Invalid, errors: |
|
128 | except formencode.Invalid, errors: | |
129 | defaults = errors.value |
|
129 | defaults = errors.value | |
130 | # remove password from filling in form again |
|
130 | # remove password from filling in form again | |
131 | del defaults['password'] |
|
131 | del defaults['password'] | |
132 | return htmlfill.render( |
|
132 | return htmlfill.render( | |
133 | render('/login.html'), |
|
133 | render('/login.html'), | |
134 | defaults=errors.value, |
|
134 | defaults=errors.value, | |
135 | errors=errors.error_dict or {}, |
|
135 | errors=errors.error_dict or {}, | |
136 | prefix_error=False, |
|
136 | prefix_error=False, | |
137 |
encoding="UTF-8" |
|
137 | encoding="UTF-8", | |
|
138 | force_defaults=False) | |||
138 | except UserCreationError, e: |
|
139 | except UserCreationError, e: | |
139 | # container auth or other auth functions that create users on |
|
140 | # container auth or other auth functions that create users on | |
140 | # the fly can throw this exception signaling that there's issue |
|
141 | # the fly can throw this exception signaling that there's issue | |
141 | # with user creation, explanation should be provided in |
|
142 | # with user creation, explanation should be provided in | |
142 | # Exception itself |
|
143 | # Exception itself | |
143 | h.flash(e, 'error') |
|
144 | h.flash(e, 'error') | |
144 |
|
145 | |||
145 | # check if we use container plugin, and try to login using it. |
|
146 | # check if we use container plugin, and try to login using it. | |
146 | auth_plugins = Setting.get_auth_plugins() |
|
147 | auth_plugins = Setting.get_auth_plugins() | |
147 | if any((importplugin(name).is_container_auth for name in auth_plugins)): |
|
148 | if any((importplugin(name).is_container_auth for name in auth_plugins)): | |
148 | from kallithea.lib import auth_modules |
|
149 | from kallithea.lib import auth_modules | |
149 | try: |
|
150 | try: | |
150 | auth_info = auth_modules.authenticate('', '', request.environ) |
|
151 | auth_info = auth_modules.authenticate('', '', request.environ) | |
151 | except UserCreationError, e: |
|
152 | except UserCreationError, e: | |
152 | log.error(e) |
|
153 | log.error(e) | |
153 | h.flash(e, 'error') |
|
154 | h.flash(e, 'error') | |
154 | # render login, with flash message about limit |
|
155 | # render login, with flash message about limit | |
155 | return render('/login.html') |
|
156 | return render('/login.html') | |
156 |
|
157 | |||
157 | if auth_info: |
|
158 | if auth_info: | |
158 | headers = self._store_user_in_session(auth_info.get('username')) |
|
159 | headers = self._store_user_in_session(auth_info.get('username')) | |
159 | raise HTTPFound(location=c.came_from, headers=headers) |
|
160 | raise HTTPFound(location=c.came_from, headers=headers) | |
160 | return render('/login.html') |
|
161 | return render('/login.html') | |
161 |
|
162 | |||
162 | @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate', |
|
163 | @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate', | |
163 | 'hg.register.manual_activate') |
|
164 | 'hg.register.manual_activate') | |
164 | def register(self): |
|
165 | def register(self): | |
165 | c.auto_active = 'hg.register.auto_activate' in User.get_default_user()\ |
|
166 | c.auto_active = 'hg.register.auto_activate' in User.get_default_user()\ | |
166 | .AuthUser.permissions['global'] |
|
167 | .AuthUser.permissions['global'] | |
167 |
|
168 | |||
168 | settings = Setting.get_app_settings() |
|
169 | settings = Setting.get_app_settings() | |
169 | captcha_private_key = settings.get('captcha_private_key') |
|
170 | captcha_private_key = settings.get('captcha_private_key') | |
170 | c.captcha_active = bool(captcha_private_key) |
|
171 | c.captcha_active = bool(captcha_private_key) | |
171 | c.captcha_public_key = settings.get('captcha_public_key') |
|
172 | c.captcha_public_key = settings.get('captcha_public_key') | |
172 |
|
173 | |||
173 | if request.POST: |
|
174 | if request.POST: | |
174 | register_form = RegisterForm()() |
|
175 | register_form = RegisterForm()() | |
175 | try: |
|
176 | try: | |
176 | form_result = register_form.to_python(dict(request.POST)) |
|
177 | form_result = register_form.to_python(dict(request.POST)) | |
177 | form_result['active'] = c.auto_active |
|
178 | form_result['active'] = c.auto_active | |
178 |
|
179 | |||
179 | if c.captcha_active: |
|
180 | if c.captcha_active: | |
180 | from kallithea.lib.recaptcha import submit |
|
181 | from kallithea.lib.recaptcha import submit | |
181 | response = submit(request.POST.get('recaptcha_challenge_field'), |
|
182 | response = submit(request.POST.get('recaptcha_challenge_field'), | |
182 | request.POST.get('recaptcha_response_field'), |
|
183 | request.POST.get('recaptcha_response_field'), | |
183 | private_key=captcha_private_key, |
|
184 | private_key=captcha_private_key, | |
184 | remoteip=self.ip_addr) |
|
185 | remoteip=self.ip_addr) | |
185 | if c.captcha_active and not response.is_valid: |
|
186 | if c.captcha_active and not response.is_valid: | |
186 | _value = form_result |
|
187 | _value = form_result | |
187 | _msg = _('bad captcha') |
|
188 | _msg = _('bad captcha') | |
188 | error_dict = {'recaptcha_field': _msg} |
|
189 | error_dict = {'recaptcha_field': _msg} | |
189 | raise formencode.Invalid(_msg, _value, None, |
|
190 | raise formencode.Invalid(_msg, _value, None, | |
190 | error_dict=error_dict) |
|
191 | error_dict=error_dict) | |
191 |
|
192 | |||
192 | UserModel().create_registration(form_result) |
|
193 | UserModel().create_registration(form_result) | |
193 | h.flash(_('You have successfully registered into Kallithea'), |
|
194 | h.flash(_('You have successfully registered into Kallithea'), | |
194 | category='success') |
|
195 | category='success') | |
195 | Session().commit() |
|
196 | Session().commit() | |
196 | return redirect(url('login_home')) |
|
197 | return redirect(url('login_home')) | |
197 |
|
198 | |||
198 | except formencode.Invalid, errors: |
|
199 | except formencode.Invalid, errors: | |
199 | return htmlfill.render( |
|
200 | return htmlfill.render( | |
200 | render('/register.html'), |
|
201 | render('/register.html'), | |
201 | defaults=errors.value, |
|
202 | defaults=errors.value, | |
202 | errors=errors.error_dict or {}, |
|
203 | errors=errors.error_dict or {}, | |
203 | prefix_error=False, |
|
204 | prefix_error=False, | |
204 |
encoding="UTF-8" |
|
205 | encoding="UTF-8", | |
|
206 | force_defaults=False) | |||
205 | except UserCreationError, e: |
|
207 | except UserCreationError, e: | |
206 | # container auth or other auth functions that create users on |
|
208 | # container auth or other auth functions that create users on | |
207 | # the fly can throw this exception signaling that there's issue |
|
209 | # the fly can throw this exception signaling that there's issue | |
208 | # with user creation, explanation should be provided in |
|
210 | # with user creation, explanation should be provided in | |
209 | # Exception itself |
|
211 | # Exception itself | |
210 | h.flash(e, 'error') |
|
212 | h.flash(e, 'error') | |
211 |
|
213 | |||
212 | return render('/register.html') |
|
214 | return render('/register.html') | |
213 |
|
215 | |||
214 | def password_reset(self): |
|
216 | def password_reset(self): | |
215 | settings = Setting.get_app_settings() |
|
217 | settings = Setting.get_app_settings() | |
216 | captcha_private_key = settings.get('captcha_private_key') |
|
218 | captcha_private_key = settings.get('captcha_private_key') | |
217 | c.captcha_active = bool(captcha_private_key) |
|
219 | c.captcha_active = bool(captcha_private_key) | |
218 | c.captcha_public_key = settings.get('captcha_public_key') |
|
220 | c.captcha_public_key = settings.get('captcha_public_key') | |
219 |
|
221 | |||
220 | if request.POST: |
|
222 | if request.POST: | |
221 | password_reset_form = PasswordResetForm()() |
|
223 | password_reset_form = PasswordResetForm()() | |
222 | try: |
|
224 | try: | |
223 | form_result = password_reset_form.to_python(dict(request.POST)) |
|
225 | form_result = password_reset_form.to_python(dict(request.POST)) | |
224 | if c.captcha_active: |
|
226 | if c.captcha_active: | |
225 | from kallithea.lib.recaptcha import submit |
|
227 | from kallithea.lib.recaptcha import submit | |
226 | response = submit(request.POST.get('recaptcha_challenge_field'), |
|
228 | response = submit(request.POST.get('recaptcha_challenge_field'), | |
227 | request.POST.get('recaptcha_response_field'), |
|
229 | request.POST.get('recaptcha_response_field'), | |
228 | private_key=captcha_private_key, |
|
230 | private_key=captcha_private_key, | |
229 | remoteip=self.ip_addr) |
|
231 | remoteip=self.ip_addr) | |
230 | if c.captcha_active and not response.is_valid: |
|
232 | if c.captcha_active and not response.is_valid: | |
231 | _value = form_result |
|
233 | _value = form_result | |
232 | _msg = _('bad captcha') |
|
234 | _msg = _('bad captcha') | |
233 | error_dict = {'recaptcha_field': _msg} |
|
235 | error_dict = {'recaptcha_field': _msg} | |
234 | raise formencode.Invalid(_msg, _value, None, |
|
236 | raise formencode.Invalid(_msg, _value, None, | |
235 | error_dict=error_dict) |
|
237 | error_dict=error_dict) | |
236 | UserModel().reset_password_link(form_result) |
|
238 | UserModel().reset_password_link(form_result) | |
237 | h.flash(_('Your password reset link was sent'), |
|
239 | h.flash(_('Your password reset link was sent'), | |
238 | category='success') |
|
240 | category='success') | |
239 | return redirect(url('login_home')) |
|
241 | return redirect(url('login_home')) | |
240 |
|
242 | |||
241 | except formencode.Invalid, errors: |
|
243 | except formencode.Invalid, errors: | |
242 | return htmlfill.render( |
|
244 | return htmlfill.render( | |
243 | render('/password_reset.html'), |
|
245 | render('/password_reset.html'), | |
244 | defaults=errors.value, |
|
246 | defaults=errors.value, | |
245 | errors=errors.error_dict or {}, |
|
247 | errors=errors.error_dict or {}, | |
246 | prefix_error=False, |
|
248 | prefix_error=False, | |
247 |
encoding="UTF-8" |
|
249 | encoding="UTF-8", | |
|
250 | force_defaults=False) | |||
248 |
|
251 | |||
249 | return render('/password_reset.html') |
|
252 | return render('/password_reset.html') | |
250 |
|
253 | |||
251 | def password_reset_confirmation(self): |
|
254 | def password_reset_confirmation(self): | |
252 | if request.GET and request.GET.get('key'): |
|
255 | if request.GET and request.GET.get('key'): | |
253 | try: |
|
256 | try: | |
254 | user = User.get_by_api_key(request.GET.get('key')) |
|
257 | user = User.get_by_api_key(request.GET.get('key')) | |
255 | data = dict(email=user.email) |
|
258 | data = dict(email=user.email) | |
256 | UserModel().reset_password(data) |
|
259 | UserModel().reset_password(data) | |
257 | h.flash(_('Your password reset was successful, ' |
|
260 | h.flash(_('Your password reset was successful, ' | |
258 | 'new password has been sent to your email'), |
|
261 | 'new password has been sent to your email'), | |
259 | category='success') |
|
262 | category='success') | |
260 | except Exception, e: |
|
263 | except Exception, e: | |
261 | log.error(e) |
|
264 | log.error(e) | |
262 | return redirect(url('reset_password')) |
|
265 | return redirect(url('reset_password')) | |
263 |
|
266 | |||
264 | return redirect(url('login_home')) |
|
267 | return redirect(url('login_home')) | |
265 |
|
268 | |||
266 | def logout(self): |
|
269 | def logout(self): | |
267 | session.delete() |
|
270 | session.delete() | |
268 | log.info('Logging out and deleting session for user') |
|
271 | log.info('Logging out and deleting session for user') | |
269 | redirect(url('home')) |
|
272 | redirect(url('home')) |
General Comments 0
You need to be logged in to leave comments.
Login now