##// END OF EJS Templates
usage of request.GET is now more consistent
marcink -
r3748:9d743ca9 beta
parent child Browse files
Show More
@@ -1,149 +1,149 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.admin
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Controller for Admin panel of Rhodecode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27
28 28 from pylons import request, tmpl_context as c, url
29 29 from sqlalchemy.orm import joinedload
30 30 from webhelpers.paginate import Page
31 31 from whoosh.qparser.default import QueryParser
32 32 from whoosh import query
33 33 from sqlalchemy.sql.expression import or_, and_, func
34 34
35 35 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
36 36 from rhodecode.lib.base import BaseController, render
37 37 from rhodecode.model.db import UserLog, User
38 38 from rhodecode.lib.utils2 import safe_int, remove_prefix, remove_suffix
39 39 from rhodecode.lib.indexers import JOURNAL_SCHEMA
40 40 from whoosh.qparser.dateparse import DateParserPlugin
41 41
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 def _journal_filter(user_log, search_term):
47 47 """
48 48 Filters sqlalchemy user_log based on search_term with whoosh Query language
49 49 http://packages.python.org/Whoosh/querylang.html
50 50
51 51 :param user_log:
52 52 :param search_term:
53 53 """
54 54 log.debug('Initial search term: %r' % search_term)
55 55 qry = None
56 56 if search_term:
57 57 qp = QueryParser('repository', schema=JOURNAL_SCHEMA)
58 58 qp.add_plugin(DateParserPlugin())
59 59 qry = qp.parse(unicode(search_term))
60 60 log.debug('Filtering using parsed query %r' % qry)
61 61
62 62 def wildcard_handler(col, wc_term):
63 63 if wc_term.startswith('*') and not wc_term.endswith('*'):
64 64 #postfix == endswith
65 65 wc_term = remove_prefix(wc_term, prefix='*')
66 66 return func.lower(col).endswith(wc_term)
67 67 elif wc_term.startswith('*') and wc_term.endswith('*'):
68 68 #wildcard == ilike
69 69 wc_term = remove_prefix(wc_term, prefix='*')
70 70 wc_term = remove_suffix(wc_term, suffix='*')
71 71 return func.lower(col).contains(wc_term)
72 72
73 73 def get_filterion(field, val, term):
74 74
75 75 if field == 'repository':
76 76 field = getattr(UserLog, 'repository_name')
77 77 elif field == 'ip':
78 78 field = getattr(UserLog, 'user_ip')
79 79 elif field == 'date':
80 80 field = getattr(UserLog, 'action_date')
81 81 elif field == 'username':
82 82 field = getattr(UserLog, 'username')
83 83 else:
84 84 field = getattr(UserLog, field)
85 85 log.debug('filter field: %s val=>%s' % (field, val))
86 86
87 87 #sql filtering
88 88 if isinstance(term, query.Wildcard):
89 89 return wildcard_handler(field, val)
90 90 elif isinstance(term, query.Prefix):
91 91 return func.lower(field).startswith(func.lower(val))
92 92 elif isinstance(term, query.DateRange):
93 93 return and_(field >= val[0], field <= val[1])
94 94 return func.lower(field) == func.lower(val)
95 95
96 96 if isinstance(qry, (query.And, query.Term, query.Prefix, query.Wildcard,
97 97 query.DateRange)):
98 98 if not isinstance(qry, query.And):
99 99 qry = [qry]
100 100 for term in qry:
101 101 field = term.fieldname
102 102 val = (term.text if not isinstance(term, query.DateRange)
103 103 else [term.startdate, term.enddate])
104 104 user_log = user_log.filter(get_filterion(field, val, term))
105 105 elif isinstance(qry, query.Or):
106 106 filters = []
107 107 for term in qry:
108 108 field = term.fieldname
109 109 val = (term.text if not isinstance(term, query.DateRange)
110 110 else [term.startdate, term.enddate])
111 111 filters.append(get_filterion(field, val, term))
112 112 user_log = user_log.filter(or_(*filters))
113 113
114 114 return user_log
115 115
116 116
117 117 class AdminController(BaseController):
118 118
119 119 @LoginRequired()
120 120 def __before__(self):
121 121 super(AdminController, self).__before__()
122 122
123 123 @HasPermissionAllDecorator('hg.admin')
124 124 def index(self):
125 125 users_log = UserLog.query()\
126 126 .options(joinedload(UserLog.user))\
127 127 .options(joinedload(UserLog.repository))
128 128
129 129 #FILTERING
130 130 c.search_term = request.GET.get('filter')
131 131 try:
132 132 users_log = _journal_filter(users_log, c.search_term)
133 133 except Exception:
134 134 # we want this to crash for now
135 135 raise
136 136
137 137 users_log = users_log.order_by(UserLog.action_date.desc())
138 138
139 p = safe_int(request.params.get('page', 1), 1)
139 p = safe_int(request.GET.get('page', 1), 1)
140 140
141 141 def url_generator(**kw):
142 142 return url.current(filter=c.search_term, **kw)
143 143
144 144 c.users_log = Page(users_log, page=p, items_per_page=10, url=url_generator)
145 145 c.log_data = render('admin/admin_log.html')
146 146
147 147 if request.environ.get('HTTP_X_PARTIAL_XHR'):
148 148 return c.log_data
149 149 return render('admin/admin.html')
@@ -1,173 +1,173 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.notifications
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 notifications controller for RhodeCode
7 7
8 8 :created_on: Nov 23, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from pylons import request
30 30 from pylons import tmpl_context as c, url
31 31 from pylons.controllers.util import redirect, abort
32 32
33 33 from webhelpers.paginate import Page
34 34
35 35 from rhodecode.lib.base import BaseController, render
36 36 from rhodecode.model.db import Notification
37 37
38 38 from rhodecode.model.notification import NotificationModel
39 39 from rhodecode.lib.auth import LoginRequired, NotAnonymous
40 40 from rhodecode.lib import helpers as h
41 41 from rhodecode.model.meta import Session
42 42 from rhodecode.lib.utils2 import safe_int
43 43
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class NotificationsController(BaseController):
49 49 """REST Controller styled on the Atom Publishing Protocol"""
50 50 # To properly map this controller, ensure your config/routing.py
51 51 # file has a resource setup:
52 52 # map.resource('notification', 'notifications', controller='_admin/notifications',
53 53 # path_prefix='/_admin', name_prefix='_admin_')
54 54
55 55 @LoginRequired()
56 56 @NotAnonymous()
57 57 def __before__(self):
58 58 super(NotificationsController, self).__before__()
59 59
60 60 def index(self, format='html'):
61 61 """GET /_admin/notifications: All items in the collection"""
62 62 # url('notifications')
63 63 c.user = self.rhodecode_user
64 64 notif = NotificationModel().get_for_user(self.rhodecode_user.user_id,
65 65 filter_=request.GET.getall('type'))
66 66
67 p = safe_int(request.params.get('page', 1), 1)
67 p = safe_int(request.GET.get('page', 1), 1)
68 68 c.notifications = Page(notif, page=p, items_per_page=10)
69 69 c.pull_request_type = Notification.TYPE_PULL_REQUEST
70 70 c.comment_type = [Notification.TYPE_CHANGESET_COMMENT,
71 71 Notification.TYPE_PULL_REQUEST_COMMENT]
72 72
73 73 _current_filter = request.GET.getall('type')
74 74 c.current_filter = 'all'
75 75 if _current_filter == [c.pull_request_type]:
76 76 c.current_filter = 'pull_request'
77 77 elif _current_filter == c.comment_type:
78 78 c.current_filter = 'comment'
79 79
80 80 return render('admin/notifications/notifications.html')
81 81
82 82 def mark_all_read(self):
83 83 if request.environ.get('HTTP_X_PARTIAL_XHR'):
84 84 nm = NotificationModel()
85 85 # mark all read
86 86 nm.mark_all_read_for_user(self.rhodecode_user.user_id,
87 87 filter_=request.GET.getall('type'))
88 88 Session().commit()
89 89 c.user = self.rhodecode_user
90 90 notif = nm.get_for_user(self.rhodecode_user.user_id,
91 91 filter_=request.GET.getall('type'))
92 92 c.notifications = Page(notif, page=1, items_per_page=10)
93 93 return render('admin/notifications/notifications_data.html')
94 94
95 95 def create(self):
96 96 """POST /_admin/notifications: Create a new item"""
97 97 # url('notifications')
98 98
99 99 def new(self, format='html'):
100 100 """GET /_admin/notifications/new: Form to create a new item"""
101 101 # url('new_notification')
102 102
103 103 def update(self, notification_id):
104 104 """PUT /_admin/notifications/id: Update an existing item"""
105 105 # Forms posted to this method should contain a hidden field:
106 106 # <input type="hidden" name="_method" value="PUT" />
107 107 # Or using helpers:
108 108 # h.form(url('notification', notification_id=ID),
109 109 # method='put')
110 110 # url('notification', notification_id=ID)
111 111 try:
112 112 no = Notification.get(notification_id)
113 113 owner = all(un.user.user_id == c.rhodecode_user.user_id
114 114 for un in no.notifications_to_users)
115 115 if h.HasPermissionAny('hg.admin')() or owner:
116 116 NotificationModel().mark_read(c.rhodecode_user.user_id, no)
117 117 Session().commit()
118 118 return 'ok'
119 119 except Exception:
120 120 Session().rollback()
121 121 log.error(traceback.format_exc())
122 122 return 'fail'
123 123
124 124 def delete(self, notification_id):
125 125 """DELETE /_admin/notifications/id: Delete an existing item"""
126 126 # Forms posted to this method should contain a hidden field:
127 127 # <input type="hidden" name="_method" value="DELETE" />
128 128 # Or using helpers:
129 129 # h.form(url('notification', notification_id=ID),
130 130 # method='delete')
131 131 # url('notification', notification_id=ID)
132 132
133 133 try:
134 134 no = Notification.get(notification_id)
135 135 owner = all(un.user.user_id == c.rhodecode_user.user_id
136 136 for un in no.notifications_to_users)
137 137 if h.HasPermissionAny('hg.admin')() or owner:
138 138 NotificationModel().delete(c.rhodecode_user.user_id, no)
139 139 Session().commit()
140 140 return 'ok'
141 141 except Exception:
142 142 Session().rollback()
143 143 log.error(traceback.format_exc())
144 144 return 'fail'
145 145
146 146 def show(self, notification_id, format='html'):
147 147 """GET /_admin/notifications/id: Show a specific item"""
148 148 # url('notification', notification_id=ID)
149 149 c.user = self.rhodecode_user
150 150 no = Notification.get(notification_id)
151 151
152 152 owner = any(un.user.user_id == c.rhodecode_user.user_id
153 153 for un in no.notifications_to_users)
154 154
155 155 if no and (h.HasPermissionAny('hg.admin', 'repository.admin')() or owner):
156 156 unotification = NotificationModel()\
157 157 .get_user_notification(c.user.user_id, no)
158 158
159 159 # if this association to user is not valid, we don't want to show
160 160 # this message
161 161 if unotification:
162 162 if not unotification.read:
163 163 unotification.mark_as_read()
164 164 Session().commit()
165 165 c.notification = no
166 166
167 167 return render('admin/notifications/show_notification.html')
168 168
169 169 return abort(403)
170 170
171 171 def edit(self, notification_id, format='html'):
172 172 """GET /_admin/notifications/id/edit: Form to edit an existing item"""
173 173 # url('edit_notification', notification_id=ID)
@@ -1,123 +1,119 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.changelog
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 changelog controller for rhodecode
7 7
8 8 :created_on: Apr 21, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from pylons import request, url, session, tmpl_context as c
30 30 from pylons.controllers.util import redirect
31 31 from pylons.i18n.translation import _
32 32
33 33 import rhodecode.lib.helpers as h
34 34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 35 from rhodecode.lib.base import BaseRepoController, render
36 36 from rhodecode.lib.helpers import RepoPage
37 37 from rhodecode.lib.compat import json
38 38 from rhodecode.lib.graphmod import _colored, _dagwalker
39 39 from rhodecode.lib.vcs.exceptions import RepositoryError, ChangesetDoesNotExistError
40 40 from rhodecode.lib.utils2 import safe_int
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44
45 45 class ChangelogController(BaseRepoController):
46 46
47 47 @LoginRequired()
48 48 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
49 49 'repository.admin')
50 50 def __before__(self):
51 51 super(ChangelogController, self).__before__()
52 52 c.affected_files_cut_off = 60
53 53
54 54 def index(self):
55 55 limit = 100
56 56 default = 20
57 if request.params.get('size'):
58 try:
59 int_size = int(request.params.get('size'))
60 except ValueError:
61 int_size = default
62 c.size = max(min(int_size, limit), 1)
57 if request.GET.get('size'):
58 c.size = max(min(safe_int(request.GET.get('size')), limit), 1)
63 59 session['changelog_size'] = c.size
64 60 session.save()
65 61 else:
66 62 c.size = int(session.get('changelog_size', default))
67 63 # min size must be 1
68 64 c.size = max(c.size, 1)
69 p = safe_int(request.params.get('page', 1), 1)
70 branch_name = request.params.get('branch', None)
65 p = safe_int(request.GET.get('page', 1), 1)
66 branch_name = request.GET.get('branch', None)
71 67 try:
72 68 collection = c.rhodecode_repo.get_changesets(start=0,
73 69 branch_name=branch_name)
74 70 c.total_cs = len(collection)
75 71
76 72 c.pagination = RepoPage(collection, page=p, item_count=c.total_cs,
77 73 items_per_page=c.size, branch=branch_name)
78 74 collection = list(c.pagination)
79 75 page_revisions = [x.raw_id for x in c.pagination]
80 76 c.comments = c.rhodecode_db_repo.get_comments(page_revisions)
81 77 c.statuses = c.rhodecode_db_repo.statuses(page_revisions)
82 78 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
83 79 log.error(traceback.format_exc())
84 80 h.flash(str(e), category='error')
85 81 return redirect(url('changelog_home', repo_name=c.repo_name))
86 82
87 83 c.branch_name = branch_name
88 84 c.branch_filters = [('', _('All Branches'))] + \
89 85 [(k, k) for k in c.rhodecode_repo.branches.keys()]
90 86
91 87 self._graph(c.rhodecode_repo, [x.revision for x in c.pagination],
92 88 c.total_cs, c.size, p)
93 89
94 90 return render('changelog/changelog.html')
95 91
96 92 def changelog_details(self, cs):
97 93 if request.environ.get('HTTP_X_PARTIAL_XHR'):
98 94 c.cs = c.rhodecode_repo.get_changeset(cs)
99 95 return render('changelog/changelog_details.html')
100 96
101 97 def _graph(self, repo, revs_int, repo_size, size, p):
102 98 """
103 99 Generates a DAG graph for repo
104 100
105 101 :param repo:
106 102 :param revs_int:
107 103 :param repo_size:
108 104 :param size:
109 105 :param p:
110 106 """
111 107 if not revs_int:
112 108 c.jsdata = json.dumps([])
113 109 return
114 110
115 111 data = []
116 112 revs = revs_int
117 113
118 114 dag = _dagwalker(repo, revs, repo.alias)
119 115 dag = _colored(dag)
120 116 for (id, type, ctx, vtx, edges) in dag:
121 117 data.append(['', vtx, edges])
122 118
123 119 c.jsdata = json.dumps(data)
@@ -1,58 +1,58 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.followers
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Followers controller for rhodecode
7 7
8 8 :created_on: Apr 23, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import logging
26 26
27 27 from pylons import tmpl_context as c, request
28 28
29 29 from rhodecode.lib.helpers import Page
30 30 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
31 31 from rhodecode.lib.base import BaseRepoController, render
32 32 from rhodecode.model.db import Repository, User, UserFollowing
33 33 from rhodecode.lib.utils2 import safe_int
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37
38 38 class FollowersController(BaseRepoController):
39 39
40 40 @LoginRequired()
41 41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
42 42 'repository.admin')
43 43 def __before__(self):
44 44 super(FollowersController, self).__before__()
45 45
46 46 def followers(self, repo_name):
47 p = safe_int(request.params.get('page', 1), 1)
47 p = safe_int(request.GET.get('page', 1), 1)
48 48 repo_id = c.rhodecode_db_repo.repo_id
49 49 d = UserFollowing.get_repo_followers(repo_id)\
50 50 .order_by(UserFollowing.follows_from)
51 51 c.followers_pager = Page(d, page=p, items_per_page=20)
52 52
53 53 c.followers_data = render('/followers/followers_data.html')
54 54
55 55 if request.environ.get('HTTP_X_PARTIAL_XHR'):
56 56 return c.followers_data
57 57
58 58 return render('/followers/followers.html')
@@ -1,191 +1,191 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.forks
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 forks controller for rhodecode
7 7
8 8 :created_on: Apr 23, 2011
9 9 :author: marcink
10 10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import logging
26 26 import formencode
27 27 import traceback
28 28 from formencode import htmlfill
29 29
30 30 from pylons import tmpl_context as c, request, url
31 31 from pylons.controllers.util import redirect
32 32 from pylons.i18n.translation import _
33 33
34 34 import rhodecode.lib.helpers as h
35 35
36 36 from rhodecode.lib.helpers import Page
37 37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator, \
38 38 NotAnonymous, HasRepoPermissionAny, HasPermissionAllDecorator,\
39 39 HasPermissionAnyDecorator
40 40 from rhodecode.lib.base import BaseRepoController, render
41 41 from rhodecode.model.db import Repository, RepoGroup, UserFollowing, User,\
42 42 RhodeCodeUi
43 43 from rhodecode.model.repo import RepoModel
44 44 from rhodecode.model.forms import RepoForkForm
45 45 from rhodecode.model.scm import ScmModel, RepoGroupList
46 46 from rhodecode.lib.utils2 import safe_int
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50
51 51 class ForksController(BaseRepoController):
52 52
53 53 @LoginRequired()
54 54 def __before__(self):
55 55 super(ForksController, self).__before__()
56 56
57 57 def __load_defaults(self):
58 58 acl_groups = RepoGroupList(RepoGroup.query().all(),
59 59 perm_set=['group.write', 'group.admin'])
60 60 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
61 61 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
62 62 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
63 63 c.landing_revs_choices = choices
64 64 c.can_update = RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE).ui_active
65 65
66 66 def __load_data(self, repo_name=None):
67 67 """
68 68 Load defaults settings for edit, and update
69 69
70 70 :param repo_name:
71 71 """
72 72 self.__load_defaults()
73 73
74 74 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
75 75 repo = db_repo.scm_instance
76 76
77 77 if c.repo_info is None:
78 78 h.not_mapped_error(repo_name)
79 79 return redirect(url('repos'))
80 80
81 81 c.default_user_id = User.get_default_user().user_id
82 82 c.in_public_journal = UserFollowing.query()\
83 83 .filter(UserFollowing.user_id == c.default_user_id)\
84 84 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
85 85
86 86 if c.repo_info.stats:
87 87 last_rev = c.repo_info.stats.stat_on_revision+1
88 88 else:
89 89 last_rev = 0
90 90 c.stats_revision = last_rev
91 91
92 92 c.repo_last_rev = repo.count() if repo.revisions else 0
93 93
94 94 if last_rev == 0 or c.repo_last_rev == 0:
95 95 c.stats_percentage = 0
96 96 else:
97 97 c.stats_percentage = '%.2f' % ((float((last_rev)) /
98 98 c.repo_last_rev) * 100)
99 99
100 100 defaults = RepoModel()._get_defaults(repo_name)
101 101 # alter the description to indicate a fork
102 102 defaults['description'] = ('fork of repository: %s \n%s'
103 103 % (defaults['repo_name'],
104 104 defaults['description']))
105 105 # add suffix to fork
106 106 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
107 107
108 108 return defaults
109 109
110 110 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
111 111 'repository.admin')
112 112 def forks(self, repo_name):
113 p = safe_int(request.params.get('page', 1), 1)
113 p = safe_int(request.GET.get('page', 1), 1)
114 114 repo_id = c.rhodecode_db_repo.repo_id
115 115 d = []
116 116 for r in Repository.get_repo_forks(repo_id):
117 117 if not HasRepoPermissionAny(
118 118 'repository.read', 'repository.write', 'repository.admin'
119 119 )(r.repo_name, 'get forks check'):
120 120 continue
121 121 d.append(r)
122 122 c.forks_pager = Page(d, page=p, items_per_page=20)
123 123
124 124 c.forks_data = render('/forks/forks_data.html')
125 125
126 126 if request.environ.get('HTTP_X_PARTIAL_XHR'):
127 127 return c.forks_data
128 128
129 129 return render('/forks/forks.html')
130 130
131 131 @NotAnonymous()
132 132 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
133 133 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
134 134 'repository.admin')
135 135 def fork(self, repo_name):
136 136 c.repo_info = Repository.get_by_repo_name(repo_name)
137 137 if not c.repo_info:
138 138 h.not_mapped_error(repo_name)
139 139 return redirect(url('home'))
140 140
141 141 defaults = self.__load_data(repo_name)
142 142
143 143 return htmlfill.render(
144 144 render('forks/fork.html'),
145 145 defaults=defaults,
146 146 encoding="UTF-8",
147 147 force_defaults=False
148 148 )
149 149
150 150 @NotAnonymous()
151 151 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
152 152 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
153 153 'repository.admin')
154 154 def fork_create(self, repo_name):
155 155 self.__load_defaults()
156 156 c.repo_info = Repository.get_by_repo_name(repo_name)
157 157 _form = RepoForkForm(old_data={'repo_type': c.repo_info.repo_type},
158 158 repo_groups=c.repo_groups_choices,
159 159 landing_revs=c.landing_revs_choices)()
160 160 form_result = {}
161 161 try:
162 162 form_result = _form.to_python(dict(request.POST))
163 163
164 164 # an approximation that is better than nothing
165 165 if not RhodeCodeUi.get_by_key(RhodeCodeUi.HOOK_UPDATE).ui_active:
166 166 form_result['update_after_clone'] = False
167 167
168 168 # create fork is done sometimes async on celery, db transaction
169 169 # management is handled there.
170 170 RepoModel().create_fork(form_result, self.rhodecode_user.user_id)
171 171 fork_url = h.link_to(form_result['repo_name_full'],
172 172 h.url('summary_home', repo_name=form_result['repo_name_full']))
173 173
174 174 h.flash(h.literal(_('Forked repository %s as %s') \
175 175 % (repo_name, fork_url)),
176 176 category='success')
177 177 except formencode.Invalid, errors:
178 178 c.new_repo = errors.value['repo_name']
179 179
180 180 return htmlfill.render(
181 181 render('forks/fork.html'),
182 182 defaults=errors.value,
183 183 errors=errors.error_dict or {},
184 184 prefix_error=False,
185 185 encoding="UTF-8")
186 186 except Exception:
187 187 log.error(traceback.format_exc())
188 188 h.flash(_('An error occurred during repository forking %s') %
189 189 repo_name, category='error')
190 190
191 191 return redirect(h.url('summary_home', repo_name=repo_name))
@@ -1,379 +1,379 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.journal
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Journal controller for pylons
7 7
8 8 :created_on: Nov 21, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import logging
26 26 from itertools import groupby
27 27
28 28 from sqlalchemy import or_
29 29 from sqlalchemy.orm import joinedload
30 30 from sqlalchemy.sql.expression import func
31 31
32 32 from webhelpers.paginate import Page
33 33 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
34 34
35 35 from webob.exc import HTTPBadRequest
36 36 from pylons import request, tmpl_context as c, response, url
37 37 from pylons.i18n.translation import _
38 38
39 39 import rhodecode.lib.helpers as h
40 40 from rhodecode.lib.auth import LoginRequired, NotAnonymous
41 41 from rhodecode.lib.base import BaseController, render
42 42 from rhodecode.model.db import UserLog, UserFollowing, Repository, User
43 43 from rhodecode.model.meta import Session
44 44 from rhodecode.lib.utils2 import safe_int, AttributeDict
45 45 from rhodecode.controllers.admin.admin import _journal_filter
46 46 from rhodecode.model.repo import RepoModel
47 47 from rhodecode.lib.compat import json
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 class JournalController(BaseController):
53 53
54 54 def __before__(self):
55 55 super(JournalController, self).__before__()
56 56 self.language = 'en-us'
57 57 self.ttl = "5"
58 58 self.feed_nr = 20
59 59 c.search_term = request.GET.get('filter')
60 60
61 61 @LoginRequired()
62 62 @NotAnonymous()
63 63 def index(self):
64 64 # Return a rendered template
65 p = safe_int(request.params.get('page', 1), 1)
65 p = safe_int(request.GET.get('page', 1), 1)
66 66 c.user = User.get(self.rhodecode_user.user_id)
67 67 c.following = self.sa.query(UserFollowing)\
68 68 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
69 69 .options(joinedload(UserFollowing.follows_repository))\
70 70 .all()
71 71
72 72 journal = self._get_journal_data(c.following)
73 73
74 74 def url_generator(**kw):
75 75 return url.current(filter=c.search_term, **kw)
76 76
77 77 c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator)
78 78 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
79 79
80 80 c.journal_data = render('journal/journal_data.html')
81 81 if request.environ.get('HTTP_X_PARTIAL_XHR'):
82 82 return c.journal_data
83 83
84 84 repos_list = Session().query(Repository)\
85 85 .filter(Repository.user_id ==
86 86 self.rhodecode_user.user_id)\
87 87 .order_by(func.lower(Repository.repo_name)).all()
88 88
89 89 repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
90 90 admin=True)
91 91 #json used to render the grid
92 92 c.data = json.dumps(repos_data)
93 93
94 94 watched_repos_data = []
95 95
96 96 ## watched repos
97 97 _render = RepoModel._render_datatable
98 98
99 99 def quick_menu(repo_name):
100 100 return _render('quick_menu', repo_name)
101 101
102 102 def repo_lnk(name, rtype, private, fork_of):
103 103 return _render('repo_name', name, rtype, private, fork_of,
104 104 short_name=False, admin=False)
105 105
106 106 def last_rev(repo_name, cs_cache):
107 107 return _render('revision', repo_name, cs_cache.get('revision'),
108 108 cs_cache.get('raw_id'), cs_cache.get('author'),
109 109 cs_cache.get('message'))
110 110
111 111 def desc(desc):
112 112 from pylons import tmpl_context as c
113 113 if c.visual.stylify_metatags:
114 114 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
115 115 else:
116 116 return h.urlify_text(h.truncate(desc, 60))
117 117
118 118 def repo_actions(repo_name):
119 119 return _render('repo_actions', repo_name)
120 120
121 121 def owner_actions(user_id, username):
122 122 return _render('user_name', user_id, username)
123 123
124 124 def toogle_follow(repo_id):
125 125 return _render('toggle_follow', repo_id)
126 126
127 127 for entry in c.following:
128 128 repo = entry.follows_repository
129 129 cs_cache = repo.changeset_cache
130 130 row = {
131 131 "menu": quick_menu(repo.repo_name),
132 132 "raw_name": repo.repo_name.lower(),
133 133 "name": repo_lnk(repo.repo_name, repo.repo_type,
134 134 repo.private, repo.fork),
135 135 "last_changeset": last_rev(repo.repo_name, cs_cache),
136 136 "raw_tip": cs_cache.get('revision'),
137 137 "action": toogle_follow(repo.repo_id)
138 138 }
139 139
140 140 watched_repos_data.append(row)
141 141
142 142 c.watched_data = json.dumps({
143 143 "totalRecords": len(c.following),
144 144 "startIndex": 0,
145 145 "sort": "name",
146 146 "dir": "asc",
147 147 "records": watched_repos_data
148 148 })
149 149 return render('journal/journal.html')
150 150
151 151 @LoginRequired(api_access=True)
152 152 @NotAnonymous()
153 153 def journal_atom(self):
154 154 """
155 155 Produce an atom-1.0 feed via feedgenerator module
156 156 """
157 157 following = self.sa.query(UserFollowing)\
158 158 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
159 159 .options(joinedload(UserFollowing.follows_repository))\
160 160 .all()
161 161 return self._atom_feed(following, public=False)
162 162
163 163 @LoginRequired(api_access=True)
164 164 @NotAnonymous()
165 165 def journal_rss(self):
166 166 """
167 167 Produce an rss feed via feedgenerator module
168 168 """
169 169 following = self.sa.query(UserFollowing)\
170 170 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
171 171 .options(joinedload(UserFollowing.follows_repository))\
172 172 .all()
173 173 return self._rss_feed(following, public=False)
174 174
175 175 def _get_daily_aggregate(self, journal):
176 176 groups = []
177 177 for k, g in groupby(journal, lambda x: x.action_as_day):
178 178 user_group = []
179 179 #groupby username if it's a present value, else fallback to journal username
180 180 for _, g2 in groupby(list(g), lambda x: x.user.username if x.user else x.username):
181 181 l = list(g2)
182 182 user_group.append((l[0].user, l))
183 183
184 184 groups.append((k, user_group,))
185 185
186 186 return groups
187 187
188 188 def _get_journal_data(self, following_repos):
189 189 repo_ids = [x.follows_repository.repo_id for x in following_repos
190 190 if x.follows_repository is not None]
191 191 user_ids = [x.follows_user.user_id for x in following_repos
192 192 if x.follows_user is not None]
193 193
194 194 filtering_criterion = None
195 195
196 196 if repo_ids and user_ids:
197 197 filtering_criterion = or_(UserLog.repository_id.in_(repo_ids),
198 198 UserLog.user_id.in_(user_ids))
199 199 if repo_ids and not user_ids:
200 200 filtering_criterion = UserLog.repository_id.in_(repo_ids)
201 201 if not repo_ids and user_ids:
202 202 filtering_criterion = UserLog.user_id.in_(user_ids)
203 203 if filtering_criterion is not None:
204 204 journal = self.sa.query(UserLog)\
205 205 .options(joinedload(UserLog.user))\
206 206 .options(joinedload(UserLog.repository))
207 207 #filter
208 208 try:
209 209 journal = _journal_filter(journal, c.search_term)
210 210 except Exception:
211 211 # we want this to crash for now
212 212 raise
213 213 journal = journal.filter(filtering_criterion)\
214 214 .order_by(UserLog.action_date.desc())
215 215 else:
216 216 journal = []
217 217
218 218 return journal
219 219
220 220 @LoginRequired()
221 221 @NotAnonymous()
222 222 def toggle_following(self):
223 223 cur_token = request.POST.get('auth_token')
224 224 token = h.get_token()
225 225 if cur_token == token:
226 226
227 227 user_id = request.POST.get('follows_user_id')
228 228 if user_id:
229 229 try:
230 230 self.scm_model.toggle_following_user(user_id,
231 231 self.rhodecode_user.user_id)
232 232 Session.commit()
233 233 return 'ok'
234 234 except Exception:
235 235 raise HTTPBadRequest()
236 236
237 237 repo_id = request.POST.get('follows_repo_id')
238 238 if repo_id:
239 239 try:
240 240 self.scm_model.toggle_following_repo(repo_id,
241 241 self.rhodecode_user.user_id)
242 242 Session.commit()
243 243 return 'ok'
244 244 except Exception:
245 245 raise HTTPBadRequest()
246 246
247 247 log.debug('token mismatch %s vs %s' % (cur_token, token))
248 248 raise HTTPBadRequest()
249 249
250 250 @LoginRequired()
251 251 def public_journal(self):
252 252 # Return a rendered template
253 p = safe_int(request.params.get('page', 1), 1)
253 p = safe_int(request.GET.get('page', 1), 1)
254 254
255 255 c.following = self.sa.query(UserFollowing)\
256 256 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
257 257 .options(joinedload(UserFollowing.follows_repository))\
258 258 .all()
259 259
260 260 journal = self._get_journal_data(c.following)
261 261
262 262 c.journal_pager = Page(journal, page=p, items_per_page=20)
263 263
264 264 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
265 265
266 266 c.journal_data = render('journal/journal_data.html')
267 267 if request.environ.get('HTTP_X_PARTIAL_XHR'):
268 268 return c.journal_data
269 269 return render('journal/public_journal.html')
270 270
271 271 def _atom_feed(self, repos, public=True):
272 272 journal = self._get_journal_data(repos)
273 273 if public:
274 274 _link = url('public_journal_atom', qualified=True)
275 275 _desc = '%s %s %s' % (c.rhodecode_name, _('public journal'),
276 276 'atom feed')
277 277 else:
278 278 _link = url('journal_atom', qualified=True)
279 279 _desc = '%s %s %s' % (c.rhodecode_name, _('journal'), 'atom feed')
280 280
281 281 feed = Atom1Feed(title=_desc,
282 282 link=_link,
283 283 description=_desc,
284 284 language=self.language,
285 285 ttl=self.ttl)
286 286
287 287 for entry in journal[:self.feed_nr]:
288 288 user = entry.user
289 289 if user is None:
290 290 #fix deleted users
291 291 user = AttributeDict({'short_contact': entry.username,
292 292 'email': '',
293 293 'full_contact': ''})
294 294 action, action_extra, ico = h.action_parser(entry, feed=True)
295 295 title = "%s - %s %s" % (user.short_contact, action(),
296 296 entry.repository.repo_name)
297 297 desc = action_extra()
298 298 _url = None
299 299 if entry.repository is not None:
300 300 _url = url('changelog_home',
301 301 repo_name=entry.repository.repo_name,
302 302 qualified=True)
303 303
304 304 feed.add_item(title=title,
305 305 pubdate=entry.action_date,
306 306 link=_url or url('', qualified=True),
307 307 author_email=user.email,
308 308 author_name=user.full_contact,
309 309 description=desc)
310 310
311 311 response.content_type = feed.mime_type
312 312 return feed.writeString('utf-8')
313 313
314 314 def _rss_feed(self, repos, public=True):
315 315 journal = self._get_journal_data(repos)
316 316 if public:
317 317 _link = url('public_journal_atom', qualified=True)
318 318 _desc = '%s %s %s' % (c.rhodecode_name, _('public journal'),
319 319 'rss feed')
320 320 else:
321 321 _link = url('journal_atom', qualified=True)
322 322 _desc = '%s %s %s' % (c.rhodecode_name, _('journal'), 'rss feed')
323 323
324 324 feed = Rss201rev2Feed(title=_desc,
325 325 link=_link,
326 326 description=_desc,
327 327 language=self.language,
328 328 ttl=self.ttl)
329 329
330 330 for entry in journal[:self.feed_nr]:
331 331 user = entry.user
332 332 if user is None:
333 333 #fix deleted users
334 334 user = AttributeDict({'short_contact': entry.username,
335 335 'email': '',
336 336 'full_contact': ''})
337 337 action, action_extra, ico = h.action_parser(entry, feed=True)
338 338 title = "%s - %s %s" % (user.short_contact, action(),
339 339 entry.repository.repo_name)
340 340 desc = action_extra()
341 341 _url = None
342 342 if entry.repository is not None:
343 343 _url = url('changelog_home',
344 344 repo_name=entry.repository.repo_name,
345 345 qualified=True)
346 346
347 347 feed.add_item(title=title,
348 348 pubdate=entry.action_date,
349 349 link=_url or url('', qualified=True),
350 350 author_email=user.email,
351 351 author_name=user.full_contact,
352 352 description=desc)
353 353
354 354 response.content_type = feed.mime_type
355 355 return feed.writeString('utf-8')
356 356
357 357 @LoginRequired(api_access=True)
358 358 def public_journal_atom(self):
359 359 """
360 360 Produce an atom-1.0 feed via feedgenerator module
361 361 """
362 362 c.following = self.sa.query(UserFollowing)\
363 363 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
364 364 .options(joinedload(UserFollowing.follows_repository))\
365 365 .all()
366 366
367 367 return self._atom_feed(c.following)
368 368
369 369 @LoginRequired(api_access=True)
370 370 def public_journal_rss(self):
371 371 """
372 372 Produce an rss2 feed via feedgenerator module
373 373 """
374 374 c.following = self.sa.query(UserFollowing)\
375 375 .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
376 376 .options(joinedload(UserFollowing.follows_repository))\
377 377 .all()
378 378
379 379 return self._rss_feed(c.following)
@@ -1,521 +1,521 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.pullrequests
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 pull requests controller for rhodecode for initializing pull requests
7 7
8 8 :created_on: May 7, 2012
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import logging
26 26 import traceback
27 27 import formencode
28 28
29 29 from webob.exc import HTTPNotFound, HTTPForbidden
30 30 from collections import defaultdict
31 31 from itertools import groupby
32 32
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36
37 37 from rhodecode.lib.compat import json
38 38 from rhodecode.lib.base import BaseRepoController, render
39 39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 40 NotAnonymous
41 41 from rhodecode.lib.helpers import Page
42 42 from rhodecode.lib import helpers as h
43 43 from rhodecode.lib import diffs
44 44 from rhodecode.lib.utils import action_logger, jsonify
45 45 from rhodecode.lib.vcs.utils import safe_str
46 46 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError
47 47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48 48 from rhodecode.lib.diffs import LimitedDiffContainer
49 49 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
50 50 ChangesetComment
51 51 from rhodecode.model.pull_request import PullRequestModel
52 52 from rhodecode.model.meta import Session
53 53 from rhodecode.model.repo import RepoModel
54 54 from rhodecode.model.comment import ChangesetCommentsModel
55 55 from rhodecode.model.changeset_status import ChangesetStatusModel
56 56 from rhodecode.model.forms import PullRequestForm
57 57 from mercurial import scmutil
58 58 from rhodecode.lib.utils2 import safe_int
59 59
60 60 log = logging.getLogger(__name__)
61 61
62 62
63 63 class PullrequestsController(BaseRepoController):
64 64
65 65 @LoginRequired()
66 66 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
67 67 'repository.admin')
68 68 def __before__(self):
69 69 super(PullrequestsController, self).__before__()
70 70 repo_model = RepoModel()
71 71 c.users_array = repo_model.get_users_js()
72 72 c.users_groups_array = repo_model.get_users_groups_js()
73 73
74 74 def _get_repo_refs(self, repo, rev=None, branch_rev=None):
75 75 """return a structure with repo's interesting changesets, suitable for
76 76 the selectors in pullrequest.html"""
77 77 # list named branches that has been merged to this named branch - it should probably merge back
78 78 peers = []
79 79
80 80 if rev:
81 81 rev = safe_str(rev)
82 82
83 83 if branch_rev:
84 84 branch_rev = safe_str(branch_rev)
85 85 # not restricting to merge() would also get branch point and be better
86 86 # (especially because it would get the branch point) ... but is currently too expensive
87 87 revs = ["sort(parents(branch(id('%s')) and merge()) - branch(id('%s')))" %
88 88 (branch_rev, branch_rev)]
89 89 otherbranches = {}
90 90 for i in scmutil.revrange(repo._repo, revs):
91 91 cs = repo.get_changeset(i)
92 92 otherbranches[cs.branch] = cs.raw_id
93 93 for branch, node in otherbranches.iteritems():
94 94 selected = 'branch:%s:%s' % (branch, node)
95 95 peers.append((selected, branch))
96 96
97 97 selected = None
98 98 branches = []
99 99 for branch, branchrev in repo.branches.iteritems():
100 100 n = 'branch:%s:%s' % (branch, branchrev)
101 101 branches.append((n, branch))
102 102 if rev == branchrev:
103 103 selected = n
104 104 bookmarks = []
105 105 for bookmark, bookmarkrev in repo.bookmarks.iteritems():
106 106 n = 'book:%s:%s' % (bookmark, bookmarkrev)
107 107 bookmarks.append((n, bookmark))
108 108 if rev == bookmarkrev:
109 109 selected = n
110 110 tags = []
111 111 for tag, tagrev in repo.tags.iteritems():
112 112 n = 'tag:%s:%s' % (tag, tagrev)
113 113 tags.append((n, tag))
114 114 if rev == tagrev and tag != 'tip': # tip is not a real tag - and its branch is better
115 115 selected = n
116 116
117 117 # prio 1: rev was selected as existing entry above
118 118
119 119 # prio 2: create special entry for rev; rev _must_ be used
120 120 specials = []
121 121 if rev and selected is None:
122 122 selected = 'rev:%s:%s' % (rev, rev)
123 123 specials = [(selected, '%s: %s' % (_("Changeset"), rev[:12]))]
124 124
125 125 # prio 3: most recent peer branch
126 126 if peers and not selected:
127 127 selected = peers[0][0][0]
128 128
129 129 # prio 4: tip revision
130 130 if not selected:
131 131 selected = 'tag:tip:%s' % repo.tags['tip']
132 132
133 133 groups = [(specials, _("Special")),
134 134 (peers, _("Peer branches")),
135 135 (bookmarks, _("Bookmarks")),
136 136 (branches, _("Branches")),
137 137 (tags, _("Tags")),
138 138 ]
139 139 return [g for g in groups if g[0]], selected
140 140
141 141 def _get_is_allowed_change_status(self, pull_request):
142 142 owner = self.rhodecode_user.user_id == pull_request.user_id
143 143 reviewer = self.rhodecode_user.user_id in [x.user_id for x in
144 144 pull_request.reviewers]
145 145 return (self.rhodecode_user.admin or owner or reviewer)
146 146
147 147 def show_all(self, repo_name):
148 148 c.pull_requests = PullRequestModel().get_all(repo_name)
149 149 c.repo_name = repo_name
150 p = safe_int(request.params.get('page', 1), 1)
150 p = safe_int(request.GET.get('page', 1), 1)
151 151
152 152 c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=10)
153 153
154 154 c.pullrequest_data = render('/pullrequests/pullrequest_data.html')
155 155
156 156 if request.environ.get('HTTP_X_PARTIAL_XHR'):
157 157 return c.pullrequest_data
158 158
159 159 return render('/pullrequests/pullrequest_show_all.html')
160 160
161 161 @NotAnonymous()
162 162 def index(self):
163 163 org_repo = c.rhodecode_db_repo
164 164
165 165 if org_repo.scm_instance.alias != 'hg':
166 166 log.error('Review not available for GIT REPOS')
167 167 raise HTTPNotFound
168 168
169 169 try:
170 170 org_repo.scm_instance.get_changeset()
171 171 except EmptyRepositoryError, e:
172 172 h.flash(h.literal(_('There are no changesets yet')),
173 173 category='warning')
174 174 redirect(url('summary_home', repo_name=org_repo.repo_name))
175 175
176 176 org_rev = request.GET.get('rev_end')
177 177 # rev_start is not directly useful - its parent could however be used
178 178 # as default for other and thus give a simple compare view
179 179 #other_rev = request.POST.get('rev_start')
180 180
181 181 c.org_repos = []
182 182 c.org_repos.append((org_repo.repo_name, org_repo.repo_name))
183 183 c.default_org_repo = org_repo.repo_name
184 184 c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance, org_rev)
185 185
186 186 c.other_repos = []
187 187 other_repos_info = {}
188 188
189 189 def add_other_repo(repo, branch_rev=None):
190 190 if repo.repo_name in other_repos_info: # shouldn't happen
191 191 return
192 192 c.other_repos.append((repo.repo_name, repo.repo_name))
193 193 other_refs, selected_other_ref = self._get_repo_refs(repo.scm_instance, branch_rev=branch_rev)
194 194 other_repos_info[repo.repo_name] = {
195 195 'user': dict(user_id=repo.user.user_id,
196 196 username=repo.user.username,
197 197 firstname=repo.user.firstname,
198 198 lastname=repo.user.lastname,
199 199 gravatar_link=h.gravatar_url(repo.user.email, 14)),
200 200 'description': repo.description.split('\n', 1)[0],
201 201 'revs': h.select('other_ref', selected_other_ref, other_refs, class_='refs')
202 202 }
203 203
204 204 # add org repo to other so we can open pull request against peer branches on itself
205 205 add_other_repo(org_repo, branch_rev=org_rev)
206 206 c.default_other_repo = org_repo.repo_name
207 207
208 208 # gather forks and add to this list ... even though it is rare to
209 209 # request forks to pull from their parent
210 210 for fork in org_repo.forks:
211 211 add_other_repo(fork)
212 212
213 213 # add parents of this fork also, but only if it's not empty
214 214 if org_repo.parent and org_repo.parent.scm_instance.revisions:
215 215 add_other_repo(org_repo.parent)
216 216 c.default_other_repo = org_repo.parent.repo_name
217 217
218 218 c.default_other_repo_info = other_repos_info[c.default_other_repo]
219 219 c.other_repos_info = json.dumps(other_repos_info)
220 220
221 221 return render('/pullrequests/pullrequest.html')
222 222
223 223 @NotAnonymous()
224 224 def create(self, repo_name):
225 225 repo = RepoModel()._get_repo(repo_name)
226 226 try:
227 227 _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
228 228 except formencode.Invalid, errors:
229 229 log.error(traceback.format_exc())
230 230 if errors.error_dict.get('revisions'):
231 231 msg = 'Revisions: %s' % errors.error_dict['revisions']
232 232 elif errors.error_dict.get('pullrequest_title'):
233 233 msg = _('Pull request requires a title with min. 3 chars')
234 234 else:
235 235 msg = _('Error creating pull request')
236 236
237 237 h.flash(msg, 'error')
238 238 return redirect(url('pullrequest_home', repo_name=repo_name))
239 239
240 240 org_repo = _form['org_repo']
241 241 org_ref = 'rev:merge:%s' % _form['merge_rev']
242 242 other_repo = _form['other_repo']
243 243 other_ref = 'rev:ancestor:%s' % _form['ancestor_rev']
244 244 revisions = reversed(_form['revisions'])
245 245 reviewers = _form['review_members']
246 246
247 247 title = _form['pullrequest_title']
248 248 description = _form['pullrequest_desc']
249 249
250 250 try:
251 251 pull_request = PullRequestModel().create(
252 252 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
253 253 other_ref, revisions, reviewers, title, description
254 254 )
255 255 Session().commit()
256 256 h.flash(_('Successfully opened new pull request'),
257 257 category='success')
258 258 except Exception:
259 259 h.flash(_('Error occurred during sending pull request'),
260 260 category='error')
261 261 log.error(traceback.format_exc())
262 262 return redirect(url('pullrequest_home', repo_name=repo_name))
263 263
264 264 return redirect(url('pullrequest_show', repo_name=other_repo,
265 265 pull_request_id=pull_request.pull_request_id))
266 266
267 267 @NotAnonymous()
268 268 @jsonify
269 269 def update(self, repo_name, pull_request_id):
270 270 pull_request = PullRequest.get_or_404(pull_request_id)
271 271 if pull_request.is_closed():
272 272 raise HTTPForbidden()
273 273 #only owner or admin can update it
274 274 owner = pull_request.author.user_id == c.rhodecode_user.user_id
275 275 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
276 276 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
277 277 request.POST.get('reviewers_ids', '').split(',')))
278 278
279 279 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
280 280 Session().commit()
281 281 return True
282 282 raise HTTPForbidden()
283 283
284 284 @NotAnonymous()
285 285 @jsonify
286 286 def delete(self, repo_name, pull_request_id):
287 287 pull_request = PullRequest.get_or_404(pull_request_id)
288 288 #only owner can delete it !
289 289 if pull_request.author.user_id == c.rhodecode_user.user_id:
290 290 PullRequestModel().delete(pull_request)
291 291 Session().commit()
292 292 h.flash(_('Successfully deleted pull request'),
293 293 category='success')
294 294 return redirect(url('admin_settings_my_account', anchor='pullrequests'))
295 295 raise HTTPForbidden()
296 296
297 297 def _load_compare_data(self, pull_request, enable_comments=True):
298 298 """
299 299 Load context data needed for generating compare diff
300 300
301 301 :param pull_request:
302 302 :type pull_request:
303 303 """
304 304 org_repo = pull_request.org_repo
305 305 (org_ref_type,
306 306 org_ref_name,
307 307 org_ref_rev) = pull_request.org_ref.split(':')
308 308
309 309 other_repo = org_repo
310 310 (other_ref_type,
311 311 other_ref_name,
312 312 other_ref_rev) = pull_request.other_ref.split(':')
313 313
314 314 # despite opening revisions for bookmarks/branches/tags, we always
315 315 # convert this to rev to prevent changes after bookmark or branch change
316 316 org_ref = ('rev', org_ref_rev)
317 317 other_ref = ('rev', other_ref_rev)
318 318
319 319 c.org_repo = org_repo
320 320 c.other_repo = other_repo
321 321
322 322 c.fulldiff = fulldiff = request.GET.get('fulldiff')
323 323
324 324 c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions]
325 325
326 326 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
327 327
328 328 c.org_ref = org_ref[1]
329 329 c.org_ref_type = org_ref[0]
330 330 c.other_ref = other_ref[1]
331 331 c.other_ref_type = other_ref[0]
332 332
333 333 diff_limit = self.cut_off_limit if not fulldiff else None
334 334
335 335 # we swap org/other ref since we run a simple diff on one repo
336 336 log.debug('running diff between %s@%s and %s@%s'
337 337 % (org_repo.scm_instance.path, org_ref,
338 338 other_repo.scm_instance.path, other_ref))
339 339 _diff = org_repo.scm_instance.get_diff(rev1=safe_str(other_ref[1]), rev2=safe_str(org_ref[1]))
340 340
341 341 diff_processor = diffs.DiffProcessor(_diff or '', format='gitdiff',
342 342 diff_limit=diff_limit)
343 343 _parsed = diff_processor.prepare()
344 344
345 345 c.limited_diff = False
346 346 if isinstance(_parsed, LimitedDiffContainer):
347 347 c.limited_diff = True
348 348
349 349 c.files = []
350 350 c.changes = {}
351 351 c.lines_added = 0
352 352 c.lines_deleted = 0
353 353 for f in _parsed:
354 354 st = f['stats']
355 355 if st[0] != 'b':
356 356 c.lines_added += st[0]
357 357 c.lines_deleted += st[1]
358 358 fid = h.FID('', f['filename'])
359 359 c.files.append([fid, f['operation'], f['filename'], f['stats']])
360 360 diff = diff_processor.as_html(enable_comments=enable_comments,
361 361 parsed_lines=[f])
362 362 c.changes[fid] = [f['operation'], f['filename'], diff]
363 363
364 364 def show(self, repo_name, pull_request_id):
365 365 repo_model = RepoModel()
366 366 c.users_array = repo_model.get_users_js()
367 367 c.users_groups_array = repo_model.get_users_groups_js()
368 368 c.pull_request = PullRequest.get_or_404(pull_request_id)
369 369 c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
370 370 cc_model = ChangesetCommentsModel()
371 371 cs_model = ChangesetStatusModel()
372 372 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
373 373 pull_request=c.pull_request,
374 374 with_revisions=True)
375 375
376 376 cs_statuses = defaultdict(list)
377 377 for st in _cs_statuses:
378 378 cs_statuses[st.author.username] += [st]
379 379
380 380 c.pull_request_reviewers = []
381 381 c.pull_request_pending_reviewers = []
382 382 for o in c.pull_request.reviewers:
383 383 st = cs_statuses.get(o.user.username, None)
384 384 if st:
385 385 sorter = lambda k: k.version
386 386 st = [(x, list(y)[0])
387 387 for x, y in (groupby(sorted(st, key=sorter), sorter))]
388 388 else:
389 389 c.pull_request_pending_reviewers.append(o.user)
390 390 c.pull_request_reviewers.append([o.user, st])
391 391
392 392 # pull_requests repo_name we opened it against
393 393 # ie. other_repo must match
394 394 if repo_name != c.pull_request.other_repo.repo_name:
395 395 raise HTTPNotFound
396 396
397 397 # load compare data into template context
398 398 enable_comments = not c.pull_request.is_closed()
399 399 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
400 400
401 401 # inline comments
402 402 c.inline_cnt = 0
403 403 c.inline_comments = cc_model.get_inline_comments(
404 404 c.rhodecode_db_repo.repo_id,
405 405 pull_request=pull_request_id)
406 406 # count inline comments
407 407 for __, lines in c.inline_comments:
408 408 for comments in lines.values():
409 409 c.inline_cnt += len(comments)
410 410 # comments
411 411 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
412 412 pull_request=pull_request_id)
413 413
414 414 try:
415 415 cur_status = c.statuses[c.pull_request.revisions[0]][0]
416 416 except Exception:
417 417 log.error(traceback.format_exc())
418 418 cur_status = 'undefined'
419 419 if c.pull_request.is_closed() and 0:
420 420 c.current_changeset_status = cur_status
421 421 else:
422 422 # changeset(pull-request) status calulation based on reviewers
423 423 c.current_changeset_status = cs_model.calculate_status(
424 424 c.pull_request_reviewers,
425 425 )
426 426 c.changeset_statuses = ChangesetStatus.STATUSES
427 427
428 428 c.as_form = False
429 429 c.ancestor = None # there is one - but right here we don't know which
430 430 return render('/pullrequests/pullrequest_show.html')
431 431
432 432 @NotAnonymous()
433 433 @jsonify
434 434 def comment(self, repo_name, pull_request_id):
435 435 pull_request = PullRequest.get_or_404(pull_request_id)
436 436 if pull_request.is_closed():
437 437 raise HTTPForbidden()
438 438
439 439 status = request.POST.get('changeset_status')
440 440 change_status = request.POST.get('change_changeset_status')
441 441 text = request.POST.get('text')
442 442 close_pr = request.POST.get('save_close')
443 443
444 444 allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
445 445 if status and change_status and allowed_to_change_status:
446 446 _def = (_('Status change -> %s')
447 447 % ChangesetStatus.get_status_lbl(status))
448 448 if close_pr:
449 449 _def = _('Closing with') + ' ' + _def
450 450 text = text or _def
451 451 comm = ChangesetCommentsModel().create(
452 452 text=text,
453 453 repo=c.rhodecode_db_repo.repo_id,
454 454 user=c.rhodecode_user.user_id,
455 455 pull_request=pull_request_id,
456 456 f_path=request.POST.get('f_path'),
457 457 line_no=request.POST.get('line'),
458 458 status_change=(ChangesetStatus.get_status_lbl(status)
459 459 if status and change_status
460 460 and allowed_to_change_status else None),
461 461 closing_pr=close_pr
462 462 )
463 463
464 464 action_logger(self.rhodecode_user,
465 465 'user_commented_pull_request:%s' % pull_request_id,
466 466 c.rhodecode_db_repo, self.ip_addr, self.sa)
467 467
468 468 if allowed_to_change_status:
469 469 # get status if set !
470 470 if status and change_status:
471 471 ChangesetStatusModel().set_status(
472 472 c.rhodecode_db_repo.repo_id,
473 473 status,
474 474 c.rhodecode_user.user_id,
475 475 comm,
476 476 pull_request=pull_request_id
477 477 )
478 478
479 479 if close_pr:
480 480 if status in ['rejected', 'approved']:
481 481 PullRequestModel().close_pull_request(pull_request_id)
482 482 action_logger(self.rhodecode_user,
483 483 'user_closed_pull_request:%s' % pull_request_id,
484 484 c.rhodecode_db_repo, self.ip_addr, self.sa)
485 485 else:
486 486 h.flash(_('Closing pull request on other statuses than '
487 487 'rejected or approved forbidden'),
488 488 category='warning')
489 489
490 490 Session().commit()
491 491
492 492 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
493 493 return redirect(h.url('pullrequest_show', repo_name=repo_name,
494 494 pull_request_id=pull_request_id))
495 495
496 496 data = {
497 497 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
498 498 }
499 499 if comm:
500 500 c.co = comm
501 501 data.update(comm.get_dict())
502 502 data.update({'rendered_text':
503 503 render('changeset/changeset_comment_block.html')})
504 504
505 505 return data
506 506
507 507 @NotAnonymous()
508 508 @jsonify
509 509 def delete_comment(self, repo_name, comment_id):
510 510 co = ChangesetComment.get(comment_id)
511 511 if co.pull_request.is_closed():
512 512 #don't allow deleting comments on closed pull request
513 513 raise HTTPForbidden()
514 514
515 515 owner = co.author.user_id == c.rhodecode_user.user_id
516 516 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
517 517 ChangesetCommentsModel().delete(comment=co)
518 518 Session().commit()
519 519 return True
520 520 else:
521 521 raise HTTPForbidden()
@@ -1,146 +1,146 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.search
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Search controller for RhodeCode
7 7
8 8 :created_on: Aug 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import logging
26 26 import traceback
27 27 import urllib
28 28 from pylons.i18n.translation import _
29 29 from pylons import request, config, tmpl_context as c
30 30
31 31 from rhodecode.lib.auth import LoginRequired
32 32 from rhodecode.lib.base import BaseRepoController, render
33 33 from rhodecode.lib.indexers import CHGSETS_SCHEMA, SCHEMA, CHGSET_IDX_NAME, \
34 34 IDX_NAME, WhooshResultWrapper
35 35
36 36 from webhelpers.paginate import Page
37 37 from webhelpers.util import update_params
38 38
39 39 from whoosh.index import open_dir, EmptyIndexError
40 40 from whoosh.qparser import QueryParser, QueryParserError
41 41 from whoosh.query import Phrase, Wildcard, Term, Prefix
42 42 from rhodecode.model.repo import RepoModel
43 43 from rhodecode.lib.utils2 import safe_str, safe_int
44 44
45 45
46 46 log = logging.getLogger(__name__)
47 47
48 48
49 49 class SearchController(BaseRepoController):
50 50
51 51 @LoginRequired()
52 52 def __before__(self):
53 53 super(SearchController, self).__before__()
54 54
55 55 def index(self, repo_name=None):
56 56 c.repo_name = repo_name
57 57 c.formated_results = []
58 58 c.runtime = ''
59 59 c.cur_query = request.GET.get('q', None)
60 60 c.cur_type = request.GET.get('type', 'content')
61 61 c.cur_search = search_type = {'content': 'content',
62 62 'commit': 'message',
63 63 'path': 'path',
64 64 'repository': 'repository'
65 65 }.get(c.cur_type, 'content')
66 66
67 67 index_name = {
68 68 'content': IDX_NAME,
69 69 'commit': CHGSET_IDX_NAME,
70 70 'path': IDX_NAME
71 71 }.get(c.cur_type, IDX_NAME)
72 72
73 73 schema_defn = {
74 74 'content': SCHEMA,
75 75 'commit': CHGSETS_SCHEMA,
76 76 'path': SCHEMA
77 77 }.get(c.cur_type, SCHEMA)
78 78
79 79 log.debug('IDX: %s' % index_name)
80 80 log.debug('SCHEMA: %s' % schema_defn)
81 81
82 82 if c.cur_query:
83 83 cur_query = c.cur_query.lower()
84 84 log.debug(cur_query)
85 85
86 86 if c.cur_query:
87 p = safe_int(request.params.get('page', 1), 1)
87 p = safe_int(request.GET.get('page', 1), 1)
88 88 highlight_items = set()
89 89 try:
90 90 idx = open_dir(config['app_conf']['index_dir'],
91 91 indexname=index_name)
92 92 searcher = idx.searcher()
93 93
94 94 qp = QueryParser(search_type, schema=schema_defn)
95 95 if c.repo_name:
96 96 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
97 97 try:
98 98 query = qp.parse(unicode(cur_query))
99 99 # extract words for highlight
100 100 if isinstance(query, Phrase):
101 101 highlight_items.update(query.words)
102 102 elif isinstance(query, Prefix):
103 103 highlight_items.add(query.text)
104 104 else:
105 105 for i in query.all_terms():
106 106 if i[0] in ['content', 'message']:
107 107 highlight_items.add(i[1])
108 108
109 109 matcher = query.matcher(searcher)
110 110
111 111 log.debug('query: %s' % query)
112 112 log.debug('hl terms: %s' % highlight_items)
113 113 results = searcher.search(query)
114 114 res_ln = len(results)
115 115 c.runtime = '%s results (%.3f seconds)' % (
116 116 res_ln, results.runtime
117 117 )
118 118
119 119 def url_generator(**kw):
120 120 q = urllib.quote(safe_str(c.cur_query))
121 121 return update_params("?q=%s&type=%s" \
122 122 % (q, safe_str(c.cur_type)), **kw)
123 123 repo_location = RepoModel().repos_path
124 124 c.formated_results = Page(
125 125 WhooshResultWrapper(search_type, searcher, matcher,
126 126 highlight_items, repo_location),
127 127 page=p,
128 128 item_count=res_ln,
129 129 items_per_page=10,
130 130 url=url_generator
131 131 )
132 132
133 133 except QueryParserError:
134 134 c.runtime = _('Invalid search query. Try quoting it.')
135 135 searcher.close()
136 136 except (EmptyIndexError, IOError):
137 137 log.error(traceback.format_exc())
138 138 log.error('Empty Index data')
139 139 c.runtime = _('There is no index to search in. '
140 140 'Please run whoosh indexer')
141 141 except (Exception):
142 142 log.error(traceback.format_exc())
143 143 c.runtime = _('An error occurred during this search operation')
144 144
145 145 # Return a rendered template
146 146 return render('/search/search.html')
@@ -1,107 +1,107 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.shortlog
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Shortlog controller for rhodecode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27
28 28 from pylons import tmpl_context as c, request, url
29 29 from pylons.i18n.translation import _
30 30
31 31 from rhodecode.lib import helpers as h
32 32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 33 from rhodecode.lib.base import BaseRepoController, render
34 34 from rhodecode.lib.helpers import RepoPage
35 35 from pylons.controllers.util import redirect
36 36 from rhodecode.lib.utils2 import safe_int
37 37 from rhodecode.lib.vcs.exceptions import NodeDoesNotExistError, ChangesetError,\
38 38 RepositoryError
39 39
40 40 log = logging.getLogger(__name__)
41 41
42 42
43 43 class ShortlogController(BaseRepoController):
44 44
45 45 @LoginRequired()
46 46 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
47 47 'repository.admin')
48 48 def __before__(self):
49 49 super(ShortlogController, self).__before__()
50 50
51 51 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
52 52 """
53 53 Safe way to get changeset if error occur it redirects to tip with
54 54 proper message
55 55
56 56 :param rev: revision to fetch
57 57 :param repo_name: repo name to redirect after
58 58 """
59 59
60 60 try:
61 61 return c.rhodecode_repo.get_changeset(rev)
62 62 except RepositoryError, e:
63 63 h.flash(str(e), category='warning')
64 64 redirect(h.url('shortlog_home', repo_name=repo_name))
65 65
66 66 def index(self, repo_name, revision=None, f_path=None):
67 p = safe_int(request.params.get('page', 1), 1)
68 size = safe_int(request.params.get('size', 20), 20)
67 p = safe_int(request.GET.get('page', 1), 1)
68 size = safe_int(request.GET.get('size', 20), 20)
69 69 collection = c.rhodecode_repo
70 70 c.file_history = f_path
71 71
72 72 def url_generator(**kw):
73 73 if f_path:
74 74 return url('shortlog_file_home', repo_name=repo_name,
75 75 revision=revision, f_path=f_path, size=size, **kw)
76 76 return url('shortlog_home', repo_name=repo_name, size=size, **kw)
77 77
78 78 if f_path:
79 79 log.debug('generating shortlog for path %s' % f_path)
80 80 # get the history for the file !
81 81 tip_cs = c.rhodecode_repo.get_changeset()
82 82 try:
83 83 collection = tip_cs.get_file_history(f_path)
84 84 except (NodeDoesNotExistError, ChangesetError):
85 85 #this node is not present at tip !
86 86 try:
87 87 cs = self.__get_cs_or_redirect(revision, repo_name)
88 88 collection = cs.get_file_history(f_path)
89 89 except RepositoryError, e:
90 90 h.flash(str(e), category='warning')
91 91 redirect(h.url('shortlog_home', repo_name=repo_name))
92 92 collection = list(reversed(collection))
93 93
94 94 c.repo_changesets = RepoPage(collection, page=p,
95 95 items_per_page=size, url=url_generator)
96 96 page_revisions = [x.raw_id for x in list(c.repo_changesets)]
97 97 c.statuses = c.rhodecode_db_repo.statuses(page_revisions)
98 98
99 99 if not c.repo_changesets:
100 100 h.flash(_('There are no changesets yet'), category='warning')
101 101 return redirect(url('summary_home', repo_name=repo_name))
102 102
103 103 c.shortlog_data = render('shortlog/shortlog_data.html')
104 104 if request.environ.get('HTTP_X_PARTIAL_XHR'):
105 105 return c.shortlog_data
106 106 r = render('shortlog/shortlog.html')
107 107 return r
General Comments 0
You need to be logged in to leave comments. Login now