##// END OF EJS Templates
audit-logs: expose tailoed audit logs in repository view
marcink -
r2156:ea1af41c default
parent child Browse files
Show More
@@ -0,0 +1,65 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2017-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 from pyramid.view import view_config
23
24 from rhodecode.apps._base import RepoAppView
25 from rhodecode.lib import helpers as h
26 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 from rhodecode.lib.utils2 import safe_int
28 from rhodecode.model.repo import RepoModel
29
30 log = logging.getLogger(__name__)
31
32
33 class AuditLogsView(RepoAppView):
34 def load_default_context(self):
35 c = self._get_local_tmpl_context()
36
37 self._register_global_c(c)
38 return c
39
40 @LoginRequired()
41 @HasRepoPermissionAnyDecorator('repository.admin')
42 @view_config(
43 route_name='edit_repo_audit_logs', request_method='GET',
44 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
45 def repo_audit_logs(self):
46 _ = self.request.translate
47 c = self.load_default_context()
48 c.db_repo = self.db_repo
49
50 c.active = 'audit'
51
52 p = safe_int(self.request.GET.get('page', 1), 1)
53
54 filter_term = self.request.GET.get('filter')
55 user_log = RepoModel().get_repo_log(c.db_repo, filter_term)
56
57 def url_generator(**kw):
58 if filter_term:
59 kw['filter'] = filter_term
60 return self.request.current_route_path(_query=kw)
61
62 c.audit_logs = h.Page(
63 user_log, page=p, items_per_page=10, url=url_generator)
64 c.filter_term = filter_term
65 return self._get_template_context(c)
@@ -0,0 +1,22 b''
1 ## -*- coding: utf-8 -*-
2 <%namespace name="base" file="/base/base.mako"/>
3
4
5 <div class="panel panel-default">
6 <div class="panel-heading">
7 <h3 class="panel-title">${_('Repository Audit Logs')} -
8 ${_ungettext('%s entry', '%s entries', c.audit_logs.item_count) % (c.audit_logs.item_count)}
9 </h3>
10 </div>
11 <div class="panel-body">
12
13 ${h.form(None, id_="filter_form", method="get")}
14 <input class="q_filter_box ${'' if c.filter_term else 'initial'}" id="j_filter" size="15" type="text" name="filter" value="${c.filter_term or ''}" placeholder="${_('audit filter...')}"/>
15 <input type='submit' value="${_('filter')}" class="btn" />
16 ${h.end_form()}
17 <p class="tooltip filterexample" style="position: inherit" title="${h.tooltip(h.journal_filter_help(c.pyramid_request))}">${_('Example Queries')}</p>
18
19 <%include file="/admin/admin_log_base.mako" />
20
21 </div>
22 </div>
@@ -1,450 +1,455 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 from rhodecode.apps._base import add_route_with_slash
20 from rhodecode.apps._base import add_route_with_slash
21
21
22
22
23 def includeme(config):
23 def includeme(config):
24
24
25 # repo creating checks, special cases that aren't repo routes
25 # repo creating checks, special cases that aren't repo routes
26 config.add_route(
26 config.add_route(
27 name='repo_creating',
27 name='repo_creating',
28 pattern='/{repo_name:.*?[^/]}/repo_creating')
28 pattern='/{repo_name:.*?[^/]}/repo_creating')
29
29
30 config.add_route(
30 config.add_route(
31 name='repo_creating_check',
31 name='repo_creating_check',
32 pattern='/{repo_name:.*?[^/]}/repo_creating_check')
32 pattern='/{repo_name:.*?[^/]}/repo_creating_check')
33
33
34 # Summary
34 # Summary
35 # NOTE(marcink): one additional route is defined in very bottom, catch
35 # NOTE(marcink): one additional route is defined in very bottom, catch
36 # all pattern
36 # all pattern
37 config.add_route(
37 config.add_route(
38 name='repo_summary_explicit',
38 name='repo_summary_explicit',
39 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
39 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
40 config.add_route(
40 config.add_route(
41 name='repo_summary_commits',
41 name='repo_summary_commits',
42 pattern='/{repo_name:.*?[^/]}/summary-commits', repo_route=True)
42 pattern='/{repo_name:.*?[^/]}/summary-commits', repo_route=True)
43
43
44 # Commits
44 # Commits
45 config.add_route(
45 config.add_route(
46 name='repo_commit',
46 name='repo_commit',
47 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}', repo_route=True)
47 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}', repo_route=True)
48
48
49 config.add_route(
49 config.add_route(
50 name='repo_commit_children',
50 name='repo_commit_children',
51 pattern='/{repo_name:.*?[^/]}/changeset_children/{commit_id}', repo_route=True)
51 pattern='/{repo_name:.*?[^/]}/changeset_children/{commit_id}', repo_route=True)
52
52
53 config.add_route(
53 config.add_route(
54 name='repo_commit_parents',
54 name='repo_commit_parents',
55 pattern='/{repo_name:.*?[^/]}/changeset_parents/{commit_id}', repo_route=True)
55 pattern='/{repo_name:.*?[^/]}/changeset_parents/{commit_id}', repo_route=True)
56
56
57 config.add_route(
57 config.add_route(
58 name='repo_commit_raw',
58 name='repo_commit_raw',
59 pattern='/{repo_name:.*?[^/]}/changeset-diff/{commit_id}', repo_route=True)
59 pattern='/{repo_name:.*?[^/]}/changeset-diff/{commit_id}', repo_route=True)
60
60
61 config.add_route(
61 config.add_route(
62 name='repo_commit_patch',
62 name='repo_commit_patch',
63 pattern='/{repo_name:.*?[^/]}/changeset-patch/{commit_id}', repo_route=True)
63 pattern='/{repo_name:.*?[^/]}/changeset-patch/{commit_id}', repo_route=True)
64
64
65 config.add_route(
65 config.add_route(
66 name='repo_commit_download',
66 name='repo_commit_download',
67 pattern='/{repo_name:.*?[^/]}/changeset-download/{commit_id}', repo_route=True)
67 pattern='/{repo_name:.*?[^/]}/changeset-download/{commit_id}', repo_route=True)
68
68
69 config.add_route(
69 config.add_route(
70 name='repo_commit_data',
70 name='repo_commit_data',
71 pattern='/{repo_name:.*?[^/]}/changeset-data/{commit_id}', repo_route=True)
71 pattern='/{repo_name:.*?[^/]}/changeset-data/{commit_id}', repo_route=True)
72
72
73 config.add_route(
73 config.add_route(
74 name='repo_commit_comment_create',
74 name='repo_commit_comment_create',
75 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/create', repo_route=True)
75 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/create', repo_route=True)
76
76
77 config.add_route(
77 config.add_route(
78 name='repo_commit_comment_preview',
78 name='repo_commit_comment_preview',
79 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/preview', repo_route=True)
79 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/preview', repo_route=True)
80
80
81 config.add_route(
81 config.add_route(
82 name='repo_commit_comment_delete',
82 name='repo_commit_comment_delete',
83 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/{comment_id}/delete', repo_route=True)
83 pattern='/{repo_name:.*?[^/]}/changeset/{commit_id}/comment/{comment_id}/delete', repo_route=True)
84
84
85 # still working url for backward compat.
85 # still working url for backward compat.
86 config.add_route(
86 config.add_route(
87 name='repo_commit_raw_deprecated',
87 name='repo_commit_raw_deprecated',
88 pattern='/{repo_name:.*?[^/]}/raw-changeset/{commit_id}', repo_route=True)
88 pattern='/{repo_name:.*?[^/]}/raw-changeset/{commit_id}', repo_route=True)
89
89
90 # Files
90 # Files
91 config.add_route(
91 config.add_route(
92 name='repo_archivefile',
92 name='repo_archivefile',
93 pattern='/{repo_name:.*?[^/]}/archive/{fname}', repo_route=True)
93 pattern='/{repo_name:.*?[^/]}/archive/{fname}', repo_route=True)
94
94
95 config.add_route(
95 config.add_route(
96 name='repo_files_diff',
96 name='repo_files_diff',
97 pattern='/{repo_name:.*?[^/]}/diff/{f_path:.*}', repo_route=True)
97 pattern='/{repo_name:.*?[^/]}/diff/{f_path:.*}', repo_route=True)
98 config.add_route( # legacy route to make old links work
98 config.add_route( # legacy route to make old links work
99 name='repo_files_diff_2way_redirect',
99 name='repo_files_diff_2way_redirect',
100 pattern='/{repo_name:.*?[^/]}/diff-2way/{f_path:.*}', repo_route=True)
100 pattern='/{repo_name:.*?[^/]}/diff-2way/{f_path:.*}', repo_route=True)
101
101
102 config.add_route(
102 config.add_route(
103 name='repo_files',
103 name='repo_files',
104 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/{f_path:.*}', repo_route=True)
104 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/{f_path:.*}', repo_route=True)
105 config.add_route(
105 config.add_route(
106 name='repo_files:default_path',
106 name='repo_files:default_path',
107 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/', repo_route=True)
107 pattern='/{repo_name:.*?[^/]}/files/{commit_id}/', repo_route=True)
108 config.add_route(
108 config.add_route(
109 name='repo_files:default_commit',
109 name='repo_files:default_commit',
110 pattern='/{repo_name:.*?[^/]}/files', repo_route=True)
110 pattern='/{repo_name:.*?[^/]}/files', repo_route=True)
111
111
112 config.add_route(
112 config.add_route(
113 name='repo_files:rendered',
113 name='repo_files:rendered',
114 pattern='/{repo_name:.*?[^/]}/render/{commit_id}/{f_path:.*}', repo_route=True)
114 pattern='/{repo_name:.*?[^/]}/render/{commit_id}/{f_path:.*}', repo_route=True)
115
115
116 config.add_route(
116 config.add_route(
117 name='repo_files:annotated',
117 name='repo_files:annotated',
118 pattern='/{repo_name:.*?[^/]}/annotate/{commit_id}/{f_path:.*}', repo_route=True)
118 pattern='/{repo_name:.*?[^/]}/annotate/{commit_id}/{f_path:.*}', repo_route=True)
119 config.add_route(
119 config.add_route(
120 name='repo_files:annotated_previous',
120 name='repo_files:annotated_previous',
121 pattern='/{repo_name:.*?[^/]}/annotate-previous/{commit_id}/{f_path:.*}', repo_route=True)
121 pattern='/{repo_name:.*?[^/]}/annotate-previous/{commit_id}/{f_path:.*}', repo_route=True)
122
122
123 config.add_route(
123 config.add_route(
124 name='repo_nodetree_full',
124 name='repo_nodetree_full',
125 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/{f_path:.*}', repo_route=True)
125 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/{f_path:.*}', repo_route=True)
126 config.add_route(
126 config.add_route(
127 name='repo_nodetree_full:default_path',
127 name='repo_nodetree_full:default_path',
128 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/', repo_route=True)
128 pattern='/{repo_name:.*?[^/]}/nodetree_full/{commit_id}/', repo_route=True)
129
129
130 config.add_route(
130 config.add_route(
131 name='repo_files_nodelist',
131 name='repo_files_nodelist',
132 pattern='/{repo_name:.*?[^/]}/nodelist/{commit_id}/{f_path:.*}', repo_route=True)
132 pattern='/{repo_name:.*?[^/]}/nodelist/{commit_id}/{f_path:.*}', repo_route=True)
133
133
134 config.add_route(
134 config.add_route(
135 name='repo_file_raw',
135 name='repo_file_raw',
136 pattern='/{repo_name:.*?[^/]}/raw/{commit_id}/{f_path:.*}', repo_route=True)
136 pattern='/{repo_name:.*?[^/]}/raw/{commit_id}/{f_path:.*}', repo_route=True)
137
137
138 config.add_route(
138 config.add_route(
139 name='repo_file_download',
139 name='repo_file_download',
140 pattern='/{repo_name:.*?[^/]}/download/{commit_id}/{f_path:.*}', repo_route=True)
140 pattern='/{repo_name:.*?[^/]}/download/{commit_id}/{f_path:.*}', repo_route=True)
141 config.add_route( # backward compat to keep old links working
141 config.add_route( # backward compat to keep old links working
142 name='repo_file_download:legacy',
142 name='repo_file_download:legacy',
143 pattern='/{repo_name:.*?[^/]}/rawfile/{commit_id}/{f_path:.*}',
143 pattern='/{repo_name:.*?[^/]}/rawfile/{commit_id}/{f_path:.*}',
144 repo_route=True)
144 repo_route=True)
145
145
146 config.add_route(
146 config.add_route(
147 name='repo_file_history',
147 name='repo_file_history',
148 pattern='/{repo_name:.*?[^/]}/history/{commit_id}/{f_path:.*}', repo_route=True)
148 pattern='/{repo_name:.*?[^/]}/history/{commit_id}/{f_path:.*}', repo_route=True)
149
149
150 config.add_route(
150 config.add_route(
151 name='repo_file_authors',
151 name='repo_file_authors',
152 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
152 pattern='/{repo_name:.*?[^/]}/authors/{commit_id}/{f_path:.*}', repo_route=True)
153
153
154 config.add_route(
154 config.add_route(
155 name='repo_files_remove_file',
155 name='repo_files_remove_file',
156 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
156 pattern='/{repo_name:.*?[^/]}/remove_file/{commit_id}/{f_path:.*}',
157 repo_route=True)
157 repo_route=True)
158 config.add_route(
158 config.add_route(
159 name='repo_files_delete_file',
159 name='repo_files_delete_file',
160 pattern='/{repo_name:.*?[^/]}/delete_file/{commit_id}/{f_path:.*}',
160 pattern='/{repo_name:.*?[^/]}/delete_file/{commit_id}/{f_path:.*}',
161 repo_route=True)
161 repo_route=True)
162 config.add_route(
162 config.add_route(
163 name='repo_files_edit_file',
163 name='repo_files_edit_file',
164 pattern='/{repo_name:.*?[^/]}/edit_file/{commit_id}/{f_path:.*}',
164 pattern='/{repo_name:.*?[^/]}/edit_file/{commit_id}/{f_path:.*}',
165 repo_route=True)
165 repo_route=True)
166 config.add_route(
166 config.add_route(
167 name='repo_files_update_file',
167 name='repo_files_update_file',
168 pattern='/{repo_name:.*?[^/]}/update_file/{commit_id}/{f_path:.*}',
168 pattern='/{repo_name:.*?[^/]}/update_file/{commit_id}/{f_path:.*}',
169 repo_route=True)
169 repo_route=True)
170 config.add_route(
170 config.add_route(
171 name='repo_files_add_file',
171 name='repo_files_add_file',
172 pattern='/{repo_name:.*?[^/]}/add_file/{commit_id}/{f_path:.*}',
172 pattern='/{repo_name:.*?[^/]}/add_file/{commit_id}/{f_path:.*}',
173 repo_route=True)
173 repo_route=True)
174 config.add_route(
174 config.add_route(
175 name='repo_files_create_file',
175 name='repo_files_create_file',
176 pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}',
176 pattern='/{repo_name:.*?[^/]}/create_file/{commit_id}/{f_path:.*}',
177 repo_route=True)
177 repo_route=True)
178
178
179 # Refs data
179 # Refs data
180 config.add_route(
180 config.add_route(
181 name='repo_refs_data',
181 name='repo_refs_data',
182 pattern='/{repo_name:.*?[^/]}/refs-data', repo_route=True)
182 pattern='/{repo_name:.*?[^/]}/refs-data', repo_route=True)
183
183
184 config.add_route(
184 config.add_route(
185 name='repo_refs_changelog_data',
185 name='repo_refs_changelog_data',
186 pattern='/{repo_name:.*?[^/]}/refs-data-changelog', repo_route=True)
186 pattern='/{repo_name:.*?[^/]}/refs-data-changelog', repo_route=True)
187
187
188 config.add_route(
188 config.add_route(
189 name='repo_stats',
189 name='repo_stats',
190 pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True)
190 pattern='/{repo_name:.*?[^/]}/repo_stats/{commit_id}', repo_route=True)
191
191
192 # Changelog
192 # Changelog
193 config.add_route(
193 config.add_route(
194 name='repo_changelog',
194 name='repo_changelog',
195 pattern='/{repo_name:.*?[^/]}/changelog', repo_route=True)
195 pattern='/{repo_name:.*?[^/]}/changelog', repo_route=True)
196 config.add_route(
196 config.add_route(
197 name='repo_changelog_file',
197 name='repo_changelog_file',
198 pattern='/{repo_name:.*?[^/]}/changelog/{commit_id}/{f_path:.*}', repo_route=True)
198 pattern='/{repo_name:.*?[^/]}/changelog/{commit_id}/{f_path:.*}', repo_route=True)
199 config.add_route(
199 config.add_route(
200 name='repo_changelog_elements',
200 name='repo_changelog_elements',
201 pattern='/{repo_name:.*?[^/]}/changelog_elements', repo_route=True)
201 pattern='/{repo_name:.*?[^/]}/changelog_elements', repo_route=True)
202 config.add_route(
202 config.add_route(
203 name='repo_changelog_elements_file',
203 name='repo_changelog_elements_file',
204 pattern='/{repo_name:.*?[^/]}/changelog_elements/{commit_id}/{f_path:.*}', repo_route=True)
204 pattern='/{repo_name:.*?[^/]}/changelog_elements/{commit_id}/{f_path:.*}', repo_route=True)
205
205
206 # Compare
206 # Compare
207 config.add_route(
207 config.add_route(
208 name='repo_compare_select',
208 name='repo_compare_select',
209 pattern='/{repo_name:.*?[^/]}/compare', repo_route=True)
209 pattern='/{repo_name:.*?[^/]}/compare', repo_route=True)
210
210
211 config.add_route(
211 config.add_route(
212 name='repo_compare',
212 name='repo_compare',
213 pattern='/{repo_name:.*?[^/]}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}', repo_route=True)
213 pattern='/{repo_name:.*?[^/]}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}', repo_route=True)
214
214
215 # Tags
215 # Tags
216 config.add_route(
216 config.add_route(
217 name='tags_home',
217 name='tags_home',
218 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
218 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
219
219
220 # Branches
220 # Branches
221 config.add_route(
221 config.add_route(
222 name='branches_home',
222 name='branches_home',
223 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
223 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
224
224
225 # Bookmarks
225 # Bookmarks
226 config.add_route(
226 config.add_route(
227 name='bookmarks_home',
227 name='bookmarks_home',
228 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
228 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
229
229
230 # Forks
230 # Forks
231 config.add_route(
231 config.add_route(
232 name='repo_fork_new',
232 name='repo_fork_new',
233 pattern='/{repo_name:.*?[^/]}/fork', repo_route=True,
233 pattern='/{repo_name:.*?[^/]}/fork', repo_route=True,
234 repo_accepted_types=['hg', 'git'])
234 repo_accepted_types=['hg', 'git'])
235
235
236 config.add_route(
236 config.add_route(
237 name='repo_fork_create',
237 name='repo_fork_create',
238 pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True,
238 pattern='/{repo_name:.*?[^/]}/fork/create', repo_route=True,
239 repo_accepted_types=['hg', 'git'])
239 repo_accepted_types=['hg', 'git'])
240
240
241 config.add_route(
241 config.add_route(
242 name='repo_forks_show_all',
242 name='repo_forks_show_all',
243 pattern='/{repo_name:.*?[^/]}/forks', repo_route=True,
243 pattern='/{repo_name:.*?[^/]}/forks', repo_route=True,
244 repo_accepted_types=['hg', 'git'])
244 repo_accepted_types=['hg', 'git'])
245 config.add_route(
245 config.add_route(
246 name='repo_forks_data',
246 name='repo_forks_data',
247 pattern='/{repo_name:.*?[^/]}/forks/data', repo_route=True,
247 pattern='/{repo_name:.*?[^/]}/forks/data', repo_route=True,
248 repo_accepted_types=['hg', 'git'])
248 repo_accepted_types=['hg', 'git'])
249
249
250 # Pull Requests
250 # Pull Requests
251 config.add_route(
251 config.add_route(
252 name='pullrequest_show',
252 name='pullrequest_show',
253 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}',
253 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}',
254 repo_route=True)
254 repo_route=True)
255
255
256 config.add_route(
256 config.add_route(
257 name='pullrequest_show_all',
257 name='pullrequest_show_all',
258 pattern='/{repo_name:.*?[^/]}/pull-request',
258 pattern='/{repo_name:.*?[^/]}/pull-request',
259 repo_route=True, repo_accepted_types=['hg', 'git'])
259 repo_route=True, repo_accepted_types=['hg', 'git'])
260
260
261 config.add_route(
261 config.add_route(
262 name='pullrequest_show_all_data',
262 name='pullrequest_show_all_data',
263 pattern='/{repo_name:.*?[^/]}/pull-request-data',
263 pattern='/{repo_name:.*?[^/]}/pull-request-data',
264 repo_route=True, repo_accepted_types=['hg', 'git'])
264 repo_route=True, repo_accepted_types=['hg', 'git'])
265
265
266 config.add_route(
266 config.add_route(
267 name='pullrequest_repo_refs',
267 name='pullrequest_repo_refs',
268 pattern='/{repo_name:.*?[^/]}/pull-request/refs/{target_repo_name:.*?[^/]}',
268 pattern='/{repo_name:.*?[^/]}/pull-request/refs/{target_repo_name:.*?[^/]}',
269 repo_route=True)
269 repo_route=True)
270
270
271 config.add_route(
271 config.add_route(
272 name='pullrequest_repo_destinations',
272 name='pullrequest_repo_destinations',
273 pattern='/{repo_name:.*?[^/]}/pull-request/repo-destinations',
273 pattern='/{repo_name:.*?[^/]}/pull-request/repo-destinations',
274 repo_route=True)
274 repo_route=True)
275
275
276 config.add_route(
276 config.add_route(
277 name='pullrequest_new',
277 name='pullrequest_new',
278 pattern='/{repo_name:.*?[^/]}/pull-request/new',
278 pattern='/{repo_name:.*?[^/]}/pull-request/new',
279 repo_route=True, repo_accepted_types=['hg', 'git'])
279 repo_route=True, repo_accepted_types=['hg', 'git'])
280
280
281 config.add_route(
281 config.add_route(
282 name='pullrequest_create',
282 name='pullrequest_create',
283 pattern='/{repo_name:.*?[^/]}/pull-request/create',
283 pattern='/{repo_name:.*?[^/]}/pull-request/create',
284 repo_route=True, repo_accepted_types=['hg', 'git'])
284 repo_route=True, repo_accepted_types=['hg', 'git'])
285
285
286 config.add_route(
286 config.add_route(
287 name='pullrequest_update',
287 name='pullrequest_update',
288 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update',
288 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/update',
289 repo_route=True)
289 repo_route=True)
290
290
291 config.add_route(
291 config.add_route(
292 name='pullrequest_merge',
292 name='pullrequest_merge',
293 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge',
293 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/merge',
294 repo_route=True)
294 repo_route=True)
295
295
296 config.add_route(
296 config.add_route(
297 name='pullrequest_delete',
297 name='pullrequest_delete',
298 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete',
298 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/delete',
299 repo_route=True)
299 repo_route=True)
300
300
301 config.add_route(
301 config.add_route(
302 name='pullrequest_comment_create',
302 name='pullrequest_comment_create',
303 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment',
303 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment',
304 repo_route=True)
304 repo_route=True)
305
305
306 config.add_route(
306 config.add_route(
307 name='pullrequest_comment_delete',
307 name='pullrequest_comment_delete',
308 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment/{comment_id}/delete',
308 pattern='/{repo_name:.*?[^/]}/pull-request/{pull_request_id:\d+}/comment/{comment_id}/delete',
309 repo_route=True, repo_accepted_types=['hg', 'git'])
309 repo_route=True, repo_accepted_types=['hg', 'git'])
310
310
311 # Settings
311 # Settings
312 config.add_route(
312 config.add_route(
313 name='edit_repo',
313 name='edit_repo',
314 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
314 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
315
315
316 # Settings advanced
316 # Settings advanced
317 config.add_route(
317 config.add_route(
318 name='edit_repo_advanced',
318 name='edit_repo_advanced',
319 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
319 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
320 config.add_route(
320 config.add_route(
321 name='edit_repo_advanced_delete',
321 name='edit_repo_advanced_delete',
322 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
322 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
323 config.add_route(
323 config.add_route(
324 name='edit_repo_advanced_locking',
324 name='edit_repo_advanced_locking',
325 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
325 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
326 config.add_route(
326 config.add_route(
327 name='edit_repo_advanced_journal',
327 name='edit_repo_advanced_journal',
328 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
328 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
329 config.add_route(
329 config.add_route(
330 name='edit_repo_advanced_fork',
330 name='edit_repo_advanced_fork',
331 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
331 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
332
332
333 # Caches
333 # Caches
334 config.add_route(
334 config.add_route(
335 name='edit_repo_caches',
335 name='edit_repo_caches',
336 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
336 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
337
337
338 # Permissions
338 # Permissions
339 config.add_route(
339 config.add_route(
340 name='edit_repo_perms',
340 name='edit_repo_perms',
341 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
341 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
342
342
343 # Maintenance
343 # Maintenance
344 config.add_route(
344 config.add_route(
345 name='edit_repo_maintenance',
345 name='edit_repo_maintenance',
346 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
346 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
347
347
348 config.add_route(
348 config.add_route(
349 name='edit_repo_maintenance_execute',
349 name='edit_repo_maintenance_execute',
350 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
350 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
351
351
352 # Fields
352 # Fields
353 config.add_route(
353 config.add_route(
354 name='edit_repo_fields',
354 name='edit_repo_fields',
355 pattern='/{repo_name:.*?[^/]}/settings/fields', repo_route=True)
355 pattern='/{repo_name:.*?[^/]}/settings/fields', repo_route=True)
356 config.add_route(
356 config.add_route(
357 name='edit_repo_fields_create',
357 name='edit_repo_fields_create',
358 pattern='/{repo_name:.*?[^/]}/settings/fields/create', repo_route=True)
358 pattern='/{repo_name:.*?[^/]}/settings/fields/create', repo_route=True)
359 config.add_route(
359 config.add_route(
360 name='edit_repo_fields_delete',
360 name='edit_repo_fields_delete',
361 pattern='/{repo_name:.*?[^/]}/settings/fields/{field_id}/delete', repo_route=True)
361 pattern='/{repo_name:.*?[^/]}/settings/fields/{field_id}/delete', repo_route=True)
362
362
363 # Locking
363 # Locking
364 config.add_route(
364 config.add_route(
365 name='repo_edit_toggle_locking',
365 name='repo_edit_toggle_locking',
366 pattern='/{repo_name:.*?[^/]}/settings/toggle_locking', repo_route=True)
366 pattern='/{repo_name:.*?[^/]}/settings/toggle_locking', repo_route=True)
367
367
368 # Remote
368 # Remote
369 config.add_route(
369 config.add_route(
370 name='edit_repo_remote',
370 name='edit_repo_remote',
371 pattern='/{repo_name:.*?[^/]}/settings/remote', repo_route=True)
371 pattern='/{repo_name:.*?[^/]}/settings/remote', repo_route=True)
372 config.add_route(
372 config.add_route(
373 name='edit_repo_remote_pull',
373 name='edit_repo_remote_pull',
374 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
374 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
375
375
376
376
377 # Statistics
377 # Statistics
378 config.add_route(
378 config.add_route(
379 name='edit_repo_statistics',
379 name='edit_repo_statistics',
380 pattern='/{repo_name:.*?[^/]}/settings/statistics', repo_route=True)
380 pattern='/{repo_name:.*?[^/]}/settings/statistics', repo_route=True)
381 config.add_route(
381 config.add_route(
382 name='edit_repo_statistics_reset',
382 name='edit_repo_statistics_reset',
383 pattern='/{repo_name:.*?[^/]}/settings/statistics/update', repo_route=True)
383 pattern='/{repo_name:.*?[^/]}/settings/statistics/update', repo_route=True)
384
384
385 # Issue trackers
385 # Issue trackers
386 config.add_route(
386 config.add_route(
387 name='edit_repo_issuetracker',
387 name='edit_repo_issuetracker',
388 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers', repo_route=True)
388 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers', repo_route=True)
389 config.add_route(
389 config.add_route(
390 name='edit_repo_issuetracker_test',
390 name='edit_repo_issuetracker_test',
391 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/test', repo_route=True)
391 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/test', repo_route=True)
392 config.add_route(
392 config.add_route(
393 name='edit_repo_issuetracker_delete',
393 name='edit_repo_issuetracker_delete',
394 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/delete', repo_route=True)
394 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/delete', repo_route=True)
395 config.add_route(
395 config.add_route(
396 name='edit_repo_issuetracker_update',
396 name='edit_repo_issuetracker_update',
397 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/update', repo_route=True)
397 pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/update', repo_route=True)
398
398
399 # VCS Settings
399 # VCS Settings
400 config.add_route(
400 config.add_route(
401 name='edit_repo_vcs',
401 name='edit_repo_vcs',
402 pattern='/{repo_name:.*?[^/]}/settings/vcs', repo_route=True)
402 pattern='/{repo_name:.*?[^/]}/settings/vcs', repo_route=True)
403 config.add_route(
403 config.add_route(
404 name='edit_repo_vcs_update',
404 name='edit_repo_vcs_update',
405 pattern='/{repo_name:.*?[^/]}/settings/vcs/update', repo_route=True)
405 pattern='/{repo_name:.*?[^/]}/settings/vcs/update', repo_route=True)
406
406
407 # svn pattern
407 # svn pattern
408 config.add_route(
408 config.add_route(
409 name='edit_repo_vcs_svn_pattern_delete',
409 name='edit_repo_vcs_svn_pattern_delete',
410 pattern='/{repo_name:.*?[^/]}/settings/vcs/svn_pattern/delete', repo_route=True)
410 pattern='/{repo_name:.*?[^/]}/settings/vcs/svn_pattern/delete', repo_route=True)
411
411
412 # Repo Review Rules (EE feature)
412 # Repo Review Rules (EE feature)
413 config.add_route(
413 config.add_route(
414 name='repo_reviewers',
414 name='repo_reviewers',
415 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
415 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
416
416
417 config.add_route(
417 config.add_route(
418 name='repo_default_reviewers_data',
418 name='repo_default_reviewers_data',
419 pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True)
419 pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True)
420
420
421 # Strip
421 # Strip
422 config.add_route(
422 config.add_route(
423 name='edit_repo_strip',
423 name='edit_repo_strip',
424 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
424 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
425
425
426 config.add_route(
426 config.add_route(
427 name='strip_check',
427 name='strip_check',
428 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
428 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
429
429
430 config.add_route(
430 config.add_route(
431 name='strip_execute',
431 name='strip_execute',
432 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
432 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
433
433
434 # Audit logs
435 config.add_route(
436 name='edit_repo_audit_logs',
437 pattern='/{repo_name:.*?[^/]}/settings/audit_logs', repo_route=True)
438
434 # ATOM/RSS Feed
439 # ATOM/RSS Feed
435 config.add_route(
440 config.add_route(
436 name='rss_feed_home',
441 name='rss_feed_home',
437 pattern='/{repo_name:.*?[^/]}/feed/rss', repo_route=True)
442 pattern='/{repo_name:.*?[^/]}/feed/rss', repo_route=True)
438
443
439 config.add_route(
444 config.add_route(
440 name='atom_feed_home',
445 name='atom_feed_home',
441 pattern='/{repo_name:.*?[^/]}/feed/atom', repo_route=True)
446 pattern='/{repo_name:.*?[^/]}/feed/atom', repo_route=True)
442
447
443 # NOTE(marcink): needs to be at the end for catch-all
448 # NOTE(marcink): needs to be at the end for catch-all
444 add_route_with_slash(
449 add_route_with_slash(
445 config,
450 config,
446 name='repo_summary',
451 name='repo_summary',
447 pattern='/{repo_name:.*?[^/]}', repo_route=True)
452 pattern='/{repo_name:.*?[^/]}', repo_route=True)
448
453
449 # Scan module for configuration decorators.
454 # Scan module for configuration decorators.
450 config.scan('.views', ignore='.tests')
455 config.scan('.views', ignore='.tests')
@@ -1,1020 +1,1032 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
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 logging
22 import os
21 import os
23 import re
22 import re
24 import shutil
23 import shutil
25 import time
24 import time
25 import logging
26 import traceback
26 import traceback
27 import datetime
27 import datetime
28
28
29 from pyramid.threadlocal import get_current_request
29 from pyramid.threadlocal import get_current_request
30 from zope.cachedescriptors.property import Lazy as LazyProperty
30 from zope.cachedescriptors.property import Lazy as LazyProperty
31
31
32 from rhodecode import events
32 from rhodecode import events
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib.auth import HasUserGroupPermissionAny
33 from rhodecode.lib.auth import HasUserGroupPermissionAny
35 from rhodecode.lib.caching_query import FromCache
34 from rhodecode.lib.caching_query import FromCache
36 from rhodecode.lib.exceptions import AttachedForksError
35 from rhodecode.lib.exceptions import AttachedForksError
37 from rhodecode.lib.hooks_base import log_delete_repository
36 from rhodecode.lib.hooks_base import log_delete_repository
37 from rhodecode.lib.user_log_filter import user_log_filter
38 from rhodecode.lib.utils import make_db_config
38 from rhodecode.lib.utils import make_db_config
39 from rhodecode.lib.utils2 import (
39 from rhodecode.lib.utils2 import (
40 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
40 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
41 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
41 get_current_rhodecode_user, safe_int, datetime_to_time,
42 action_logger_generic)
42 from rhodecode.lib.vcs.backends import get_backend
43 from rhodecode.lib.vcs.backends import get_backend
43 from rhodecode.model import BaseModel
44 from rhodecode.model import BaseModel
44 from rhodecode.model.db import (_hash_key,
45 from rhodecode.model.db import (
45 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
46 _hash_key, joinedload, or_, Repository, UserRepoToPerm, UserGroupRepoToPerm,
46 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
47 UserRepoGroupToPerm, UserGroupRepoGroupToPerm, User, Permission,
47 RepoGroup, RepositoryField)
48 Statistics, UserGroup, RepoGroup, RepositoryField, UserLog)
48
49
49 from rhodecode.model.settings import VcsSettingsModel
50 from rhodecode.model.settings import VcsSettingsModel
50
51
51
52
52 log = logging.getLogger(__name__)
53 log = logging.getLogger(__name__)
53
54
54
55
55 class RepoModel(BaseModel):
56 class RepoModel(BaseModel):
56
57
57 cls = Repository
58 cls = Repository
58
59
59 def _get_user_group(self, users_group):
60 def _get_user_group(self, users_group):
60 return self._get_instance(UserGroup, users_group,
61 return self._get_instance(UserGroup, users_group,
61 callback=UserGroup.get_by_group_name)
62 callback=UserGroup.get_by_group_name)
62
63
63 def _get_repo_group(self, repo_group):
64 def _get_repo_group(self, repo_group):
64 return self._get_instance(RepoGroup, repo_group,
65 return self._get_instance(RepoGroup, repo_group,
65 callback=RepoGroup.get_by_group_name)
66 callback=RepoGroup.get_by_group_name)
66
67
67 def _create_default_perms(self, repository, private):
68 def _create_default_perms(self, repository, private):
68 # create default permission
69 # create default permission
69 default = 'repository.read'
70 default = 'repository.read'
70 def_user = User.get_default_user()
71 def_user = User.get_default_user()
71 for p in def_user.user_perms:
72 for p in def_user.user_perms:
72 if p.permission.permission_name.startswith('repository.'):
73 if p.permission.permission_name.startswith('repository.'):
73 default = p.permission.permission_name
74 default = p.permission.permission_name
74 break
75 break
75
76
76 default_perm = 'repository.none' if private else default
77 default_perm = 'repository.none' if private else default
77
78
78 repo_to_perm = UserRepoToPerm()
79 repo_to_perm = UserRepoToPerm()
79 repo_to_perm.permission = Permission.get_by_key(default_perm)
80 repo_to_perm.permission = Permission.get_by_key(default_perm)
80
81
81 repo_to_perm.repository = repository
82 repo_to_perm.repository = repository
82 repo_to_perm.user_id = def_user.user_id
83 repo_to_perm.user_id = def_user.user_id
83
84
84 return repo_to_perm
85 return repo_to_perm
85
86
86 @LazyProperty
87 @LazyProperty
87 def repos_path(self):
88 def repos_path(self):
88 """
89 """
89 Gets the repositories root path from database
90 Gets the repositories root path from database
90 """
91 """
91 settings_model = VcsSettingsModel(sa=self.sa)
92 settings_model = VcsSettingsModel(sa=self.sa)
92 return settings_model.get_repos_location()
93 return settings_model.get_repos_location()
93
94
94 def get(self, repo_id, cache=False):
95 def get(self, repo_id, cache=False):
95 repo = self.sa.query(Repository) \
96 repo = self.sa.query(Repository) \
96 .filter(Repository.repo_id == repo_id)
97 .filter(Repository.repo_id == repo_id)
97
98
98 if cache:
99 if cache:
99 repo = repo.options(
100 repo = repo.options(
100 FromCache("sql_cache_short", "get_repo_%s" % repo_id))
101 FromCache("sql_cache_short", "get_repo_%s" % repo_id))
101 return repo.scalar()
102 return repo.scalar()
102
103
103 def get_repo(self, repository):
104 def get_repo(self, repository):
104 return self._get_repo(repository)
105 return self._get_repo(repository)
105
106
106 def get_by_repo_name(self, repo_name, cache=False):
107 def get_by_repo_name(self, repo_name, cache=False):
107 repo = self.sa.query(Repository) \
108 repo = self.sa.query(Repository) \
108 .filter(Repository.repo_name == repo_name)
109 .filter(Repository.repo_name == repo_name)
109
110
110 if cache:
111 if cache:
111 name_key = _hash_key(repo_name)
112 name_key = _hash_key(repo_name)
112 repo = repo.options(
113 repo = repo.options(
113 FromCache("sql_cache_short", "get_repo_%s" % name_key))
114 FromCache("sql_cache_short", "get_repo_%s" % name_key))
114 return repo.scalar()
115 return repo.scalar()
115
116
116 def _extract_id_from_repo_name(self, repo_name):
117 def _extract_id_from_repo_name(self, repo_name):
117 if repo_name.startswith('/'):
118 if repo_name.startswith('/'):
118 repo_name = repo_name.lstrip('/')
119 repo_name = repo_name.lstrip('/')
119 by_id_match = re.match(r'^_(\d{1,})', repo_name)
120 by_id_match = re.match(r'^_(\d{1,})', repo_name)
120 if by_id_match:
121 if by_id_match:
121 return by_id_match.groups()[0]
122 return by_id_match.groups()[0]
122
123
123 def get_repo_by_id(self, repo_name):
124 def get_repo_by_id(self, repo_name):
124 """
125 """
125 Extracts repo_name by id from special urls.
126 Extracts repo_name by id from special urls.
126 Example url is _11/repo_name
127 Example url is _11/repo_name
127
128
128 :param repo_name:
129 :param repo_name:
129 :return: repo object if matched else None
130 :return: repo object if matched else None
130 """
131 """
131
132
132 try:
133 try:
133 _repo_id = self._extract_id_from_repo_name(repo_name)
134 _repo_id = self._extract_id_from_repo_name(repo_name)
134 if _repo_id:
135 if _repo_id:
135 return self.get(_repo_id)
136 return self.get(_repo_id)
136 except Exception:
137 except Exception:
137 log.exception('Failed to extract repo_name from URL')
138 log.exception('Failed to extract repo_name from URL')
138
139
139 return None
140 return None
140
141
141 def get_repos_for_root(self, root, traverse=False):
142 def get_repos_for_root(self, root, traverse=False):
142 if traverse:
143 if traverse:
143 like_expression = u'{}%'.format(safe_unicode(root))
144 like_expression = u'{}%'.format(safe_unicode(root))
144 repos = Repository.query().filter(
145 repos = Repository.query().filter(
145 Repository.repo_name.like(like_expression)).all()
146 Repository.repo_name.like(like_expression)).all()
146 else:
147 else:
147 if root and not isinstance(root, RepoGroup):
148 if root and not isinstance(root, RepoGroup):
148 raise ValueError(
149 raise ValueError(
149 'Root must be an instance '
150 'Root must be an instance '
150 'of RepoGroup, got:{} instead'.format(type(root)))
151 'of RepoGroup, got:{} instead'.format(type(root)))
151 repos = Repository.query().filter(Repository.group == root).all()
152 repos = Repository.query().filter(Repository.group == root).all()
152 return repos
153 return repos
153
154
154 def get_url(self, repo, request=None, permalink=False):
155 def get_url(self, repo, request=None, permalink=False):
155 if not request:
156 if not request:
156 request = get_current_request()
157 request = get_current_request()
157
158
158 if not request:
159 if not request:
159 return
160 return
160
161
161 if permalink:
162 if permalink:
162 return request.route_url(
163 return request.route_url(
163 'repo_summary', repo_name=safe_str(repo.repo_id))
164 'repo_summary', repo_name=safe_str(repo.repo_id))
164 else:
165 else:
165 return request.route_url(
166 return request.route_url(
166 'repo_summary', repo_name=safe_str(repo.repo_name))
167 'repo_summary', repo_name=safe_str(repo.repo_name))
167
168
168 def get_commit_url(self, repo, commit_id, request=None, permalink=False):
169 def get_commit_url(self, repo, commit_id, request=None, permalink=False):
169 if not request:
170 if not request:
170 request = get_current_request()
171 request = get_current_request()
171
172
172 if not request:
173 if not request:
173 return
174 return
174
175
175 if permalink:
176 if permalink:
176 return request.route_url(
177 return request.route_url(
177 'repo_commit', repo_name=safe_str(repo.repo_id),
178 'repo_commit', repo_name=safe_str(repo.repo_id),
178 commit_id=commit_id)
179 commit_id=commit_id)
179
180
180 else:
181 else:
181 return request.route_url(
182 return request.route_url(
182 'repo_commit', repo_name=safe_str(repo.repo_name),
183 'repo_commit', repo_name=safe_str(repo.repo_name),
183 commit_id=commit_id)
184 commit_id=commit_id)
184
185
186 def get_repo_log(self, repo, filter_term):
187 repo_log = UserLog.query()\
188 .filter(or_(UserLog.repository_id == repo.repo_id,
189 UserLog.repository_name == repo.repo_name))\
190 .options(joinedload(UserLog.user))\
191 .options(joinedload(UserLog.repository))\
192 .order_by(UserLog.action_date.desc())
193
194 repo_log = user_log_filter(repo_log, filter_term)
195 return repo_log
196
185 @classmethod
197 @classmethod
186 def update_repoinfo(cls, repositories=None):
198 def update_repoinfo(cls, repositories=None):
187 if not repositories:
199 if not repositories:
188 repositories = Repository.getAll()
200 repositories = Repository.getAll()
189 for repo in repositories:
201 for repo in repositories:
190 repo.update_commit_cache()
202 repo.update_commit_cache()
191
203
192 def get_repos_as_dict(self, repo_list=None, admin=False,
204 def get_repos_as_dict(self, repo_list=None, admin=False,
193 super_user_actions=False):
205 super_user_actions=False):
194 _render = get_current_request().get_partial_renderer(
206 _render = get_current_request().get_partial_renderer(
195 'data_table/_dt_elements.mako')
207 'data_table/_dt_elements.mako')
196 c = _render.get_call_context()
208 c = _render.get_call_context()
197
209
198 def quick_menu(repo_name):
210 def quick_menu(repo_name):
199 return _render('quick_menu', repo_name)
211 return _render('quick_menu', repo_name)
200
212
201 def repo_lnk(name, rtype, rstate, private, fork_of):
213 def repo_lnk(name, rtype, rstate, private, fork_of):
202 return _render('repo_name', name, rtype, rstate, private, fork_of,
214 return _render('repo_name', name, rtype, rstate, private, fork_of,
203 short_name=not admin, admin=False)
215 short_name=not admin, admin=False)
204
216
205 def last_change(last_change):
217 def last_change(last_change):
206 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
218 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
207 last_change = last_change + datetime.timedelta(seconds=
219 last_change = last_change + datetime.timedelta(seconds=
208 (datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
220 (datetime.datetime.now() - datetime.datetime.utcnow()).seconds)
209 return _render("last_change", last_change)
221 return _render("last_change", last_change)
210
222
211 def rss_lnk(repo_name):
223 def rss_lnk(repo_name):
212 return _render("rss", repo_name)
224 return _render("rss", repo_name)
213
225
214 def atom_lnk(repo_name):
226 def atom_lnk(repo_name):
215 return _render("atom", repo_name)
227 return _render("atom", repo_name)
216
228
217 def last_rev(repo_name, cs_cache):
229 def last_rev(repo_name, cs_cache):
218 return _render('revision', repo_name, cs_cache.get('revision'),
230 return _render('revision', repo_name, cs_cache.get('revision'),
219 cs_cache.get('raw_id'), cs_cache.get('author'),
231 cs_cache.get('raw_id'), cs_cache.get('author'),
220 cs_cache.get('message'))
232 cs_cache.get('message'))
221
233
222 def desc(desc):
234 def desc(desc):
223 return _render('repo_desc', desc, c.visual.stylify_metatags)
235 return _render('repo_desc', desc, c.visual.stylify_metatags)
224
236
225 def state(repo_state):
237 def state(repo_state):
226 return _render("repo_state", repo_state)
238 return _render("repo_state", repo_state)
227
239
228 def repo_actions(repo_name):
240 def repo_actions(repo_name):
229 return _render('repo_actions', repo_name, super_user_actions)
241 return _render('repo_actions', repo_name, super_user_actions)
230
242
231 def user_profile(username):
243 def user_profile(username):
232 return _render('user_profile', username)
244 return _render('user_profile', username)
233
245
234 repos_data = []
246 repos_data = []
235 for repo in repo_list:
247 for repo in repo_list:
236 cs_cache = repo.changeset_cache
248 cs_cache = repo.changeset_cache
237 row = {
249 row = {
238 "menu": quick_menu(repo.repo_name),
250 "menu": quick_menu(repo.repo_name),
239
251
240 "name": repo_lnk(repo.repo_name, repo.repo_type,
252 "name": repo_lnk(repo.repo_name, repo.repo_type,
241 repo.repo_state, repo.private, repo.fork),
253 repo.repo_state, repo.private, repo.fork),
242 "name_raw": repo.repo_name.lower(),
254 "name_raw": repo.repo_name.lower(),
243
255
244 "last_change": last_change(repo.last_db_change),
256 "last_change": last_change(repo.last_db_change),
245 "last_change_raw": datetime_to_time(repo.last_db_change),
257 "last_change_raw": datetime_to_time(repo.last_db_change),
246
258
247 "last_changeset": last_rev(repo.repo_name, cs_cache),
259 "last_changeset": last_rev(repo.repo_name, cs_cache),
248 "last_changeset_raw": cs_cache.get('revision'),
260 "last_changeset_raw": cs_cache.get('revision'),
249
261
250 "desc": desc(repo.description_safe),
262 "desc": desc(repo.description_safe),
251 "owner": user_profile(repo.user.username),
263 "owner": user_profile(repo.user.username),
252
264
253 "state": state(repo.repo_state),
265 "state": state(repo.repo_state),
254 "rss": rss_lnk(repo.repo_name),
266 "rss": rss_lnk(repo.repo_name),
255
267
256 "atom": atom_lnk(repo.repo_name),
268 "atom": atom_lnk(repo.repo_name),
257 }
269 }
258 if admin:
270 if admin:
259 row.update({
271 row.update({
260 "action": repo_actions(repo.repo_name),
272 "action": repo_actions(repo.repo_name),
261 })
273 })
262 repos_data.append(row)
274 repos_data.append(row)
263
275
264 return repos_data
276 return repos_data
265
277
266 def _get_defaults(self, repo_name):
278 def _get_defaults(self, repo_name):
267 """
279 """
268 Gets information about repository, and returns a dict for
280 Gets information about repository, and returns a dict for
269 usage in forms
281 usage in forms
270
282
271 :param repo_name:
283 :param repo_name:
272 """
284 """
273
285
274 repo_info = Repository.get_by_repo_name(repo_name)
286 repo_info = Repository.get_by_repo_name(repo_name)
275
287
276 if repo_info is None:
288 if repo_info is None:
277 return None
289 return None
278
290
279 defaults = repo_info.get_dict()
291 defaults = repo_info.get_dict()
280 defaults['repo_name'] = repo_info.just_name
292 defaults['repo_name'] = repo_info.just_name
281
293
282 groups = repo_info.groups_with_parents
294 groups = repo_info.groups_with_parents
283 parent_group = groups[-1] if groups else None
295 parent_group = groups[-1] if groups else None
284
296
285 # we use -1 as this is how in HTML, we mark an empty group
297 # we use -1 as this is how in HTML, we mark an empty group
286 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
298 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
287
299
288 keys_to_process = (
300 keys_to_process = (
289 {'k': 'repo_type', 'strip': False},
301 {'k': 'repo_type', 'strip': False},
290 {'k': 'repo_enable_downloads', 'strip': True},
302 {'k': 'repo_enable_downloads', 'strip': True},
291 {'k': 'repo_description', 'strip': True},
303 {'k': 'repo_description', 'strip': True},
292 {'k': 'repo_enable_locking', 'strip': True},
304 {'k': 'repo_enable_locking', 'strip': True},
293 {'k': 'repo_landing_rev', 'strip': True},
305 {'k': 'repo_landing_rev', 'strip': True},
294 {'k': 'clone_uri', 'strip': False},
306 {'k': 'clone_uri', 'strip': False},
295 {'k': 'repo_private', 'strip': True},
307 {'k': 'repo_private', 'strip': True},
296 {'k': 'repo_enable_statistics', 'strip': True}
308 {'k': 'repo_enable_statistics', 'strip': True}
297 )
309 )
298
310
299 for item in keys_to_process:
311 for item in keys_to_process:
300 attr = item['k']
312 attr = item['k']
301 if item['strip']:
313 if item['strip']:
302 attr = remove_prefix(item['k'], 'repo_')
314 attr = remove_prefix(item['k'], 'repo_')
303
315
304 val = defaults[attr]
316 val = defaults[attr]
305 if item['k'] == 'repo_landing_rev':
317 if item['k'] == 'repo_landing_rev':
306 val = ':'.join(defaults[attr])
318 val = ':'.join(defaults[attr])
307 defaults[item['k']] = val
319 defaults[item['k']] = val
308 if item['k'] == 'clone_uri':
320 if item['k'] == 'clone_uri':
309 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
321 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
310
322
311 # fill owner
323 # fill owner
312 if repo_info.user:
324 if repo_info.user:
313 defaults.update({'user': repo_info.user.username})
325 defaults.update({'user': repo_info.user.username})
314 else:
326 else:
315 replacement_user = User.get_first_super_admin().username
327 replacement_user = User.get_first_super_admin().username
316 defaults.update({'user': replacement_user})
328 defaults.update({'user': replacement_user})
317
329
318 return defaults
330 return defaults
319
331
320 def update(self, repo, **kwargs):
332 def update(self, repo, **kwargs):
321 try:
333 try:
322 cur_repo = self._get_repo(repo)
334 cur_repo = self._get_repo(repo)
323 source_repo_name = cur_repo.repo_name
335 source_repo_name = cur_repo.repo_name
324 if 'user' in kwargs:
336 if 'user' in kwargs:
325 cur_repo.user = User.get_by_username(kwargs['user'])
337 cur_repo.user = User.get_by_username(kwargs['user'])
326
338
327 if 'repo_group' in kwargs:
339 if 'repo_group' in kwargs:
328 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
340 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
329 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
341 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
330
342
331 update_keys = [
343 update_keys = [
332 (1, 'repo_description'),
344 (1, 'repo_description'),
333 (1, 'repo_landing_rev'),
345 (1, 'repo_landing_rev'),
334 (1, 'repo_private'),
346 (1, 'repo_private'),
335 (1, 'repo_enable_downloads'),
347 (1, 'repo_enable_downloads'),
336 (1, 'repo_enable_locking'),
348 (1, 'repo_enable_locking'),
337 (1, 'repo_enable_statistics'),
349 (1, 'repo_enable_statistics'),
338 (0, 'clone_uri'),
350 (0, 'clone_uri'),
339 (0, 'fork_id')
351 (0, 'fork_id')
340 ]
352 ]
341 for strip, k in update_keys:
353 for strip, k in update_keys:
342 if k in kwargs:
354 if k in kwargs:
343 val = kwargs[k]
355 val = kwargs[k]
344 if strip:
356 if strip:
345 k = remove_prefix(k, 'repo_')
357 k = remove_prefix(k, 'repo_')
346
358
347 setattr(cur_repo, k, val)
359 setattr(cur_repo, k, val)
348
360
349 new_name = cur_repo.get_new_name(kwargs['repo_name'])
361 new_name = cur_repo.get_new_name(kwargs['repo_name'])
350 cur_repo.repo_name = new_name
362 cur_repo.repo_name = new_name
351
363
352 # if private flag is set, reset default permission to NONE
364 # if private flag is set, reset default permission to NONE
353 if kwargs.get('repo_private'):
365 if kwargs.get('repo_private'):
354 EMPTY_PERM = 'repository.none'
366 EMPTY_PERM = 'repository.none'
355 RepoModel().grant_user_permission(
367 RepoModel().grant_user_permission(
356 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
368 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
357 )
369 )
358
370
359 # handle extra fields
371 # handle extra fields
360 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
372 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
361 kwargs):
373 kwargs):
362 k = RepositoryField.un_prefix_key(field)
374 k = RepositoryField.un_prefix_key(field)
363 ex_field = RepositoryField.get_by_key_name(
375 ex_field = RepositoryField.get_by_key_name(
364 key=k, repo=cur_repo)
376 key=k, repo=cur_repo)
365 if ex_field:
377 if ex_field:
366 ex_field.field_value = kwargs[field]
378 ex_field.field_value = kwargs[field]
367 self.sa.add(ex_field)
379 self.sa.add(ex_field)
368 cur_repo.updated_on = datetime.datetime.now()
380 cur_repo.updated_on = datetime.datetime.now()
369 self.sa.add(cur_repo)
381 self.sa.add(cur_repo)
370
382
371 if source_repo_name != new_name:
383 if source_repo_name != new_name:
372 # rename repository
384 # rename repository
373 self._rename_filesystem_repo(
385 self._rename_filesystem_repo(
374 old=source_repo_name, new=new_name)
386 old=source_repo_name, new=new_name)
375
387
376 return cur_repo
388 return cur_repo
377 except Exception:
389 except Exception:
378 log.error(traceback.format_exc())
390 log.error(traceback.format_exc())
379 raise
391 raise
380
392
381 def _create_repo(self, repo_name, repo_type, description, owner,
393 def _create_repo(self, repo_name, repo_type, description, owner,
382 private=False, clone_uri=None, repo_group=None,
394 private=False, clone_uri=None, repo_group=None,
383 landing_rev='rev:tip', fork_of=None,
395 landing_rev='rev:tip', fork_of=None,
384 copy_fork_permissions=False, enable_statistics=False,
396 copy_fork_permissions=False, enable_statistics=False,
385 enable_locking=False, enable_downloads=False,
397 enable_locking=False, enable_downloads=False,
386 copy_group_permissions=False,
398 copy_group_permissions=False,
387 state=Repository.STATE_PENDING):
399 state=Repository.STATE_PENDING):
388 """
400 """
389 Create repository inside database with PENDING state, this should be
401 Create repository inside database with PENDING state, this should be
390 only executed by create() repo. With exception of importing existing
402 only executed by create() repo. With exception of importing existing
391 repos
403 repos
392 """
404 """
393 from rhodecode.model.scm import ScmModel
405 from rhodecode.model.scm import ScmModel
394
406
395 owner = self._get_user(owner)
407 owner = self._get_user(owner)
396 fork_of = self._get_repo(fork_of)
408 fork_of = self._get_repo(fork_of)
397 repo_group = self._get_repo_group(safe_int(repo_group))
409 repo_group = self._get_repo_group(safe_int(repo_group))
398
410
399 try:
411 try:
400 repo_name = safe_unicode(repo_name)
412 repo_name = safe_unicode(repo_name)
401 description = safe_unicode(description)
413 description = safe_unicode(description)
402 # repo name is just a name of repository
414 # repo name is just a name of repository
403 # while repo_name_full is a full qualified name that is combined
415 # while repo_name_full is a full qualified name that is combined
404 # with name and path of group
416 # with name and path of group
405 repo_name_full = repo_name
417 repo_name_full = repo_name
406 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
418 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
407
419
408 new_repo = Repository()
420 new_repo = Repository()
409 new_repo.repo_state = state
421 new_repo.repo_state = state
410 new_repo.enable_statistics = False
422 new_repo.enable_statistics = False
411 new_repo.repo_name = repo_name_full
423 new_repo.repo_name = repo_name_full
412 new_repo.repo_type = repo_type
424 new_repo.repo_type = repo_type
413 new_repo.user = owner
425 new_repo.user = owner
414 new_repo.group = repo_group
426 new_repo.group = repo_group
415 new_repo.description = description or repo_name
427 new_repo.description = description or repo_name
416 new_repo.private = private
428 new_repo.private = private
417 new_repo.clone_uri = clone_uri
429 new_repo.clone_uri = clone_uri
418 new_repo.landing_rev = landing_rev
430 new_repo.landing_rev = landing_rev
419
431
420 new_repo.enable_statistics = enable_statistics
432 new_repo.enable_statistics = enable_statistics
421 new_repo.enable_locking = enable_locking
433 new_repo.enable_locking = enable_locking
422 new_repo.enable_downloads = enable_downloads
434 new_repo.enable_downloads = enable_downloads
423
435
424 if repo_group:
436 if repo_group:
425 new_repo.enable_locking = repo_group.enable_locking
437 new_repo.enable_locking = repo_group.enable_locking
426
438
427 if fork_of:
439 if fork_of:
428 parent_repo = fork_of
440 parent_repo = fork_of
429 new_repo.fork = parent_repo
441 new_repo.fork = parent_repo
430
442
431 events.trigger(events.RepoPreCreateEvent(new_repo))
443 events.trigger(events.RepoPreCreateEvent(new_repo))
432
444
433 self.sa.add(new_repo)
445 self.sa.add(new_repo)
434
446
435 EMPTY_PERM = 'repository.none'
447 EMPTY_PERM = 'repository.none'
436 if fork_of and copy_fork_permissions:
448 if fork_of and copy_fork_permissions:
437 repo = fork_of
449 repo = fork_of
438 user_perms = UserRepoToPerm.query() \
450 user_perms = UserRepoToPerm.query() \
439 .filter(UserRepoToPerm.repository == repo).all()
451 .filter(UserRepoToPerm.repository == repo).all()
440 group_perms = UserGroupRepoToPerm.query() \
452 group_perms = UserGroupRepoToPerm.query() \
441 .filter(UserGroupRepoToPerm.repository == repo).all()
453 .filter(UserGroupRepoToPerm.repository == repo).all()
442
454
443 for perm in user_perms:
455 for perm in user_perms:
444 UserRepoToPerm.create(
456 UserRepoToPerm.create(
445 perm.user, new_repo, perm.permission)
457 perm.user, new_repo, perm.permission)
446
458
447 for perm in group_perms:
459 for perm in group_perms:
448 UserGroupRepoToPerm.create(
460 UserGroupRepoToPerm.create(
449 perm.users_group, new_repo, perm.permission)
461 perm.users_group, new_repo, perm.permission)
450 # in case we copy permissions and also set this repo to private
462 # in case we copy permissions and also set this repo to private
451 # override the default user permission to make it a private
463 # override the default user permission to make it a private
452 # repo
464 # repo
453 if private:
465 if private:
454 RepoModel(self.sa).grant_user_permission(
466 RepoModel(self.sa).grant_user_permission(
455 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
467 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
456
468
457 elif repo_group and copy_group_permissions:
469 elif repo_group and copy_group_permissions:
458 user_perms = UserRepoGroupToPerm.query() \
470 user_perms = UserRepoGroupToPerm.query() \
459 .filter(UserRepoGroupToPerm.group == repo_group).all()
471 .filter(UserRepoGroupToPerm.group == repo_group).all()
460
472
461 group_perms = UserGroupRepoGroupToPerm.query() \
473 group_perms = UserGroupRepoGroupToPerm.query() \
462 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
474 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
463
475
464 for perm in user_perms:
476 for perm in user_perms:
465 perm_name = perm.permission.permission_name.replace(
477 perm_name = perm.permission.permission_name.replace(
466 'group.', 'repository.')
478 'group.', 'repository.')
467 perm_obj = Permission.get_by_key(perm_name)
479 perm_obj = Permission.get_by_key(perm_name)
468 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
480 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
469
481
470 for perm in group_perms:
482 for perm in group_perms:
471 perm_name = perm.permission.permission_name.replace(
483 perm_name = perm.permission.permission_name.replace(
472 'group.', 'repository.')
484 'group.', 'repository.')
473 perm_obj = Permission.get_by_key(perm_name)
485 perm_obj = Permission.get_by_key(perm_name)
474 UserGroupRepoToPerm.create(
486 UserGroupRepoToPerm.create(
475 perm.users_group, new_repo, perm_obj)
487 perm.users_group, new_repo, perm_obj)
476
488
477 if private:
489 if private:
478 RepoModel(self.sa).grant_user_permission(
490 RepoModel(self.sa).grant_user_permission(
479 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
491 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
480
492
481 else:
493 else:
482 perm_obj = self._create_default_perms(new_repo, private)
494 perm_obj = self._create_default_perms(new_repo, private)
483 self.sa.add(perm_obj)
495 self.sa.add(perm_obj)
484
496
485 # now automatically start following this repository as owner
497 # now automatically start following this repository as owner
486 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
498 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
487 owner.user_id)
499 owner.user_id)
488
500
489 # we need to flush here, in order to check if database won't
501 # we need to flush here, in order to check if database won't
490 # throw any exceptions, create filesystem dirs at the very end
502 # throw any exceptions, create filesystem dirs at the very end
491 self.sa.flush()
503 self.sa.flush()
492 events.trigger(events.RepoCreateEvent(new_repo))
504 events.trigger(events.RepoCreateEvent(new_repo))
493 return new_repo
505 return new_repo
494
506
495 except Exception:
507 except Exception:
496 log.error(traceback.format_exc())
508 log.error(traceback.format_exc())
497 raise
509 raise
498
510
499 def create(self, form_data, cur_user):
511 def create(self, form_data, cur_user):
500 """
512 """
501 Create repository using celery tasks
513 Create repository using celery tasks
502
514
503 :param form_data:
515 :param form_data:
504 :param cur_user:
516 :param cur_user:
505 """
517 """
506 from rhodecode.lib.celerylib import tasks, run_task
518 from rhodecode.lib.celerylib import tasks, run_task
507 return run_task(tasks.create_repo, form_data, cur_user)
519 return run_task(tasks.create_repo, form_data, cur_user)
508
520
509 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
521 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
510 perm_deletions=None, check_perms=True,
522 perm_deletions=None, check_perms=True,
511 cur_user=None):
523 cur_user=None):
512 if not perm_additions:
524 if not perm_additions:
513 perm_additions = []
525 perm_additions = []
514 if not perm_updates:
526 if not perm_updates:
515 perm_updates = []
527 perm_updates = []
516 if not perm_deletions:
528 if not perm_deletions:
517 perm_deletions = []
529 perm_deletions = []
518
530
519 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
531 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
520
532
521 changes = {
533 changes = {
522 'added': [],
534 'added': [],
523 'updated': [],
535 'updated': [],
524 'deleted': []
536 'deleted': []
525 }
537 }
526 # update permissions
538 # update permissions
527 for member_id, perm, member_type in perm_updates:
539 for member_id, perm, member_type in perm_updates:
528 member_id = int(member_id)
540 member_id = int(member_id)
529 if member_type == 'user':
541 if member_type == 'user':
530 member_name = User.get(member_id).username
542 member_name = User.get(member_id).username
531 # this updates also current one if found
543 # this updates also current one if found
532 self.grant_user_permission(
544 self.grant_user_permission(
533 repo=repo, user=member_id, perm=perm)
545 repo=repo, user=member_id, perm=perm)
534 else: # set for user group
546 else: # set for user group
535 # check if we have permissions to alter this usergroup
547 # check if we have permissions to alter this usergroup
536 member_name = UserGroup.get(member_id).users_group_name
548 member_name = UserGroup.get(member_id).users_group_name
537 if not check_perms or HasUserGroupPermissionAny(
549 if not check_perms or HasUserGroupPermissionAny(
538 *req_perms)(member_name, user=cur_user):
550 *req_perms)(member_name, user=cur_user):
539 self.grant_user_group_permission(
551 self.grant_user_group_permission(
540 repo=repo, group_name=member_id, perm=perm)
552 repo=repo, group_name=member_id, perm=perm)
541
553
542 changes['updated'].append({'type': member_type, 'id': member_id,
554 changes['updated'].append({'type': member_type, 'id': member_id,
543 'name': member_name, 'new_perm': perm})
555 'name': member_name, 'new_perm': perm})
544
556
545 # set new permissions
557 # set new permissions
546 for member_id, perm, member_type in perm_additions:
558 for member_id, perm, member_type in perm_additions:
547 member_id = int(member_id)
559 member_id = int(member_id)
548 if member_type == 'user':
560 if member_type == 'user':
549 member_name = User.get(member_id).username
561 member_name = User.get(member_id).username
550 self.grant_user_permission(
562 self.grant_user_permission(
551 repo=repo, user=member_id, perm=perm)
563 repo=repo, user=member_id, perm=perm)
552 else: # set for user group
564 else: # set for user group
553 # check if we have permissions to alter this usergroup
565 # check if we have permissions to alter this usergroup
554 member_name = UserGroup.get(member_id).users_group_name
566 member_name = UserGroup.get(member_id).users_group_name
555 if not check_perms or HasUserGroupPermissionAny(
567 if not check_perms or HasUserGroupPermissionAny(
556 *req_perms)(member_name, user=cur_user):
568 *req_perms)(member_name, user=cur_user):
557 self.grant_user_group_permission(
569 self.grant_user_group_permission(
558 repo=repo, group_name=member_id, perm=perm)
570 repo=repo, group_name=member_id, perm=perm)
559 changes['added'].append({'type': member_type, 'id': member_id,
571 changes['added'].append({'type': member_type, 'id': member_id,
560 'name': member_name, 'new_perm': perm})
572 'name': member_name, 'new_perm': perm})
561 # delete permissions
573 # delete permissions
562 for member_id, perm, member_type in perm_deletions:
574 for member_id, perm, member_type in perm_deletions:
563 member_id = int(member_id)
575 member_id = int(member_id)
564 if member_type == 'user':
576 if member_type == 'user':
565 member_name = User.get(member_id).username
577 member_name = User.get(member_id).username
566 self.revoke_user_permission(repo=repo, user=member_id)
578 self.revoke_user_permission(repo=repo, user=member_id)
567 else: # set for user group
579 else: # set for user group
568 # check if we have permissions to alter this usergroup
580 # check if we have permissions to alter this usergroup
569 member_name = UserGroup.get(member_id).users_group_name
581 member_name = UserGroup.get(member_id).users_group_name
570 if not check_perms or HasUserGroupPermissionAny(
582 if not check_perms or HasUserGroupPermissionAny(
571 *req_perms)(member_name, user=cur_user):
583 *req_perms)(member_name, user=cur_user):
572 self.revoke_user_group_permission(
584 self.revoke_user_group_permission(
573 repo=repo, group_name=member_id)
585 repo=repo, group_name=member_id)
574
586
575 changes['deleted'].append({'type': member_type, 'id': member_id,
587 changes['deleted'].append({'type': member_type, 'id': member_id,
576 'name': member_name, 'new_perm': perm})
588 'name': member_name, 'new_perm': perm})
577 return changes
589 return changes
578
590
579 def create_fork(self, form_data, cur_user):
591 def create_fork(self, form_data, cur_user):
580 """
592 """
581 Simple wrapper into executing celery task for fork creation
593 Simple wrapper into executing celery task for fork creation
582
594
583 :param form_data:
595 :param form_data:
584 :param cur_user:
596 :param cur_user:
585 """
597 """
586 from rhodecode.lib.celerylib import tasks, run_task
598 from rhodecode.lib.celerylib import tasks, run_task
587 return run_task(tasks.create_repo_fork, form_data, cur_user)
599 return run_task(tasks.create_repo_fork, form_data, cur_user)
588
600
589 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
601 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
590 """
602 """
591 Delete given repository, forks parameter defines what do do with
603 Delete given repository, forks parameter defines what do do with
592 attached forks. Throws AttachedForksError if deleted repo has attached
604 attached forks. Throws AttachedForksError if deleted repo has attached
593 forks
605 forks
594
606
595 :param repo:
607 :param repo:
596 :param forks: str 'delete' or 'detach'
608 :param forks: str 'delete' or 'detach'
597 :param fs_remove: remove(archive) repo from filesystem
609 :param fs_remove: remove(archive) repo from filesystem
598 """
610 """
599 if not cur_user:
611 if not cur_user:
600 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
612 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
601 repo = self._get_repo(repo)
613 repo = self._get_repo(repo)
602 if repo:
614 if repo:
603 if forks == 'detach':
615 if forks == 'detach':
604 for r in repo.forks:
616 for r in repo.forks:
605 r.fork = None
617 r.fork = None
606 self.sa.add(r)
618 self.sa.add(r)
607 elif forks == 'delete':
619 elif forks == 'delete':
608 for r in repo.forks:
620 for r in repo.forks:
609 self.delete(r, forks='delete')
621 self.delete(r, forks='delete')
610 elif [f for f in repo.forks]:
622 elif [f for f in repo.forks]:
611 raise AttachedForksError()
623 raise AttachedForksError()
612
624
613 old_repo_dict = repo.get_dict()
625 old_repo_dict = repo.get_dict()
614 events.trigger(events.RepoPreDeleteEvent(repo))
626 events.trigger(events.RepoPreDeleteEvent(repo))
615 try:
627 try:
616 self.sa.delete(repo)
628 self.sa.delete(repo)
617 if fs_remove:
629 if fs_remove:
618 self._delete_filesystem_repo(repo)
630 self._delete_filesystem_repo(repo)
619 else:
631 else:
620 log.debug('skipping removal from filesystem')
632 log.debug('skipping removal from filesystem')
621 old_repo_dict.update({
633 old_repo_dict.update({
622 'deleted_by': cur_user,
634 'deleted_by': cur_user,
623 'deleted_on': time.time(),
635 'deleted_on': time.time(),
624 })
636 })
625 log_delete_repository(**old_repo_dict)
637 log_delete_repository(**old_repo_dict)
626 events.trigger(events.RepoDeleteEvent(repo))
638 events.trigger(events.RepoDeleteEvent(repo))
627 except Exception:
639 except Exception:
628 log.error(traceback.format_exc())
640 log.error(traceback.format_exc())
629 raise
641 raise
630
642
631 def grant_user_permission(self, repo, user, perm):
643 def grant_user_permission(self, repo, user, perm):
632 """
644 """
633 Grant permission for user on given repository, or update existing one
645 Grant permission for user on given repository, or update existing one
634 if found
646 if found
635
647
636 :param repo: Instance of Repository, repository_id, or repository name
648 :param repo: Instance of Repository, repository_id, or repository name
637 :param user: Instance of User, user_id or username
649 :param user: Instance of User, user_id or username
638 :param perm: Instance of Permission, or permission_name
650 :param perm: Instance of Permission, or permission_name
639 """
651 """
640 user = self._get_user(user)
652 user = self._get_user(user)
641 repo = self._get_repo(repo)
653 repo = self._get_repo(repo)
642 permission = self._get_perm(perm)
654 permission = self._get_perm(perm)
643
655
644 # check if we have that permission already
656 # check if we have that permission already
645 obj = self.sa.query(UserRepoToPerm) \
657 obj = self.sa.query(UserRepoToPerm) \
646 .filter(UserRepoToPerm.user == user) \
658 .filter(UserRepoToPerm.user == user) \
647 .filter(UserRepoToPerm.repository == repo) \
659 .filter(UserRepoToPerm.repository == repo) \
648 .scalar()
660 .scalar()
649 if obj is None:
661 if obj is None:
650 # create new !
662 # create new !
651 obj = UserRepoToPerm()
663 obj = UserRepoToPerm()
652 obj.repository = repo
664 obj.repository = repo
653 obj.user = user
665 obj.user = user
654 obj.permission = permission
666 obj.permission = permission
655 self.sa.add(obj)
667 self.sa.add(obj)
656 log.debug('Granted perm %s to %s on %s', perm, user, repo)
668 log.debug('Granted perm %s to %s on %s', perm, user, repo)
657 action_logger_generic(
669 action_logger_generic(
658 'granted permission: {} to user: {} on repo: {}'.format(
670 'granted permission: {} to user: {} on repo: {}'.format(
659 perm, user, repo), namespace='security.repo')
671 perm, user, repo), namespace='security.repo')
660 return obj
672 return obj
661
673
662 def revoke_user_permission(self, repo, user):
674 def revoke_user_permission(self, repo, user):
663 """
675 """
664 Revoke permission for user on given repository
676 Revoke permission for user on given repository
665
677
666 :param repo: Instance of Repository, repository_id, or repository name
678 :param repo: Instance of Repository, repository_id, or repository name
667 :param user: Instance of User, user_id or username
679 :param user: Instance of User, user_id or username
668 """
680 """
669
681
670 user = self._get_user(user)
682 user = self._get_user(user)
671 repo = self._get_repo(repo)
683 repo = self._get_repo(repo)
672
684
673 obj = self.sa.query(UserRepoToPerm) \
685 obj = self.sa.query(UserRepoToPerm) \
674 .filter(UserRepoToPerm.repository == repo) \
686 .filter(UserRepoToPerm.repository == repo) \
675 .filter(UserRepoToPerm.user == user) \
687 .filter(UserRepoToPerm.user == user) \
676 .scalar()
688 .scalar()
677 if obj:
689 if obj:
678 self.sa.delete(obj)
690 self.sa.delete(obj)
679 log.debug('Revoked perm on %s on %s', repo, user)
691 log.debug('Revoked perm on %s on %s', repo, user)
680 action_logger_generic(
692 action_logger_generic(
681 'revoked permission from user: {} on repo: {}'.format(
693 'revoked permission from user: {} on repo: {}'.format(
682 user, repo), namespace='security.repo')
694 user, repo), namespace='security.repo')
683
695
684 def grant_user_group_permission(self, repo, group_name, perm):
696 def grant_user_group_permission(self, repo, group_name, perm):
685 """
697 """
686 Grant permission for user group on given repository, or update
698 Grant permission for user group on given repository, or update
687 existing one if found
699 existing one if found
688
700
689 :param repo: Instance of Repository, repository_id, or repository name
701 :param repo: Instance of Repository, repository_id, or repository name
690 :param group_name: Instance of UserGroup, users_group_id,
702 :param group_name: Instance of UserGroup, users_group_id,
691 or user group name
703 or user group name
692 :param perm: Instance of Permission, or permission_name
704 :param perm: Instance of Permission, or permission_name
693 """
705 """
694 repo = self._get_repo(repo)
706 repo = self._get_repo(repo)
695 group_name = self._get_user_group(group_name)
707 group_name = self._get_user_group(group_name)
696 permission = self._get_perm(perm)
708 permission = self._get_perm(perm)
697
709
698 # check if we have that permission already
710 # check if we have that permission already
699 obj = self.sa.query(UserGroupRepoToPerm) \
711 obj = self.sa.query(UserGroupRepoToPerm) \
700 .filter(UserGroupRepoToPerm.users_group == group_name) \
712 .filter(UserGroupRepoToPerm.users_group == group_name) \
701 .filter(UserGroupRepoToPerm.repository == repo) \
713 .filter(UserGroupRepoToPerm.repository == repo) \
702 .scalar()
714 .scalar()
703
715
704 if obj is None:
716 if obj is None:
705 # create new
717 # create new
706 obj = UserGroupRepoToPerm()
718 obj = UserGroupRepoToPerm()
707
719
708 obj.repository = repo
720 obj.repository = repo
709 obj.users_group = group_name
721 obj.users_group = group_name
710 obj.permission = permission
722 obj.permission = permission
711 self.sa.add(obj)
723 self.sa.add(obj)
712 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
724 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
713 action_logger_generic(
725 action_logger_generic(
714 'granted permission: {} to usergroup: {} on repo: {}'.format(
726 'granted permission: {} to usergroup: {} on repo: {}'.format(
715 perm, group_name, repo), namespace='security.repo')
727 perm, group_name, repo), namespace='security.repo')
716
728
717 return obj
729 return obj
718
730
719 def revoke_user_group_permission(self, repo, group_name):
731 def revoke_user_group_permission(self, repo, group_name):
720 """
732 """
721 Revoke permission for user group on given repository
733 Revoke permission for user group on given repository
722
734
723 :param repo: Instance of Repository, repository_id, or repository name
735 :param repo: Instance of Repository, repository_id, or repository name
724 :param group_name: Instance of UserGroup, users_group_id,
736 :param group_name: Instance of UserGroup, users_group_id,
725 or user group name
737 or user group name
726 """
738 """
727 repo = self._get_repo(repo)
739 repo = self._get_repo(repo)
728 group_name = self._get_user_group(group_name)
740 group_name = self._get_user_group(group_name)
729
741
730 obj = self.sa.query(UserGroupRepoToPerm) \
742 obj = self.sa.query(UserGroupRepoToPerm) \
731 .filter(UserGroupRepoToPerm.repository == repo) \
743 .filter(UserGroupRepoToPerm.repository == repo) \
732 .filter(UserGroupRepoToPerm.users_group == group_name) \
744 .filter(UserGroupRepoToPerm.users_group == group_name) \
733 .scalar()
745 .scalar()
734 if obj:
746 if obj:
735 self.sa.delete(obj)
747 self.sa.delete(obj)
736 log.debug('Revoked perm to %s on %s', repo, group_name)
748 log.debug('Revoked perm to %s on %s', repo, group_name)
737 action_logger_generic(
749 action_logger_generic(
738 'revoked permission from usergroup: {} on repo: {}'.format(
750 'revoked permission from usergroup: {} on repo: {}'.format(
739 group_name, repo), namespace='security.repo')
751 group_name, repo), namespace='security.repo')
740
752
741 def delete_stats(self, repo_name):
753 def delete_stats(self, repo_name):
742 """
754 """
743 removes stats for given repo
755 removes stats for given repo
744
756
745 :param repo_name:
757 :param repo_name:
746 """
758 """
747 repo = self._get_repo(repo_name)
759 repo = self._get_repo(repo_name)
748 try:
760 try:
749 obj = self.sa.query(Statistics) \
761 obj = self.sa.query(Statistics) \
750 .filter(Statistics.repository == repo).scalar()
762 .filter(Statistics.repository == repo).scalar()
751 if obj:
763 if obj:
752 self.sa.delete(obj)
764 self.sa.delete(obj)
753 except Exception:
765 except Exception:
754 log.error(traceback.format_exc())
766 log.error(traceback.format_exc())
755 raise
767 raise
756
768
757 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
769 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
758 field_type='str', field_desc=''):
770 field_type='str', field_desc=''):
759
771
760 repo = self._get_repo(repo_name)
772 repo = self._get_repo(repo_name)
761
773
762 new_field = RepositoryField()
774 new_field = RepositoryField()
763 new_field.repository = repo
775 new_field.repository = repo
764 new_field.field_key = field_key
776 new_field.field_key = field_key
765 new_field.field_type = field_type # python type
777 new_field.field_type = field_type # python type
766 new_field.field_value = field_value
778 new_field.field_value = field_value
767 new_field.field_desc = field_desc
779 new_field.field_desc = field_desc
768 new_field.field_label = field_label
780 new_field.field_label = field_label
769 self.sa.add(new_field)
781 self.sa.add(new_field)
770 return new_field
782 return new_field
771
783
772 def delete_repo_field(self, repo_name, field_key):
784 def delete_repo_field(self, repo_name, field_key):
773 repo = self._get_repo(repo_name)
785 repo = self._get_repo(repo_name)
774 field = RepositoryField.get_by_key_name(field_key, repo)
786 field = RepositoryField.get_by_key_name(field_key, repo)
775 if field:
787 if field:
776 self.sa.delete(field)
788 self.sa.delete(field)
777
789
778 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
790 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
779 clone_uri=None, repo_store_location=None,
791 clone_uri=None, repo_store_location=None,
780 use_global_config=False):
792 use_global_config=False):
781 """
793 """
782 makes repository on filesystem. It's group aware means it'll create
794 makes repository on filesystem. It's group aware means it'll create
783 a repository within a group, and alter the paths accordingly of
795 a repository within a group, and alter the paths accordingly of
784 group location
796 group location
785
797
786 :param repo_name:
798 :param repo_name:
787 :param alias:
799 :param alias:
788 :param parent:
800 :param parent:
789 :param clone_uri:
801 :param clone_uri:
790 :param repo_store_location:
802 :param repo_store_location:
791 """
803 """
792 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
804 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
793 from rhodecode.model.scm import ScmModel
805 from rhodecode.model.scm import ScmModel
794
806
795 if Repository.NAME_SEP in repo_name:
807 if Repository.NAME_SEP in repo_name:
796 raise ValueError(
808 raise ValueError(
797 'repo_name must not contain groups got `%s`' % repo_name)
809 'repo_name must not contain groups got `%s`' % repo_name)
798
810
799 if isinstance(repo_group, RepoGroup):
811 if isinstance(repo_group, RepoGroup):
800 new_parent_path = os.sep.join(repo_group.full_path_splitted)
812 new_parent_path = os.sep.join(repo_group.full_path_splitted)
801 else:
813 else:
802 new_parent_path = repo_group or ''
814 new_parent_path = repo_group or ''
803
815
804 if repo_store_location:
816 if repo_store_location:
805 _paths = [repo_store_location]
817 _paths = [repo_store_location]
806 else:
818 else:
807 _paths = [self.repos_path, new_parent_path, repo_name]
819 _paths = [self.repos_path, new_parent_path, repo_name]
808 # we need to make it str for mercurial
820 # we need to make it str for mercurial
809 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
821 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
810
822
811 # check if this path is not a repository
823 # check if this path is not a repository
812 if is_valid_repo(repo_path, self.repos_path):
824 if is_valid_repo(repo_path, self.repos_path):
813 raise Exception('This path %s is a valid repository' % repo_path)
825 raise Exception('This path %s is a valid repository' % repo_path)
814
826
815 # check if this path is a group
827 # check if this path is a group
816 if is_valid_repo_group(repo_path, self.repos_path):
828 if is_valid_repo_group(repo_path, self.repos_path):
817 raise Exception('This path %s is a valid group' % repo_path)
829 raise Exception('This path %s is a valid group' % repo_path)
818
830
819 log.info('creating repo %s in %s from url: `%s`',
831 log.info('creating repo %s in %s from url: `%s`',
820 repo_name, safe_unicode(repo_path),
832 repo_name, safe_unicode(repo_path),
821 obfuscate_url_pw(clone_uri))
833 obfuscate_url_pw(clone_uri))
822
834
823 backend = get_backend(repo_type)
835 backend = get_backend(repo_type)
824
836
825 config_repo = None if use_global_config else repo_name
837 config_repo = None if use_global_config else repo_name
826 if config_repo and new_parent_path:
838 if config_repo and new_parent_path:
827 config_repo = Repository.NAME_SEP.join(
839 config_repo = Repository.NAME_SEP.join(
828 (new_parent_path, config_repo))
840 (new_parent_path, config_repo))
829 config = make_db_config(clear_session=False, repo=config_repo)
841 config = make_db_config(clear_session=False, repo=config_repo)
830 config.set('extensions', 'largefiles', '')
842 config.set('extensions', 'largefiles', '')
831
843
832 # patch and reset hooks section of UI config to not run any
844 # patch and reset hooks section of UI config to not run any
833 # hooks on creating remote repo
845 # hooks on creating remote repo
834 config.clear_section('hooks')
846 config.clear_section('hooks')
835
847
836 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
848 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
837 if repo_type == 'git':
849 if repo_type == 'git':
838 repo = backend(
850 repo = backend(
839 repo_path, config=config, create=True, src_url=clone_uri,
851 repo_path, config=config, create=True, src_url=clone_uri,
840 bare=True)
852 bare=True)
841 else:
853 else:
842 repo = backend(
854 repo = backend(
843 repo_path, config=config, create=True, src_url=clone_uri)
855 repo_path, config=config, create=True, src_url=clone_uri)
844
856
845 ScmModel().install_hooks(repo, repo_type=repo_type)
857 ScmModel().install_hooks(repo, repo_type=repo_type)
846
858
847 log.debug('Created repo %s with %s backend',
859 log.debug('Created repo %s with %s backend',
848 safe_unicode(repo_name), safe_unicode(repo_type))
860 safe_unicode(repo_name), safe_unicode(repo_type))
849 return repo
861 return repo
850
862
851 def _rename_filesystem_repo(self, old, new):
863 def _rename_filesystem_repo(self, old, new):
852 """
864 """
853 renames repository on filesystem
865 renames repository on filesystem
854
866
855 :param old: old name
867 :param old: old name
856 :param new: new name
868 :param new: new name
857 """
869 """
858 log.info('renaming repo from %s to %s', old, new)
870 log.info('renaming repo from %s to %s', old, new)
859
871
860 old_path = os.path.join(self.repos_path, old)
872 old_path = os.path.join(self.repos_path, old)
861 new_path = os.path.join(self.repos_path, new)
873 new_path = os.path.join(self.repos_path, new)
862 if os.path.isdir(new_path):
874 if os.path.isdir(new_path):
863 raise Exception(
875 raise Exception(
864 'Was trying to rename to already existing dir %s' % new_path
876 'Was trying to rename to already existing dir %s' % new_path
865 )
877 )
866 shutil.move(old_path, new_path)
878 shutil.move(old_path, new_path)
867
879
868 def _delete_filesystem_repo(self, repo):
880 def _delete_filesystem_repo(self, repo):
869 """
881 """
870 removes repo from filesystem, the removal is acctually made by
882 removes repo from filesystem, the removal is acctually made by
871 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
883 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
872 repository is no longer valid for rhodecode, can be undeleted later on
884 repository is no longer valid for rhodecode, can be undeleted later on
873 by reverting the renames on this repository
885 by reverting the renames on this repository
874
886
875 :param repo: repo object
887 :param repo: repo object
876 """
888 """
877 rm_path = os.path.join(self.repos_path, repo.repo_name)
889 rm_path = os.path.join(self.repos_path, repo.repo_name)
878 repo_group = repo.group
890 repo_group = repo.group
879 log.info("Removing repository %s", rm_path)
891 log.info("Removing repository %s", rm_path)
880 # disable hg/git internal that it doesn't get detected as repo
892 # disable hg/git internal that it doesn't get detected as repo
881 alias = repo.repo_type
893 alias = repo.repo_type
882
894
883 config = make_db_config(clear_session=False)
895 config = make_db_config(clear_session=False)
884 config.set('extensions', 'largefiles', '')
896 config.set('extensions', 'largefiles', '')
885 bare = getattr(repo.scm_instance(config=config), 'bare', False)
897 bare = getattr(repo.scm_instance(config=config), 'bare', False)
886
898
887 # skip this for bare git repos
899 # skip this for bare git repos
888 if not bare:
900 if not bare:
889 # disable VCS repo
901 # disable VCS repo
890 vcs_path = os.path.join(rm_path, '.%s' % alias)
902 vcs_path = os.path.join(rm_path, '.%s' % alias)
891 if os.path.exists(vcs_path):
903 if os.path.exists(vcs_path):
892 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
904 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
893
905
894 _now = datetime.datetime.now()
906 _now = datetime.datetime.now()
895 _ms = str(_now.microsecond).rjust(6, '0')
907 _ms = str(_now.microsecond).rjust(6, '0')
896 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
908 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
897 repo.just_name)
909 repo.just_name)
898 if repo_group:
910 if repo_group:
899 # if repository is in group, prefix the removal path with the group
911 # if repository is in group, prefix the removal path with the group
900 args = repo_group.full_path_splitted + [_d]
912 args = repo_group.full_path_splitted + [_d]
901 _d = os.path.join(*args)
913 _d = os.path.join(*args)
902
914
903 if os.path.isdir(rm_path):
915 if os.path.isdir(rm_path):
904 shutil.move(rm_path, os.path.join(self.repos_path, _d))
916 shutil.move(rm_path, os.path.join(self.repos_path, _d))
905
917
906
918
907 class ReadmeFinder:
919 class ReadmeFinder:
908 """
920 """
909 Utility which knows how to find a readme for a specific commit.
921 Utility which knows how to find a readme for a specific commit.
910
922
911 The main idea is that this is a configurable algorithm. When creating an
923 The main idea is that this is a configurable algorithm. When creating an
912 instance you can define parameters, currently only the `default_renderer`.
924 instance you can define parameters, currently only the `default_renderer`.
913 Based on this configuration the method :meth:`search` behaves slightly
925 Based on this configuration the method :meth:`search` behaves slightly
914 different.
926 different.
915 """
927 """
916
928
917 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
929 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
918 path_re = re.compile(r'^docs?', re.IGNORECASE)
930 path_re = re.compile(r'^docs?', re.IGNORECASE)
919
931
920 default_priorities = {
932 default_priorities = {
921 None: 0,
933 None: 0,
922 '.text': 2,
934 '.text': 2,
923 '.txt': 3,
935 '.txt': 3,
924 '.rst': 1,
936 '.rst': 1,
925 '.rest': 2,
937 '.rest': 2,
926 '.md': 1,
938 '.md': 1,
927 '.mkdn': 2,
939 '.mkdn': 2,
928 '.mdown': 3,
940 '.mdown': 3,
929 '.markdown': 4,
941 '.markdown': 4,
930 }
942 }
931
943
932 path_priority = {
944 path_priority = {
933 'doc': 0,
945 'doc': 0,
934 'docs': 1,
946 'docs': 1,
935 }
947 }
936
948
937 FALLBACK_PRIORITY = 99
949 FALLBACK_PRIORITY = 99
938
950
939 RENDERER_TO_EXTENSION = {
951 RENDERER_TO_EXTENSION = {
940 'rst': ['.rst', '.rest'],
952 'rst': ['.rst', '.rest'],
941 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
953 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
942 }
954 }
943
955
944 def __init__(self, default_renderer=None):
956 def __init__(self, default_renderer=None):
945 self._default_renderer = default_renderer
957 self._default_renderer = default_renderer
946 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
958 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
947 default_renderer, [])
959 default_renderer, [])
948
960
949 def search(self, commit, path='/'):
961 def search(self, commit, path='/'):
950 """
962 """
951 Find a readme in the given `commit`.
963 Find a readme in the given `commit`.
952 """
964 """
953 nodes = commit.get_nodes(path)
965 nodes = commit.get_nodes(path)
954 matches = self._match_readmes(nodes)
966 matches = self._match_readmes(nodes)
955 matches = self._sort_according_to_priority(matches)
967 matches = self._sort_according_to_priority(matches)
956 if matches:
968 if matches:
957 return matches[0].node
969 return matches[0].node
958
970
959 paths = self._match_paths(nodes)
971 paths = self._match_paths(nodes)
960 paths = self._sort_paths_according_to_priority(paths)
972 paths = self._sort_paths_according_to_priority(paths)
961 for path in paths:
973 for path in paths:
962 match = self.search(commit, path=path)
974 match = self.search(commit, path=path)
963 if match:
975 if match:
964 return match
976 return match
965
977
966 return None
978 return None
967
979
968 def _match_readmes(self, nodes):
980 def _match_readmes(self, nodes):
969 for node in nodes:
981 for node in nodes:
970 if not node.is_file():
982 if not node.is_file():
971 continue
983 continue
972 path = node.path.rsplit('/', 1)[-1]
984 path = node.path.rsplit('/', 1)[-1]
973 match = self.readme_re.match(path)
985 match = self.readme_re.match(path)
974 if match:
986 if match:
975 extension = match.group(1)
987 extension = match.group(1)
976 yield ReadmeMatch(node, match, self._priority(extension))
988 yield ReadmeMatch(node, match, self._priority(extension))
977
989
978 def _match_paths(self, nodes):
990 def _match_paths(self, nodes):
979 for node in nodes:
991 for node in nodes:
980 if not node.is_dir():
992 if not node.is_dir():
981 continue
993 continue
982 match = self.path_re.match(node.path)
994 match = self.path_re.match(node.path)
983 if match:
995 if match:
984 yield node.path
996 yield node.path
985
997
986 def _priority(self, extension):
998 def _priority(self, extension):
987 renderer_priority = (
999 renderer_priority = (
988 0 if extension in self._renderer_extensions else 1)
1000 0 if extension in self._renderer_extensions else 1)
989 extension_priority = self.default_priorities.get(
1001 extension_priority = self.default_priorities.get(
990 extension, self.FALLBACK_PRIORITY)
1002 extension, self.FALLBACK_PRIORITY)
991 return (renderer_priority, extension_priority)
1003 return (renderer_priority, extension_priority)
992
1004
993 def _sort_according_to_priority(self, matches):
1005 def _sort_according_to_priority(self, matches):
994
1006
995 def priority_and_path(match):
1007 def priority_and_path(match):
996 return (match.priority, match.path)
1008 return (match.priority, match.path)
997
1009
998 return sorted(matches, key=priority_and_path)
1010 return sorted(matches, key=priority_and_path)
999
1011
1000 def _sort_paths_according_to_priority(self, paths):
1012 def _sort_paths_according_to_priority(self, paths):
1001
1013
1002 def priority_and_path(path):
1014 def priority_and_path(path):
1003 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1015 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1004
1016
1005 return sorted(paths, key=priority_and_path)
1017 return sorted(paths, key=priority_and_path)
1006
1018
1007
1019
1008 class ReadmeMatch:
1020 class ReadmeMatch:
1009
1021
1010 def __init__(self, node, match, priority):
1022 def __init__(self, node, match, priority):
1011 self.node = node
1023 self.node = node
1012 self._match = match
1024 self._match = match
1013 self.priority = priority
1025 self.priority = priority
1014
1026
1015 @property
1027 @property
1016 def path(self):
1028 def path(self):
1017 return self.node.path
1029 return self.node.path
1018
1030
1019 def __repr__(self):
1031 def __repr__(self):
1020 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
1032 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
@@ -1,99 +1,102 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 ##
2 ##
3 ## See also repo_settings.html
3 ## See also repo_settings.html
4 ##
4 ##
5 <%inherit file="/base/base.mako"/>
5 <%inherit file="/base/base.mako"/>
6
6
7 <%def name="title()">
7 <%def name="title()">
8 ${_('%s repository settings') % c.rhodecode_db_repo.repo_name}
8 ${_('%s repository settings') % c.rhodecode_db_repo.repo_name}
9 %if c.rhodecode_name:
9 %if c.rhodecode_name:
10 &middot; ${h.branding(c.rhodecode_name)}
10 &middot; ${h.branding(c.rhodecode_name)}
11 %endif
11 %endif
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Settings')}
15 ${_('Settings')}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_nav()">
18 <%def name="menu_bar_nav()">
19 ${self.menu_items(active='repositories')}
19 ${self.menu_items(active='repositories')}
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_subnav()">
22 <%def name="menu_bar_subnav()">
23 ${self.repo_menu(active='options')}
23 ${self.repo_menu(active='options')}
24 </%def>
24 </%def>
25
25
26 <%def name="main_content()">
26 <%def name="main_content()">
27 % if hasattr(c, 'repo_edit_template'):
27 % if hasattr(c, 'repo_edit_template'):
28 <%include file="${c.repo_edit_template}"/>
28 <%include file="${c.repo_edit_template}"/>
29 % else:
29 % else:
30 <%include file="/admin/repos/repo_edit_${c.active}.mako"/>
30 <%include file="/admin/repos/repo_edit_${c.active}.mako"/>
31 % endif
31 % endif
32 </%def>
32 </%def>
33
33
34
34
35 <%def name="main()">
35 <%def name="main()">
36 <div class="box">
36 <div class="box">
37 <div class="title">
37 <div class="title">
38 ${self.repo_page_title(c.rhodecode_db_repo)}
38 ${self.repo_page_title(c.rhodecode_db_repo)}
39 ${self.breadcrumbs()}
39 ${self.breadcrumbs()}
40 </div>
40 </div>
41
41
42 <div class="sidebar-col-wrapper scw-small">
42 <div class="sidebar-col-wrapper scw-small">
43 <div class="sidebar">
43 <div class="sidebar">
44 <ul class="nav nav-pills nav-stacked">
44 <ul class="nav nav-pills nav-stacked">
45 <li class="${'active' if c.active=='settings' else ''}">
45 <li class="${'active' if c.active=='settings' else ''}">
46 <a href="${h.route_path('edit_repo', repo_name=c.repo_name)}">${_('Settings')}</a>
46 <a href="${h.route_path('edit_repo', repo_name=c.repo_name)}">${_('Settings')}</a>
47 </li>
47 </li>
48 <li class="${'active' if c.active=='permissions' else ''}">
48 <li class="${'active' if c.active=='permissions' else ''}">
49 <a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
49 <a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
50 </li>
50 </li>
51 <li class="${'active' if c.active=='advanced' else ''}">
51 <li class="${'active' if c.active=='advanced' else ''}">
52 <a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
52 <a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
53 </li>
53 </li>
54 <li class="${'active' if c.active=='vcs' else ''}">
54 <li class="${'active' if c.active=='vcs' else ''}">
55 <a href="${h.route_path('edit_repo_vcs', repo_name=c.repo_name)}">${_('VCS')}</a>
55 <a href="${h.route_path('edit_repo_vcs', repo_name=c.repo_name)}">${_('VCS')}</a>
56 </li>
56 </li>
57 <li class="${'active' if c.active=='fields' else ''}">
57 <li class="${'active' if c.active=='fields' else ''}">
58 <a href="${h.route_path('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
58 <a href="${h.route_path('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
59 </li>
59 </li>
60 <li class="${'active' if c.active=='issuetracker' else ''}">
60 <li class="${'active' if c.active=='issuetracker' else ''}">
61 <a href="${h.route_path('edit_repo_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
61 <a href="${h.route_path('edit_repo_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
62 </li>
62 </li>
63 <li class="${'active' if c.active=='caches' else ''}">
63 <li class="${'active' if c.active=='caches' else ''}">
64 <a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
64 <a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
65 </li>
65 </li>
66 %if c.rhodecode_db_repo.repo_type != 'svn':
66 %if c.rhodecode_db_repo.repo_type != 'svn':
67 <li class="${'active' if c.active=='remote' else ''}">
67 <li class="${'active' if c.active=='remote' else ''}">
68 <a href="${h.route_path('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
68 <a href="${h.route_path('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
69 </li>
69 </li>
70 %endif
70 %endif
71 <li class="${'active' if c.active=='statistics' else ''}">
71 <li class="${'active' if c.active=='statistics' else ''}">
72 <a href="${h.route_path('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
72 <a href="${h.route_path('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
73 </li>
73 </li>
74 <li class="${'active' if c.active=='integrations' else ''}">
74 <li class="${'active' if c.active=='integrations' else ''}">
75 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
75 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
76 </li>
76 </li>
77 %if c.rhodecode_db_repo.repo_type != 'svn':
77 %if c.rhodecode_db_repo.repo_type != 'svn':
78 <li class="${'active' if c.active=='reviewers' else ''}">
78 <li class="${'active' if c.active=='reviewers' else ''}">
79 <a href="${h.route_path('repo_reviewers', repo_name=c.repo_name)}">${_('Reviewer Rules')}</a>
79 <a href="${h.route_path('repo_reviewers', repo_name=c.repo_name)}">${_('Reviewer Rules')}</a>
80 </li>
80 </li>
81 %endif
81 %endif
82 <li class="${'active' if c.active=='maintenance' else ''}">
82 <li class="${'active' if c.active=='maintenance' else ''}">
83 <a href="${h.route_path('edit_repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a>
83 <a href="${h.route_path('edit_repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a>
84 </li>
84 </li>
85 <li class="${'active' if c.active=='strip' else ''}">
85 <li class="${'active' if c.active=='strip' else ''}">
86 <a href="${h.route_path('edit_repo_strip', repo_name=c.repo_name)}">${_('Strip')}</a>
86 <a href="${h.route_path('edit_repo_strip', repo_name=c.repo_name)}">${_('Strip')}</a>
87 </li>
87 </li>
88 <li class="${'active' if c.active=='audit' else ''}">
89 <a href="${h.route_path('edit_repo_audit_logs', repo_name=c.repo_name)}">${_('Audit logs')}</a>
90 </li>
88
91
89 </ul>
92 </ul>
90 </div>
93 </div>
91
94
92 <div class="main-content-full-width">
95 <div class="main-content-full-width">
93 ${self.main_content()}
96 ${self.main_content()}
94 </div>
97 </div>
95
98
96 </div>
99 </div>
97 </div>
100 </div>
98
101
99 </%def> No newline at end of file
102 </%def>
@@ -1,57 +1,57 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('%s user settings') % c.user.username}
5 ${_('%s user settings') % c.user.username}
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
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('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;
16 % if c.user.active:
16 % if c.user.active:
17 ${c.user.username}
17 ${c.user.username}
18 % else:
18 % else:
19 <strike title="${_('This user is set as disabled')}">${c.user.username}</strike>
19 <strike title="${_('This user is set as disabled')}">${c.user.username}</strike>
20 % endif
20 % endif
21
21
22 </%def>
22 </%def>
23
23
24 <%def name="menu_bar_nav()">
24 <%def name="menu_bar_nav()">
25 ${self.menu_items(active='admin')}
25 ${self.menu_items(active='admin')}
26 </%def>
26 </%def>
27
27
28 <%def name="main()">
28 <%def name="main()">
29 <div class="box user_settings">
29 <div class="box user_settings">
30 <div class="title">
30 <div class="title">
31 ${self.breadcrumbs()}
31 ${self.breadcrumbs()}
32 </div>
32 </div>
33
33
34 ##main
34 ##main
35 <div class="sidebar-col-wrapper">
35 <div class="sidebar-col-wrapper">
36 <div class="sidebar">
36 <div class="sidebar">
37 <ul class="nav nav-pills nav-stacked">
37 <ul class="nav nav-pills nav-stacked">
38 <li class="${'active' if c.active=='profile' else ''}"><a href="${h.route_path('user_edit', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
38 <li class="${'active' if c.active=='profile' else ''}"><a href="${h.route_path('user_edit', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
39 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
39 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
40 <li class="${'active' if c.active in ['ssh_keys','ssh_keys_generate'] else ''}"><a href="${h.route_path('edit_user_ssh_keys', user_id=c.user.user_id)}">${_('SSH Keys')}</a></li>
40 <li class="${'active' if c.active in ['ssh_keys','ssh_keys_generate'] else ''}"><a href="${h.route_path('edit_user_ssh_keys', user_id=c.user.user_id)}">${_('SSH Keys')}</a></li>
41 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.route_path('user_edit_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
41 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.route_path('user_edit_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
42 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.route_path('user_edit_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
42 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.route_path('user_edit_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
43 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.route_path('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
43 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.route_path('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
44 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
44 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
45 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.route_path('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
45 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.route_path('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
46 <li class="${'active' if c.active=='groups' else ''}"><a href="${h.route_path('edit_user_groups_management', user_id=c.user.user_id)}">${_('User Groups Management')}</a></li>
46 <li class="${'active' if c.active=='groups' else ''}"><a href="${h.route_path('edit_user_groups_management', user_id=c.user.user_id)}">${_('User Groups Management')}</a></li>
47 <li class="${'active' if c.active=='audit' else ''}"><a href="${h.route_path('edit_user_audit_logs', user_id=c.user.user_id)}">${_('User audit')}</a></li>
47 <li class="${'active' if c.active=='audit' else ''}"><a href="${h.route_path('edit_user_audit_logs', user_id=c.user.user_id)}">${_('Audit logs')}</a></li>
48 </ul>
48 </ul>
49 </div>
49 </div>
50
50
51 <div class="main-content-full-width">
51 <div class="main-content-full-width">
52 <%include file="/admin/users/user_edit_${c.active}.mako"/>
52 <%include file="/admin/users/user_edit_${c.active}.mako"/>
53 </div>
53 </div>
54 </div>
54 </div>
55 </div>
55 </div>
56
56
57 </%def>
57 </%def>
General Comments 0
You need to be logged in to leave comments. Login now