##// END OF EJS Templates
repo-schema: added a custom schema for repo settings....
marcink -
r1719:1b64c19a default
parent child Browse files
Show More
@@ -1,178 +1,179 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
3 # Copyright (C) 2011-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
21 import logging
22
22
23 import deform
23 import deform
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26
26
27 from rhodecode.apps._base import RepoAppView
27 from rhodecode.apps._base import RepoAppView
28 from rhodecode.forms import RcForm
28 from rhodecode.forms import RcForm
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib import audit_logger
30 from rhodecode.lib import audit_logger
31 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
32 LoginRequired, HasRepoPermissionAnyDecorator,
32 LoginRequired, HasRepoPermissionAnyDecorator,
33 HasRepoPermissionAllDecorator, CSRFRequired)
33 HasRepoPermissionAllDecorator, CSRFRequired)
34 from rhodecode.model.db import RepositoryField, RepoGroup
34 from rhodecode.model.db import RepositoryField, RepoGroup
35 from rhodecode.model.meta import Session
35 from rhodecode.model.meta import Session
36 from rhodecode.model.repo import RepoModel
36 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.scm import RepoGroupList, ScmModel
37 from rhodecode.model.scm import RepoGroupList, ScmModel
38 from rhodecode.model.validation_schema.schemas import repo_schema
38 from rhodecode.model.validation_schema.schemas import repo_schema
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42
42
43 class RepoSettingsView(RepoAppView):
43 class RepoSettingsView(RepoAppView):
44
44
45 def load_default_context(self):
45 def load_default_context(self):
46 c = self._get_local_tmpl_context()
46 c = self._get_local_tmpl_context()
47
47
48 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
48 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
49 c.repo_info = self.db_repo
49 c.repo_info = self.db_repo
50
50
51 acl_groups = RepoGroupList(
51 acl_groups = RepoGroupList(
52 RepoGroup.query().all(),
52 RepoGroup.query().all(),
53 perm_set=['group.write', 'group.admin'])
53 perm_set=['group.write', 'group.admin'])
54 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
54 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
55 c.repo_groups_choices = map(lambda k: k[0], c.repo_groups)
55 c.repo_groups_choices = map(lambda k: k[0], c.repo_groups)
56
56
57 # in case someone no longer have a group.write access to a repository
57 # in case someone no longer have a group.write access to a repository
58 # pre fill the list with this entry, we don't care if this is the same
58 # pre fill the list with this entry, we don't care if this is the same
59 # but it will allow saving repo data properly.
59 # but it will allow saving repo data properly.
60 repo_group = self.db_repo.group
60 repo_group = self.db_repo.group
61 if repo_group and repo_group.group_id not in c.repo_groups_choices:
61 if repo_group and repo_group.group_id not in c.repo_groups_choices:
62 c.repo_groups_choices.append(repo_group.group_id)
62 c.repo_groups_choices.append(repo_group.group_id)
63 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
63 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
64
64
65 if c.repository_requirements_missing or self.rhodecode_vcs_repo is None:
65 if c.repository_requirements_missing or self.rhodecode_vcs_repo is None:
66 # we might be in missing requirement state, so we load things
66 # we might be in missing requirement state, so we load things
67 # without touching scm_instance()
67 # without touching scm_instance()
68 c.landing_revs_choices, c.landing_revs = \
68 c.landing_revs_choices, c.landing_revs = \
69 ScmModel().get_repo_landing_revs()
69 ScmModel().get_repo_landing_revs()
70 else:
70 else:
71 c.landing_revs_choices, c.landing_revs = \
71 c.landing_revs_choices, c.landing_revs = \
72 ScmModel().get_repo_landing_revs(self.db_repo)
72 ScmModel().get_repo_landing_revs(self.db_repo)
73
73
74 c.personal_repo_group = c.auth_user.personal_repo_group
74 c.personal_repo_group = c.auth_user.personal_repo_group
75 c.repo_fields = RepositoryField.query()\
75 c.repo_fields = RepositoryField.query()\
76 .filter(RepositoryField.repository == self.db_repo).all()
76 .filter(RepositoryField.repository == self.db_repo).all()
77
77
78 self._register_global_c(c)
78 self._register_global_c(c)
79 return c
79 return c
80
80
81 def _get_schema(self, c, old_values=None):
81 def _get_schema(self, c, old_values=None):
82 return repo_schema.RepoSettingsSchema().bind(
82 return repo_schema.RepoSettingsSchema().bind(
83 repo_type=self.db_repo.repo_type,
83 repo_type_options=[self.db_repo.repo_type],
84 repo_type_options=[self.db_repo.repo_type],
84 repo_ref_options=c.landing_revs_choices,
85 repo_ref_options=c.landing_revs_choices,
85 repo_ref_items=c.landing_revs,
86 repo_ref_items=c.landing_revs,
86 repo_repo_group_options=c.repo_groups_choices,
87 repo_repo_group_options=c.repo_groups_choices,
87 repo_repo_group_items=c.repo_groups,
88 repo_repo_group_items=c.repo_groups,
88 # user caller
89 # user caller
89 user=self._rhodecode_user,
90 user=self._rhodecode_user,
90 old_values=old_values
91 old_values=old_values
91 )
92 )
92
93
93 @LoginRequired()
94 @LoginRequired()
94 @HasRepoPermissionAnyDecorator('repository.admin')
95 @HasRepoPermissionAnyDecorator('repository.admin')
95 @view_config(
96 @view_config(
96 route_name='edit_repo', request_method='GET',
97 route_name='edit_repo', request_method='GET',
97 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
98 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
98 def edit_settings(self):
99 def edit_settings(self):
99 c = self.load_default_context()
100 c = self.load_default_context()
100 c.active = 'settings'
101 c.active = 'settings'
101
102
102 defaults = RepoModel()._get_defaults(self.db_repo_name)
103 defaults = RepoModel()._get_defaults(self.db_repo_name)
103 defaults['repo_owner'] = defaults['user']
104 defaults['repo_owner'] = defaults['user']
104 defaults['repo_landing_commit_ref'] = defaults['repo_landing_rev']
105 defaults['repo_landing_commit_ref'] = defaults['repo_landing_rev']
105
106
106 schema = self._get_schema(c)
107 schema = self._get_schema(c)
107 c.form = RcForm(schema, appstruct=defaults)
108 c.form = RcForm(schema, appstruct=defaults)
108 return self._get_template_context(c)
109 return self._get_template_context(c)
109
110
110 @LoginRequired()
111 @LoginRequired()
111 @HasRepoPermissionAllDecorator('repository.admin')
112 @HasRepoPermissionAllDecorator('repository.admin')
112 @CSRFRequired()
113 @CSRFRequired()
113 @view_config(
114 @view_config(
114 route_name='edit_repo', request_method='POST',
115 route_name='edit_repo', request_method='POST',
115 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
116 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
116 def edit_settings_update(self):
117 def edit_settings_update(self):
117 _ = self.request.translate
118 _ = self.request.translate
118 c = self.load_default_context()
119 c = self.load_default_context()
119 c.active = 'settings'
120 c.active = 'settings'
120 old_repo_name = self.db_repo_name
121 old_repo_name = self.db_repo_name
121
122
122 old_values = self.db_repo.get_api_data()
123 old_values = self.db_repo.get_api_data()
123 schema = self._get_schema(c, old_values=old_values)
124 schema = self._get_schema(c, old_values=old_values)
124
125
125 c.form = RcForm(schema)
126 c.form = RcForm(schema)
126 pstruct = self.request.POST.items()
127 pstruct = self.request.POST.items()
127 pstruct.append(('repo_type', self.db_repo.repo_type))
128 pstruct.append(('repo_type', self.db_repo.repo_type))
128 try:
129 try:
129 schema_data = c.form.validate(pstruct)
130 schema_data = c.form.validate(pstruct)
130 except deform.ValidationFailure as err_form:
131 except deform.ValidationFailure as err_form:
131 return self._get_template_context(c)
132 return self._get_template_context(c)
132
133
133 # data is now VALID, proceed with updates
134 # data is now VALID, proceed with updates
134 # save validated data back into the updates dict
135 # save validated data back into the updates dict
135 validated_updates = dict(
136 validated_updates = dict(
136 repo_name=schema_data['repo_group']['repo_name_without_group'],
137 repo_name=schema_data['repo_group']['repo_name_without_group'],
137 repo_group=schema_data['repo_group']['repo_group_id'],
138 repo_group=schema_data['repo_group']['repo_group_id'],
138
139
139 user=schema_data['repo_owner'],
140 user=schema_data['repo_owner'],
140 repo_description=schema_data['repo_description'],
141 repo_description=schema_data['repo_description'],
141 repo_private=schema_data['repo_private'],
142 repo_private=schema_data['repo_private'],
142 clone_uri=schema_data['repo_clone_uri'],
143 clone_uri=schema_data['repo_clone_uri'],
143 repo_landing_rev=schema_data['repo_landing_commit_ref'],
144 repo_landing_rev=schema_data['repo_landing_commit_ref'],
144 repo_enable_statistics=schema_data['repo_enable_statistics'],
145 repo_enable_statistics=schema_data['repo_enable_statistics'],
145 repo_enable_locking=schema_data['repo_enable_locking'],
146 repo_enable_locking=schema_data['repo_enable_locking'],
146 repo_enable_downloads=schema_data['repo_enable_downloads'],
147 repo_enable_downloads=schema_data['repo_enable_downloads'],
147 )
148 )
148 # detect if CLONE URI changed, if we get OLD means we keep old values
149 # detect if CLONE URI changed, if we get OLD means we keep old values
149 if schema_data['repo_clone_uri_change'] == 'OLD':
150 if schema_data['repo_clone_uri_change'] == 'OLD':
150 validated_updates['clone_uri'] = self.db_repo.clone_uri
151 validated_updates['clone_uri'] = self.db_repo.clone_uri
151
152
152 # use the new full name for redirect
153 # use the new full name for redirect
153 new_repo_name = schema_data['repo_group']['repo_name_with_group']
154 new_repo_name = schema_data['repo_group']['repo_name_with_group']
154
155
155 # save extra fields into our validated data
156 # save extra fields into our validated data
156 for key, value in pstruct:
157 for key, value in pstruct:
157 if key.startswith(RepositoryField.PREFIX):
158 if key.startswith(RepositoryField.PREFIX):
158 validated_updates[key] = value
159 validated_updates[key] = value
159
160
160 try:
161 try:
161 RepoModel().update(self.db_repo, **validated_updates)
162 RepoModel().update(self.db_repo, **validated_updates)
162 ScmModel().mark_for_invalidation(new_repo_name)
163 ScmModel().mark_for_invalidation(new_repo_name)
163
164
164 audit_logger.store(
165 audit_logger.store(
165 'repo.edit', action_data={'old_data': old_values},
166 'repo.edit', action_data={'old_data': old_values},
166 user=self._rhodecode_user, repo=self.db_repo)
167 user=self._rhodecode_user, repo=self.db_repo)
167
168
168 Session().commit()
169 Session().commit()
169
170
170 h.flash(_('Repository {} updated successfully').format(
171 h.flash(_('Repository {} updated successfully').format(
171 old_repo_name), category='success')
172 old_repo_name), category='success')
172 except Exception:
173 except Exception:
173 log.exception("Exception during update of repository")
174 log.exception("Exception during update of repository")
174 h.flash(_('Error occurred during update of repository {}').format(
175 h.flash(_('Error occurred during update of repository {}').format(
175 old_repo_name), category='error')
176 old_repo_name), category='error')
176
177
177 raise HTTPFound(
178 raise HTTPFound(
178 self.request.route_path('edit_repo', repo_name=new_repo_name))
179 self.request.route_path('edit_repo', repo_name=new_repo_name))
@@ -1,360 +1,414 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
20
21 import colander
21 import colander
22 import deform.widget
22 import deform.widget
23
23
24 from rhodecode.translation import _
24 from rhodecode.translation import _
25 from rhodecode.model.validation_schema.utils import convert_to_optgroup
25 from rhodecode.model.validation_schema.utils import convert_to_optgroup
26 from rhodecode.model.validation_schema import validators, preparers, types
26 from rhodecode.model.validation_schema import validators, preparers, types
27
27
28 DEFAULT_LANDING_REF = 'rev:tip'
28 DEFAULT_LANDING_REF = 'rev:tip'
29
29
30
30
31 def get_group_and_repo(repo_name):
31 def get_group_and_repo(repo_name):
32 from rhodecode.model.repo_group import RepoGroupModel
32 from rhodecode.model.repo_group import RepoGroupModel
33 return RepoGroupModel()._get_group_name_and_parent(
33 return RepoGroupModel()._get_group_name_and_parent(
34 repo_name, get_object=True)
34 repo_name, get_object=True)
35
35
36
36
37 def get_repo_group(repo_group_id):
37 def get_repo_group(repo_group_id):
38 from rhodecode.model.repo_group import RepoGroup
38 from rhodecode.model.repo_group import RepoGroup
39 return RepoGroup.get(repo_group_id), RepoGroup.CHOICES_SEPARATOR
39 return RepoGroup.get(repo_group_id), RepoGroup.CHOICES_SEPARATOR
40
40
41
41
42 @colander.deferred
42 @colander.deferred
43 def deferred_repo_type_validator(node, kw):
43 def deferred_repo_type_validator(node, kw):
44 options = kw.get('repo_type_options', [])
44 options = kw.get('repo_type_options', [])
45 return colander.OneOf([x for x in options])
45 return colander.OneOf([x for x in options])
46
46
47
47
48 @colander.deferred
48 @colander.deferred
49 def deferred_repo_owner_validator(node, kw):
49 def deferred_repo_owner_validator(node, kw):
50
50
51 def repo_owner_validator(node, value):
51 def repo_owner_validator(node, value):
52 from rhodecode.model.db import User
52 from rhodecode.model.db import User
53 existing = User.get_by_username(value)
53 existing = User.get_by_username(value)
54 if not existing:
54 if not existing:
55 msg = _(u'Repo owner with id `{}` does not exists').format(value)
55 msg = _(u'Repo owner with id `{}` does not exists').format(value)
56 raise colander.Invalid(node, msg)
56 raise colander.Invalid(node, msg)
57
57
58 return repo_owner_validator
58 return repo_owner_validator
59
59
60
60
61 @colander.deferred
61 @colander.deferred
62 def deferred_landing_ref_validator(node, kw):
62 def deferred_landing_ref_validator(node, kw):
63 options = kw.get(
63 options = kw.get(
64 'repo_ref_options', [DEFAULT_LANDING_REF])
64 'repo_ref_options', [DEFAULT_LANDING_REF])
65 return colander.OneOf([x for x in options])
65 return colander.OneOf([x for x in options])
66
66
67
67
68 @colander.deferred
68 @colander.deferred
69 def deferred_clone_uri_validator(node, kw):
70 repo_type = kw.get('repo_type')
71 validator = validators.CloneUriValidator(repo_type)
72 return validator
73
74
75 @colander.deferred
69 def deferred_landing_ref_widget(node, kw):
76 def deferred_landing_ref_widget(node, kw):
70 items = kw.get(
77 items = kw.get(
71 'repo_ref_items', [(DEFAULT_LANDING_REF, DEFAULT_LANDING_REF)])
78 'repo_ref_items', [(DEFAULT_LANDING_REF, DEFAULT_LANDING_REF)])
72 items = convert_to_optgroup(items)
79 items = convert_to_optgroup(items)
73 return deform.widget.Select2Widget(values=items)
80 return deform.widget.Select2Widget(values=items)
74
81
75
82
76 @colander.deferred
83 @colander.deferred
77 def deferred_fork_of_validator(node, kw):
84 def deferred_fork_of_validator(node, kw):
78 old_values = kw.get('old_values') or {}
85 old_values = kw.get('old_values') or {}
79
86
80 def fork_of_validator(node, value):
87 def fork_of_validator(node, value):
81 from rhodecode.model.db import Repository, RepoGroup
88 from rhodecode.model.db import Repository, RepoGroup
82 existing = Repository.get_by_repo_name(value)
89 existing = Repository.get_by_repo_name(value)
83 if not existing:
90 if not existing:
84 msg = _(u'Fork with id `{}` does not exists').format(value)
91 msg = _(u'Fork with id `{}` does not exists').format(value)
85 raise colander.Invalid(node, msg)
92 raise colander.Invalid(node, msg)
86 elif old_values['repo_name'] == existing.repo_name:
93 elif old_values['repo_name'] == existing.repo_name:
87 msg = _(u'Cannot set fork of '
94 msg = _(u'Cannot set fork of '
88 u'parameter of this repository to itself').format(value)
95 u'parameter of this repository to itself').format(value)
89 raise colander.Invalid(node, msg)
96 raise colander.Invalid(node, msg)
90
97
91 return fork_of_validator
98 return fork_of_validator
92
99
93
100
94 @colander.deferred
101 @colander.deferred
95 def deferred_can_write_to_group_validator(node, kw):
102 def deferred_can_write_to_group_validator(node, kw):
96 request_user = kw.get('user')
103 request_user = kw.get('user')
97 old_values = kw.get('old_values') or {}
104 old_values = kw.get('old_values') or {}
98
105
99 def can_write_to_group_validator(node, value):
106 def can_write_to_group_validator(node, value):
100 """
107 """
101 Checks if given repo path is writable by user. This includes checks if
108 Checks if given repo path is writable by user. This includes checks if
102 user is allowed to create repositories under root path or under
109 user is allowed to create repositories under root path or under
103 repo group paths
110 repo group paths
104 """
111 """
105
112
106 from rhodecode.lib.auth import (
113 from rhodecode.lib.auth import (
107 HasPermissionAny, HasRepoGroupPermissionAny)
114 HasPermissionAny, HasRepoGroupPermissionAny)
108 from rhodecode.model.repo_group import RepoGroupModel
115 from rhodecode.model.repo_group import RepoGroupModel
109
116
110 messages = {
117 messages = {
111 'invalid_repo_group':
118 'invalid_repo_group':
112 _(u"Repository group `{}` does not exist"),
119 _(u"Repository group `{}` does not exist"),
113 # permissions denied we expose as not existing, to prevent
120 # permissions denied we expose as not existing, to prevent
114 # resource discovery
121 # resource discovery
115 'permission_denied':
122 'permission_denied':
116 _(u"Repository group `{}` does not exist"),
123 _(u"Repository group `{}` does not exist"),
117 'permission_denied_root':
124 'permission_denied_root':
118 _(u"You do not have the permission to store "
125 _(u"You do not have the permission to store "
119 u"repositories in the root location.")
126 u"repositories in the root location.")
120 }
127 }
121
128
122 value = value['repo_group_name']
129 value = value['repo_group_name']
123
130
124 is_root_location = value is types.RootLocation
131 is_root_location = value is types.RootLocation
125 # NOT initialized validators, we must call them
132 # NOT initialized validators, we must call them
126 can_create_repos_at_root = HasPermissionAny(
133 can_create_repos_at_root = HasPermissionAny(
127 'hg.admin', 'hg.create.repository')
134 'hg.admin', 'hg.create.repository')
128
135
129 # if values is root location, we simply need to check if we can write
136 # if values is root location, we simply need to check if we can write
130 # to root location !
137 # to root location !
131 if is_root_location:
138 if is_root_location:
132 if can_create_repos_at_root(user=request_user):
139 if can_create_repos_at_root(user=request_user):
133 # we can create repo group inside tool-level. No more checks
140 # we can create repo group inside tool-level. No more checks
134 # are required
141 # are required
135 return
142 return
136 else:
143 else:
137 # "fake" node name as repo_name, otherwise we oddly report
144 # "fake" node name as repo_name, otherwise we oddly report
138 # the error as if it was coming form repo_group
145 # the error as if it was coming form repo_group
139 # however repo_group is empty when using root location.
146 # however repo_group is empty when using root location.
140 node.name = 'repo_name'
147 node.name = 'repo_name'
141 raise colander.Invalid(node, messages['permission_denied_root'])
148 raise colander.Invalid(node, messages['permission_denied_root'])
142
149
143 # parent group not exists ? throw an error
150 # parent group not exists ? throw an error
144 repo_group = RepoGroupModel().get_by_group_name(value)
151 repo_group = RepoGroupModel().get_by_group_name(value)
145 if value and not repo_group:
152 if value and not repo_group:
146 raise colander.Invalid(
153 raise colander.Invalid(
147 node, messages['invalid_repo_group'].format(value))
154 node, messages['invalid_repo_group'].format(value))
148
155
149 gr_name = repo_group.group_name
156 gr_name = repo_group.group_name
150
157
151 # create repositories with write permission on group is set to true
158 # create repositories with write permission on group is set to true
152 create_on_write = HasPermissionAny(
159 create_on_write = HasPermissionAny(
153 'hg.create.write_on_repogroup.true')(user=request_user)
160 'hg.create.write_on_repogroup.true')(user=request_user)
154
161
155 group_admin = HasRepoGroupPermissionAny('group.admin')(
162 group_admin = HasRepoGroupPermissionAny('group.admin')(
156 gr_name, 'can write into group validator', user=request_user)
163 gr_name, 'can write into group validator', user=request_user)
157 group_write = HasRepoGroupPermissionAny('group.write')(
164 group_write = HasRepoGroupPermissionAny('group.write')(
158 gr_name, 'can write into group validator', user=request_user)
165 gr_name, 'can write into group validator', user=request_user)
159
166
160 forbidden = not (group_admin or (group_write and create_on_write))
167 forbidden = not (group_admin or (group_write and create_on_write))
161
168
162 # TODO: handling of old values, and detecting no-change in path
169 # TODO: handling of old values, and detecting no-change in path
163 # to skip permission checks in such cases. This only needs to be
170 # to skip permission checks in such cases. This only needs to be
164 # implemented if we use this schema in forms as well
171 # implemented if we use this schema in forms as well
165
172
166 # gid = (old_data['repo_group'].get('group_id')
173 # gid = (old_data['repo_group'].get('group_id')
167 # if (old_data and 'repo_group' in old_data) else None)
174 # if (old_data and 'repo_group' in old_data) else None)
168 # value_changed = gid != safe_int(value)
175 # value_changed = gid != safe_int(value)
169 # new = not old_data
176 # new = not old_data
170
177
171 # do check if we changed the value, there's a case that someone got
178 # do check if we changed the value, there's a case that someone got
172 # revoked write permissions to a repository, he still created, we
179 # revoked write permissions to a repository, he still created, we
173 # don't need to check permission if he didn't change the value of
180 # don't need to check permission if he didn't change the value of
174 # groups in form box
181 # groups in form box
175 # if value_changed or new:
182 # if value_changed or new:
176 # # parent group need to be existing
183 # # parent group need to be existing
177 # TODO: ENDS HERE
184 # TODO: ENDS HERE
178
185
179 if repo_group and forbidden:
186 if repo_group and forbidden:
180 msg = messages['permission_denied'].format(value)
187 msg = messages['permission_denied'].format(value)
181 raise colander.Invalid(node, msg)
188 raise colander.Invalid(node, msg)
182
189
183 return can_write_to_group_validator
190 return can_write_to_group_validator
184
191
185
192
186 @colander.deferred
193 @colander.deferred
187 def deferred_unique_name_validator(node, kw):
194 def deferred_unique_name_validator(node, kw):
188 request_user = kw.get('user')
195 request_user = kw.get('user')
189 old_values = kw.get('old_values') or {}
196 old_values = kw.get('old_values') or {}
190
197
191 def unique_name_validator(node, value):
198 def unique_name_validator(node, value):
192 from rhodecode.model.db import Repository, RepoGroup
199 from rhodecode.model.db import Repository, RepoGroup
193 name_changed = value != old_values.get('repo_name')
200 name_changed = value != old_values.get('repo_name')
194
201
195 existing = Repository.get_by_repo_name(value)
202 existing = Repository.get_by_repo_name(value)
196 if name_changed and existing:
203 if name_changed and existing:
197 msg = _(u'Repository with name `{}` already exists').format(value)
204 msg = _(u'Repository with name `{}` already exists').format(value)
198 raise colander.Invalid(node, msg)
205 raise colander.Invalid(node, msg)
199
206
200 existing_group = RepoGroup.get_by_group_name(value)
207 existing_group = RepoGroup.get_by_group_name(value)
201 if name_changed and existing_group:
208 if name_changed and existing_group:
202 msg = _(u'Repository group with name `{}` already exists').format(
209 msg = _(u'Repository group with name `{}` already exists').format(
203 value)
210 value)
204 raise colander.Invalid(node, msg)
211 raise colander.Invalid(node, msg)
205 return unique_name_validator
212 return unique_name_validator
206
213
207
214
208 @colander.deferred
215 @colander.deferred
209 def deferred_repo_name_validator(node, kw):
216 def deferred_repo_name_validator(node, kw):
210 def no_git_suffix_validator(node, value):
217 def no_git_suffix_validator(node, value):
211 if value.endswith('.git'):
218 if value.endswith('.git'):
212 msg = _('Repository name cannot end with .git')
219 msg = _('Repository name cannot end with .git')
213 raise colander.Invalid(node, msg)
220 raise colander.Invalid(node, msg)
214 return colander.All(
221 return colander.All(
215 no_git_suffix_validator, validators.valid_name_validator)
222 no_git_suffix_validator, validators.valid_name_validator)
216
223
217
224
218 @colander.deferred
225 @colander.deferred
219 def deferred_repo_group_validator(node, kw):
226 def deferred_repo_group_validator(node, kw):
220 options = kw.get(
227 options = kw.get(
221 'repo_repo_group_options')
228 'repo_repo_group_options')
222 return colander.OneOf([x for x in options])
229 return colander.OneOf([x for x in options])
223
230
224
231
225 @colander.deferred
232 @colander.deferred
226 def deferred_repo_group_widget(node, kw):
233 def deferred_repo_group_widget(node, kw):
227 items = kw.get('repo_repo_group_items')
234 items = kw.get('repo_repo_group_items')
228 return deform.widget.Select2Widget(values=items)
235 return deform.widget.Select2Widget(values=items)
229
236
230
237
231 class GroupType(colander.Mapping):
238 class GroupType(colander.Mapping):
232 def _validate(self, node, value):
239 def _validate(self, node, value):
233 try:
240 try:
234 return dict(repo_group_name=value)
241 return dict(repo_group_name=value)
235 except Exception as e:
242 except Exception as e:
236 raise colander.Invalid(
243 raise colander.Invalid(
237 node, '"${val}" is not a mapping type: ${err}'.format(
244 node, '"${val}" is not a mapping type: ${err}'.format(
238 val=value, err=e))
245 val=value, err=e))
239
246
240 def deserialize(self, node, cstruct):
247 def deserialize(self, node, cstruct):
241 if cstruct is colander.null:
248 if cstruct is colander.null:
242 return cstruct
249 return cstruct
243
250
244 appstruct = super(GroupType, self).deserialize(node, cstruct)
251 appstruct = super(GroupType, self).deserialize(node, cstruct)
245 validated_name = appstruct['repo_group_name']
252 validated_name = appstruct['repo_group_name']
246
253
247 # inject group based on once deserialized data
254 # inject group based on once deserialized data
248 (repo_name_without_group,
255 (repo_name_without_group,
249 parent_group_name,
256 parent_group_name,
250 parent_group) = get_group_and_repo(validated_name)
257 parent_group) = get_group_and_repo(validated_name)
251
258
252 appstruct['repo_name_with_group'] = validated_name
259 appstruct['repo_name_with_group'] = validated_name
253 appstruct['repo_name_without_group'] = repo_name_without_group
260 appstruct['repo_name_without_group'] = repo_name_without_group
254 appstruct['repo_group_name'] = parent_group_name or types.RootLocation
261 appstruct['repo_group_name'] = parent_group_name or types.RootLocation
255
262
256 if parent_group:
263 if parent_group:
257 appstruct['repo_group_id'] = parent_group.group_id
264 appstruct['repo_group_id'] = parent_group.group_id
258
265
259 return appstruct
266 return appstruct
260
267
261
268
262 class GroupSchema(colander.SchemaNode):
269 class GroupSchema(colander.SchemaNode):
263 schema_type = GroupType
270 schema_type = GroupType
264 validator = deferred_can_write_to_group_validator
271 validator = deferred_can_write_to_group_validator
265 missing = colander.null
272 missing = colander.null
266
273
267
274
268 class RepoGroup(GroupSchema):
275 class RepoGroup(GroupSchema):
269 repo_group_name = colander.SchemaNode(
276 repo_group_name = colander.SchemaNode(
270 types.GroupNameType())
277 types.GroupNameType())
271 repo_group_id = colander.SchemaNode(
278 repo_group_id = colander.SchemaNode(
272 colander.String(), missing=None)
279 colander.String(), missing=None)
273 repo_name_without_group = colander.SchemaNode(
280 repo_name_without_group = colander.SchemaNode(
274 colander.String(), missing=None)
281 colander.String(), missing=None)
275
282
276
283
277 class RepoGroupAccessSchema(colander.MappingSchema):
284 class RepoGroupAccessSchema(colander.MappingSchema):
278 repo_group = RepoGroup()
285 repo_group = RepoGroup()
279
286
280
287
281 class RepoNameUniqueSchema(colander.MappingSchema):
288 class RepoNameUniqueSchema(colander.MappingSchema):
282 unique_repo_name = colander.SchemaNode(
289 unique_repo_name = colander.SchemaNode(
283 colander.String(),
290 colander.String(),
284 validator=deferred_unique_name_validator)
291 validator=deferred_unique_name_validator)
285
292
286
293
287 class RepoSchema(colander.MappingSchema):
294 class RepoSchema(colander.MappingSchema):
288
295
289 repo_name = colander.SchemaNode(
296 repo_name = colander.SchemaNode(
290 types.RepoNameType(),
297 types.RepoNameType(),
291 validator=deferred_repo_name_validator)
298 validator=deferred_repo_name_validator)
292
299
293 repo_type = colander.SchemaNode(
300 repo_type = colander.SchemaNode(
294 colander.String(),
301 colander.String(),
295 validator=deferred_repo_type_validator)
302 validator=deferred_repo_type_validator)
296
303
297 repo_owner = colander.SchemaNode(
304 repo_owner = colander.SchemaNode(
298 colander.String(),
305 colander.String(),
299 validator=deferred_repo_owner_validator,
306 validator=deferred_repo_owner_validator,
300 widget=deform.widget.TextInputWidget())
307 widget=deform.widget.TextInputWidget())
301
308
302 repo_description = colander.SchemaNode(
309 repo_description = colander.SchemaNode(
303 colander.String(), missing='',
310 colander.String(), missing='',
304 widget=deform.widget.TextAreaWidget())
311 widget=deform.widget.TextAreaWidget())
305
312
306 repo_landing_commit_ref = colander.SchemaNode(
313 repo_landing_commit_ref = colander.SchemaNode(
307 colander.String(),
314 colander.String(),
308 validator=deferred_landing_ref_validator,
315 validator=deferred_landing_ref_validator,
309 preparers=[preparers.strip_preparer],
316 preparers=[preparers.strip_preparer],
310 missing=DEFAULT_LANDING_REF,
317 missing=DEFAULT_LANDING_REF,
311 widget=deferred_landing_ref_widget)
318 widget=deferred_landing_ref_widget)
312
319
313 repo_clone_uri = colander.SchemaNode(
320 repo_clone_uri = colander.SchemaNode(
314 colander.String(),
321 colander.String(),
315 validator=colander.All(colander.Length(min=1)),
322 validator=colander.All(colander.Length(min=1)),
316 preparers=[preparers.strip_preparer],
323 preparers=[preparers.strip_preparer],
317 missing='')
324 missing='')
318
325
319 repo_fork_of = colander.SchemaNode(
326 repo_fork_of = colander.SchemaNode(
320 colander.String(),
327 colander.String(),
321 validator=deferred_fork_of_validator,
328 validator=deferred_fork_of_validator,
322 missing=None)
329 missing=None)
323
330
324 repo_private = colander.SchemaNode(
331 repo_private = colander.SchemaNode(
325 types.StringBooleanType(),
332 types.StringBooleanType(),
326 missing=False, widget=deform.widget.CheckboxWidget())
333 missing=False, widget=deform.widget.CheckboxWidget())
327 repo_copy_permissions = colander.SchemaNode(
334 repo_copy_permissions = colander.SchemaNode(
328 types.StringBooleanType(),
335 types.StringBooleanType(),
329 missing=False, widget=deform.widget.CheckboxWidget())
336 missing=False, widget=deform.widget.CheckboxWidget())
330 repo_enable_statistics = colander.SchemaNode(
337 repo_enable_statistics = colander.SchemaNode(
331 types.StringBooleanType(),
338 types.StringBooleanType(),
332 missing=False, widget=deform.widget.CheckboxWidget())
339 missing=False, widget=deform.widget.CheckboxWidget())
333 repo_enable_downloads = colander.SchemaNode(
340 repo_enable_downloads = colander.SchemaNode(
334 types.StringBooleanType(),
341 types.StringBooleanType(),
335 missing=False, widget=deform.widget.CheckboxWidget())
342 missing=False, widget=deform.widget.CheckboxWidget())
336 repo_enable_locking = colander.SchemaNode(
343 repo_enable_locking = colander.SchemaNode(
337 types.StringBooleanType(),
344 types.StringBooleanType(),
338 missing=False, widget=deform.widget.CheckboxWidget())
345 missing=False, widget=deform.widget.CheckboxWidget())
339
346
340 def deserialize(self, cstruct):
347 def deserialize(self, cstruct):
341 """
348 """
342 Custom deserialize that allows to chain validation, and verify
349 Custom deserialize that allows to chain validation, and verify
343 permissions, and as last step uniqueness
350 permissions, and as last step uniqueness
344 """
351 """
345
352
346 # first pass, to validate given data
353 # first pass, to validate given data
347 appstruct = super(RepoSchema, self).deserialize(cstruct)
354 appstruct = super(RepoSchema, self).deserialize(cstruct)
348 validated_name = appstruct['repo_name']
355 validated_name = appstruct['repo_name']
349
356
350 # second pass to validate permissions to repo_group
357 # second pass to validate permissions to repo_group
351 second = RepoGroupAccessSchema().bind(**self.bindings)
358 second = RepoGroupAccessSchema().bind(**self.bindings)
352 appstruct_second = second.deserialize({'repo_group': validated_name})
359 appstruct_second = second.deserialize({'repo_group': validated_name})
353 # save result
360 # save result
354 appstruct['repo_group'] = appstruct_second['repo_group']
361 appstruct['repo_group'] = appstruct_second['repo_group']
355
362
356 # thirds to validate uniqueness
363 # thirds to validate uniqueness
357 third = RepoNameUniqueSchema().bind(**self.bindings)
364 third = RepoNameUniqueSchema().bind(**self.bindings)
358 third.deserialize({'unique_repo_name': validated_name})
365 third.deserialize({'unique_repo_name': validated_name})
359
366
360 return appstruct
367 return appstruct
368
369
370 class RepoSettingsSchema(RepoSchema):
371 repo_group = colander.SchemaNode(
372 colander.Integer(),
373 validator=deferred_repo_group_validator,
374 widget=deferred_repo_group_widget,
375 missing='')
376
377 repo_clone_uri_change = colander.SchemaNode(
378 colander.String(),
379 missing='NEW')
380
381 repo_clone_uri = colander.SchemaNode(
382 colander.String(),
383 preparers=[preparers.strip_preparer],
384 validator=deferred_clone_uri_validator,
385 missing='')
386
387 def deserialize(self, cstruct):
388 """
389 Custom deserialize that allows to chain validation, and verify
390 permissions, and as last step uniqueness
391 """
392
393 # first pass, to validate given data
394 appstruct = super(RepoSchema, self).deserialize(cstruct)
395 validated_name = appstruct['repo_name']
396 # because of repoSchema adds repo-group as an ID, we inject it as
397 # full name here because validators require it, it's unwrapped later
398 # so it's safe to use and final name is going to be without group anyway
399
400 group, separator = get_repo_group(appstruct['repo_group'])
401 if group:
402 validated_name = separator.join([group.group_name, validated_name])
403
404 # second pass to validate permissions to repo_group
405 second = RepoGroupAccessSchema().bind(**self.bindings)
406 appstruct_second = second.deserialize({'repo_group': validated_name})
407 # save result
408 appstruct['repo_group'] = appstruct_second['repo_group']
409
410 # thirds to validate uniqueness
411 third = RepoNameUniqueSchema().bind(**self.bindings)
412 third.deserialize({'unique_repo_name': validated_name})
413
414 return appstruct
@@ -1,48 +1,140 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-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
1 import os
21 import os
2 import re
22 import re
23 import logging
24
3
25
4 import ipaddress
26 import ipaddress
5 import colander
27 import colander
6
28
7 from rhodecode.translation import _
29 from rhodecode.translation import _
8 from rhodecode.lib.utils2 import glob2re
30 from rhodecode.lib.utils2 import glob2re
9
31
32 log = logging.getLogger(__name__)
33
10
34
11 def ip_addr_validator(node, value):
35 def ip_addr_validator(node, value):
12 try:
36 try:
13 # this raises an ValueError if address is not IpV4 or IpV6
37 # this raises an ValueError if address is not IpV4 or IpV6
14 ipaddress.ip_network(value, strict=False)
38 ipaddress.ip_network(value, strict=False)
15 except ValueError:
39 except ValueError:
16 msg = _(u'Please enter a valid IPv4 or IpV6 address')
40 msg = _(u'Please enter a valid IPv4 or IpV6 address')
17 raise colander.Invalid(node, msg)
41 raise colander.Invalid(node, msg)
18
42
19
43
20 class IpAddrValidator(object):
44 class IpAddrValidator(object):
21 def __init__(self, strict=True):
45 def __init__(self, strict=True):
22 self.strict = strict
46 self.strict = strict
23
47
24 def __call__(self, node, value):
48 def __call__(self, node, value):
25 try:
49 try:
26 # this raises an ValueError if address is not IpV4 or IpV6
50 # this raises an ValueError if address is not IpV4 or IpV6
27 ipaddress.ip_network(value, strict=self.strict)
51 ipaddress.ip_network(value, strict=self.strict)
28 except ValueError:
52 except ValueError:
29 msg = _(u'Please enter a valid IPv4 or IpV6 address')
53 msg = _(u'Please enter a valid IPv4 or IpV6 address')
30 raise colander.Invalid(node, msg)
54 raise colander.Invalid(node, msg)
31
55
32
56
33 def glob_validator(node, value):
57 def glob_validator(node, value):
34 try:
58 try:
35 re.compile('^' + glob2re(value) + '$')
59 re.compile('^' + glob2re(value) + '$')
36 except Exception:
60 except Exception:
37 msg = _(u'Invalid glob pattern')
61 msg = _(u'Invalid glob pattern')
38 raise colander.Invalid(node, msg)
62 raise colander.Invalid(node, msg)
39
63
40
64
41 def valid_name_validator(node, value):
65 def valid_name_validator(node, value):
42 from rhodecode.model.validation_schema import types
66 from rhodecode.model.validation_schema import types
43 if value is types.RootLocation:
67 if value is types.RootLocation:
44 return
68 return
45
69
46 msg = _('Name must start with a letter or number. Got `{}`').format(value)
70 msg = _('Name must start with a letter or number. Got `{}`').format(value)
47 if not re.match(r'^[a-zA-z0-9]{1,}', value):
71 if not re.match(r'^[a-zA-z0-9]{1,}', value):
48 raise colander.Invalid(node, msg)
72 raise colander.Invalid(node, msg)
73
74
75 class InvalidCloneUrl(Exception):
76 allowed_prefixes = ()
77
78
79 def url_validator(url, repo_type, config):
80 from rhodecode.lib.vcs.backends.hg import MercurialRepository
81 from rhodecode.lib.vcs.backends.git import GitRepository
82 from rhodecode.lib.vcs.backends.svn import SubversionRepository
83
84 if repo_type == 'hg':
85 allowed_prefixes = ('http', 'svn+http', 'git+http')
86
87 if 'http' in url[:4]:
88 # initially check if it's at least the proper URL
89 # or does it pass basic auth
90
91 MercurialRepository.check_url(url, config)
92 elif 'svn+http' in url[:8]: # svn->hg import
93 SubversionRepository.check_url(url, config)
94 elif 'git+http' in url[:8]: # git->hg import
95 raise NotImplementedError()
96 else:
97 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
98 'Allowed url must start with one of %s'
99 % (url, ','.join(allowed_prefixes)))
100 exc.allowed_prefixes = allowed_prefixes
101 raise exc
102
103 elif repo_type == 'git':
104 allowed_prefixes = ('http', 'svn+http', 'hg+http')
105 if 'http' in url[:4]:
106 # initially check if it's at least the proper URL
107 # or does it pass basic auth
108 GitRepository.check_url(url, config)
109 elif 'svn+http' in url[:8]: # svn->git import
110 raise NotImplementedError()
111 elif 'hg+http' in url[:8]: # hg->git import
112 raise NotImplementedError()
113 else:
114 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
115 'Allowed url must start with one of %s'
116 % (url, ','.join(allowed_prefixes)))
117 exc.allowed_prefixes = allowed_prefixes
118 raise exc
119
120
121 class CloneUriValidator(object):
122 def __init__(self, repo_type):
123 self.repo_type = repo_type
124
125 def __call__(self, node, value):
126 from rhodecode.lib.utils import make_db_config
127 try:
128 config = make_db_config(clear_session=False)
129 url_validator(value, self.repo_type, config)
130 except InvalidCloneUrl as e:
131 log.warning(e)
132 msg = _(u'Invalid clone url, provide a valid clone '
133 u'url starting with one of {allowed_prefixes}').format(
134 allowed_prefixes=e.allowed_prefixes)
135 raise colander.Invalid(node, msg)
136 except Exception:
137 log.exception('Url validation failed')
138 msg = _(u'invalid clone url for {repo_type} repository').format(
139 repo_type=self.repo_type)
140 raise colander.Invalid(node, msg)
@@ -1,249 +1,260 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%namespace name="base" file="/base/base.mako"/>
2 <%namespace name="base" file="/base/base.mako"/>
3
3
4 <div class="panel panel-default">
4 <div class="panel panel-default">
5 <div class="panel-heading">
5 <div class="panel-heading">
6 <h3 class="panel-title">${_('Settings for Repository: %s') % c.rhodecode_db_repo.repo_name}</h3>
6 <h3 class="panel-title">${_('Settings for Repository: %s') % c.rhodecode_db_repo.repo_name}</h3>
7 </div>
7 </div>
8 <div class="panel-body">
8 <div class="panel-body">
9 ${h.secure_form(h.route_path('edit_repo', repo_name=c.rhodecode_db_repo.repo_name), method='POST')}
9 ${h.secure_form(h.route_path('edit_repo', repo_name=c.rhodecode_db_repo.repo_name), method='POST')}
10 <div class="form">
10 <div class="form">
11 <!-- fields -->
11 <!-- fields -->
12 <div class="fields">
12 <div class="fields">
13
13
14 <div class="field">
14 <div class="field">
15 <div class="label">
15 <div class="label">
16 <label for="repo_name">${_('Name')}:</label>
16 <label for="repo_name">${_('Name')}:</label>
17 </div>
17 </div>
18 <div class="input">
18 <div class="input">
19 ${c.form['repo_name'].render(css_class='medium', oid='repo_name')|n}
19 ${c.form['repo_name'].render(css_class='medium', oid='repo_name')|n}
20 ${c.form.render_error(request, c.form['repo_name'])|n}
20 ${c.form.render_error(request, c.form['repo_name'])|n}
21
21
22 <p class="help-block">${_('Non-changeable id')}: `_${c.rhodecode_db_repo.repo_id}` <span><a href="#" onclick="$('#clone_id').toggle();return false">${_('what is that ?')}</a></span></p>
22 <p class="help-block">${_('Non-changeable id')}: `_${c.rhodecode_db_repo.repo_id}` <span><a href="#" onclick="$('#clone_id').toggle();return false">${_('what is that ?')}</a></span></p>
23 <p id="clone_id" style="display:none;">
23 <p id="clone_id" style="display:none;">
24 ${_('URL by id')}: `${c.rhodecode_db_repo.clone_url(with_id=True)}` <br/>
24 ${_('URL by id')}: `${c.rhodecode_db_repo.clone_url(with_id=True)}` <br/>
25 ${_('''In case this repository is renamed or moved into another group the repository url changes.
25 ${_('''In case this repository is renamed or moved into another group the repository url changes.
26 Using above url guarantees that this repository will always be accessible under such url.
26 Using above url guarantees that this repository will always be accessible under such url.
27 Useful for CI systems, or any other cases that you need to hardcode the url into 3rd party service.''')}</p>
27 Useful for CI systems, or any other cases that you need to hardcode the url into 3rd party service.''')}</p>
28 </div>
28 </div>
29 </div>
29 </div>
30
30
31 <div class="field">
31 <div class="field">
32 <div class="label">
32 <div class="label">
33 <label for="repo_group">${_('Repository group')}:</label>
33 <label for="repo_group">${_('Repository group')}:</label>
34 </div>
34 </div>
35 <div class="select">
35 <div class="select">
36 ${c.form['repo_group'].render(css_class='medium', oid='repo_group')|n}
36 ${c.form['repo_group'].render(css_class='medium', oid='repo_group')|n}
37 ${c.form.render_error(request, c.form['repo_group'])|n}
37 ${c.form.render_error(request, c.form['repo_group'])|n}
38
38
39 % if c.personal_repo_group:
39 % if c.personal_repo_group:
40 <a class="btn" href="#" data-personal-group-name="${c.personal_repo_group.group_name}" data-personal-group-id="${c.personal_repo_group.group_id}" onclick="selectMyGroup(this); return false">
40 <a class="btn" href="#" data-personal-group-name="${c.personal_repo_group.group_name}" data-personal-group-id="${c.personal_repo_group.group_id}" onclick="selectMyGroup(this); return false">
41 ${_('Select my personal group (`%(repo_group_name)s`)') % {'repo_group_name': c.personal_repo_group.group_name}}
41 ${_('Select my personal group (`%(repo_group_name)s`)') % {'repo_group_name': c.personal_repo_group.group_name}}
42 </a>
42 </a>
43 % endif
43 % endif
44 <p class="help-block">${_('Optional select a group to put this repository into.')}</p>
44 <p class="help-block">${_('Optional select a group to put this repository into.')}</p>
45 </div>
45 </div>
46 </div>
46 </div>
47
47
48 % if c.rhodecode_db_repo.repo_type != 'svn':
48 % if c.rhodecode_db_repo.repo_type != 'svn':
49 <div class="field">
49 <div class="field">
50 <div class="label">
50 <div class="label">
51 <label for="clone_uri">${_('Remote uri')}:</label>
51 <label for="clone_uri">${_('Remote uri')}:</label>
52 </div>
52 </div>
53 <div class="input">
53 <div class="input">
54 %if c.rhodecode_db_repo.clone_uri:
54 %if c.rhodecode_db_repo.clone_uri:
55 ## display
55 ## display, if we don't have any errors
56 % if not c.form['repo_clone_uri'].error:
56 <div id="clone_uri_hidden" class='text-as-placeholder'>
57 <div id="clone_uri_hidden" class='text-as-placeholder'>
57 <span id="clone_uri_hidden_value">${c.rhodecode_db_repo.clone_uri_hidden}</span>
58 <span id="clone_uri_hidden_value">${c.rhodecode_db_repo.clone_uri_hidden}</span>
58 <span class="link" id="edit_clone_uri"><i class="icon-edit"></i>${_('edit')}</span>
59 <span class="link" id="edit_clone_uri"><i class="icon-edit"></i>${_('edit')}</span>
59 </div>
60 </div>
61 % endif
62
60 ## alter field
63 ## alter field
61 <div id="alter_clone_uri" style="display: none">
64 <div id="alter_clone_uri" style="${'' if c.form['repo_clone_uri'].error else 'display: none'}">
62 ${c.form['repo_clone_uri'].render(css_class='medium', oid='clone_uri', placeholder=_('enter new value, or leave empty to remove'))|n}
65 ${c.form['repo_clone_uri'].render(css_class='medium', oid='clone_uri', placeholder=_('enter new value, or leave empty to remove'))|n}
63 ${c.form.render_error(request, c.form['repo_clone_uri'])|n}
66 ${c.form.render_error(request, c.form['repo_clone_uri'])|n}
67 % if c.form['repo_clone_uri'].error:
68 ## we got error from form subit, means we modify the url
69 ${h.hidden('repo_clone_uri_change', 'MOD')}
70 % else:
64 ${h.hidden('repo_clone_uri_change', 'OLD')}
71 ${h.hidden('repo_clone_uri_change', 'OLD')}
72 % endif
65
73
74 % if not c.form['repo_clone_uri'].error:
66 <span class="link" id="cancel_edit_clone_uri">${_('cancel')}</span>
75 <span class="link" id="cancel_edit_clone_uri">${_('cancel')}</span>
76 % endif
77
67 </div>
78 </div>
68 %else:
79 %else:
69 ## not set yet, display form to set it
80 ## not set yet, display form to set it
70 ${c.form['repo_clone_uri'].render(css_class='medium', oid='clone_uri')|n}
81 ${c.form['repo_clone_uri'].render(css_class='medium', oid='clone_uri')|n}
71 ${c.form.render_error(request, c.form['repo_clone_uri'])|n}
82 ${c.form.render_error(request, c.form['repo_clone_uri'])|n}
72 ${h.hidden('repo_clone_uri_change', 'NEW')}
83 ${h.hidden('repo_clone_uri_change', 'NEW')}
73 %endif
84 %endif
74 <p id="alter_clone_uri_help_block" class="help-block">
85 <p id="alter_clone_uri_help_block" class="help-block">
75 <% pull_link = h.literal(h.link_to('remote sync', h.url('edit_repo_remote', repo_name=c.repo_name))) %>
86 <% pull_link = h.literal(h.link_to('remote sync', h.url('edit_repo_remote', repo_name=c.repo_name))) %>
76 ${_('http[s] url where from repository was imported, this field can used for doing {pull_link}.').format(pull_link=pull_link)|n} <br/>
87 ${_('http[s] url where from repository was imported, this field can used for doing {pull_link}.').format(pull_link=pull_link)|n} <br/>
77 ${_('This field is stored encrypted inside Database, a format of http://user:password@server.com/repo_name can be used and will be hidden from display.')}
88 ${_('This field is stored encrypted inside Database, a format of http://user:password@server.com/repo_name can be used and will be hidden from display.')}
78 </p>
89 </p>
79 </div>
90 </div>
80 </div>
91 </div>
81 % else:
92 % else:
82 ${h.hidden('repo_clone_uri', '')}
93 ${h.hidden('repo_clone_uri', '')}
83 % endif
94 % endif
84
95
85 <div class="field">
96 <div class="field">
86 <div class="label">
97 <div class="label">
87 <label for="repo_landing_commit_ref">${_('Landing commit')}:</label>
98 <label for="repo_landing_commit_ref">${_('Landing commit')}:</label>
88 </div>
99 </div>
89 <div class="select">
100 <div class="select">
90 ${c.form['repo_landing_commit_ref'].render(css_class='medium', oid='repo_landing_commit_ref')|n}
101 ${c.form['repo_landing_commit_ref'].render(css_class='medium', oid='repo_landing_commit_ref')|n}
91 ${c.form.render_error(request, c.form['repo_landing_commit_ref'])|n}
102 ${c.form.render_error(request, c.form['repo_landing_commit_ref'])|n}
92 <p class="help-block">${_('Default commit for files page, downloads, full text search index and readme')}</p>
103 <p class="help-block">${_('Default commit for files page, downloads, full text search index and readme')}</p>
93 </div>
104 </div>
94 </div>
105 </div>
95
106
96 <div class="field badged-field">
107 <div class="field badged-field">
97 <div class="label">
108 <div class="label">
98 <label for="repo_owner">${_('Owner')}:</label>
109 <label for="repo_owner">${_('Owner')}:</label>
99 </div>
110 </div>
100 <div class="input">
111 <div class="input">
101 <div class="badge-input-container">
112 <div class="badge-input-container">
102 <div class="user-badge">
113 <div class="user-badge">
103 ${base.gravatar_with_user(c.rhodecode_db_repo.user.email, show_disabled=not c.rhodecode_db_repo.user.active)}
114 ${base.gravatar_with_user(c.rhodecode_db_repo.user.email, show_disabled=not c.rhodecode_db_repo.user.active)}
104 </div>
115 </div>
105 <div class="badge-input-wrap">
116 <div class="badge-input-wrap">
106 ${c.form['repo_owner'].render(css_class='medium', oid='repo_owner')|n}
117 ${c.form['repo_owner'].render(css_class='medium', oid='repo_owner')|n}
107 </div>
118 </div>
108 </div>
119 </div>
109 ${c.form.render_error(request, c.form['repo_owner'])|n}
120 ${c.form.render_error(request, c.form['repo_owner'])|n}
110 <p class="help-block">${_('Change owner of this repository.')}</p>
121 <p class="help-block">${_('Change owner of this repository.')}</p>
111 </div>
122 </div>
112 </div>
123 </div>
113
124
114 <div class="field">
125 <div class="field">
115 <div class="label label-textarea">
126 <div class="label label-textarea">
116 <label for="repo_description">${_('Description')}:</label>
127 <label for="repo_description">${_('Description')}:</label>
117 </div>
128 </div>
118 <div class="textarea text-area editor">
129 <div class="textarea text-area editor">
119 ${c.form['repo_description'].render(css_class='medium', oid='repo_description')|n}
130 ${c.form['repo_description'].render(css_class='medium', oid='repo_description')|n}
120 ${c.form.render_error(request, c.form['repo_description'])|n}
131 ${c.form.render_error(request, c.form['repo_description'])|n}
121 <p class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</p>
132 <p class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</p>
122 </div>
133 </div>
123 </div>
134 </div>
124
135
125 <div class="field">
136 <div class="field">
126 <div class="label label-checkbox">
137 <div class="label label-checkbox">
127 <label for="${c.form['repo_private'].oid}">${_('Private repository')}:</label>
138 <label for="${c.form['repo_private'].oid}">${_('Private repository')}:</label>
128 </div>
139 </div>
129 <div class="checkboxes">
140 <div class="checkboxes">
130 ${c.form['repo_private'].render(css_class='medium')|n}
141 ${c.form['repo_private'].render(css_class='medium')|n}
131 ${c.form.render_error(request, c.form['repo_private'])|n}
142 ${c.form.render_error(request, c.form['repo_private'])|n}
132 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
143 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
133 </div>
144 </div>
134 </div>
145 </div>
135 <div class="field">
146 <div class="field">
136 <div class="label label-checkbox">
147 <div class="label label-checkbox">
137 <label for="${c.form['repo_enable_statistics'].oid}">${_('Enable statistics')}:</label>
148 <label for="${c.form['repo_enable_statistics'].oid}">${_('Enable statistics')}:</label>
138 </div>
149 </div>
139 <div class="checkboxes">
150 <div class="checkboxes">
140 ${c.form['repo_enable_statistics'].render(css_class='medium')|n}
151 ${c.form['repo_enable_statistics'].render(css_class='medium')|n}
141 ${c.form.render_error(request, c.form['repo_enable_statistics'])|n}
152 ${c.form.render_error(request, c.form['repo_enable_statistics'])|n}
142 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
153 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
143 </div>
154 </div>
144 </div>
155 </div>
145 <div class="field">
156 <div class="field">
146 <div class="label label-checkbox">
157 <div class="label label-checkbox">
147 <label for="${c.form['repo_enable_downloads'].oid}">${_('Enable downloads')}:</label>
158 <label for="${c.form['repo_enable_downloads'].oid}">${_('Enable downloads')}:</label>
148 </div>
159 </div>
149 <div class="checkboxes">
160 <div class="checkboxes">
150 ${c.form['repo_enable_downloads'].render(css_class='medium')|n}
161 ${c.form['repo_enable_downloads'].render(css_class='medium')|n}
151 ${c.form.render_error(request, c.form['repo_enable_downloads'])|n}
162 ${c.form.render_error(request, c.form['repo_enable_downloads'])|n}
152 <span class="help-block">${_('Enable download menu on summary page.')}</span>
163 <span class="help-block">${_('Enable download menu on summary page.')}</span>
153 </div>
164 </div>
154 </div>
165 </div>
155 <div class="field">
166 <div class="field">
156 <div class="label label-checkbox">
167 <div class="label label-checkbox">
157 <label for="${c.form['repo_enable_locking'].oid}">${_('Enable automatic locking')}:</label>
168 <label for="${c.form['repo_enable_locking'].oid}">${_('Enable automatic locking')}:</label>
158 </div>
169 </div>
159 <div class="checkboxes">
170 <div class="checkboxes">
160 ${c.form['repo_enable_locking'].render(css_class='medium')|n}
171 ${c.form['repo_enable_locking'].render(css_class='medium')|n}
161 ${c.form.render_error(request, c.form['repo_enable_locking'])|n}
172 ${c.form.render_error(request, c.form['repo_enable_locking'])|n}
162 <span class="help-block">${_('Enable automatic locking on repository. Pulling from this repository creates a lock that can be released by pushing back by the same user')}</span>
173 <span class="help-block">${_('Enable automatic locking on repository. Pulling from this repository creates a lock that can be released by pushing back by the same user')}</span>
163 </div>
174 </div>
164 </div>
175 </div>
165
176
166 %if c.visual.repository_fields:
177 %if c.visual.repository_fields:
167 ## EXTRA FIELDS
178 ## EXTRA FIELDS
168 %for field in c.repo_fields:
179 %for field in c.repo_fields:
169 <div class="field">
180 <div class="field">
170 <div class="label">
181 <div class="label">
171 <label for="${field.field_key_prefixed}">${field.field_label} (${field.field_key}):</label>
182 <label for="${field.field_key_prefixed}">${field.field_label} (${field.field_key}):</label>
172 </div>
183 </div>
173 <div class="input input-medium">
184 <div class="input input-medium">
174 ${h.text(field.field_key_prefixed, field.field_value, class_='medium')}
185 ${h.text(field.field_key_prefixed, field.field_value, class_='medium')}
175 %if field.field_desc:
186 %if field.field_desc:
176 <span class="help-block">${field.field_desc}</span>
187 <span class="help-block">${field.field_desc}</span>
177 %endif
188 %endif
178 </div>
189 </div>
179 </div>
190 </div>
180 %endfor
191 %endfor
181 %endif
192 %endif
182 <div class="buttons">
193 <div class="buttons">
183 ${h.submit('save',_('Save'),class_="btn")}
194 ${h.submit('save',_('Save'),class_="btn")}
184 ${h.reset('reset',_('Reset'),class_="btn")}
195 ${h.reset('reset',_('Reset'),class_="btn")}
185 </div>
196 </div>
186 </div>
197 </div>
187 </div>
198 </div>
188 ${h.end_form()}
199 ${h.end_form()}
189 </div>
200 </div>
190 </div>
201 </div>
191
202
192 <script>
203 <script>
193 $(document).ready(function(){
204 $(document).ready(function(){
194 var cloneUrl = function() {
205 var cloneUrl = function() {
195 var alterButton = $('#alter_clone_uri');
206 var alterButton = $('#alter_clone_uri');
196 var editButton = $('#edit_clone_uri');
207 var editButton = $('#edit_clone_uri');
197 var cancelEditButton = $('#cancel_edit_clone_uri');
208 var cancelEditButton = $('#cancel_edit_clone_uri');
198 var hiddenUrl = $('#clone_uri_hidden');
209 var hiddenUrl = $('#clone_uri_hidden');
199 var hiddenUrlValue = $('#clone_uri_hidden_value');
210 var hiddenUrlValue = $('#clone_uri_hidden_value');
200 var input = $('#clone_uri');
211 var input = $('#clone_uri');
201 var helpBlock = $('#alter_clone_uri_help_block');
212 var helpBlock = $('#alter_clone_uri_help_block');
202 var changedFlag = $('#repo_clone_uri_change');
213 var changedFlag = $('#repo_clone_uri_change');
203 var originalText = helpBlock.html();
214 var originalText = helpBlock.html();
204 var obfuscatedUrl = hiddenUrlValue.html();
215 var obfuscatedUrl = hiddenUrlValue.html();
205
216
206 var edit = function(e) {
217 var edit = function(e) {
207 alterButton.show();
218 alterButton.show();
208 editButton.hide();
219 editButton.hide();
209 hiddenUrl.hide();
220 hiddenUrl.hide();
210
221
211 //add the old value next to input for verification
222 //add the old value next to input for verification
212 helpBlock.html("(" + obfuscatedUrl + ")" + "<br\>" + originalText);
223 helpBlock.html("(" + obfuscatedUrl + ")" + "<br\>" + originalText);
213 changedFlag.val('MOD');
224 changedFlag.val('MOD');
214 };
225 };
215
226
216 var cancelEdit = function(e) {
227 var cancelEdit = function(e) {
217 alterButton.hide();
228 alterButton.hide();
218 editButton.show();
229 editButton.show();
219 hiddenUrl.show();
230 hiddenUrl.show();
220
231
221 helpBlock.html(originalText);
232 helpBlock.html(originalText);
222 changedFlag.val('OLD');
233 changedFlag.val('OLD');
223 input.val('');
234 input.val('');
224 };
235 };
225
236
226 var initEvents = function() {
237 var initEvents = function() {
227 editButton.on('click', edit);
238 editButton.on('click', edit);
228 cancelEditButton.on('click', cancelEdit);
239 cancelEditButton.on('click', cancelEdit);
229 };
240 };
230
241
231 var setInitialState = function() {
242 var setInitialState = function() {
232 if (input.hasClass('error')) {
243 if (input.hasClass('error')) {
233 alterButton.show();
244 alterButton.show();
234 editButton.hide();
245 editButton.hide();
235 hiddenUrl.hide();
246 hiddenUrl.hide();
236 }
247 }
237 };
248 };
238
249
239 setInitialState();
250 setInitialState();
240 initEvents();
251 initEvents();
241 }();
252 }();
242
253
243 selectMyGroup = function(element) {
254 selectMyGroup = function(element) {
244 $("#repo_group").val($(element).data('personalGroupId')).trigger("change");
255 $("#repo_group").val($(element).data('personalGroupId')).trigger("change");
245 };
256 };
246
257
247 UsersAutoComplete('repo_owner', '${c.rhodecode_user.user_id}');
258 UsersAutoComplete('repo_owner', '${c.rhodecode_user.user_id}');
248 });
259 });
249 </script>
260 </script>
General Comments 0
You need to be logged in to leave comments. Login now