##// END OF EJS Templates
controllers: consistently use formfill.render with force_defaults=False...
Mads Kiilerich -
r4941:c04c2734 default
parent child Browse files
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=True,
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