##// END OF EJS Templates
Repo type can't be changed. Fixes issue 836
Simon Lopez -
r3858:d11ecf2f default
parent child Browse files
Show More
@@ -1,703 +1,703 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import shutil
27 import shutil
28 import logging
28 import logging
29 import traceback
29 import traceback
30 from datetime import datetime
30 from datetime import datetime
31
31
32 from rhodecode.lib.vcs.backends import get_backend
32 from rhodecode.lib.vcs.backends import get_backend
33 from rhodecode.lib.compat import json
33 from rhodecode.lib.compat import json
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
35 remove_prefix, obfuscate_url_pw
35 remove_prefix, obfuscate_url_pw
36 from rhodecode.lib.caching_query import FromCache
36 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
38
38
39 from rhodecode.model import BaseModel
39 from rhodecode.model import BaseModel
40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
41 Statistics, UserGroup, UserGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
41 Statistics, UserGroup, UserGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
42 RhodeCodeSetting, RepositoryField
42 RhodeCodeSetting, RepositoryField
43 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
44 from rhodecode.lib.auth import HasRepoPermissionAny
44 from rhodecode.lib.auth import HasRepoPermissionAny
45 from rhodecode.lib.exceptions import AttachedForksError
45 from rhodecode.lib.exceptions import AttachedForksError
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class RepoModel(BaseModel):
50 class RepoModel(BaseModel):
51
51
52 cls = Repository
52 cls = Repository
53 URL_SEPARATOR = Repository.url_sep()
53 URL_SEPARATOR = Repository.url_sep()
54
54
55 def __get_users_group(self, users_group):
55 def __get_users_group(self, users_group):
56 return self._get_instance(UserGroup, users_group,
56 return self._get_instance(UserGroup, users_group,
57 callback=UserGroup.get_by_group_name)
57 callback=UserGroup.get_by_group_name)
58
58
59 def _get_repos_group(self, repos_group):
59 def _get_repos_group(self, repos_group):
60 return self._get_instance(RepoGroup, repos_group,
60 return self._get_instance(RepoGroup, repos_group,
61 callback=RepoGroup.get_by_group_name)
61 callback=RepoGroup.get_by_group_name)
62
62
63 @LazyProperty
63 @LazyProperty
64 def repos_path(self):
64 def repos_path(self):
65 """
65 """
66 Get's the repositories root path from database
66 Get's the repositories root path from database
67 """
67 """
68
68
69 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
69 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
70 return q.ui_value
70 return q.ui_value
71
71
72 def get(self, repo_id, cache=False):
72 def get(self, repo_id, cache=False):
73 repo = self.sa.query(Repository)\
73 repo = self.sa.query(Repository)\
74 .filter(Repository.repo_id == repo_id)
74 .filter(Repository.repo_id == repo_id)
75
75
76 if cache:
76 if cache:
77 repo = repo.options(FromCache("sql_cache_short",
77 repo = repo.options(FromCache("sql_cache_short",
78 "get_repo_%s" % repo_id))
78 "get_repo_%s" % repo_id))
79 return repo.scalar()
79 return repo.scalar()
80
80
81 def get_repo(self, repository):
81 def get_repo(self, repository):
82 return self._get_repo(repository)
82 return self._get_repo(repository)
83
83
84 def get_by_repo_name(self, repo_name, cache=False):
84 def get_by_repo_name(self, repo_name, cache=False):
85 repo = self.sa.query(Repository)\
85 repo = self.sa.query(Repository)\
86 .filter(Repository.repo_name == repo_name)
86 .filter(Repository.repo_name == repo_name)
87
87
88 if cache:
88 if cache:
89 repo = repo.options(FromCache("sql_cache_short",
89 repo = repo.options(FromCache("sql_cache_short",
90 "get_repo_%s" % repo_name))
90 "get_repo_%s" % repo_name))
91 return repo.scalar()
91 return repo.scalar()
92
92
93 def get_all_user_repos(self, user):
93 def get_all_user_repos(self, user):
94 """
94 """
95 Get's all repositories that user have at least read access
95 Get's all repositories that user have at least read access
96
96
97 :param user:
97 :param user:
98 :type user:
98 :type user:
99 """
99 """
100 from rhodecode.lib.auth import AuthUser
100 from rhodecode.lib.auth import AuthUser
101 user = self._get_user(user)
101 user = self._get_user(user)
102 repos = AuthUser(user_id=user.user_id).permissions['repositories']
102 repos = AuthUser(user_id=user.user_id).permissions['repositories']
103 access_check = lambda r: r[1] in ['repository.read',
103 access_check = lambda r: r[1] in ['repository.read',
104 'repository.write',
104 'repository.write',
105 'repository.admin']
105 'repository.admin']
106 repos = [x[0] for x in filter(access_check, repos.items())]
106 repos = [x[0] for x in filter(access_check, repos.items())]
107 return Repository.query().filter(Repository.repo_name.in_(repos))
107 return Repository.query().filter(Repository.repo_name.in_(repos))
108
108
109 def get_users_js(self):
109 def get_users_js(self):
110 users = self.sa.query(User).filter(User.active == True).all()
110 users = self.sa.query(User).filter(User.active == True).all()
111 return json.dumps([
111 return json.dumps([
112 {
112 {
113 'id': u.user_id,
113 'id': u.user_id,
114 'fname': u.name,
114 'fname': u.name,
115 'lname': u.lastname,
115 'lname': u.lastname,
116 'nname': u.username,
116 'nname': u.username,
117 'gravatar_lnk': h.gravatar_url(u.email, 14)
117 'gravatar_lnk': h.gravatar_url(u.email, 14)
118 } for u in users]
118 } for u in users]
119 )
119 )
120
120
121 def get_users_groups_js(self):
121 def get_users_groups_js(self):
122 users_groups = self.sa.query(UserGroup)\
122 users_groups = self.sa.query(UserGroup)\
123 .filter(UserGroup.users_group_active == True).all()
123 .filter(UserGroup.users_group_active == True).all()
124
124
125 return json.dumps([
125 return json.dumps([
126 {
126 {
127 'id': gr.users_group_id,
127 'id': gr.users_group_id,
128 'grname': gr.users_group_name,
128 'grname': gr.users_group_name,
129 'grmembers': len(gr.members),
129 'grmembers': len(gr.members),
130 } for gr in users_groups]
130 } for gr in users_groups]
131 )
131 )
132
132
133 @classmethod
133 @classmethod
134 def _render_datatable(cls, tmpl, *args, **kwargs):
134 def _render_datatable(cls, tmpl, *args, **kwargs):
135 import rhodecode
135 import rhodecode
136 from pylons import tmpl_context as c
136 from pylons import tmpl_context as c
137 from pylons.i18n.translation import _
137 from pylons.i18n.translation import _
138
138
139 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
139 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
140 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
140 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
141
141
142 tmpl = template.get_def(tmpl)
142 tmpl = template.get_def(tmpl)
143 kwargs.update(dict(_=_, h=h, c=c))
143 kwargs.update(dict(_=_, h=h, c=c))
144 return tmpl.render(*args, **kwargs)
144 return tmpl.render(*args, **kwargs)
145
145
146 @classmethod
146 @classmethod
147 def update_repoinfo(cls, repositories=None):
147 def update_repoinfo(cls, repositories=None):
148 if not repositories:
148 if not repositories:
149 repositories = Repository.getAll()
149 repositories = Repository.getAll()
150 for repo in repositories:
150 for repo in repositories:
151 repo.update_changeset_cache()
151 repo.update_changeset_cache()
152
152
153 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
153 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
154 super_user_actions=False):
154 super_user_actions=False):
155 _render = self._render_datatable
155 _render = self._render_datatable
156
156
157 def quick_menu(repo_name):
157 def quick_menu(repo_name):
158 return _render('quick_menu', repo_name)
158 return _render('quick_menu', repo_name)
159
159
160 def repo_lnk(name, rtype, private, fork_of):
160 def repo_lnk(name, rtype, private, fork_of):
161 return _render('repo_name', name, rtype, private, fork_of,
161 return _render('repo_name', name, rtype, private, fork_of,
162 short_name=not admin, admin=False)
162 short_name=not admin, admin=False)
163
163
164 def last_change(last_change):
164 def last_change(last_change):
165 return _render("last_change", last_change)
165 return _render("last_change", last_change)
166
166
167 def rss_lnk(repo_name):
167 def rss_lnk(repo_name):
168 return _render("rss", repo_name)
168 return _render("rss", repo_name)
169
169
170 def atom_lnk(repo_name):
170 def atom_lnk(repo_name):
171 return _render("atom", repo_name)
171 return _render("atom", repo_name)
172
172
173 def last_rev(repo_name, cs_cache):
173 def last_rev(repo_name, cs_cache):
174 return _render('revision', repo_name, cs_cache.get('revision'),
174 return _render('revision', repo_name, cs_cache.get('revision'),
175 cs_cache.get('raw_id'), cs_cache.get('author'),
175 cs_cache.get('raw_id'), cs_cache.get('author'),
176 cs_cache.get('message'))
176 cs_cache.get('message'))
177
177
178 def desc(desc):
178 def desc(desc):
179 from pylons import tmpl_context as c
179 from pylons import tmpl_context as c
180 if c.visual.stylify_metatags:
180 if c.visual.stylify_metatags:
181 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
181 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
182 else:
182 else:
183 return h.urlify_text(h.truncate(desc, 60))
183 return h.urlify_text(h.truncate(desc, 60))
184
184
185 def repo_actions(repo_name):
185 def repo_actions(repo_name):
186 return _render('repo_actions', repo_name, super_user_actions)
186 return _render('repo_actions', repo_name, super_user_actions)
187
187
188 def owner_actions(user_id, username):
188 def owner_actions(user_id, username):
189 return _render('user_name', user_id, username)
189 return _render('user_name', user_id, username)
190
190
191 repos_data = []
191 repos_data = []
192 for repo in repos_list:
192 for repo in repos_list:
193 if perm_check:
193 if perm_check:
194 # check permission at this level
194 # check permission at this level
195 if not HasRepoPermissionAny(
195 if not HasRepoPermissionAny(
196 'repository.read', 'repository.write', 'repository.admin'
196 'repository.read', 'repository.write', 'repository.admin'
197 )(repo.repo_name, 'get_repos_as_dict check'):
197 )(repo.repo_name, 'get_repos_as_dict check'):
198 continue
198 continue
199 cs_cache = repo.changeset_cache
199 cs_cache = repo.changeset_cache
200 row = {
200 row = {
201 "menu": quick_menu(repo.repo_name),
201 "menu": quick_menu(repo.repo_name),
202 "raw_name": repo.repo_name.lower(),
202 "raw_name": repo.repo_name.lower(),
203 "name": repo_lnk(repo.repo_name, repo.repo_type,
203 "name": repo_lnk(repo.repo_name, repo.repo_type,
204 repo.private, repo.fork),
204 repo.private, repo.fork),
205 "last_change": last_change(repo.last_db_change),
205 "last_change": last_change(repo.last_db_change),
206 "last_changeset": last_rev(repo.repo_name, cs_cache),
206 "last_changeset": last_rev(repo.repo_name, cs_cache),
207 "raw_tip": cs_cache.get('revision'),
207 "raw_tip": cs_cache.get('revision'),
208 "desc": desc(repo.description),
208 "desc": desc(repo.description),
209 "owner": h.person(repo.user.username),
209 "owner": h.person(repo.user.username),
210 "rss": rss_lnk(repo.repo_name),
210 "rss": rss_lnk(repo.repo_name),
211 "atom": atom_lnk(repo.repo_name),
211 "atom": atom_lnk(repo.repo_name),
212
212
213 }
213 }
214 if admin:
214 if admin:
215 row.update({
215 row.update({
216 "action": repo_actions(repo.repo_name),
216 "action": repo_actions(repo.repo_name),
217 "owner": owner_actions(repo.user.user_id,
217 "owner": owner_actions(repo.user.user_id,
218 h.person(repo.user.username))
218 h.person(repo.user.username))
219 })
219 })
220 repos_data.append(row)
220 repos_data.append(row)
221
221
222 return {
222 return {
223 "totalRecords": len(repos_list),
223 "totalRecords": len(repos_list),
224 "startIndex": 0,
224 "startIndex": 0,
225 "sort": "name",
225 "sort": "name",
226 "dir": "asc",
226 "dir": "asc",
227 "records": repos_data
227 "records": repos_data
228 }
228 }
229
229
230 def _get_defaults(self, repo_name):
230 def _get_defaults(self, repo_name):
231 """
231 """
232 Get's information about repository, and returns a dict for
232 Get's information about repository, and returns a dict for
233 usage in forms
233 usage in forms
234
234
235 :param repo_name:
235 :param repo_name:
236 """
236 """
237
237
238 repo_info = Repository.get_by_repo_name(repo_name)
238 repo_info = Repository.get_by_repo_name(repo_name)
239
239
240 if repo_info is None:
240 if repo_info is None:
241 return None
241 return None
242
242
243 defaults = repo_info.get_dict()
243 defaults = repo_info.get_dict()
244 group, repo_name, repo_name_full = repo_info.groups_and_repo
244 group, repo_name, repo_name_full = repo_info.groups_and_repo
245 defaults['repo_name'] = repo_name
245 defaults['repo_name'] = repo_name
246 defaults['repo_group'] = getattr(group[-1] if group else None,
246 defaults['repo_group'] = getattr(group[-1] if group else None,
247 'group_id', None)
247 'group_id', None)
248
248
249 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
249 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
250 (1, 'repo_description'), (1, 'repo_enable_locking'),
250 (1, 'repo_description'), (1, 'repo_enable_locking'),
251 (1, 'repo_landing_rev'), (0, 'clone_uri'),
251 (1, 'repo_landing_rev'), (0, 'clone_uri'),
252 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
252 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
253 attr = k
253 attr = k
254 if strip:
254 if strip:
255 attr = remove_prefix(k, 'repo_')
255 attr = remove_prefix(k, 'repo_')
256
256
257 defaults[k] = defaults[attr]
257 defaults[k] = defaults[attr]
258
258
259 # fill owner
259 # fill owner
260 if repo_info.user:
260 if repo_info.user:
261 defaults.update({'user': repo_info.user.username})
261 defaults.update({'user': repo_info.user.username})
262 else:
262 else:
263 replacement_user = User.query().filter(User.admin ==
263 replacement_user = User.query().filter(User.admin ==
264 True).first().username
264 True).first().username
265 defaults.update({'user': replacement_user})
265 defaults.update({'user': replacement_user})
266
266
267 # fill repository users
267 # fill repository users
268 for p in repo_info.repo_to_perm:
268 for p in repo_info.repo_to_perm:
269 defaults.update({'u_perm_%s' % p.user.username:
269 defaults.update({'u_perm_%s' % p.user.username:
270 p.permission.permission_name})
270 p.permission.permission_name})
271
271
272 # fill repository groups
272 # fill repository groups
273 for p in repo_info.users_group_to_perm:
273 for p in repo_info.users_group_to_perm:
274 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
274 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
275 p.permission.permission_name})
275 p.permission.permission_name})
276
276
277 return defaults
277 return defaults
278
278
279 def update(self, org_repo_name, **kwargs):
279 def update(self, org_repo_name, **kwargs):
280 try:
280 try:
281 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
281 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
282
282
283 if 'user' in kwargs:
283 if 'user' in kwargs:
284 cur_repo.user = User.get_by_username(kwargs['user'])
284 cur_repo.user = User.get_by_username(kwargs['user'])
285
285
286 if 'repo_group' in kwargs:
286 if 'repo_group' in kwargs:
287 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
287 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
288
288
289 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
289 for strip, k in [(1, 'repo_enable_downloads'),
290 (1, 'repo_description'), (1, 'repo_enable_locking'),
290 (1, 'repo_description'), (1, 'repo_enable_locking'),
291 (1, 'repo_landing_rev'), (0, 'clone_uri'),
291 (1, 'repo_landing_rev'), (0, 'clone_uri'),
292 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
292 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
293 if k in kwargs:
293 if k in kwargs:
294 val = kwargs[k]
294 val = kwargs[k]
295 if strip:
295 if strip:
296 k = remove_prefix(k, 'repo_')
296 k = remove_prefix(k, 'repo_')
297 setattr(cur_repo, k, val)
297 setattr(cur_repo, k, val)
298
298
299 new_name = cur_repo.get_new_name(kwargs['repo_name'])
299 new_name = cur_repo.get_new_name(kwargs['repo_name'])
300 cur_repo.repo_name = new_name
300 cur_repo.repo_name = new_name
301 #if private flag is set, reset default permission to NONE
301 #if private flag is set, reset default permission to NONE
302
302
303 if kwargs.get('repo_private'):
303 if kwargs.get('repo_private'):
304 EMPTY_PERM = 'repository.none'
304 EMPTY_PERM = 'repository.none'
305 RepoModel().grant_user_permission(
305 RepoModel().grant_user_permission(
306 repo=cur_repo, user='default', perm=EMPTY_PERM
306 repo=cur_repo, user='default', perm=EMPTY_PERM
307 )
307 )
308 #handle extra fields
308 #handle extra fields
309 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
309 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
310 k = RepositoryField.un_prefix_key(field)
310 k = RepositoryField.un_prefix_key(field)
311 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
311 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
312 if ex_field:
312 if ex_field:
313 ex_field.field_value = kwargs[field]
313 ex_field.field_value = kwargs[field]
314 self.sa.add(ex_field)
314 self.sa.add(ex_field)
315 self.sa.add(cur_repo)
315 self.sa.add(cur_repo)
316
316
317 if org_repo_name != new_name:
317 if org_repo_name != new_name:
318 # rename repository
318 # rename repository
319 self.__rename_repo(old=org_repo_name, new=new_name)
319 self.__rename_repo(old=org_repo_name, new=new_name)
320
320
321 return cur_repo
321 return cur_repo
322 except Exception:
322 except Exception:
323 log.error(traceback.format_exc())
323 log.error(traceback.format_exc())
324 raise
324 raise
325
325
326 def create_repo(self, repo_name, repo_type, description, owner,
326 def create_repo(self, repo_name, repo_type, description, owner,
327 private=False, clone_uri=None, repos_group=None,
327 private=False, clone_uri=None, repos_group=None,
328 landing_rev='tip', just_db=False, fork_of=None,
328 landing_rev='tip', just_db=False, fork_of=None,
329 copy_fork_permissions=False, enable_statistics=False,
329 copy_fork_permissions=False, enable_statistics=False,
330 enable_locking=False, enable_downloads=False):
330 enable_locking=False, enable_downloads=False):
331 """
331 """
332 Create repository
332 Create repository
333
333
334 """
334 """
335 from rhodecode.model.scm import ScmModel
335 from rhodecode.model.scm import ScmModel
336
336
337 owner = self._get_user(owner)
337 owner = self._get_user(owner)
338 fork_of = self._get_repo(fork_of)
338 fork_of = self._get_repo(fork_of)
339 repos_group = self._get_repos_group(repos_group)
339 repos_group = self._get_repos_group(repos_group)
340 try:
340 try:
341
341
342 # repo name is just a name of repository
342 # repo name is just a name of repository
343 # while repo_name_full is a full qualified name that is combined
343 # while repo_name_full is a full qualified name that is combined
344 # with name and path of group
344 # with name and path of group
345 repo_name_full = repo_name
345 repo_name_full = repo_name
346 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
346 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
347
347
348 new_repo = Repository()
348 new_repo = Repository()
349 new_repo.enable_statistics = False
349 new_repo.enable_statistics = False
350 new_repo.repo_name = repo_name_full
350 new_repo.repo_name = repo_name_full
351 new_repo.repo_type = repo_type
351 new_repo.repo_type = repo_type
352 new_repo.user = owner
352 new_repo.user = owner
353 new_repo.group = repos_group
353 new_repo.group = repos_group
354 new_repo.description = description or repo_name
354 new_repo.description = description or repo_name
355 new_repo.private = private
355 new_repo.private = private
356 new_repo.clone_uri = clone_uri
356 new_repo.clone_uri = clone_uri
357 new_repo.landing_rev = landing_rev
357 new_repo.landing_rev = landing_rev
358
358
359 new_repo.enable_statistics = enable_statistics
359 new_repo.enable_statistics = enable_statistics
360 new_repo.enable_locking = enable_locking
360 new_repo.enable_locking = enable_locking
361 new_repo.enable_downloads = enable_downloads
361 new_repo.enable_downloads = enable_downloads
362
362
363 if repos_group:
363 if repos_group:
364 new_repo.enable_locking = repos_group.enable_locking
364 new_repo.enable_locking = repos_group.enable_locking
365
365
366 if fork_of:
366 if fork_of:
367 parent_repo = fork_of
367 parent_repo = fork_of
368 new_repo.fork = parent_repo
368 new_repo.fork = parent_repo
369
369
370 self.sa.add(new_repo)
370 self.sa.add(new_repo)
371
371
372 def _create_default_perms():
372 def _create_default_perms():
373 # create default permission
373 # create default permission
374 repo_to_perm = UserRepoToPerm()
374 repo_to_perm = UserRepoToPerm()
375 default = 'repository.read'
375 default = 'repository.read'
376 for p in User.get_by_username('default').user_perms:
376 for p in User.get_by_username('default').user_perms:
377 if p.permission.permission_name.startswith('repository.'):
377 if p.permission.permission_name.startswith('repository.'):
378 default = p.permission.permission_name
378 default = p.permission.permission_name
379 break
379 break
380
380
381 default_perm = 'repository.none' if private else default
381 default_perm = 'repository.none' if private else default
382
382
383 repo_to_perm.permission_id = self.sa.query(Permission)\
383 repo_to_perm.permission_id = self.sa.query(Permission)\
384 .filter(Permission.permission_name == default_perm)\
384 .filter(Permission.permission_name == default_perm)\
385 .one().permission_id
385 .one().permission_id
386
386
387 repo_to_perm.repository = new_repo
387 repo_to_perm.repository = new_repo
388 repo_to_perm.user_id = User.get_by_username('default').user_id
388 repo_to_perm.user_id = User.get_by_username('default').user_id
389
389
390 self.sa.add(repo_to_perm)
390 self.sa.add(repo_to_perm)
391
391
392 if fork_of:
392 if fork_of:
393 if copy_fork_permissions:
393 if copy_fork_permissions:
394 repo = fork_of
394 repo = fork_of
395 user_perms = UserRepoToPerm.query()\
395 user_perms = UserRepoToPerm.query()\
396 .filter(UserRepoToPerm.repository == repo).all()
396 .filter(UserRepoToPerm.repository == repo).all()
397 group_perms = UserGroupRepoToPerm.query()\
397 group_perms = UserGroupRepoToPerm.query()\
398 .filter(UserGroupRepoToPerm.repository == repo).all()
398 .filter(UserGroupRepoToPerm.repository == repo).all()
399
399
400 for perm in user_perms:
400 for perm in user_perms:
401 UserRepoToPerm.create(perm.user, new_repo,
401 UserRepoToPerm.create(perm.user, new_repo,
402 perm.permission)
402 perm.permission)
403
403
404 for perm in group_perms:
404 for perm in group_perms:
405 UserGroupRepoToPerm.create(perm.users_group, new_repo,
405 UserGroupRepoToPerm.create(perm.users_group, new_repo,
406 perm.permission)
406 perm.permission)
407 else:
407 else:
408 _create_default_perms()
408 _create_default_perms()
409 else:
409 else:
410 _create_default_perms()
410 _create_default_perms()
411
411
412 if not just_db:
412 if not just_db:
413 self.__create_repo(repo_name, repo_type,
413 self.__create_repo(repo_name, repo_type,
414 repos_group,
414 repos_group,
415 clone_uri)
415 clone_uri)
416 log_create_repository(new_repo.get_dict(),
416 log_create_repository(new_repo.get_dict(),
417 created_by=owner.username)
417 created_by=owner.username)
418
418
419 # now automatically start following this repository as owner
419 # now automatically start following this repository as owner
420 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
420 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
421 owner.user_id)
421 owner.user_id)
422 return new_repo
422 return new_repo
423 except Exception:
423 except Exception:
424 log.error(traceback.format_exc())
424 log.error(traceback.format_exc())
425 raise
425 raise
426
426
427 def create(self, form_data, cur_user, just_db=False, fork=None):
427 def create(self, form_data, cur_user, just_db=False, fork=None):
428 """
428 """
429 Backward compatibility function, just a wrapper on top of create_repo
429 Backward compatibility function, just a wrapper on top of create_repo
430
430
431 :param form_data:
431 :param form_data:
432 :param cur_user:
432 :param cur_user:
433 :param just_db:
433 :param just_db:
434 :param fork:
434 :param fork:
435 """
435 """
436 owner = cur_user
436 owner = cur_user
437 repo_name = form_data['repo_name_full']
437 repo_name = form_data['repo_name_full']
438 repo_type = form_data['repo_type']
438 repo_type = form_data['repo_type']
439 description = form_data['repo_description']
439 description = form_data['repo_description']
440 private = form_data['repo_private']
440 private = form_data['repo_private']
441 clone_uri = form_data.get('clone_uri')
441 clone_uri = form_data.get('clone_uri')
442 repos_group = form_data['repo_group']
442 repos_group = form_data['repo_group']
443 landing_rev = form_data['repo_landing_rev']
443 landing_rev = form_data['repo_landing_rev']
444 copy_fork_permissions = form_data.get('copy_permissions')
444 copy_fork_permissions = form_data.get('copy_permissions')
445 fork_of = form_data.get('fork_parent_id')
445 fork_of = form_data.get('fork_parent_id')
446
446
447 ## repo creation defaults, private and repo_type are filled in form
447 ## repo creation defaults, private and repo_type are filled in form
448 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
448 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
449 enable_statistics = defs.get('repo_enable_statistics')
449 enable_statistics = defs.get('repo_enable_statistics')
450 enable_locking = defs.get('repo_enable_locking')
450 enable_locking = defs.get('repo_enable_locking')
451 enable_downloads = defs.get('repo_enable_downloads')
451 enable_downloads = defs.get('repo_enable_downloads')
452
452
453 return self.create_repo(
453 return self.create_repo(
454 repo_name, repo_type, description, owner, private, clone_uri,
454 repo_name, repo_type, description, owner, private, clone_uri,
455 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
455 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
456 enable_statistics, enable_locking, enable_downloads
456 enable_statistics, enable_locking, enable_downloads
457 )
457 )
458
458
459 def create_fork(self, form_data, cur_user):
459 def create_fork(self, form_data, cur_user):
460 """
460 """
461 Simple wrapper into executing celery task for fork creation
461 Simple wrapper into executing celery task for fork creation
462
462
463 :param form_data:
463 :param form_data:
464 :param cur_user:
464 :param cur_user:
465 """
465 """
466 from rhodecode.lib.celerylib import tasks, run_task
466 from rhodecode.lib.celerylib import tasks, run_task
467 run_task(tasks.create_repo_fork, form_data, cur_user)
467 run_task(tasks.create_repo_fork, form_data, cur_user)
468
468
469 def delete(self, repo, forks=None):
469 def delete(self, repo, forks=None):
470 """
470 """
471 Delete given repository, forks parameter defines what do do with
471 Delete given repository, forks parameter defines what do do with
472 attached forks. Throws AttachedForksError if deleted repo has attached
472 attached forks. Throws AttachedForksError if deleted repo has attached
473 forks
473 forks
474
474
475 :param repo:
475 :param repo:
476 :param forks: str 'delete' or 'detach'
476 :param forks: str 'delete' or 'detach'
477 """
477 """
478 repo = self._get_repo(repo)
478 repo = self._get_repo(repo)
479 if repo:
479 if repo:
480 if forks == 'detach':
480 if forks == 'detach':
481 for r in repo.forks:
481 for r in repo.forks:
482 r.fork = None
482 r.fork = None
483 self.sa.add(r)
483 self.sa.add(r)
484 elif forks == 'delete':
484 elif forks == 'delete':
485 for r in repo.forks:
485 for r in repo.forks:
486 self.delete(r, forks='delete')
486 self.delete(r, forks='delete')
487 elif [f for f in repo.forks]:
487 elif [f for f in repo.forks]:
488 raise AttachedForksError()
488 raise AttachedForksError()
489
489
490 old_repo_dict = repo.get_dict()
490 old_repo_dict = repo.get_dict()
491 owner = repo.user
491 owner = repo.user
492 try:
492 try:
493 self.sa.delete(repo)
493 self.sa.delete(repo)
494 self.__delete_repo(repo)
494 self.__delete_repo(repo)
495 log_delete_repository(old_repo_dict,
495 log_delete_repository(old_repo_dict,
496 deleted_by=owner.username)
496 deleted_by=owner.username)
497 except Exception:
497 except Exception:
498 log.error(traceback.format_exc())
498 log.error(traceback.format_exc())
499 raise
499 raise
500
500
501 def grant_user_permission(self, repo, user, perm):
501 def grant_user_permission(self, repo, user, perm):
502 """
502 """
503 Grant permission for user on given repository, or update existing one
503 Grant permission for user on given repository, or update existing one
504 if found
504 if found
505
505
506 :param repo: Instance of Repository, repository_id, or repository name
506 :param repo: Instance of Repository, repository_id, or repository name
507 :param user: Instance of User, user_id or username
507 :param user: Instance of User, user_id or username
508 :param perm: Instance of Permission, or permission_name
508 :param perm: Instance of Permission, or permission_name
509 """
509 """
510 user = self._get_user(user)
510 user = self._get_user(user)
511 repo = self._get_repo(repo)
511 repo = self._get_repo(repo)
512 permission = self._get_perm(perm)
512 permission = self._get_perm(perm)
513
513
514 # check if we have that permission already
514 # check if we have that permission already
515 obj = self.sa.query(UserRepoToPerm)\
515 obj = self.sa.query(UserRepoToPerm)\
516 .filter(UserRepoToPerm.user == user)\
516 .filter(UserRepoToPerm.user == user)\
517 .filter(UserRepoToPerm.repository == repo)\
517 .filter(UserRepoToPerm.repository == repo)\
518 .scalar()
518 .scalar()
519 if obj is None:
519 if obj is None:
520 # create new !
520 # create new !
521 obj = UserRepoToPerm()
521 obj = UserRepoToPerm()
522 obj.repository = repo
522 obj.repository = repo
523 obj.user = user
523 obj.user = user
524 obj.permission = permission
524 obj.permission = permission
525 self.sa.add(obj)
525 self.sa.add(obj)
526 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
526 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
527
527
528 def revoke_user_permission(self, repo, user):
528 def revoke_user_permission(self, repo, user):
529 """
529 """
530 Revoke permission for user on given repository
530 Revoke permission for user on given repository
531
531
532 :param repo: Instance of Repository, repository_id, or repository name
532 :param repo: Instance of Repository, repository_id, or repository name
533 :param user: Instance of User, user_id or username
533 :param user: Instance of User, user_id or username
534 """
534 """
535
535
536 user = self._get_user(user)
536 user = self._get_user(user)
537 repo = self._get_repo(repo)
537 repo = self._get_repo(repo)
538
538
539 obj = self.sa.query(UserRepoToPerm)\
539 obj = self.sa.query(UserRepoToPerm)\
540 .filter(UserRepoToPerm.repository == repo)\
540 .filter(UserRepoToPerm.repository == repo)\
541 .filter(UserRepoToPerm.user == user)\
541 .filter(UserRepoToPerm.user == user)\
542 .scalar()
542 .scalar()
543 if obj:
543 if obj:
544 self.sa.delete(obj)
544 self.sa.delete(obj)
545 log.debug('Revoked perm on %s on %s' % (repo, user))
545 log.debug('Revoked perm on %s on %s' % (repo, user))
546
546
547 def grant_users_group_permission(self, repo, group_name, perm):
547 def grant_users_group_permission(self, repo, group_name, perm):
548 """
548 """
549 Grant permission for user group on given repository, or update
549 Grant permission for user group on given repository, or update
550 existing one if found
550 existing one if found
551
551
552 :param repo: Instance of Repository, repository_id, or repository name
552 :param repo: Instance of Repository, repository_id, or repository name
553 :param group_name: Instance of UserGroup, users_group_id,
553 :param group_name: Instance of UserGroup, users_group_id,
554 or user group name
554 or user group name
555 :param perm: Instance of Permission, or permission_name
555 :param perm: Instance of Permission, or permission_name
556 """
556 """
557 repo = self._get_repo(repo)
557 repo = self._get_repo(repo)
558 group_name = self.__get_users_group(group_name)
558 group_name = self.__get_users_group(group_name)
559 permission = self._get_perm(perm)
559 permission = self._get_perm(perm)
560
560
561 # check if we have that permission already
561 # check if we have that permission already
562 obj = self.sa.query(UserGroupRepoToPerm)\
562 obj = self.sa.query(UserGroupRepoToPerm)\
563 .filter(UserGroupRepoToPerm.users_group == group_name)\
563 .filter(UserGroupRepoToPerm.users_group == group_name)\
564 .filter(UserGroupRepoToPerm.repository == repo)\
564 .filter(UserGroupRepoToPerm.repository == repo)\
565 .scalar()
565 .scalar()
566
566
567 if obj is None:
567 if obj is None:
568 # create new
568 # create new
569 obj = UserGroupRepoToPerm()
569 obj = UserGroupRepoToPerm()
570
570
571 obj.repository = repo
571 obj.repository = repo
572 obj.users_group = group_name
572 obj.users_group = group_name
573 obj.permission = permission
573 obj.permission = permission
574 self.sa.add(obj)
574 self.sa.add(obj)
575 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
575 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
576
576
577 def revoke_users_group_permission(self, repo, group_name):
577 def revoke_users_group_permission(self, repo, group_name):
578 """
578 """
579 Revoke permission for user group on given repository
579 Revoke permission for user group on given repository
580
580
581 :param repo: Instance of Repository, repository_id, or repository name
581 :param repo: Instance of Repository, repository_id, or repository name
582 :param group_name: Instance of UserGroup, users_group_id,
582 :param group_name: Instance of UserGroup, users_group_id,
583 or user group name
583 or user group name
584 """
584 """
585 repo = self._get_repo(repo)
585 repo = self._get_repo(repo)
586 group_name = self.__get_users_group(group_name)
586 group_name = self.__get_users_group(group_name)
587
587
588 obj = self.sa.query(UserGroupRepoToPerm)\
588 obj = self.sa.query(UserGroupRepoToPerm)\
589 .filter(UserGroupRepoToPerm.repository == repo)\
589 .filter(UserGroupRepoToPerm.repository == repo)\
590 .filter(UserGroupRepoToPerm.users_group == group_name)\
590 .filter(UserGroupRepoToPerm.users_group == group_name)\
591 .scalar()
591 .scalar()
592 if obj:
592 if obj:
593 self.sa.delete(obj)
593 self.sa.delete(obj)
594 log.debug('Revoked perm to %s on %s' % (repo, group_name))
594 log.debug('Revoked perm to %s on %s' % (repo, group_name))
595
595
596 def delete_stats(self, repo_name):
596 def delete_stats(self, repo_name):
597 """
597 """
598 removes stats for given repo
598 removes stats for given repo
599
599
600 :param repo_name:
600 :param repo_name:
601 """
601 """
602 repo = self._get_repo(repo_name)
602 repo = self._get_repo(repo_name)
603 try:
603 try:
604 obj = self.sa.query(Statistics)\
604 obj = self.sa.query(Statistics)\
605 .filter(Statistics.repository == repo).scalar()
605 .filter(Statistics.repository == repo).scalar()
606 if obj:
606 if obj:
607 self.sa.delete(obj)
607 self.sa.delete(obj)
608 except Exception:
608 except Exception:
609 log.error(traceback.format_exc())
609 log.error(traceback.format_exc())
610 raise
610 raise
611
611
612 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
612 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
613 """
613 """
614 makes repository on filesystem. It's group aware means it'll create
614 makes repository on filesystem. It's group aware means it'll create
615 a repository within a group, and alter the paths accordingly of
615 a repository within a group, and alter the paths accordingly of
616 group location
616 group location
617
617
618 :param repo_name:
618 :param repo_name:
619 :param alias:
619 :param alias:
620 :param parent_id:
620 :param parent_id:
621 :param clone_uri:
621 :param clone_uri:
622 """
622 """
623 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
623 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
624 from rhodecode.model.scm import ScmModel
624 from rhodecode.model.scm import ScmModel
625
625
626 if parent:
626 if parent:
627 new_parent_path = os.sep.join(parent.full_path_splitted)
627 new_parent_path = os.sep.join(parent.full_path_splitted)
628 else:
628 else:
629 new_parent_path = ''
629 new_parent_path = ''
630
630
631 # we need to make it str for mercurial
631 # we need to make it str for mercurial
632 repo_path = os.path.join(*map(lambda x: safe_str(x),
632 repo_path = os.path.join(*map(lambda x: safe_str(x),
633 [self.repos_path, new_parent_path, repo_name]))
633 [self.repos_path, new_parent_path, repo_name]))
634
634
635 # check if this path is not a repository
635 # check if this path is not a repository
636 if is_valid_repo(repo_path, self.repos_path):
636 if is_valid_repo(repo_path, self.repos_path):
637 raise Exception('This path %s is a valid repository' % repo_path)
637 raise Exception('This path %s is a valid repository' % repo_path)
638
638
639 # check if this path is a group
639 # check if this path is a group
640 if is_valid_repos_group(repo_path, self.repos_path):
640 if is_valid_repos_group(repo_path, self.repos_path):
641 raise Exception('This path %s is a valid group' % repo_path)
641 raise Exception('This path %s is a valid group' % repo_path)
642
642
643 log.info('creating repo %s in %s @ %s' % (
643 log.info('creating repo %s in %s @ %s' % (
644 repo_name, safe_unicode(repo_path),
644 repo_name, safe_unicode(repo_path),
645 obfuscate_url_pw(clone_uri)
645 obfuscate_url_pw(clone_uri)
646 )
646 )
647 )
647 )
648 backend = get_backend(alias)
648 backend = get_backend(alias)
649 if alias == 'hg':
649 if alias == 'hg':
650 backend(repo_path, create=True, src_url=clone_uri)
650 backend(repo_path, create=True, src_url=clone_uri)
651 elif alias == 'git':
651 elif alias == 'git':
652 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
652 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
653 # add rhodecode hook into this repo
653 # add rhodecode hook into this repo
654 ScmModel().install_git_hook(repo=r)
654 ScmModel().install_git_hook(repo=r)
655 else:
655 else:
656 raise Exception('Undefined alias %s' % alias)
656 raise Exception('Undefined alias %s' % alias)
657
657
658 def __rename_repo(self, old, new):
658 def __rename_repo(self, old, new):
659 """
659 """
660 renames repository on filesystem
660 renames repository on filesystem
661
661
662 :param old: old name
662 :param old: old name
663 :param new: new name
663 :param new: new name
664 """
664 """
665 log.info('renaming repo from %s to %s' % (old, new))
665 log.info('renaming repo from %s to %s' % (old, new))
666
666
667 old_path = os.path.join(self.repos_path, old)
667 old_path = os.path.join(self.repos_path, old)
668 new_path = os.path.join(self.repos_path, new)
668 new_path = os.path.join(self.repos_path, new)
669 if os.path.isdir(new_path):
669 if os.path.isdir(new_path):
670 raise Exception(
670 raise Exception(
671 'Was trying to rename to already existing dir %s' % new_path
671 'Was trying to rename to already existing dir %s' % new_path
672 )
672 )
673 shutil.move(old_path, new_path)
673 shutil.move(old_path, new_path)
674
674
675 def __delete_repo(self, repo):
675 def __delete_repo(self, repo):
676 """
676 """
677 removes repo from filesystem, the removal is acctually made by
677 removes repo from filesystem, the removal is acctually made by
678 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
678 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
679 repository is no longer valid for rhodecode, can be undeleted later on
679 repository is no longer valid for rhodecode, can be undeleted later on
680 by reverting the renames on this repository
680 by reverting the renames on this repository
681
681
682 :param repo: repo object
682 :param repo: repo object
683 """
683 """
684 rm_path = os.path.join(self.repos_path, repo.repo_name)
684 rm_path = os.path.join(self.repos_path, repo.repo_name)
685 log.info("Removing %s" % (rm_path))
685 log.info("Removing %s" % (rm_path))
686 # disable hg/git internal that it doesn't get detected as repo
686 # disable hg/git internal that it doesn't get detected as repo
687 alias = repo.repo_type
687 alias = repo.repo_type
688
688
689 bare = getattr(repo.scm_instance, 'bare', False)
689 bare = getattr(repo.scm_instance, 'bare', False)
690
690
691 if not bare:
691 if not bare:
692 # skip this for bare git repos
692 # skip this for bare git repos
693 shutil.move(os.path.join(rm_path, '.%s' % alias),
693 shutil.move(os.path.join(rm_path, '.%s' % alias),
694 os.path.join(rm_path, 'rm__.%s' % alias))
694 os.path.join(rm_path, 'rm__.%s' % alias))
695 # disable repo
695 # disable repo
696 _now = datetime.now()
696 _now = datetime.now()
697 _ms = str(_now.microsecond).rjust(6, '0')
697 _ms = str(_now.microsecond).rjust(6, '0')
698 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
698 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
699 repo.just_name)
699 repo.just_name)
700 if repo.group:
700 if repo.group:
701 args = repo.group.full_path_splitted + [_d]
701 args = repo.group.full_path_splitted + [_d]
702 _d = os.path.join(*args)
702 _d = os.path.join(*args)
703 shutil.move(rm_path, os.path.join(self.repos_path, _d))
703 shutil.move(rm_path, os.path.join(self.repos_path, _d))
@@ -1,395 +1,387 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.html"/>
5 <%inherit file="/base/base.html"/>
6
6
7 <%def name="title()">
7 <%def name="title()">
8 ${_('Edit repository')} ${c.repo_info.repo_name} &middot; ${c.rhodecode_name}
8 ${_('Edit repository')} ${c.repo_info.repo_name} &middot; ${c.rhodecode_name}
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${_('Settings')}
12 ${_('Settings')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('admin')}
16 ${self.menu('admin')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 ${self.context_bar('options')}
20 ${self.context_bar('options')}
21 <div class="box box-left">
21 <div class="box box-left">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
27 <div class="form">
27 <div class="form">
28 <!-- fields -->
28 <!-- fields -->
29 <div class="fields">
29 <div class="fields">
30 <div class="field">
30 <div class="field">
31 <div class="label">
31 <div class="label">
32 <label for="repo_name">${_('Name')}:</label>
32 <label for="repo_name">${_('Name')}:</label>
33 </div>
33 </div>
34 <div class="input">
34 <div class="input">
35 ${h.text('repo_name',class_="medium")}
35 ${h.text('repo_name',class_="medium")}
36 </div>
36 </div>
37 </div>
37 </div>
38 <div class="field">
38 <div class="field">
39 <div class="label">
39 <div class="label">
40 <label for="clone_uri">${_('Clone uri')}:</label>
40 <label for="clone_uri">${_('Clone uri')}:</label>
41 </div>
41 </div>
42 <div class="input">
42 <div class="input">
43 ${h.text('clone_uri',class_="medium")}
43 ${h.text('clone_uri',class_="medium")}
44 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
44 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
45 </div>
45 </div>
46 </div>
46 </div>
47 <div class="field">
47 <div class="field">
48 <div class="label">
48 <div class="label">
49 <label for="repo_group">${_('Repository group')}:</label>
49 <label for="repo_group">${_('Repository group')}:</label>
50 </div>
50 </div>
51 <div class="input">
51 <div class="input">
52 ${h.select('repo_group','',c.repo_groups,class_="medium")}
52 ${h.select('repo_group','',c.repo_groups,class_="medium")}
53 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
53 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
54 </div>
54 </div>
55 </div>
55 </div>
56 <div class="field">
56 <div class="field">
57 <div class="label">
57 <div class="label">
58 <label for="repo_type">${_('Type')}:</label>
59 </div>
60 <div class="input">
61 ${h.select('repo_type','hg',c.backends,class_="medium")}
62 </div>
63 </div>
64 <div class="field">
65 <div class="label">
66 <label for="repo_landing_rev">${_('Landing revision')}:</label>
58 <label for="repo_landing_rev">${_('Landing revision')}:</label>
67 </div>
59 </div>
68 <div class="input">
60 <div class="input">
69 ${h.select('repo_landing_rev','',c.landing_revs,class_="medium")}
61 ${h.select('repo_landing_rev','',c.landing_revs,class_="medium")}
70 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
62 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
71 </div>
63 </div>
72 </div>
64 </div>
73 <div class="field">
65 <div class="field">
74 <div class="label label-textarea">
66 <div class="label label-textarea">
75 <label for="repo_description">${_('Description')}:</label>
67 <label for="repo_description">${_('Description')}:</label>
76 </div>
68 </div>
77 <div class="textarea text-area editor">
69 <div class="textarea text-area editor">
78 ${h.textarea('repo_description')}
70 ${h.textarea('repo_description')}
79 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
71 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
80 </div>
72 </div>
81 </div>
73 </div>
82
74
83 <div class="field">
75 <div class="field">
84 <div class="label label-checkbox">
76 <div class="label label-checkbox">
85 <label for="repo_private">${_('Private repository')}:</label>
77 <label for="repo_private">${_('Private repository')}:</label>
86 </div>
78 </div>
87 <div class="checkboxes">
79 <div class="checkboxes">
88 ${h.checkbox('repo_private',value="True")}
80 ${h.checkbox('repo_private',value="True")}
89 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
81 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
90 </div>
82 </div>
91 </div>
83 </div>
92 <div class="field">
84 <div class="field">
93 <div class="label label-checkbox">
85 <div class="label label-checkbox">
94 <label for="repo_enable_statistics">${_('Enable statistics')}:</label>
86 <label for="repo_enable_statistics">${_('Enable statistics')}:</label>
95 </div>
87 </div>
96 <div class="checkboxes">
88 <div class="checkboxes">
97 ${h.checkbox('repo_enable_statistics',value="True")}
89 ${h.checkbox('repo_enable_statistics',value="True")}
98 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
90 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
99 </div>
91 </div>
100 </div>
92 </div>
101 <div class="field">
93 <div class="field">
102 <div class="label label-checkbox">
94 <div class="label label-checkbox">
103 <label for="repo_enable_downloads">${_('Enable downloads')}:</label>
95 <label for="repo_enable_downloads">${_('Enable downloads')}:</label>
104 </div>
96 </div>
105 <div class="checkboxes">
97 <div class="checkboxes">
106 ${h.checkbox('repo_enable_downloads',value="True")}
98 ${h.checkbox('repo_enable_downloads',value="True")}
107 <span class="help-block">${_('Enable download menu on summary page.')}</span>
99 <span class="help-block">${_('Enable download menu on summary page.')}</span>
108 </div>
100 </div>
109 </div>
101 </div>
110 <div class="field">
102 <div class="field">
111 <div class="label label-checkbox">
103 <div class="label label-checkbox">
112 <label for="repo_enable_locking">${_('Enable locking')}:</label>
104 <label for="repo_enable_locking">${_('Enable locking')}:</label>
113 </div>
105 </div>
114 <div class="checkboxes">
106 <div class="checkboxes">
115 ${h.checkbox('repo_enable_locking',value="True")}
107 ${h.checkbox('repo_enable_locking',value="True")}
116 <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
108 <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
117 </div>
109 </div>
118 </div>
110 </div>
119 <div class="field">
111 <div class="field">
120 <div class="label">
112 <div class="label">
121 <label for="user">${_('Owner')}:</label>
113 <label for="user">${_('Owner')}:</label>
122 </div>
114 </div>
123 <div class="input input-medium ac">
115 <div class="input input-medium ac">
124 <div class="perm_ac">
116 <div class="perm_ac">
125 ${h.text('user',class_='yui-ac-input')}
117 ${h.text('user',class_='yui-ac-input')}
126 <span class="help-block">${_('Change owner of this repository.')}</span>
118 <span class="help-block">${_('Change owner of this repository.')}</span>
127 <div id="owner_container"></div>
119 <div id="owner_container"></div>
128 </div>
120 </div>
129 </div>
121 </div>
130 </div>
122 </div>
131 %if c.visual.repository_fields:
123 %if c.visual.repository_fields:
132 ## EXTRA FIELDS
124 ## EXTRA FIELDS
133 %for field in c.repo_fields:
125 %for field in c.repo_fields:
134 <div class="field">
126 <div class="field">
135 <div class="label">
127 <div class="label">
136 <label for="${field.field_key_prefixed}">${field.field_label} (${field.field_key}):</label>
128 <label for="${field.field_key_prefixed}">${field.field_label} (${field.field_key}):</label>
137 </div>
129 </div>
138 <div class="input input-medium">
130 <div class="input input-medium">
139 ${h.text(field.field_key_prefixed, field.field_value, class_='medium')}
131 ${h.text(field.field_key_prefixed, field.field_value, class_='medium')}
140 %if field.field_desc:
132 %if field.field_desc:
141 <span class="help-block">${field.field_desc}</span>
133 <span class="help-block">${field.field_desc}</span>
142 %endif
134 %endif
143 </div>
135 </div>
144 </div>
136 </div>
145 %endfor
137 %endfor
146 %endif
138 %endif
147 <div class="buttons">
139 <div class="buttons">
148 ${h.submit('save',_('Save'),class_="ui-btn large")}
140 ${h.submit('save',_('Save'),class_="ui-btn large")}
149 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
141 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
150 </div>
142 </div>
151 </div>
143 </div>
152 </div>
144 </div>
153 ${h.end_form()}
145 ${h.end_form()}
154 </div>
146 </div>
155
147
156 <div class="box box-right">
148 <div class="box box-right">
157 <div class="title">
149 <div class="title">
158 <h5>${_('Permissions')}</h5>
150 <h5>${_('Permissions')}</h5>
159 </div>
151 </div>
160 ${h.form(url('set_repo_perm_member', repo_name=c.repo_info.repo_name),method='post')}
152 ${h.form(url('set_repo_perm_member', repo_name=c.repo_info.repo_name),method='post')}
161 <div class="form">
153 <div class="form">
162 <div class="fields">
154 <div class="fields">
163 <div class="field">
155 <div class="field">
164 <div class="label">
156 <div class="label">
165 <label for="input">${_('Permissions')}:</label>
157 <label for="input">${_('Permissions')}:</label>
166 </div>
158 </div>
167 <div class="input">
159 <div class="input">
168 ${h.hidden('repo_private')}
160 ${h.hidden('repo_private')}
169 <%include file="repo_edit_perms.html"/>
161 <%include file="repo_edit_perms.html"/>
170 </div>
162 </div>
171 </div>
163 </div>
172 <div class="buttons">
164 <div class="buttons">
173 ${h.submit('save',_('Save'),class_="ui-btn large")}
165 ${h.submit('save',_('Save'),class_="ui-btn large")}
174 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
166 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
175 </div>
167 </div>
176 </div>
168 </div>
177 </div>
169 </div>
178 ${h.end_form()}
170 ${h.end_form()}
179 </div>
171 </div>
180
172
181
173
182 <div class="box box-right" style="clear:right">
174 <div class="box box-right" style="clear:right">
183 <div class="title">
175 <div class="title">
184 <h5>${_('Advanced settings')}</h5>
176 <h5>${_('Advanced settings')}</h5>
185 </div>
177 </div>
186
178
187 <h3>${_('Statistics')}</h3>
179 <h3>${_('Statistics')}</h3>
188 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
180 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
189 <div class="form">
181 <div class="form">
190 <div class="fields">
182 <div class="fields">
191 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")}
183 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")}
192 <div class="field" style="border:none;color:#888">
184 <div class="field" style="border:none;color:#888">
193 <ul>
185 <ul>
194 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
186 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
195 <li>${_('Stats gathered')}: ${c.stats_percentage}%</li>
187 <li>${_('Stats gathered')}: ${c.stats_percentage}%</li>
196 </ul>
188 </ul>
197 </div>
189 </div>
198 </div>
190 </div>
199 </div>
191 </div>
200 ${h.end_form()}
192 ${h.end_form()}
201
193
202 %if c.repo_info.clone_uri:
194 %if c.repo_info.clone_uri:
203 <h3>${_('Remote')}</h3>
195 <h3>${_('Remote')}</h3>
204 ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')}
196 ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')}
205 <div class="form">
197 <div class="form">
206 <div class="fields">
198 <div class="fields">
207 ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
199 ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
208 <div class="field" style="border:none">
200 <div class="field" style="border:none">
209 <ul>
201 <ul>
210 <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li>
202 <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li>
211 </ul>
203 </ul>
212 </div>
204 </div>
213 </div>
205 </div>
214 </div>
206 </div>
215 ${h.end_form()}
207 ${h.end_form()}
216 %endif
208 %endif
217
209
218 <h3>${_('Cache')}</h3>
210 <h3>${_('Cache')}</h3>
219 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
211 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
220 <div class="form">
212 <div class="form">
221 <div class="fields">
213 <div class="fields">
222 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
214 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
223 <div class="field" style="border:none;color:#888">
215 <div class="field" style="border:none;color:#888">
224 <ul>
216 <ul>
225 <li>${_('Manually invalidate cache for this repository. On first access repository will be cached again')}
217 <li>${_('Manually invalidate cache for this repository. On first access repository will be cached again')}
226 </li>
218 </li>
227 </ul>
219 </ul>
228 </div>
220 </div>
229 <div class="field" style="border:none;">
221 <div class="field" style="border:none;">
230 ${_('List of cached values')}
222 ${_('List of cached values')}
231 <table>
223 <table>
232 <tr>
224 <tr>
233 <th>${_('Prefix')}</th>
225 <th>${_('Prefix')}</th>
234 <th>${_('Key')}</th>
226 <th>${_('Key')}</th>
235 <th>${_('Active')}</th>
227 <th>${_('Active')}</th>
236 </tr>
228 </tr>
237 %for cache in c.repo_info.cache_keys:
229 %for cache in c.repo_info.cache_keys:
238 <tr>
230 <tr>
239 <td>${cache.get_prefix() or '-'}</td>
231 <td>${cache.get_prefix() or '-'}</td>
240 <td>${cache.cache_key}</td>
232 <td>${cache.cache_key}</td>
241 <td>${h.boolicon(cache.cache_active)}</td>
233 <td>${h.boolicon(cache.cache_active)}</td>
242 </tr>
234 </tr>
243 %endfor
235 %endfor
244 </table>
236 </table>
245 </div>
237 </div>
246 </div>
238 </div>
247 </div>
239 </div>
248 ${h.end_form()}
240 ${h.end_form()}
249
241
250 <h3>${_('Public journal')}</h3>
242 <h3>${_('Public journal')}</h3>
251 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
243 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
252 <div class="form">
244 <div class="form">
253 ${h.hidden('auth_token',str(h.get_token()))}
245 ${h.hidden('auth_token',str(h.get_token()))}
254 <div class="field">
246 <div class="field">
255 %if c.in_public_journal:
247 %if c.in_public_journal:
256 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
248 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
257 %else:
249 %else:
258 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
250 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
259 %endif
251 %endif
260 </div>
252 </div>
261 <div class="field" style="border:none;color:#888">
253 <div class="field" style="border:none;color:#888">
262 <ul>
254 <ul>
263 <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
255 <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
264 </li>
256 </li>
265 </ul>
257 </ul>
266 </div>
258 </div>
267 </div>
259 </div>
268 ${h.end_form()}
260 ${h.end_form()}
269
261
270 <h3>${_('Locking')}</h3>
262 <h3>${_('Locking')}</h3>
271 ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')}
263 ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')}
272 <div class="form">
264 <div class="form">
273 <div class="fields">
265 <div class="fields">
274 %if c.repo_info.locked[0]:
266 %if c.repo_info.locked[0]:
275 ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")}
267 ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")}
276 ${'Locked by %s on %s' % (h.person_by_id(c.repo_info.locked[0]),h.fmt_date(h.time_to_datetime(c.repo_info.locked[1])))}
268 ${'Locked by %s on %s' % (h.person_by_id(c.repo_info.locked[0]),h.fmt_date(h.time_to_datetime(c.repo_info.locked[1])))}
277 %else:
269 %else:
278 ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")}
270 ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")}
279 ${_('Repository is not locked')}
271 ${_('Repository is not locked')}
280 %endif
272 %endif
281 </div>
273 </div>
282 <div class="field" style="border:none;color:#888">
274 <div class="field" style="border:none;color:#888">
283 <ul>
275 <ul>
284 <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
276 <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
285 </li>
277 </li>
286 </ul>
278 </ul>
287 </div>
279 </div>
288 </div>
280 </div>
289 ${h.end_form()}
281 ${h.end_form()}
290
282
291 <h3>${_('Set as fork of')}</h3>
283 <h3>${_('Set as fork of')}</h3>
292 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
284 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
293 <div class="form">
285 <div class="form">
294 <div class="fields">
286 <div class="fields">
295 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
287 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
296 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
288 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
297 </div>
289 </div>
298 <div class="field" style="border:none;color:#888">
290 <div class="field" style="border:none;color:#888">
299 <ul>
291 <ul>
300 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
292 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
301 </ul>
293 </ul>
302 </div>
294 </div>
303 </div>
295 </div>
304 ${h.end_form()}
296 ${h.end_form()}
305
297
306 <h3>${_('Delete')}</h3>
298 <h3>${_('Delete')}</h3>
307 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
299 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
308 <div class="form">
300 <div class="form">
309 <div class="fields">
301 <div class="fields">
310 <div class="field" style="border:none;color:#888">
302 <div class="field" style="border:none;color:#888">
311 ## <div class="label">
303 ## <div class="label">
312 ## <label for="">${_('Remove repository')}:</label>
304 ## <label for="">${_('Remove repository')}:</label>
313 ## </div>
305 ## </div>
314 <div class="checkboxes">
306 <div class="checkboxes">
315 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
307 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
316 %if c.repo_info.forks.count():
308 %if c.repo_info.forks.count():
317 - ${ungettext('this repository has %s fork', 'this repository has %s forks', c.repo_info.forks.count()) % c.repo_info.forks.count()}
309 - ${ungettext('this repository has %s fork', 'this repository has %s forks', c.repo_info.forks.count()) % c.repo_info.forks.count()}
318 <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label>
310 <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label>
319 <input type="radio" name="forks" value="delete_forks" /> <label for="forks">${_('Delete forks')}</label>
311 <input type="radio" name="forks" value="delete_forks" /> <label for="forks">${_('Delete forks')}</label>
320 %endif
312 %endif
321 <ul>
313 <ul>
322 <li>${_('This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems. If you need to fully delete it from file system please do it manually')}</li>
314 <li>${_('This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems. If you need to fully delete it from file system please do it manually')}</li>
323 </ul>
315 </ul>
324 </div>
316 </div>
325 </div>
317 </div>
326 </div>
318 </div>
327 </div>
319 </div>
328 ${h.end_form()}
320 ${h.end_form()}
329 </div>
321 </div>
330
322
331 ##TODO: this should be controlled by the VISUAL setting
323 ##TODO: this should be controlled by the VISUAL setting
332 %if c.visual.repository_fields:
324 %if c.visual.repository_fields:
333 <div class="box box-left" style="clear:left">
325 <div class="box box-left" style="clear:left">
334 <!-- box / title -->
326 <!-- box / title -->
335 <div class="title">
327 <div class="title">
336 <h5>${_('Extra fields')}</h5>
328 <h5>${_('Extra fields')}</h5>
337 </div>
329 </div>
338
330
339 <div class="emails_wrap">
331 <div class="emails_wrap">
340 <table class="noborder">
332 <table class="noborder">
341 %for field in c.repo_fields:
333 %for field in c.repo_fields:
342 <tr>
334 <tr>
343 <td>${field.field_label} (${field.field_key})</td>
335 <td>${field.field_label} (${field.field_key})</td>
344 <td>${field.field_type}</td>
336 <td>${field.field_type}</td>
345 <td>
337 <td>
346 ${h.form(url('delete_repo_fields', repo_name=c.repo_info.repo_name, field_id=field.repo_field_id),method='delete')}
338 ${h.form(url('delete_repo_fields', repo_name=c.repo_info.repo_name, field_id=field.repo_field_id),method='delete')}
347 ${h.submit('remove_%s' % field.repo_field_id, _('delete'), id="remove_field_%s" % field.repo_field_id,
339 ${h.submit('remove_%s' % field.repo_field_id, _('delete'), id="remove_field_%s" % field.repo_field_id,
348 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this field: %s') % field.field_key+"');")}
340 class_="delete_icon action_button", onclick="return confirm('"+_('Confirm to delete this field: %s') % field.field_key+"');")}
349 ${h.end_form()}
341 ${h.end_form()}
350 </td>
342 </td>
351 </tr>
343 </tr>
352 %endfor
344 %endfor
353 </table>
345 </table>
354 </div>
346 </div>
355
347
356 ${h.form(url('create_repo_fields', repo_name=c.repo_info.repo_name),method='put')}
348 ${h.form(url('create_repo_fields', repo_name=c.repo_info.repo_name),method='put')}
357 <div class="form">
349 <div class="form">
358 <!-- fields -->
350 <!-- fields -->
359 <div class="fields">
351 <div class="fields">
360 <div class="field">
352 <div class="field">
361 <div class="label">
353 <div class="label">
362 <label for="new_field_key">${_('New field key')}:</label>
354 <label for="new_field_key">${_('New field key')}:</label>
363 </div>
355 </div>
364 <div class="input">
356 <div class="input">
365 ${h.text('new_field_key', class_='small')}
357 ${h.text('new_field_key', class_='small')}
366 </div>
358 </div>
367 </div>
359 </div>
368 <div class="field">
360 <div class="field">
369 <div class="label">
361 <div class="label">
370 <label for="new_field_label">${_('New field label')}:</label>
362 <label for="new_field_label">${_('New field label')}:</label>
371 </div>
363 </div>
372 <div class="input">
364 <div class="input">
373 ${h.text('new_field_label', class_='small', placeholder=_('Enter short label'))}
365 ${h.text('new_field_label', class_='small', placeholder=_('Enter short label'))}
374 </div>
366 </div>
375 </div>
367 </div>
376
368
377 <div class="field">
369 <div class="field">
378 <div class="label">
370 <div class="label">
379 <label for="new_field_desc">${_('New field description')}:</label>
371 <label for="new_field_desc">${_('New field description')}:</label>
380 </div>
372 </div>
381 <div class="input">
373 <div class="input">
382 ${h.text('new_field_desc', class_='small', placeholder=_('Enter description of a field'))}
374 ${h.text('new_field_desc', class_='small', placeholder=_('Enter description of a field'))}
383 </div>
375 </div>
384 </div>
376 </div>
385
377
386 <div class="buttons">
378 <div class="buttons">
387 ${h.submit('save',_('Add'),class_="ui-btn large")}
379 ${h.submit('save',_('Add'),class_="ui-btn large")}
388 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
380 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
389 </div>
381 </div>
390 </div>
382 </div>
391 </div>
383 </div>
392 ${h.end_form()}
384 ${h.end_form()}
393 </div>
385 </div>
394 %endif
386 %endif
395 </%def>
387 </%def>
General Comments 0
You need to be logged in to leave comments. Login now