##// END OF EJS Templates
controllers: remove empty __before__ methods...
Thomas De Schampheleire -
r6512:2f931307 default
parent child Browse files
Show More
@@ -1,58 +1,55 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.controllers.followers
16 16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 Followers controller for Kallithea
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Apr 23, 2011
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28 import logging
29 29
30 30 from tg import tmpl_context as c, request
31 31
32 32 from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator
33 33 from kallithea.lib.base import BaseRepoController, render
34 34 from kallithea.lib.page import Page
35 35 from kallithea.lib.utils2 import safe_int
36 36 from kallithea.model.db import UserFollowing
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 class FollowersController(BaseRepoController):
42 42
43 def __before__(self):
44 super(FollowersController, self).__before__()
45
46 43 @LoginRequired()
47 44 @HasRepoPermissionLevelDecorator('read')
48 45 def followers(self, repo_name):
49 46 p = safe_int(request.GET.get('page'), 1)
50 47 repo_id = c.db_repo.repo_id
51 48 d = UserFollowing.get_repo_followers(repo_id) \
52 49 .order_by(UserFollowing.follows_from)
53 50 c.followers_pager = Page(d, page=p, items_per_page=20)
54 51
55 52 if request.environ.get('HTTP_X_PARTIAL_XHR'):
56 53 return render('/followers/followers_data.html')
57 54
58 55 return render('/followers/followers.html')
@@ -1,184 +1,181 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.controllers.forks
16 16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 forks controller for Kallithea
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Apr 23, 2011
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28 import logging
29 29 import formencode
30 30 import traceback
31 31 from formencode import htmlfill
32 32
33 33 from tg import tmpl_context as c, request
34 34 from tg.i18n import ugettext as _
35 35 from webob.exc import HTTPFound
36 36
37 37 import kallithea.lib.helpers as h
38 38
39 39 from kallithea.config.routing import url
40 40 from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator, \
41 41 NotAnonymous, HasRepoPermissionLevel, HasPermissionAnyDecorator, HasPermissionAny
42 42 from kallithea.lib.base import BaseRepoController, render
43 43 from kallithea.lib.page import Page
44 44 from kallithea.lib.utils2 import safe_int
45 45 from kallithea.model.db import Repository, UserFollowing, User, Ui
46 46 from kallithea.model.repo import RepoModel
47 47 from kallithea.model.forms import RepoForkForm
48 48 from kallithea.model.scm import ScmModel, AvailableRepoGroupChoices
49 49
50 50 log = logging.getLogger(__name__)
51 51
52 52
53 53 class ForksController(BaseRepoController):
54 54
55 def __before__(self):
56 super(ForksController, self).__before__()
57
58 55 def __load_defaults(self):
59 56 if HasPermissionAny('hg.create.write_on_repogroup.true')():
60 57 repo_group_perm_level = 'write'
61 58 else:
62 59 repo_group_perm_level = 'admin'
63 60 c.repo_groups = AvailableRepoGroupChoices(['hg.create.repository'], repo_group_perm_level)
64 61
65 62 c.landing_revs_choices, c.landing_revs = ScmModel().get_repo_landing_revs()
66 63
67 64 c.can_update = Ui.get_by_key('hooks', Ui.HOOK_UPDATE).ui_active
68 65
69 66 def __load_data(self):
70 67 """
71 68 Load defaults settings for edit, and update
72 69 """
73 70 self.__load_defaults()
74 71
75 72 c.repo_info = c.db_repo
76 73 repo = c.db_repo.scm_instance
77 74
78 75 if c.repo_info is None:
79 76 h.not_mapped_error(c.repo_name)
80 77 raise HTTPFound(location=url('repos'))
81 78
82 79 c.default_user_id = User.get_default_user().user_id
83 80 c.in_public_journal = UserFollowing.query() \
84 81 .filter(UserFollowing.user_id == c.default_user_id) \
85 82 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
86 83
87 84 if c.repo_info.stats:
88 85 last_rev = c.repo_info.stats.stat_on_revision+1
89 86 else:
90 87 last_rev = 0
91 88 c.stats_revision = last_rev
92 89
93 90 c.repo_last_rev = repo.count() if repo.revisions else 0
94 91
95 92 if last_rev == 0 or c.repo_last_rev == 0:
96 93 c.stats_percentage = 0
97 94 else:
98 95 c.stats_percentage = '%.2f' % ((float((last_rev)) /
99 96 c.repo_last_rev) * 100)
100 97
101 98 defaults = RepoModel()._get_defaults(c.repo_name)
102 99 # alter the description to indicate a fork
103 100 defaults['description'] = ('fork of repository: %s \n%s'
104 101 % (defaults['repo_name'],
105 102 defaults['description']))
106 103 # add suffix to fork
107 104 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
108 105
109 106 return defaults
110 107
111 108 @LoginRequired()
112 109 @HasRepoPermissionLevelDecorator('read')
113 110 def forks(self, repo_name):
114 111 p = safe_int(request.GET.get('page'), 1)
115 112 repo_id = c.db_repo.repo_id
116 113 d = []
117 114 for r in Repository.get_repo_forks(repo_id):
118 115 if not HasRepoPermissionLevel('read')(r.repo_name, 'get forks check'):
119 116 continue
120 117 d.append(r)
121 118 c.forks_pager = Page(d, page=p, items_per_page=20)
122 119
123 120 if request.environ.get('HTTP_X_PARTIAL_XHR'):
124 121 return render('/forks/forks_data.html')
125 122
126 123 return render('/forks/forks.html')
127 124
128 125 @LoginRequired()
129 126 @NotAnonymous()
130 127 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
131 128 @HasRepoPermissionLevelDecorator('read')
132 129 def fork(self, repo_name):
133 130 c.repo_info = Repository.get_by_repo_name(repo_name)
134 131 if not c.repo_info:
135 132 h.not_mapped_error(repo_name)
136 133 raise HTTPFound(location=url('home'))
137 134
138 135 defaults = self.__load_data()
139 136
140 137 return htmlfill.render(
141 138 render('forks/fork.html'),
142 139 defaults=defaults,
143 140 encoding="UTF-8",
144 141 force_defaults=False)
145 142
146 143 @LoginRequired()
147 144 @NotAnonymous()
148 145 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
149 146 @HasRepoPermissionLevelDecorator('read')
150 147 def fork_create(self, repo_name):
151 148 self.__load_defaults()
152 149 c.repo_info = Repository.get_by_repo_name(repo_name)
153 150 _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
154 151 repo_groups=c.repo_groups,
155 152 landing_revs=c.landing_revs_choices)()
156 153 form_result = {}
157 154 task_id = None
158 155 try:
159 156 form_result = _form.to_python(dict(request.POST))
160 157
161 158 # an approximation that is better than nothing
162 159 if not Ui.get_by_key('hooks', Ui.HOOK_UPDATE).ui_active:
163 160 form_result['update_after_clone'] = False
164 161
165 162 # create fork is done sometimes async on celery, db transaction
166 163 # management is handled there.
167 164 task = RepoModel().create_fork(form_result, request.authuser.user_id)
168 165 task_id = task.task_id
169 166 except formencode.Invalid as errors:
170 167 return htmlfill.render(
171 168 render('forks/fork.html'),
172 169 defaults=errors.value,
173 170 errors=errors.error_dict or {},
174 171 prefix_error=False,
175 172 encoding="UTF-8",
176 173 force_defaults=False)
177 174 except Exception:
178 175 log.error(traceback.format_exc())
179 176 h.flash(_('An error occurred during repository forking %s') %
180 177 repo_name, category='error')
181 178
182 179 raise HTTPFound(location=h.url('repo_creating_home',
183 180 repo_name=form_result['repo_name_full'],
184 181 task_id=task_id))
@@ -1,148 +1,145 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.controllers.home
16 16 ~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 Home controller for Kallithea
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Feb 18, 2010
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26
27 27 """
28 28
29 29 import logging
30 30
31 31 from tg import tmpl_context as c, request
32 32 from tg.i18n import ugettext as _
33 33 from webob.exc import HTTPBadRequest
34 34 from sqlalchemy.sql.expression import func
35 35
36 36 from kallithea.lib.utils import conditional_cache
37 37 from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator
38 38 from kallithea.lib.base import BaseController, render, jsonify
39 39 from kallithea.model.db import Repository, RepoGroup
40 40 from kallithea.model.repo import RepoModel
41 41
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 class HomeController(BaseController):
47 47
48 def __before__(self):
49 super(HomeController, self).__before__()
50
51 48 def about(self):
52 49 return render('/about.html')
53 50
54 51 @LoginRequired()
55 52 def index(self):
56 53 c.groups = self.scm_model.get_repo_groups()
57 54 c.group = None
58 55
59 56 repos_list = Repository.query(sorted=True).filter_by(group=None).all()
60 57
61 58 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
62 59 admin=False, short_name=True)
63 60 #data used to render the grid
64 61 c.data = repos_data
65 62
66 63 return render('/index.html')
67 64
68 65 @LoginRequired()
69 66 @jsonify
70 67 def repo_switcher_data(self):
71 68 #wrapper for conditional cache
72 69 def _c():
73 70 log.debug('generating switcher repo/groups list')
74 71 all_repos = Repository.query(sorted=True).all()
75 72 repo_iter = self.scm_model.get_repos(all_repos)
76 73 all_groups = RepoGroup.query(sorted=True).all()
77 74 repo_groups_iter = self.scm_model.get_repo_groups(all_groups)
78 75
79 76 res = [{
80 77 'text': _('Groups'),
81 78 'children': [
82 79 {'id': obj.group_name,
83 80 'text': obj.group_name,
84 81 'type': 'group',
85 82 'obj': {}}
86 83 for obj in repo_groups_iter
87 84 ],
88 85 },
89 86 {
90 87 'text': _('Repositories'),
91 88 'children': [
92 89 {'id': obj.repo_name,
93 90 'text': obj.repo_name,
94 91 'type': 'repo',
95 92 'obj': obj.get_dict()}
96 93 for obj in repo_iter
97 94 ],
98 95 }]
99 96
100 97 data = {
101 98 'more': False,
102 99 'results': res,
103 100 }
104 101 return data
105 102
106 103 if request.is_xhr:
107 104 condition = False
108 105 compute = conditional_cache('short_term', 'cache_desc',
109 106 condition=condition, func=_c)
110 107 return compute()
111 108 else:
112 109 raise HTTPBadRequest()
113 110
114 111 @LoginRequired()
115 112 @HasRepoPermissionLevelDecorator('read')
116 113 @jsonify
117 114 def repo_refs_data(self, repo_name):
118 115 repo = Repository.get_by_repo_name(repo_name).scm_instance
119 116 res = []
120 117 _branches = repo.branches.items()
121 118 if _branches:
122 119 res.append({
123 120 'text': _('Branch'),
124 121 'children': [{'id': rev, 'text': name, 'type': 'branch'} for name, rev in _branches]
125 122 })
126 123 _closed_branches = repo.closed_branches.items()
127 124 if _closed_branches:
128 125 res.append({
129 126 'text': _('Closed Branches'),
130 127 'children': [{'id': rev, 'text': name, 'type': 'closed-branch'} for name, rev in _closed_branches]
131 128 })
132 129 _tags = repo.tags.items()
133 130 if _tags:
134 131 res.append({
135 132 'text': _('Tag'),
136 133 'children': [{'id': rev, 'text': name, 'type': 'tag'} for name, rev in _tags]
137 134 })
138 135 _bookmarks = repo.bookmarks.items()
139 136 if _bookmarks:
140 137 res.append({
141 138 'text': _('Bookmark'),
142 139 'children': [{'id': rev, 'text': name, 'type': 'book'} for name, rev in _bookmarks]
143 140 })
144 141 data = {
145 142 'more': False,
146 143 'results': res
147 144 }
148 145 return data
@@ -1,264 +1,261 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.controllers.login
16 16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 Login controller for Kallithea
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Apr 22, 2010
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28
29 29 import logging
30 30 import re
31 31 import formencode
32 32
33 33 from formencode import htmlfill
34 34 from tg.i18n import ugettext as _
35 35 from tg import request, session, tmpl_context as c
36 36 from webob.exc import HTTPFound, HTTPBadRequest
37 37
38 38 import kallithea.lib.helpers as h
39 39 from kallithea.config.routing import url
40 40 from kallithea.lib.auth import AuthUser, HasPermissionAnyDecorator
41 41 from kallithea.lib.base import BaseController, log_in_user, render
42 42 from kallithea.lib.exceptions import UserCreationError
43 43 from kallithea.lib.utils2 import safe_str
44 44 from kallithea.model.db import User, Setting
45 45 from kallithea.model.forms import \
46 46 LoginForm, RegisterForm, PasswordResetRequestForm, PasswordResetConfirmationForm
47 47 from kallithea.model.user import UserModel
48 48 from kallithea.model.meta import Session
49 49
50 50
51 51 log = logging.getLogger(__name__)
52 52
53 53
54 54 class LoginController(BaseController):
55 55
56 def __before__(self):
57 super(LoginController, self).__before__()
58
59 56 def _validate_came_from(self, came_from,
60 57 _re=re.compile(r"/(?!/)[-!#$%&'()*+,./:;=?@_~0-9A-Za-z]*$")):
61 58 """Return True if came_from is valid and can and should be used.
62 59
63 60 Determines if a URI reference is valid and relative to the origin;
64 61 or in RFC 3986 terms, whether it matches this production:
65 62
66 63 origin-relative-ref = path-absolute [ "?" query ] [ "#" fragment ]
67 64
68 65 with the exception that '%' escapes are not validated and '#' is
69 66 allowed inside the fragment part.
70 67 """
71 68 return _re.match(came_from) is not None
72 69
73 70 def index(self):
74 71 c.came_from = safe_str(request.GET.get('came_from', ''))
75 72 if c.came_from:
76 73 if not self._validate_came_from(c.came_from):
77 74 log.error('Invalid came_from (not server-relative): %r', c.came_from)
78 75 raise HTTPBadRequest()
79 76 else:
80 77 c.came_from = url('home')
81 78
82 79 ip_allowed = AuthUser.check_ip_allowed(request.authuser, request.ip_addr)
83 80
84 81 # redirect if already logged in
85 82 if request.authuser.is_authenticated and ip_allowed:
86 83 raise HTTPFound(location=c.came_from)
87 84
88 85 if request.POST:
89 86 # import Login Form validator class
90 87 login_form = LoginForm()()
91 88 try:
92 89 c.form_result = login_form.to_python(dict(request.POST))
93 90 # form checks for username/password, now we're authenticated
94 91 username = c.form_result['username']
95 92 user = User.get_by_username_or_email(username, case_insensitive=True)
96 93 except formencode.Invalid as errors:
97 94 defaults = errors.value
98 95 # remove password from filling in form again
99 96 defaults.pop('password', None)
100 97 return htmlfill.render(
101 98 render('/login.html'),
102 99 defaults=errors.value,
103 100 errors=errors.error_dict or {},
104 101 prefix_error=False,
105 102 encoding="UTF-8",
106 103 force_defaults=False)
107 104 except UserCreationError as e:
108 105 # container auth or other auth functions that create users on
109 106 # the fly can throw this exception signaling that there's issue
110 107 # with user creation, explanation should be provided in
111 108 # Exception itself
112 109 h.flash(e, 'error')
113 110 else:
114 111 log_in_user(user, c.form_result['remember'],
115 112 is_external_auth=False)
116 113 raise HTTPFound(location=c.came_from)
117 114
118 115 return render('/login.html')
119 116
120 117 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
121 118 'hg.register.manual_activate')
122 119 def register(self):
123 120 c.auto_active = 'hg.register.auto_activate' in User.get_default_user() \
124 121 .AuthUser.permissions['global']
125 122
126 123 settings = Setting.get_app_settings()
127 124 captcha_private_key = settings.get('captcha_private_key')
128 125 c.captcha_active = bool(captcha_private_key)
129 126 c.captcha_public_key = settings.get('captcha_public_key')
130 127
131 128 if request.POST:
132 129 register_form = RegisterForm()()
133 130 try:
134 131 form_result = register_form.to_python(dict(request.POST))
135 132 form_result['active'] = c.auto_active
136 133
137 134 if c.captcha_active:
138 135 from kallithea.lib.recaptcha import submit
139 136 response = submit(request.POST.get('recaptcha_challenge_field'),
140 137 request.POST.get('recaptcha_response_field'),
141 138 private_key=captcha_private_key,
142 139 remoteip=request.ip_addr)
143 140 if c.captcha_active and not response.is_valid:
144 141 _value = form_result
145 142 _msg = _('Bad captcha')
146 143 error_dict = {'recaptcha_field': _msg}
147 144 raise formencode.Invalid(_msg, _value, None,
148 145 error_dict=error_dict)
149 146
150 147 UserModel().create_registration(form_result)
151 148 h.flash(_('You have successfully registered with %s') % (c.site_name or 'Kallithea'),
152 149 category='success')
153 150 Session().commit()
154 151 raise HTTPFound(location=url('login_home'))
155 152
156 153 except formencode.Invalid as errors:
157 154 return htmlfill.render(
158 155 render('/register.html'),
159 156 defaults=errors.value,
160 157 errors=errors.error_dict or {},
161 158 prefix_error=False,
162 159 encoding="UTF-8",
163 160 force_defaults=False)
164 161 except UserCreationError as e:
165 162 # container auth or other auth functions that create users on
166 163 # the fly can throw this exception signaling that there's issue
167 164 # with user creation, explanation should be provided in
168 165 # Exception itself
169 166 h.flash(e, 'error')
170 167
171 168 return render('/register.html')
172 169
173 170 def password_reset(self):
174 171 settings = Setting.get_app_settings()
175 172 captcha_private_key = settings.get('captcha_private_key')
176 173 c.captcha_active = bool(captcha_private_key)
177 174 c.captcha_public_key = settings.get('captcha_public_key')
178 175
179 176 if request.POST:
180 177 password_reset_form = PasswordResetRequestForm()()
181 178 try:
182 179 form_result = password_reset_form.to_python(dict(request.POST))
183 180 if c.captcha_active:
184 181 from kallithea.lib.recaptcha import submit
185 182 response = submit(request.POST.get('recaptcha_challenge_field'),
186 183 request.POST.get('recaptcha_response_field'),
187 184 private_key=captcha_private_key,
188 185 remoteip=request.ip_addr)
189 186 if c.captcha_active and not response.is_valid:
190 187 _value = form_result
191 188 _msg = _('Bad captcha')
192 189 error_dict = {'recaptcha_field': _msg}
193 190 raise formencode.Invalid(_msg, _value, None,
194 191 error_dict=error_dict)
195 192 redirect_link = UserModel().send_reset_password_email(form_result)
196 193 h.flash(_('A password reset confirmation code has been sent'),
197 194 category='success')
198 195 raise HTTPFound(location=redirect_link)
199 196
200 197 except formencode.Invalid as errors:
201 198 return htmlfill.render(
202 199 render('/password_reset.html'),
203 200 defaults=errors.value,
204 201 errors=errors.error_dict or {},
205 202 prefix_error=False,
206 203 encoding="UTF-8",
207 204 force_defaults=False)
208 205
209 206 return render('/password_reset.html')
210 207
211 208 def password_reset_confirmation(self):
212 209 # This controller handles both GET and POST requests, though we
213 210 # only ever perform the actual password change on POST (since
214 211 # GET requests are not allowed to have side effects, and do not
215 212 # receive automatic CSRF protection).
216 213
217 214 # The template needs the email address outside of the form.
218 215 c.email = request.params.get('email')
219 216
220 217 if not request.POST:
221 218 return htmlfill.render(
222 219 render('/password_reset_confirmation.html'),
223 220 defaults=dict(request.params),
224 221 encoding='UTF-8')
225 222
226 223 form = PasswordResetConfirmationForm()()
227 224 try:
228 225 form_result = form.to_python(dict(request.POST))
229 226 except formencode.Invalid as errors:
230 227 return htmlfill.render(
231 228 render('/password_reset_confirmation.html'),
232 229 defaults=errors.value,
233 230 errors=errors.error_dict or {},
234 231 prefix_error=False,
235 232 encoding='UTF-8')
236 233
237 234 if not UserModel().verify_reset_password_token(
238 235 form_result['email'],
239 236 form_result['timestamp'],
240 237 form_result['token'],
241 238 ):
242 239 return htmlfill.render(
243 240 render('/password_reset_confirmation.html'),
244 241 defaults=form_result,
245 242 errors={'token': _('Invalid password reset token')},
246 243 prefix_error=False,
247 244 encoding='UTF-8')
248 245
249 246 UserModel().reset_password(form_result['email'], form_result['password'])
250 247 h.flash(_('Successfully updated password'), category='success')
251 248 raise HTTPFound(location=url('login_home'))
252 249
253 250 def logout(self):
254 251 session.delete()
255 252 log.info('Logging out and deleting session for user')
256 253 raise HTTPFound(location=url('home'))
257 254
258 255 def authentication_token(self):
259 256 """Return the CSRF protection token for the session - just like it
260 257 could have been screen scraped from a page with a form.
261 258 Only intended for testing but might also be useful for other kinds
262 259 of automation.
263 260 """
264 261 return h.authentication_token()
@@ -1,149 +1,146 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.controllers.search
16 16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 Search controller for Kallithea
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Aug 7, 2010
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28 import logging
29 29 import traceback
30 30 import urllib
31 31 from tg.i18n import ugettext as _
32 32 from tg import request, config, tmpl_context as c
33 33
34 34 from whoosh.index import open_dir, EmptyIndexError
35 35 from whoosh.qparser import QueryParser, QueryParserError
36 36 from whoosh.query import Phrase, Prefix
37 37 from webhelpers.util import update_params
38 38
39 39 from kallithea.lib.auth import LoginRequired
40 40 from kallithea.lib.base import BaseRepoController, render
41 41 from kallithea.lib.indexers import CHGSETS_SCHEMA, SCHEMA, CHGSET_IDX_NAME, \
42 42 IDX_NAME, WhooshResultWrapper
43 43 from kallithea.lib.page import Page
44 44 from kallithea.lib.utils2 import safe_str, safe_int
45 45 from kallithea.model.repo import RepoModel
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 class SearchController(BaseRepoController):
51 51
52 def __before__(self):
53 super(SearchController, self).__before__()
54
55 52 @LoginRequired()
56 53 def index(self, repo_name=None):
57 54 c.repo_name = repo_name
58 55 c.formated_results = []
59 56 c.runtime = ''
60 57 c.cur_query = request.GET.get('q', None)
61 58 c.cur_type = request.GET.get('type', 'content')
62 59 c.cur_search = search_type = {'content': 'content',
63 60 'commit': 'message',
64 61 'path': 'path',
65 62 'repository': 'repository'
66 63 }.get(c.cur_type, 'content')
67 64
68 65 index_name = {
69 66 'content': IDX_NAME,
70 67 'commit': CHGSET_IDX_NAME,
71 68 'path': IDX_NAME
72 69 }.get(c.cur_type, IDX_NAME)
73 70
74 71 schema_defn = {
75 72 'content': SCHEMA,
76 73 'commit': CHGSETS_SCHEMA,
77 74 'path': SCHEMA
78 75 }.get(c.cur_type, SCHEMA)
79 76
80 77 log.debug('IDX: %s', index_name)
81 78 log.debug('SCHEMA: %s', schema_defn)
82 79
83 80 if c.cur_query:
84 81 cur_query = c.cur_query.lower()
85 82 log.debug(cur_query)
86 83
87 84 if c.cur_query:
88 85 p = safe_int(request.GET.get('page'), 1)
89 86 highlight_items = set()
90 87 try:
91 88 idx = open_dir(config['app_conf']['index_dir'],
92 89 indexname=index_name)
93 90 searcher = idx.searcher()
94 91
95 92 qp = QueryParser(search_type, schema=schema_defn)
96 93 if c.repo_name:
97 94 # use "repository_rawname:" instead of "repository:"
98 95 # for case-sensitive matching
99 96 cur_query = u'repository_rawname:%s %s' % (c.repo_name, cur_query)
100 97 try:
101 98 query = qp.parse(unicode(cur_query))
102 99 # extract words for highlight
103 100 if isinstance(query, Phrase):
104 101 highlight_items.update(query.words)
105 102 elif isinstance(query, Prefix):
106 103 highlight_items.add(query.text)
107 104 else:
108 105 for i in query.all_terms():
109 106 if i[0] in ['content', 'message']:
110 107 highlight_items.add(i[1])
111 108
112 109 matcher = query.matcher(searcher)
113 110
114 111 log.debug('query: %s', query)
115 112 log.debug('hl terms: %s', highlight_items)
116 113 results = searcher.search(query)
117 114 res_ln = len(results)
118 115 c.runtime = '%s results (%.3f seconds)' % (
119 116 res_ln, results.runtime
120 117 )
121 118
122 119 def url_generator(**kw):
123 120 q = urllib.quote(safe_str(c.cur_query))
124 121 return update_params("?q=%s&type=%s" \
125 122 % (q, safe_str(c.cur_type)), **kw)
126 123 repo_location = RepoModel().repos_path
127 124 c.formated_results = Page(
128 125 WhooshResultWrapper(search_type, searcher, matcher,
129 126 highlight_items, repo_location),
130 127 page=p,
131 128 item_count=res_ln,
132 129 items_per_page=10,
133 130 url=url_generator
134 131 )
135 132
136 133 except QueryParserError:
137 134 c.runtime = _('Invalid search query. Try quoting it.')
138 135 searcher.close()
139 136 except (EmptyIndexError, IOError):
140 137 log.error(traceback.format_exc())
141 138 log.error('Empty Index data')
142 139 c.runtime = _('There is no index to search in. '
143 140 'Please run whoosh indexer')
144 141 except Exception:
145 142 log.error(traceback.format_exc())
146 143 c.runtime = _('An error occurred during search operation.')
147 144
148 145 # Return a rendered template
149 146 return render('/search/search.html')
@@ -1,224 +1,221 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.controllers.summary
16 16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 Summary controller for Kallithea
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Apr 18, 2010
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28 import traceback
29 29 import calendar
30 30 import logging
31 31 import itertools
32 32 from time import mktime
33 33 from datetime import timedelta, date
34 34
35 35 from tg import tmpl_context as c, request
36 36 from tg.i18n import ugettext as _
37 37 from webob.exc import HTTPBadRequest
38 38
39 39 from beaker.cache import cache_region, region_invalidate
40 40
41 41 from kallithea.lib.vcs.exceptions import ChangesetError, EmptyRepositoryError, \
42 42 NodeDoesNotExistError
43 43 from kallithea.config.conf import ALL_READMES, ALL_EXTS, LANGUAGES_EXTENSIONS_MAP
44 44 from kallithea.model.db import Statistics, CacheInvalidation, User
45 45 from kallithea.lib.utils2 import safe_str
46 46 from kallithea.lib.auth import LoginRequired, HasRepoPermissionLevelDecorator, \
47 47 NotAnonymous
48 48 from kallithea.lib.base import BaseRepoController, render, jsonify
49 49 from kallithea.lib.vcs.backends.base import EmptyChangeset
50 50 from kallithea.lib.markup_renderer import MarkupRenderer
51 51 from kallithea.lib.celerylib.tasks import get_commits_stats
52 52 from kallithea.lib.compat import json
53 53 from kallithea.lib.vcs.nodes import FileNode
54 54 from kallithea.controllers.changelog import _load_changelog_summary
55 55
56 56 log = logging.getLogger(__name__)
57 57
58 58 README_FILES = [''.join([x[0][0], x[1][0]]) for x in
59 59 sorted(list(itertools.product(ALL_READMES, ALL_EXTS)),
60 60 key=lambda y:y[0][1] + y[1][1])]
61 61
62 62
63 63 class SummaryController(BaseRepoController):
64 64
65 def __before__(self):
66 super(SummaryController, self).__before__()
67
68 65 def __get_readme_data(self, db_repo):
69 66 repo_name = db_repo.repo_name
70 67 log.debug('Looking for README file')
71 68
72 69 @cache_region('long_term', '_get_readme_from_cache')
73 70 def _get_readme_from_cache(key, kind):
74 71 readme_data = None
75 72 readme_file = None
76 73 try:
77 74 # gets the landing revision! or tip if fails
78 75 cs = db_repo.get_landing_changeset()
79 76 if isinstance(cs, EmptyChangeset):
80 77 raise EmptyRepositoryError()
81 78 renderer = MarkupRenderer()
82 79 for f in README_FILES:
83 80 try:
84 81 readme = cs.get_node(f)
85 82 if not isinstance(readme, FileNode):
86 83 continue
87 84 readme_file = f
88 85 log.debug('Found README file `%s` rendering...',
89 86 readme_file)
90 87 readme_data = renderer.render(readme.content,
91 88 filename=f)
92 89 break
93 90 except NodeDoesNotExistError:
94 91 continue
95 92 except ChangesetError:
96 93 log.error(traceback.format_exc())
97 94 pass
98 95 except EmptyRepositoryError:
99 96 pass
100 97
101 98 return readme_data, readme_file
102 99
103 100 kind = 'README'
104 101 valid = CacheInvalidation.test_and_set_valid(repo_name, kind)
105 102 if not valid:
106 103 region_invalidate(_get_readme_from_cache, None, '_get_readme_from_cache', repo_name, kind)
107 104 return _get_readme_from_cache(repo_name, kind)
108 105
109 106 @LoginRequired()
110 107 @HasRepoPermissionLevelDecorator('read')
111 108 def index(self, repo_name):
112 109 _load_changelog_summary()
113 110
114 111 if request.authuser.is_default_user:
115 112 username = ''
116 113 else:
117 114 username = safe_str(request.authuser.username)
118 115
119 116 _def_clone_uri = _def_clone_uri_by_id = c.clone_uri_tmpl
120 117 if '{repo}' in _def_clone_uri:
121 118 _def_clone_uri_by_id = _def_clone_uri.replace('{repo}', '_{repoid}')
122 119 elif '{repoid}' in _def_clone_uri:
123 120 _def_clone_uri_by_id = _def_clone_uri.replace('_{repoid}', '{repo}')
124 121
125 122 c.clone_repo_url = c.db_repo.clone_url(user=username,
126 123 uri_tmpl=_def_clone_uri)
127 124 c.clone_repo_url_id = c.db_repo.clone_url(user=username,
128 125 uri_tmpl=_def_clone_uri_by_id)
129 126
130 127 if c.db_repo.enable_statistics:
131 128 c.show_stats = True
132 129 else:
133 130 c.show_stats = False
134 131
135 132 stats = Statistics.query() \
136 133 .filter(Statistics.repository == c.db_repo) \
137 134 .scalar()
138 135
139 136 c.stats_percentage = 0
140 137
141 138 if stats and stats.languages:
142 139 c.no_data = False is c.db_repo.enable_statistics
143 140 lang_stats_d = json.loads(stats.languages)
144 141
145 142 lang_stats = ((x, {"count": y,
146 143 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
147 144 for x, y in lang_stats_d.items())
148 145
149 146 c.trending_languages = (
150 147 sorted(lang_stats, reverse=True, key=lambda k: k[1])[:10]
151 148 )
152 149 else:
153 150 c.no_data = True
154 151 c.trending_languages = []
155 152
156 153 c.enable_downloads = c.db_repo.enable_downloads
157 154 c.readme_data, c.readme_file = \
158 155 self.__get_readme_data(c.db_repo)
159 156 return render('summary/summary.html')
160 157
161 158 @LoginRequired()
162 159 @NotAnonymous()
163 160 @HasRepoPermissionLevelDecorator('read')
164 161 @jsonify
165 162 def repo_size(self, repo_name):
166 163 if request.is_xhr:
167 164 return c.db_repo._repo_size()
168 165 else:
169 166 raise HTTPBadRequest()
170 167
171 168 @LoginRequired()
172 169 @HasRepoPermissionLevelDecorator('read')
173 170 def statistics(self, repo_name):
174 171 if c.db_repo.enable_statistics:
175 172 c.show_stats = True
176 173 c.no_data_msg = _('No data ready yet')
177 174 else:
178 175 c.show_stats = False
179 176 c.no_data_msg = _('Statistics are disabled for this repository')
180 177
181 178 td = date.today() + timedelta(days=1)
182 179 td_1m = td - timedelta(days=calendar.mdays[td.month])
183 180 td_1y = td - timedelta(days=365)
184 181
185 182 ts_min_m = mktime(td_1m.timetuple())
186 183 ts_min_y = mktime(td_1y.timetuple())
187 184 ts_max_y = mktime(td.timetuple())
188 185 c.ts_min = ts_min_m
189 186 c.ts_max = ts_max_y
190 187
191 188 stats = Statistics.query() \
192 189 .filter(Statistics.repository == c.db_repo) \
193 190 .scalar()
194 191 c.stats_percentage = 0
195 192 if stats and stats.languages:
196 193 c.no_data = False is c.db_repo.enable_statistics
197 194 lang_stats_d = json.loads(stats.languages)
198 195 c.commit_data = stats.commit_activity
199 196 c.overview_data = stats.commit_activity_combined
200 197
201 198 lang_stats = ((x, {"count": y,
202 199 "desc": LANGUAGES_EXTENSIONS_MAP.get(x)})
203 200 for x, y in lang_stats_d.items())
204 201
205 202 c.trending_languages = (
206 203 sorted(lang_stats, reverse=True, key=lambda k: k[1])[:10]
207 204 )
208 205 last_rev = stats.stat_on_revision + 1
209 206 c.repo_last_rev = c.db_repo_scm_instance.count() \
210 207 if c.db_repo_scm_instance.revisions else 0
211 208 if last_rev == 0 or c.repo_last_rev == 0:
212 209 pass
213 210 else:
214 211 c.stats_percentage = '%.2f' % ((float((last_rev)) /
215 212 c.repo_last_rev) * 100)
216 213 else:
217 214 c.commit_data = {}
218 215 c.overview_data = ([[ts_min_y, 0], [ts_max_y, 10]])
219 216 c.trending_languages = {}
220 217 c.no_data = True
221 218
222 219 recurse_limit = 500 # don't recurse more than 500 times when parsing
223 220 get_commits_stats(c.db_repo.repo_name, ts_min_y, ts_max_y, recurse_limit)
224 221 return render('summary/statistics.html')
General Comments 0
You need to be logged in to leave comments. Login now