##// END OF EJS Templates
audit-logs: introduced new view to replace admin journal....
marcink -
r1758:98d57cd2 default
parent child Browse files
Show More
@@ -0,0 +1,187 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 import os
22 import csv
23 import datetime
24
25 import pytest
26
27 from rhodecode.tests import *
28 from rhodecode.tests.fixture import FIXTURES
29 from rhodecode.model.db import UserLog
30 from rhodecode.model.meta import Session
31 from rhodecode.lib.utils2 import safe_unicode
32
33
34 def route_path(name, params=None, **kwargs):
35 import urllib
36 from rhodecode.apps._base import ADMIN_PREFIX
37
38 base_url = {
39 'admin_home': ADMIN_PREFIX,
40 'admin_audit_logs': ADMIN_PREFIX + '/audit_logs',
41
42 }[name].format(**kwargs)
43
44 if params:
45 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
46 return base_url
47
48
49 class TestAdminController(TestController):
50
51 @pytest.fixture(scope='class', autouse=True)
52 def prepare(self, request, pylonsapp):
53 UserLog.query().delete()
54 Session().commit()
55
56 def strptime(val):
57 fmt = '%Y-%m-%d %H:%M:%S'
58 if '.' not in val:
59 return datetime.datetime.strptime(val, fmt)
60
61 nofrag, frag = val.split(".")
62 date = datetime.datetime.strptime(nofrag, fmt)
63
64 frag = frag[:6] # truncate to microseconds
65 frag += (6 - len(frag)) * '0' # add 0s
66 return date.replace(microsecond=int(frag))
67
68 with open(os.path.join(FIXTURES, 'journal_dump.csv')) as f:
69 for row in csv.DictReader(f):
70 ul = UserLog()
71 for k, v in row.iteritems():
72 v = safe_unicode(v)
73 if k == 'action_date':
74 v = strptime(v)
75 if k in ['user_id', 'repository_id']:
76 # nullable due to FK problems
77 v = None
78 setattr(ul, k, v)
79 Session().add(ul)
80 Session().commit()
81
82 @request.addfinalizer
83 def cleanup():
84 UserLog.query().delete()
85 Session().commit()
86
87 def test_index(self):
88 self.log_user()
89 response = self.app.get(route_path('admin_audit_logs'))
90 response.mustcontain('Admin audit logs')
91
92 def test_filter_all_entries(self):
93 self.log_user()
94 response = self.app.get(route_path('admin_audit_logs'))
95 all_count = UserLog.query().count()
96 response.mustcontain('%s entries' % all_count)
97
98 def test_filter_journal_filter_exact_match_on_repository(self):
99 self.log_user()
100 response = self.app.get(route_path('admin_audit_logs',
101 params=dict(filter='repository:rhodecode')))
102 response.mustcontain('3 entries')
103
104 def test_filter_journal_filter_exact_match_on_repository_CamelCase(self):
105 self.log_user()
106 response = self.app.get(route_path('admin_audit_logs',
107 params=dict(filter='repository:RhodeCode')))
108 response.mustcontain('3 entries')
109
110 def test_filter_journal_filter_wildcard_on_repository(self):
111 self.log_user()
112 response = self.app.get(route_path('admin_audit_logs',
113 params=dict(filter='repository:*test*')))
114 response.mustcontain('862 entries')
115
116 def test_filter_journal_filter_prefix_on_repository(self):
117 self.log_user()
118 response = self.app.get(route_path('admin_audit_logs',
119 params=dict(filter='repository:test*')))
120 response.mustcontain('257 entries')
121
122 def test_filter_journal_filter_prefix_on_repository_CamelCase(self):
123 self.log_user()
124 response = self.app.get(route_path('admin_audit_logs',
125 params=dict(filter='repository:Test*')))
126 response.mustcontain('257 entries')
127
128 def test_filter_journal_filter_prefix_on_repository_and_user(self):
129 self.log_user()
130 response = self.app.get(route_path('admin_audit_logs',
131 params=dict(filter='repository:test* AND username:demo')))
132 response.mustcontain('130 entries')
133
134 def test_filter_journal_filter_prefix_on_repository_or_target_repo(self):
135 self.log_user()
136 response = self.app.get(route_path('admin_audit_logs',
137 params=dict(filter='repository:test* OR repository:rhodecode')))
138 response.mustcontain('260 entries') # 257 + 3
139
140 def test_filter_journal_filter_exact_match_on_username(self):
141 self.log_user()
142 response = self.app.get(route_path('admin_audit_logs',
143 params=dict(filter='username:demo')))
144 response.mustcontain('1087 entries')
145
146 def test_filter_journal_filter_exact_match_on_username_camelCase(self):
147 self.log_user()
148 response = self.app.get(route_path('admin_audit_logs',
149 params=dict(filter='username:DemO')))
150 response.mustcontain('1087 entries')
151
152 def test_filter_journal_filter_wildcard_on_username(self):
153 self.log_user()
154 response = self.app.get(route_path('admin_audit_logs',
155 params=dict(filter='username:*test*')))
156 entries_count = UserLog.query().filter(UserLog.username.ilike('%test%')).count()
157 response.mustcontain('{} entries'.format(entries_count))
158
159 def test_filter_journal_filter_prefix_on_username(self):
160 self.log_user()
161 response = self.app.get(route_path('admin_audit_logs',
162 params=dict(filter='username:demo*')))
163 response.mustcontain('1101 entries')
164
165 def test_filter_journal_filter_prefix_on_user_or_other_user(self):
166 self.log_user()
167 response = self.app.get(route_path('admin_audit_logs',
168 params=dict(filter='username:demo OR username:volcan')))
169 response.mustcontain('1095 entries') # 1087 + 8
170
171 def test_filter_journal_filter_wildcard_on_action(self):
172 self.log_user()
173 response = self.app.get(route_path('admin_audit_logs',
174 params=dict(filter='action:*pull_request*')))
175 response.mustcontain('187 entries')
176
177 def test_filter_journal_filter_on_date(self):
178 self.log_user()
179 response = self.app.get(route_path('admin_audit_logs',
180 params=dict(filter='date:20121010')))
181 response.mustcontain('47 entries')
182
183 def test_filter_journal_filter_on_date_2(self):
184 self.log_user()
185 response = self.app.get(route_path('admin_audit_logs',
186 params=dict(filter='date:20121020')))
187 response.mustcontain('17 entries')
@@ -0,0 +1,82 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 import pytest
22
23 from rhodecode.tests import TestController
24 from rhodecode.tests.fixture import Fixture
25
26 fixture = Fixture()
27
28
29 def route_path(name, params=None, **kwargs):
30 import urllib
31 from rhodecode.apps._base import ADMIN_PREFIX
32
33 base_url = {
34 'admin_home': ADMIN_PREFIX,
35 'pullrequest_show': '/{repo_name}/pull-request/{pull_request_id}',
36 'pull_requests_global': ADMIN_PREFIX + '/pull-request/{pull_request_id}',
37 'pull_requests_global_0': ADMIN_PREFIX + '/pull_requests/{pull_request_id}',
38 'pull_requests_global_1': ADMIN_PREFIX + '/pull-requests/{pull_request_id}',
39
40 }[name].format(**kwargs)
41
42 if params:
43 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
44 return base_url
45
46
47 class TestAdminMainView(TestController):
48
49 def test_redirect_admin_home(self):
50 self.log_user()
51 response = self.app.get(route_path('admin_home'), status=302)
52 assert response.location.endswith('/audit_logs')
53
54 def test_redirect_pull_request_view(self, view):
55 self.log_user()
56 self.app.get(
57 route_path(view, pull_request_id='xxxx'),
58 status=404)
59
60 @pytest.mark.backends("git", "hg")
61 @pytest.mark.parametrize('view', [
62 'pull_requests_global',
63 'pull_requests_global_0',
64 'pull_requests_global_1',
65 ])
66 def test_redirect_pull_request_view(self, view, pr_util):
67 self.log_user()
68 pull_request = pr_util.create_pull_request()
69 pull_request_id = pull_request.pull_request_id
70
71 response = self.app.get(
72 route_path(view, pull_request_id=pull_request_id),
73 status=302)
74 assert response.location.endswith(
75 'pull-request/{}'.format(pull_request_id))
76
77 repo_name = pull_request.target_repo.repo_name
78 redirect_url = route_path(
79 'pullrequest_show', repo_name=repo_name,
80 pull_request_id=pull_request.pull_request_id)
81
82 assert redirect_url in response.location
@@ -0,0 +1,73 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 import logging
22
23 from pyramid.view import view_config
24 from sqlalchemy.orm import joinedload
25
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.model.db import UserLog
28 from rhodecode.lib.user_log_filter import user_log_filter
29 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
30 from rhodecode.lib.utils2 import safe_int
31 from rhodecode.lib.helpers import Page
32
33 log = logging.getLogger(__name__)
34
35
36 class AdminAuditLogsView(BaseAppView):
37 def load_default_context(self):
38 c = self._get_local_tmpl_context()
39 self._register_global_c(c)
40 return c
41
42 @LoginRequired()
43 @HasPermissionAllDecorator('hg.admin')
44 @view_config(
45 route_name='admin_audit_logs', request_method='GET',
46 renderer='rhodecode:templates/admin/admin_audit_logs.mako')
47 def admin_audit_logs(self):
48 c = self.load_default_context()
49
50 users_log = UserLog.query()\
51 .options(joinedload(UserLog.user))\
52 .options(joinedload(UserLog.repository))
53
54 # FILTERING
55 c.search_term = self.request.GET.get('filter')
56 try:
57 users_log = user_log_filter(users_log, c.search_term)
58 except Exception:
59 # we want this to crash for now
60 raise
61
62 users_log = users_log.order_by(UserLog.action_date.desc())
63
64 p = safe_int(self.request.GET.get('page', 1), 1)
65
66 def url_generator(**kw):
67 if c.search_term:
68 kw['filter'] = c.search_term
69 return self.request.current_route_path(_query=kw)
70
71 c.audit_logs = Page(users_log, page=p, items_per_page=10,
72 url=url_generator)
73 return self._get_template_context(c)
@@ -0,0 +1,63 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 import logging
22
23
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
26
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
30 from rhodecode.model.db import PullRequest
31
32
33 log = logging.getLogger(__name__)
34
35
36 class AdminMainView(BaseAppView):
37
38 @LoginRequired()
39 @HasPermissionAllDecorator('hg.admin')
40 @view_config(
41 route_name='admin_home', request_method='GET')
42 def admin_main(self):
43 # redirect _admin to audit logs...
44 raise HTTPFound(h.route_path('admin_audit_logs'))
45
46 @LoginRequired()
47 @view_config(route_name='pull_requests_global_0', request_method='GET')
48 @view_config(route_name='pull_requests_global_1', request_method='GET')
49 @view_config(route_name='pull_requests_global', request_method='GET')
50 def pull_requests(self):
51 """
52 Global redirect for Pull Requests
53
54 :param pull_request_id: id of pull requests in the system
55 """
56
57 pull_request_id = self.request.matchdict.get('pull_request_id')
58 pull_request = PullRequest.get_or_404(pull_request_id, pyramid_exc=True)
59 repo_name = pull_request.target_repo.repo_name
60
61 raise HTTPFound(
62 h.route_path('pullrequest_show', repo_name=repo_name,
63 pull_request_id=pull_request_id))
@@ -30,6 +30,20 b' def admin_routes(config):'
30 """
30 """
31
31
32 config.add_route(
32 config.add_route(
33 name='admin_audit_logs',
34 pattern='/audit_logs')
35
36 config.add_route(
37 name='pull_requests_global_0', # backward compat
38 pattern='/pull_requests/{pull_request_id:[0-9]+}')
39 config.add_route(
40 name='pull_requests_global_1', # backward compat
41 pattern='/pull-requests/{pull_request_id:[0-9]+}')
42 config.add_route(
43 name='pull_requests_global',
44 pattern='/pull-request/{pull_request_id:[0-9]+}')
45
46 config.add_route(
33 name='admin_settings_open_source',
47 name='admin_settings_open_source',
34 pattern='/settings/open_source')
48 pattern='/settings/open_source')
35 config.add_route(
49 config.add_route(
@@ -93,6 +107,8 b' def includeme(config):'
93 navigation_registry = NavigationRegistry(labs_active=labs_active)
107 navigation_registry = NavigationRegistry(labs_active=labs_active)
94 config.registry.registerUtility(navigation_registry)
108 config.registry.registerUtility(navigation_registry)
95
109
110 # main admin routes
111 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
96 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
112 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
97
113
98 # Scan module for configuration decorators.
114 # Scan module for configuration decorators.
@@ -41,6 +41,12 b' def includeme(config):'
41 name='bookmarks_home',
41 name='bookmarks_home',
42 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
42 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
43
43
44 # Pull Requests
45 config.add_route(
46 name='pullrequest_show',
47 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id}',
48 repo_route=True)
49
44 # Settings
50 # Settings
45 config.add_route(
51 config.add_route(
46 name='edit_repo',
52 name='edit_repo',
@@ -561,22 +561,6 b' def make_map(config):'
561 action='show', conditions={'method': ['GET']},
561 action='show', conditions={'method': ['GET']},
562 requirements=URL_NAME_REQUIREMENTS)
562 requirements=URL_NAME_REQUIREMENTS)
563
563
564 # ADMIN MAIN PAGES
565 with rmap.submapper(path_prefix=ADMIN_PREFIX,
566 controller='admin/admin') as m:
567 m.connect('admin_home', '', action='index')
568 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
569 action='add_repo')
570 m.connect(
571 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
572 action='pull_requests')
573 m.connect(
574 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
575 action='pull_requests')
576 m.connect(
577 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
578 action='pull_requests')
579
580 # USER JOURNAL
564 # USER JOURNAL
581 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
565 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
582 controller='journal', action='index')
566 controller='journal', action='index')
@@ -34,11 +34,11 b' from webob.exc import HTTPBadRequest'
34 from pylons import request, tmpl_context as c, response, url
34 from pylons import request, tmpl_context as c, response, url
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36
36
37 from rhodecode.controllers.admin.admin import user_log_filter
38 from rhodecode.model.db import UserLog, UserFollowing, User, UserApiKeys
37 from rhodecode.model.db import UserLog, UserFollowing, User, UserApiKeys
39 from rhodecode.model.meta import Session
38 from rhodecode.model.meta import Session
40 import rhodecode.lib.helpers as h
39 import rhodecode.lib.helpers as h
41 from rhodecode.lib.helpers import Page
40 from rhodecode.lib.helpers import Page
41 from rhodecode.lib.user_log_filter import user_log_filter
42 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
42 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired
43 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.base import BaseController, render
44 from rhodecode.lib.utils2 import safe_int, AttributeDict
44 from rhodecode.lib.utils2 import safe_int, AttributeDict
@@ -69,6 +69,11 b' function registerRCRoutes() {'
69 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
69 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
70 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
70 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
71 pyroutes.register('ops_ping', '_admin/ops/ping', []);
71 pyroutes.register('ops_ping', '_admin/ops/ping', []);
72 pyroutes.register('admin_home', '/_admin', []);
73 pyroutes.register('admin_audit_logs', '_admin/audit_logs', []);
74 pyroutes.register('pull_requests_global_0', '_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
75 pyroutes.register('pull_requests_global_1', '_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
76 pyroutes.register('pull_requests_global', '_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
72 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
77 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
73 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
78 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
74 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
79 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
@@ -100,6 +105,7 b' function registerRCRoutes() {'
100 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
105 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
101 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
106 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
102 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
107 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
108 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
103 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
109 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
104 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
110 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
105 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
111 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
@@ -2,7 +2,7 b''
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Admin journal')}
5 ${_('Admin audit logs')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
@@ -10,9 +10,9 b''
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.form(None, id_="filter_form", method="get")}
12 ${h.form(None, id_="filter_form", method="get")}
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 or ''}" placeholder="${_('journal 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 or ''}" placeholder="${_('filter...')}"/>
14 <input type='submit' value="${_('filter')}" class="btn" />
14 <input type='submit' value="${_('filter')}" class="btn" />
15 ${_('Admin journal')} - ${ungettext('%s entry', '%s entries', c.audit_logs.item_count) % (c.audit_logs.item_count)}
15 ${_('Audit logs')} - ${_ungettext('%s entry', '%s entries', c.audit_logs.item_count) % (c.audit_logs.item_count)}
16 ${h.end_form()}
16 ${h.end_form()}
17 <p class="tooltip filterexample" title="${h.tooltip(h.journal_filter_help())}">${_('Example Queries')}</p>
17 <p class="tooltip filterexample" title="${h.tooltip(h.journal_filter_help())}">${_('Example Queries')}</p>
18 </%def>
18 </%def>
@@ -29,7 +29,7 b''
29 <!-- end box / title -->
29 <!-- end box / title -->
30 <div class="table">
30 <div class="table">
31 <div id="user_log">
31 <div id="user_log">
32 ${c.log_data}
32 <%include file="/admin/admin_log_base.mako" />
33 </div>
33 </div>
34 </div>
34 </div>
35 </div>
35 </div>
@@ -9,7 +9,7 b''
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Authentication Plugins')}
14 ${_('Authentication Plugins')}
15 </%def>
15 </%def>
@@ -9,7 +9,7 b''
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Authentication Plugins'),request.resource_path(resource.__parent__, route_name='auth_home'))}
14 ${h.link_to(_('Authentication Plugins'),request.resource_path(resource.__parent__, route_name='auth_home'))}
15 &raquo;
15 &raquo;
@@ -9,7 +9,7 b''
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Repositories defaults')}
14 ${_('Repositories defaults')}
15 </%def>
15 </%def>
@@ -18,7 +18,7 b''
18 </%def>
18 </%def>
19
19
20 <%def name="breadcrumbs_links()">
20 <%def name="breadcrumbs_links()">
21 ${h.link_to(_('Admin'),h.url('admin_home'))}
21 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
22 &raquo;
22 &raquo;
23 ${_('Integrations')}
23 ${_('Integrations')}
24 </%def>
24 </%def>
@@ -12,7 +12,7 b''
12 repo_name=c.repo.repo_name,
12 repo_name=c.repo.repo_name,
13 integration=current_IntegrationType.key))}
13 integration=current_IntegrationType.key))}
14 %elif c.repo_group:
14 %elif c.repo_group:
15 ${h.link_to(_('Admin'),h.url('admin_home'))}
15 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
17 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
18 &raquo;
18 &raquo;
@@ -25,7 +25,7 b''
25 repo_group_name=c.repo_group.group_name,
25 repo_group_name=c.repo_group.group_name,
26 integration=current_IntegrationType.key))}
26 integration=current_IntegrationType.key))}
27 %else:
27 %else:
28 ${h.link_to(_('Admin'),h.url('admin_home'))}
28 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
29 &raquo;
29 &raquo;
30 ${h.link_to(_('Settings'),h.url('admin_settings'))}
30 ${h.link_to(_('Settings'),h.url('admin_settings'))}
31 &raquo;
31 &raquo;
@@ -5,13 +5,13 b''
5 %if c.repo:
5 %if c.repo:
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
7 %elif c.repo_group:
7 %elif c.repo_group:
8 ${h.link_to(_('Admin'),h.url('admin_home'))}
8 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
10 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
12 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
13 %else:
13 %else:
14 ${h.link_to(_('Admin'),h.url('admin_home'))}
14 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
15 &raquo;
15 &raquo;
16 ${h.link_to(_('Settings'),h.url('admin_settings'))}
16 ${h.link_to(_('Settings'),h.url('admin_settings'))}
17 %endif
17 %endif
@@ -8,7 +8,7 b''
8 &raquo;
8 &raquo;
9 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
9 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
10 %elif c.repo_group:
10 %elif c.repo_group:
11 ${h.link_to(_('Admin'),h.url('admin_home'))}
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 &raquo;
12 &raquo;
13 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
13 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
14 &raquo;
14 &raquo;
@@ -16,7 +16,7 b''
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
18 %else:
18 %else:
19 ${h.link_to(_('Admin'),h.url('admin_home'))}
19 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
20 &raquo;
20 &raquo;
21 ${h.link_to(_('Settings'),h.url('admin_settings'))}
21 ${h.link_to(_('Settings'),h.url('admin_settings'))}
22 &raquo;
22 &raquo;
@@ -9,7 +9,7 b''
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Permissions')}
14 ${_('Permissions')}
15 </%def>
15 </%def>
@@ -9,7 +9,7 b''
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Repository groups'),h.url('repo_groups'))}
14 ${h.link_to(_('Repository groups'),h.url('repo_groups'))}
15 &raquo;
15 &raquo;
@@ -9,7 +9,7 b''
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
14 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
15 %if c.repo_group.parent_group:
15 %if c.repo_group.parent_group:
@@ -10,7 +10,7 b''
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="repo_group_count">0</span> ${_('repository groups')}
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="repo_group_count">0</span> ${_('repository groups')}
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
@@ -10,7 +10,7 b''
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 %if c.rhodecode_user.is_admin:
12 %if c.rhodecode_user.is_admin:
13 ${h.link_to(_('Admin'),h.url('admin_home'))}
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
14 &raquo;
14 &raquo;
15 ${h.link_to(_('Repositories'),h.url('repos'))}
15 ${h.link_to(_('Repositories'),h.url('repos'))}
16 %else:
16 %else:
@@ -10,7 +10,7 b''
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="repo_count">0</span> ${_('repositories')}
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="repo_count">0</span> ${_('repositories')}
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
@@ -9,7 +9,7 b''
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Settings')}
14 ${_('Settings')}
15 </%def>
15 </%def>
@@ -8,7 +8,7 b''
8 %endif
8 %endif
9 </%def>
9 </%def>
10 <%def name="breadcrumbs_links()">
10 <%def name="breadcrumbs_links()">
11 ${h.link_to(_('Admin'),h.url('admin_home'))}
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 &raquo;
12 &raquo;
13 ${h.link_to(_('User groups'),h.url('users_groups'))}
13 ${h.link_to(_('User groups'),h.url('users_groups'))}
14 &raquo;
14 &raquo;
@@ -9,7 +9,7 b''
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('User Groups'),h.url('users_groups'))}
14 ${h.link_to(_('User Groups'),h.url('users_groups'))}
15 &raquo;
15 &raquo;
@@ -10,7 +10,7 b''
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_group_count">0</span> ${_('user groups')}
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="user_group_count">0</span> ${_('user groups')}
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
@@ -8,7 +8,7 b''
8 %endif
8 %endif
9 </%def>
9 </%def>
10 <%def name="breadcrumbs_links()">
10 <%def name="breadcrumbs_links()">
11 ${h.link_to(_('Admin'),h.url('admin_home'))}
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 &raquo;
12 &raquo;
13 ${h.link_to(_('Users'),h.route_path('users'))}
13 ${h.link_to(_('Users'),h.route_path('users'))}
14 &raquo;
14 &raquo;
@@ -9,7 +9,7 b''
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.url('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Users'),h.route_path('users'))}
14 ${h.link_to(_('Users'),h.route_path('users'))}
15 &raquo;
15 &raquo;
@@ -10,7 +10,7 b''
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; <span id="user_count">0</span>
13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="user_count">0</span>
14 </%def>
14 </%def>
15
15
16 <%def name="menu_bar_nav()">
16 <%def name="menu_bar_nav()">
@@ -72,7 +72,7 b''
72
72
73 <%def name="admin_menu()">
73 <%def name="admin_menu()">
74 <ul class="admin_menu submenu">
74 <ul class="admin_menu submenu">
75 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
@@ -53,19 +53,6 b' class TestPullrequestsController:'
53 'pullrequest', repo_name=repo_name)
53 'pullrequest', repo_name=repo_name)
54 response.mustcontain(create_pr_link)
54 response.mustcontain(create_pr_link)
55
55
56 def test_global_redirect_of_pr(self, backend, pr_util):
57 pull_request = pr_util.create_pull_request()
58
59 response = self.app.get(
60 url('pull_requests_global',
61 pull_request_id=pull_request.pull_request_id))
62
63 repo_name = pull_request.target_repo.repo_name
64 redirect_url = url('pullrequest_show', repo_name=repo_name,
65 pull_request_id=pull_request.pull_request_id)
66 assert response.status == '302 Found'
67 assert redirect_url in response.location
68
69 def test_create_pr_form_with_raw_commit_id(self, backend):
56 def test_create_pr_form_with_raw_commit_id(self, backend):
70 repo = backend.repo
57 repo = backend.repo
71
58
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
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