##// END OF EJS Templates
journal: ported controller to pyramid code
marcink -
r1933:b6b05465 default
parent child Browse files
Show More
@@ -0,0 +1,53 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 from rhodecode.apps._base import ADMIN_PREFIX
23
24
25 def admin_routes(config):
26
27 config.add_route(
28 name='journal', pattern='/journal')
29 config.add_route(
30 name='journal_rss', pattern='/journal/rss')
31 config.add_route(
32 name='journal_atom', pattern='/journal/atom')
33
34 config.add_route(
35 name='journal_public', pattern='/public_journal')
36 config.add_route(
37 name='journal_public_atom', pattern='/public_journal/atom')
38 config.add_route(
39 name='journal_public_atom_old', pattern='/public_journal_atom')
40
41 config.add_route(
42 name='journal_public_rss', pattern='/public_journal/rss')
43 config.add_route(
44 name='journal_public_rss_old', pattern='/public_journal_rss')
45
46 config.add_route(
47 name='toggle_following', pattern='/toggle_following')
48
49
50 def includeme(config):
51 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
52 # Scan module for configuration decorators.
53 config.scan() No newline at end of file
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
@@ -0,0 +1,377 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 import logging
23 import itertools
24
25 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
26
27 from pyramid.view import view_config
28 from pyramid.httpexceptions import HTTPBadRequest
29 from pyramid.response import Response
30 from pyramid.renderers import render
31
32 from rhodecode.apps._base import BaseAppView
33 from rhodecode.model.db import (
34 or_, joinedload, UserLog, UserFollowing, User, UserApiKeys)
35 from rhodecode.model.meta import Session
36 import rhodecode.lib.helpers as h
37 from rhodecode.lib.helpers import Page
38 from rhodecode.lib.user_log_filter import user_log_filter
39 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
40 from rhodecode.lib.utils2 import safe_int, AttributeDict
41 from rhodecode.model.scm import ScmModel
42
43 log = logging.getLogger(__name__)
44
45
46 class JournalView(BaseAppView):
47
48 def load_default_context(self):
49 c = self._get_local_tmpl_context(include_app_defaults=True)
50 self._register_global_c(c)
51 self._load_defaults(c.rhodecode_name)
52
53 # TODO(marcink): what is this, why we need a global register ?
54 c.search_term = self.request.GET.get('filter') or ''
55 return c
56
57 def _get_config(self, rhodecode_name):
58 import rhodecode
59 config = rhodecode.CONFIG
60
61 return {
62 'language': 'en-us',
63 'feed_ttl': '5', # TTL of feed,
64 'feed_items_per_page':
65 safe_int(config.get('rss_items_per_page', 20)),
66 'rhodecode_name': rhodecode_name
67 }
68
69 def _load_defaults(self, rhodecode_name):
70 config = self._get_config(rhodecode_name)
71 # common values for feeds
72 self.language = config["language"]
73 self.ttl = config["feed_ttl"]
74 self.feed_items_per_page = config['feed_items_per_page']
75 self.rhodecode_name = config['rhodecode_name']
76
77 def _get_daily_aggregate(self, journal):
78 groups = []
79 for k, g in itertools.groupby(journal, lambda x: x.action_as_day):
80 user_group = []
81 # groupby username if it's a present value, else
82 # fallback to journal username
83 for _, g2 in itertools.groupby(
84 list(g), lambda x: x.user.username if x.user else x.username):
85 l = list(g2)
86 user_group.append((l[0].user, l))
87
88 groups.append((k, user_group,))
89
90 return groups
91
92 def _get_journal_data(self, following_repos, search_term):
93 repo_ids = [x.follows_repository.repo_id for x in following_repos
94 if x.follows_repository is not None]
95 user_ids = [x.follows_user.user_id for x in following_repos
96 if x.follows_user is not None]
97
98 filtering_criterion = None
99
100 if repo_ids and user_ids:
101 filtering_criterion = or_(UserLog.repository_id.in_(repo_ids),
102 UserLog.user_id.in_(user_ids))
103 if repo_ids and not user_ids:
104 filtering_criterion = UserLog.repository_id.in_(repo_ids)
105 if not repo_ids and user_ids:
106 filtering_criterion = UserLog.user_id.in_(user_ids)
107 if filtering_criterion is not None:
108 journal = Session().query(UserLog)\
109 .options(joinedload(UserLog.user))\
110 .options(joinedload(UserLog.repository))
111 # filter
112 try:
113 journal = user_log_filter(journal, search_term)
114 except Exception:
115 # we want this to crash for now
116 raise
117 journal = journal.filter(filtering_criterion)\
118 .order_by(UserLog.action_date.desc())
119 else:
120 journal = []
121
122 return journal
123
124 def _atom_feed(self, repos, search_term, public=True):
125 _ = self.request.translate
126 journal = self._get_journal_data(repos, search_term)
127 if public:
128 _link = h.route_url('journal_public_atom')
129 _desc = '%s %s %s' % (self.rhodecode_name, _('public journal'),
130 'atom feed')
131 else:
132 _link = h.route_url('journal_atom')
133 _desc = '%s %s %s' % (self.rhodecode_name, _('journal'), 'atom feed')
134
135 feed = Atom1Feed(
136 title=_desc, link=_link, description=_desc,
137 language=self.language, ttl=self.ttl)
138
139 for entry in journal[:self.feed_items_per_page]:
140 user = entry.user
141 if user is None:
142 # fix deleted users
143 user = AttributeDict({'short_contact': entry.username,
144 'email': '',
145 'full_contact': ''})
146 action, action_extra, ico = h.action_parser(entry, feed=True)
147 title = "%s - %s %s" % (user.short_contact, action(),
148 entry.repository.repo_name)
149 desc = action_extra()
150 _url = h.route_url('home')
151 if entry.repository is not None:
152 _url = h.route_url('repo_changelog',
153 repo_name=entry.repository.repo_name)
154
155 feed.add_item(title=title,
156 pubdate=entry.action_date,
157 link=_url,
158 author_email=user.email,
159 author_name=user.full_contact,
160 description=desc)
161
162 response = Response(feed.writeString('utf-8'))
163 response.content_type = feed.mime_type
164 return response
165
166 def _rss_feed(self, repos, search_term, public=True):
167 _ = self.request.translate
168 journal = self._get_journal_data(repos, search_term)
169 if public:
170 _link = h.route_url('journal_public_atom')
171 _desc = '%s %s %s' % (
172 self.rhodecode_name, _('public journal'), 'rss feed')
173 else:
174 _link = h.route_url('journal_atom')
175 _desc = '%s %s %s' % (
176 self.rhodecode_name, _('journal'), 'rss feed')
177
178 feed = Rss201rev2Feed(
179 title=_desc, link=_link, description=_desc,
180 language=self.language, ttl=self.ttl)
181
182 for entry in journal[:self.feed_items_per_page]:
183 user = entry.user
184 if user is None:
185 # fix deleted users
186 user = AttributeDict({'short_contact': entry.username,
187 'email': '',
188 'full_contact': ''})
189 action, action_extra, ico = h.action_parser(entry, feed=True)
190 title = "%s - %s %s" % (user.short_contact, action(),
191 entry.repository.repo_name)
192 desc = action_extra()
193 _url = h.route_url('home')
194 if entry.repository is not None:
195 _url = h.route_url('repo_changelog',
196 repo_name=entry.repository.repo_name)
197
198 feed.add_item(title=title,
199 pubdate=entry.action_date,
200 link=_url,
201 author_email=user.email,
202 author_name=user.full_contact,
203 description=desc)
204
205 response = Response(feed.writeString('utf-8'))
206 response.content_type = feed.mime_type
207 return response
208
209 @LoginRequired()
210 @NotAnonymous()
211 @view_config(
212 route_name='journal', request_method='GET',
213 renderer=None)
214 def journal(self):
215 c = self.load_default_context()
216
217 p = safe_int(self.request.GET.get('page', 1), 1)
218 c.user = User.get(self._rhodecode_user.user_id)
219 following = Session().query(UserFollowing)\
220 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
221 .options(joinedload(UserFollowing.follows_repository))\
222 .all()
223
224 journal = self._get_journal_data(following, c.search_term)
225
226 def url_generator(**kw):
227 query_params = {
228 'filter': c.search_term
229 }
230 query_params.update(kw)
231 return self.request.current_route_path(_query=query_params)
232
233 c.journal_pager = Page(
234 journal, page=p, items_per_page=20, url=url_generator)
235 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
236
237 c.journal_data = render(
238 'rhodecode:templates/journal/journal_data.mako',
239 self._get_template_context(c), self.request)
240
241 if self.request.is_xhr:
242 return Response(c.journal_data)
243
244 html = render(
245 'rhodecode:templates/journal/journal.mako',
246 self._get_template_context(c), self.request)
247 return Response(html)
248
249 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
250 @NotAnonymous()
251 @view_config(
252 route_name='journal_atom', request_method='GET',
253 renderer=None)
254 def journal_atom(self):
255 """
256 Produce an atom-1.0 feed via feedgenerator module
257 """
258 c = self.load_default_context()
259 following_repos = Session().query(UserFollowing)\
260 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
261 .options(joinedload(UserFollowing.follows_repository))\
262 .all()
263 return self._atom_feed(following_repos, c.search_term, public=False)
264
265 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
266 @NotAnonymous()
267 @view_config(
268 route_name='journal_rss', request_method='GET',
269 renderer=None)
270 def journal_rss(self):
271 """
272 Produce an rss feed via feedgenerator module
273 """
274 c = self.load_default_context()
275 following_repos = Session().query(UserFollowing)\
276 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
277 .options(joinedload(UserFollowing.follows_repository))\
278 .all()
279 return self._rss_feed(following_repos, c.search_term, public=False)
280
281 @LoginRequired()
282 @NotAnonymous()
283 @CSRFRequired()
284 @view_config(
285 route_name='toggle_following', request_method='POST',
286 renderer='json_ext')
287 def toggle_following(self):
288 user_id = self.request.POST.get('follows_user_id')
289 if user_id:
290 try:
291 ScmModel().toggle_following_user(
292 user_id, self._rhodecode_user.user_id)
293 Session().commit()
294 return 'ok'
295 except Exception:
296 raise HTTPBadRequest()
297
298 repo_id = self.request.POST.get('follows_repo_id')
299 if repo_id:
300 try:
301 ScmModel().toggle_following_repo(
302 repo_id, self._rhodecode_user.user_id)
303 Session().commit()
304 return 'ok'
305 except Exception:
306 raise HTTPBadRequest()
307
308 raise HTTPBadRequest()
309
310 @LoginRequired()
311 @view_config(
312 route_name='journal_public', request_method='GET',
313 renderer=None)
314 def journal_public(self):
315 c = self.load_default_context()
316 # Return a rendered template
317 p = safe_int(self.request.GET.get('page', 1), 1)
318
319 c.following = Session().query(UserFollowing)\
320 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
321 .options(joinedload(UserFollowing.follows_repository))\
322 .all()
323
324 journal = self._get_journal_data(c.following, c.search_term)
325
326 def url_generator(**kw):
327 query_params = {}
328 query_params.update(kw)
329 return self.request.current_route_path(_query=query_params)
330
331 c.journal_pager = Page(
332 journal, page=p, items_per_page=20, url=url_generator)
333 c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)
334
335 c.journal_data = render(
336 'rhodecode:templates/journal/journal_data.mako',
337 self._get_template_context(c), self.request)
338
339 if self.request.is_xhr:
340 return Response(c.journal_data)
341
342 html = render(
343 'rhodecode:templates/journal/public_journal.mako',
344 self._get_template_context(c), self.request)
345 return Response(html)
346
347 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
348 @view_config(
349 route_name='journal_public_atom', request_method='GET',
350 renderer=None)
351 def journal_public_atom(self):
352 """
353 Produce an atom-1.0 feed via feedgenerator module
354 """
355 c = self.load_default_context()
356 following_repos = Session().query(UserFollowing)\
357 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
358 .options(joinedload(UserFollowing.follows_repository))\
359 .all()
360
361 return self._atom_feed(following_repos, c.search_term)
362
363 @LoginRequired(auth_token_access=[UserApiKeys.ROLE_FEED])
364 @view_config(
365 route_name='journal_public_rss', request_method='GET',
366 renderer=None)
367 def journal_public_rss(self):
368 """
369 Produce an rss2 feed via feedgenerator module
370 """
371 c = self.load_default_context()
372 following_repos = Session().query(UserFollowing)\
373 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
374 .options(joinedload(UserFollowing.follows_repository))\
375 .all()
376
377 return self._rss_feed(following_repos, c.search_term)
@@ -19,24 +19,62 b''
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import datetime
21 import datetime
22 from rhodecode.tests import TestController, url
22
23 import pytest
24
25 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.tests import TestController
23 from rhodecode.model.db import UserFollowing, Repository
27 from rhodecode.model.db import UserFollowing, Repository
24
28
25
29
26 class TestJournalController(TestController):
30 def route_path(name, params=None, **kwargs):
31 import urllib
27
32
28 def test_index(self):
33 base_url = {
34 'journal': ADMIN_PREFIX + '/journal',
35 'journal_rss': ADMIN_PREFIX + '/journal/rss',
36 'journal_atom': ADMIN_PREFIX + '/journal/atom',
37 'journal_public': ADMIN_PREFIX + '/public_journal',
38 'journal_public_atom': ADMIN_PREFIX + '/public_journal/atom',
39 'journal_public_atom_old': ADMIN_PREFIX + '/public_journal_atom',
40 'journal_public_rss': ADMIN_PREFIX + '/public_journal/rss',
41 'journal_public_rss_old': ADMIN_PREFIX + '/public_journal_rss',
42 'toggle_following': ADMIN_PREFIX + '/toggle_following',
43 }[name].format(**kwargs)
44
45 if params:
46 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
47 return base_url
48
49
50 class TestJournalViews(TestController):
51
52 def test_journal(self):
29 self.log_user()
53 self.log_user()
30 response = self.app.get(url(controller='journal', action='index'))
54 response = self.app.get(route_path('journal'))
31 response.mustcontain(
55 # response.mustcontain(
32 """<div class="journal_day">%s</div>""" % datetime.date.today())
56 # """<div class="journal_day">%s</div>""" % datetime.date.today())
57
58 @pytest.mark.parametrize("feed_type, content_type", [
59 ('rss', "application/rss+xml"),
60 ('atom', "application/atom+xml")
61 ])
62 def test_journal_feed(self, feed_type, content_type):
63 self.log_user()
64 response = self.app.get(
65 route_path(
66 'journal_{}'.format(feed_type)),
67 status=200)
68
69 assert response.content_type == content_type
33
70
34 def test_toggle_following_repository(self, backend):
71 def test_toggle_following_repository(self, backend):
35 user = self.log_user()
72 user = self.log_user()
36 repo = Repository.get_by_repo_name(backend.repo_name)
73 repo = Repository.get_by_repo_name(backend.repo_name)
37 repo_id = repo.repo_id
74 repo_id = repo.repo_id
38 self.app.post(url('toggle_following'), {'follows_repo_id': repo_id,
75 self.app.post(
39 'csrf_token': self.csrf_token})
76 route_path('toggle_following'), {'follows_repo_id': repo_id,
77 'csrf_token': self.csrf_token})
40
78
41 followings = UserFollowing.query()\
79 followings = UserFollowing.query()\
42 .filter(UserFollowing.user_id == user['user_id'])\
80 .filter(UserFollowing.user_id == user['user_id'])\
@@ -44,8 +82,9 b' class TestJournalController(TestControll'
44
82
45 assert len(followings) == 0
83 assert len(followings) == 0
46
84
47 self.app.post(url('toggle_following'), {'follows_repo_id': repo_id,
85 self.app.post(
48 'csrf_token': self.csrf_token})
86 route_path('toggle_following'), {'follows_repo_id': repo_id,
87 'csrf_token': self.csrf_token})
49
88
50 followings = UserFollowing.query()\
89 followings = UserFollowing.query()\
51 .filter(UserFollowing.user_id == user['user_id'])\
90 .filter(UserFollowing.user_id == user['user_id'])\
@@ -53,12 +92,15 b' class TestJournalController(TestControll'
53
92
54 assert len(followings) == 1
93 assert len(followings) == 1
55
94
56 def test_public_journal_atom(self):
95 @pytest.mark.parametrize("feed_type, content_type", [
96 ('rss', "application/rss+xml"),
97 ('atom', "application/atom+xml")
98 ])
99 def test_public_journal_feed(self, feed_type, content_type):
57 self.log_user()
100 self.log_user()
58 response = self.app.get(url(controller='journal',
101 response = self.app.get(
59 action='public_journal_atom'),)
102 route_path(
103 'journal_public_{}'.format(feed_type)),
104 status=200)
60
105
61 def test_public_journal_rss(self):
106 assert response.content_type == content_type
62 self.log_user()
63 response = self.app.get(url(controller='journal',
64 action='public_journal_rss'),)
@@ -288,6 +288,7 b' def includeme(config):'
288 config.include('rhodecode.apps.channelstream')
288 config.include('rhodecode.apps.channelstream')
289 config.include('rhodecode.apps.login')
289 config.include('rhodecode.apps.login')
290 config.include('rhodecode.apps.home')
290 config.include('rhodecode.apps.home')
291 config.include('rhodecode.apps.journal')
291 config.include('rhodecode.apps.repository')
292 config.include('rhodecode.apps.repository')
292 config.include('rhodecode.apps.repo_group')
293 config.include('rhodecode.apps.repo_group')
293 config.include('rhodecode.apps.search')
294 config.include('rhodecode.apps.search')
@@ -444,35 +444,6 b' def make_map(config):'
444 m.connect('my_account_password', '/my_account/password',
444 m.connect('my_account_password', '/my_account/password',
445 action='my_account_password', conditions={'method': ['GET']})
445 action='my_account_password', conditions={'method': ['GET']})
446
446
447 # USER JOURNAL
448 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
449 controller='journal', action='index')
450 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
451 controller='journal', action='journal_rss')
452 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
453 controller='journal', action='journal_atom')
454
455 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
456 controller='journal', action='public_journal')
457
458 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
459 controller='journal', action='public_journal_rss')
460
461 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
462 controller='journal', action='public_journal_rss')
463
464 rmap.connect('public_journal_atom',
465 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
466 action='public_journal_atom')
467
468 rmap.connect('public_journal_atom_old',
469 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
470 action='public_journal_atom')
471
472 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
473 controller='journal', action='toggle_following', jsroute=True,
474 conditions={'method': ['POST']})
475
476 #==========================================================================
447 #==========================================================================
477 # REPOSITORY ROUTES
448 # REPOSITORY ROUTES
478 #==========================================================================
449 #==========================================================================
@@ -15,7 +15,6 b' function registerRCRoutes() {'
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
17 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
18 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
19 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
18 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
20 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
19 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
21 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
20 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
@@ -92,6 +91,15 b' function registerRCRoutes() {'
92 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
91 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
93 pyroutes.register('repo_list_data', '/_repos', []);
92 pyroutes.register('repo_list_data', '/_repos', []);
94 pyroutes.register('goto_switcher_data', '/_goto_data', []);
93 pyroutes.register('goto_switcher_data', '/_goto_data', []);
94 pyroutes.register('journal', '/_admin/journal', []);
95 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
96 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
97 pyroutes.register('journal_public', '/_admin/public_journal', []);
98 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
99 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
100 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
101 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
102 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
95 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
103 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
96 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
104 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
97 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
105 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
@@ -381,13 +381,13 b''
381 ## ROOT MENU
381 ## ROOT MENU
382 %if c.rhodecode_user.username != h.DEFAULT_USER:
382 %if c.rhodecode_user.username != h.DEFAULT_USER:
383 <li class="${is_active('journal')}">
383 <li class="${is_active('journal')}">
384 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
384 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
385 <div class="menulabel">${_('Journal')}</div>
385 <div class="menulabel">${_('Journal')}</div>
386 </a>
386 </a>
387 </li>
387 </li>
388 %else:
388 %else:
389 <li class="${is_active('journal')}">
389 <li class="${is_active('journal')}">
390 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
390 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
391 <div class="menulabel">${_('Public journal')}</div>
391 <div class="menulabel">${_('Public journal')}</div>
392 </a>
392 </a>
393 </li>
393 </li>
@@ -6,12 +6,13 b''
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9 <%def name="breadcrumbs()">
10 <%def name="breadcrumbs()">
10 <h1 class="block-left">
11 <h1 class="block-left">
11 ${h.form(None, id_="filter_form", method="get")}
12 ${h.form(None, id_="filter_form", method="get")}
12 <input class="q_filter_box ${'' if c.search_term else 'initial'}" id="j_filter" size="15" type="text" name="filter" value="${c.search_term}" placeholder="${_('quick filter...')}"/>
13 <input class="q_filter_box ${'' if c.search_term else 'initial'}" id="j_filter" size="15" type="text" name="filter" value="${c.search_term}" placeholder="${_('quick filter...')}"/>
13 <input type='submit' value="${_('Filter')}" class="btn" />
14 <input type='submit' value="${_('Filter')}" class="btn" />
14 ${_('Journal')} - ${ungettext('%s entry', '%s entries', c.journal_pager.item_count) % (c.journal_pager.item_count)}
15 ${_('Journal')} - ${_ungettext('%s entry', '%s entries', c.journal_pager.item_count) % (c.journal_pager.item_count)}
15 ${h.end_form()}
16 ${h.end_form()}
16 </h1>
17 </h1>
17 <p class="tooltip filterexample" title="${h.tooltip(h.journal_filter_help(c.pyramid_request))}">${_('Example Queries')}</p>
18 <p class="tooltip filterexample" title="${h.tooltip(h.journal_filter_help(c.pyramid_request))}">${_('Example Queries')}</p>
@@ -20,9 +21,10 b''
20 ${self.menu_items(active='journal')}
21 ${self.menu_items(active='journal')}
21 </%def>
22 </%def>
22 <%def name="head_extra()">
23 <%def name="head_extra()">
23 <link href="${h.url('journal_atom', auth_token=c.rhodecode_user.feed_token)}" rel="alternate" title="${_('ATOM journal feed')}" type="application/atom+xml" />
24 <link href="${h.route_path('journal_atom', _query=dict(auth_token=c.rhodecode_user.feed_token))}" rel="alternate" title="${_('ATOM journal feed')}" type="application/atom+xml" />
24 <link href="${h.url('journal_rss', auth_token=c.rhodecode_user.feed_token)}" rel="alternate" title="${_('RSS journal feed')}" type="application/rss+xml" />
25 <link href="${h.route_path('journal_rss', _query=dict(auth_token=c.rhodecode_user.feed_token))}" rel="alternate" title="${_('RSS journal feed')}" type="application/rss+xml" />
25 </%def>
26 </%def>
27
26 <%def name="main()">
28 <%def name="main()">
27
29
28 <div class="box">
30 <div class="box">
@@ -31,14 +33,14 b''
31 ${self.breadcrumbs()}
33 ${self.breadcrumbs()}
32 <ul class="links icon-only-links block-right">
34 <ul class="links icon-only-links block-right">
33 <li>
35 <li>
34 <span><a id="refresh" href="${h.url('journal')}"><i class="icon-refresh"></i></a></span>
36 <span><a id="refresh" href="${h.route_path('journal')}"><i class="icon-refresh"></i></a></span>
35 </li>
37 </li>
36 <li>
38 <li>
37 <span><a href="${h.url('journal_atom', auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a></span>
39 <span><a href="${h.route_path('journal_atom', _query=dict(auth_token=c.rhodecode_user.feed_token))}"><i class="icon-rss-sign"></i></a></span>
38 </li>
40 </li>
39 </ul>
41 </ul>
40 </div>
42 </div>
41 <div id="journal">${c.journal_data}</div>
43 <div id="journal">${c.journal_data|n}</div>
42 </div>
44 </div>
43
45
44 <script type="text/javascript">
46 <script type="text/javascript">
@@ -48,7 +50,7 b''
48 show_more_event();
50 show_more_event();
49 });
51 });
50 $(document).pjax('#refresh', '#journal',
52 $(document).pjax('#refresh', '#journal',
51 {url: "${h.url.current(filter=c.search_term)}", push: false});
53 {url: "${request.current_route_path(_query=dict(filter=c.search_term))}", push: false});
52
54
53 </script>
55 </script>
54 </%def>
56 </%def>
@@ -6,32 +6,38 b''
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9 <%def name="breadcrumbs()">
10 <%def name="breadcrumbs()">
10 ${h.branding(c.rhodecode_name)}
11 <h1 class="block-left">
12 ${_('Public Journal')} - ${_ungettext('%s entry', '%s entries', c.journal_pager.item_count) % (c.journal_pager.item_count)}
13 </h1>
11 </%def>
14 </%def>
15
12 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
13 ${self.menu_items(active='journal')}
17 ${self.menu_items(active='journal')}
14 </%def>
18 </%def>
19
15 <%def name="head_extra()">
20 <%def name="head_extra()">
16 <link href="${h.url('public_journal_atom')}" rel="alternate" title="${_('ATOM public journal feed')}" type="application/atom+xml" />
21 <link href="${h.route_path('journal_public_atom')}" rel="alternate" title="${_('ATOM public journal feed')}" type="application/atom+xml" />
17 <link href="${h.url('public_journal_rss')}" rel="alternate" title="${_('RSS public journal feed')}" type="application/rss+xml" />
22 <link href="${h.route_path('journal_public_rss')}" rel="alternate" title="${_('RSS public journal feed')}" type="application/rss+xml" />
18 </%def>
23 </%def>
24
19 <%def name="main()">
25 <%def name="main()">
20
26
21 <div class="box">
27 <div class="box">
22 <!-- box / title -->
28 <!-- box / title -->
23 <div class="title">
29 <div class="title journal">
24 <h5>${_('Public Journal')}</h5>
30 ${self.breadcrumbs()}
25 <ul class="links">
31
32 <ul class="links icon-only-links block-right">
26 <li>
33 <li>
27 <span>
34 <span>
28 <a href="${h.url('public_journal_atom')}"> <i class="icon-rss-sign" ></i></a>
35 <a href="${h.route_path('journal_public_atom')}"> <i class="icon-rss-sign" ></i></a>
29 </span>
36 </span>
30 </li>
37 </li>
31 </ul>
38 </ul>
32 </div>
39 </div>
33
40 <div id="journal">${c.journal_data|n}</div>
34 <div id="journal">${c.journal_data}</div>
35 </div>
41 </div>
36
42
37 </%def>
43 </%def>
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now