##// END OF EJS Templates
#56 added ajax removal of users groups,...
marcink -
r1015:65129c33 beta
parent child Browse files
Show More
@@ -0,0 +1,95 b''
1 <table id="permissions_manage">
2 <tr>
3 <td>${_('none')}</td>
4 <td>${_('read')}</td>
5 <td>${_('write')}</td>
6 <td>${_('admin')}</td>
7 <td>${_('member')}</td>
8 <td></td>
9 </tr>
10 ## USERS
11 <script type="text/javascript">
12 function ajaxActionUser(user_id,field_id){
13 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
14 var callback = { success:function(o){
15 var tr = YUD.get(String(field_id));
16 tr.parentNode.removeChild(tr);},
17 failure:function(o){
18 alert("${_('Failed to remove user')}");},};
19 var postData = '_method=delete&user_id='+user_id;
20 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);};
21 </script>
22 %for r2p in c.repo_info.repo_to_perm:
23 %if r2p.user.username =='default' and c.repo_info.private:
24 <tr>
25 <td colspan="4">
26 <span class="private_repo_msg">
27 ${_('private repository')}
28 </span>
29 </td>
30 <td class="private_repo_msg"><img style="vertical-align:bottom" src="/images/icons/user.png"/>${r2p.user.username}</td>
31 </tr>
32 %else:
33 <tr id="id${id(r2p.user.username)}">
34 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.none')}</td>
35 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.read')}</td>
36 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.write')}</td>
37 <td>${h.radio('u_perm_%s' % r2p.user.username,'repository.admin')}</td>
38 <td style="white-space: nowrap;"><img style="vertical-align:bottom" src="/images/icons/user.png"/>${r2p.user.username}</td>
39 <td>
40 %if r2p.user.username !='default':
41 <span class="delete_icon action_button" onclick="ajaxActionUser(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
42 </span>
43 %endif
44 </td>
45 </tr>
46 %endif
47 %endfor
48
49 ## USERS GROUPS
50 <script type="text/javascript">
51 function ajaxActionUsersGroup(users_group_id,field_id){
52 var sUrl = "${h.url('delete_repo_users_group',repo_name=c.repo_name)}";
53 var callback = { success:function(o){
54 var tr = YUD.get(String(field_id));
55 tr.parentNode.removeChild(tr);},
56 failure:function(o){
57 alert("${_('Failed to remove users group')}");},};
58 var postData = '_method=delete&users_group_id='+users_group_id;
59 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);};
60 </script>
61 %for g2p in c.repo_info.users_group_to_perm:
62 <tr id="id${id(g2p.users_group.users_group_name)}">
63 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.none')}</td>
64 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.read')}</td>
65 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.write')}</td>
66 <td>${h.radio('g_perm_%s' % g2p.users_group.users_group_name,'repository.admin')}</td>
67 <td><img style="vertical-align:bottom" src="/images/icons/group.png"/>${g2p.users_group.users_group_name}</td>
68 <td>
69 <span class="delete_icon action_button" onclick="ajaxActionUsersGroup(${g2p.users_group.users_group_id},'${'id%s'%id(g2p.users_group.users_group_name)}')">
70 </span>
71 </td>
72 </tr>
73 %endfor
74 <tr id="add_perm_input">
75 <td>${h.radio('perm_new_member','repository.none')}</td>
76 <td>${h.radio('perm_new_member','repository.read')}</td>
77 <td>${h.radio('perm_new_member','repository.write')}</td>
78 <td>${h.radio('perm_new_member','repository.admin')}</td>
79 <td class='ac'>
80 <div class="perm_ac" id="perm_ac">
81 ${h.text('perm_new_member_name',class_='yui-ac-input')}
82 ${h.hidden('perm_new_member_type')}
83 <div id="perm_container"></div>
84 </div>
85 </td>
86 <td></td>
87 </tr>
88 <tr>
89 <td colspan="6">
90 <span id="add_perm" class="add_icon" style="cursor: pointer;">
91 ${_('Add another member')}
92 </span>
93 </td>
94 </tr>
95 </table> No newline at end of file
@@ -1,219 +1,224 b''
1 1 """
2 2 Routes configuration
3 3
4 4 The more specific and detailed routes should be defined first so they
5 5 may take precedent over the more generic routes. For more information
6 6 refer to the routes manual at http://routes.groovie.org/docs/
7 7 """
8 8 from __future__ import with_statement
9 9 from routes import Mapper
10 10 from rhodecode.lib.utils import check_repo_fast as cr
11 11
12 12 def make_map(config):
13 13 """Create, configure and return the routes Mapper"""
14 14 map = Mapper(directory=config['pylons.paths']['controllers'],
15 15 always_scan=config['debug'])
16 16 map.minimization = False
17 17 map.explicit = False
18 18
19 19 def check_repo(environ, match_dict):
20 20 """
21 21 check for valid repository for proper 404 handling
22 22 :param environ:
23 23 :param match_dict:
24 24 """
25 25 repo_name = match_dict.get('repo_name')
26 26 return not cr(repo_name, config['base_path'])
27 27
28 28 # The ErrorController route (handles 404/500 error pages); it should
29 29 # likely stay at the top, ensuring it can always be resolved
30 30 map.connect('/error/{action}', controller='error')
31 31 map.connect('/error/{action}/{id}', controller='error')
32 32
33 33 #==========================================================================
34 34 # CUSTOM ROUTES HERE
35 35 #==========================================================================
36 36
37 37 #MAIN PAGE
38 38 map.connect('home', '/', controller='home', action='index')
39 39 map.connect('bugtracker', "http://bitbucket.org/marcinkuzminski/rhodecode/issues", _static=True)
40 40 map.connect('gpl_license', "http://www.gnu.org/licenses/gpl.html", _static=True)
41 41 #ADMIN REPOSITORY REST ROUTES
42 42 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
43 43 m.connect("repos", "/repos",
44 44 action="create", conditions=dict(method=["POST"]))
45 45 m.connect("repos", "/repos",
46 46 action="index", conditions=dict(method=["GET"]))
47 47 m.connect("formatted_repos", "/repos.{format}",
48 48 action="index",
49 49 conditions=dict(method=["GET"]))
50 50 m.connect("new_repo", "/repos/new",
51 51 action="new", conditions=dict(method=["GET"]))
52 52 m.connect("formatted_new_repo", "/repos/new.{format}",
53 53 action="new", conditions=dict(method=["GET"]))
54 54 m.connect("/repos/{repo_name:.*}",
55 55 action="update", conditions=dict(method=["PUT"],
56 56 function=check_repo))
57 57 m.connect("/repos/{repo_name:.*}",
58 58 action="delete", conditions=dict(method=["DELETE"],
59 59 function=check_repo))
60 60 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
61 61 action="edit", conditions=dict(method=["GET"],
62 62 function=check_repo))
63 63 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
64 64 action="edit", conditions=dict(method=["GET"],
65 65 function=check_repo))
66 66 m.connect("repo", "/repos/{repo_name:.*}",
67 67 action="show", conditions=dict(method=["GET"],
68 68 function=check_repo))
69 69 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
70 70 action="show", conditions=dict(method=["GET"],
71 71 function=check_repo))
72 72 #ajax delete repo perm user
73 73 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
74 74 action="delete_perm_user", conditions=dict(method=["DELETE"],
75 75 function=check_repo))
76 #ajax delete repo perm users_group
77 m.connect('delete_repo_users_group', "/repos_delete_users_group/{repo_name:.*}",
78 action="delete_perm_users_group", conditions=dict(method=["DELETE"],
79 function=check_repo))
80
76 81 #settings actions
77 82 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
78 83 action="repo_stats", conditions=dict(method=["DELETE"],
79 84 function=check_repo))
80 85 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
81 86 action="repo_cache", conditions=dict(method=["DELETE"],
82 87 function=check_repo))
83 88
84 89 #ADMIN USER REST ROUTES
85 90 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
86 91
87 92 #ADMIN USER REST ROUTES
88 93 map.resource('users_group', 'users_groups', controller='admin/users_groups', path_prefix='/_admin')
89 94
90 95 #ADMIN GROUP REST ROUTES
91 96 map.resource('group', 'groups', controller='admin/groups', path_prefix='/_admin')
92 97
93 98 #ADMIN PERMISSIONS REST ROUTES
94 99 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
95 100
96 101 ##ADMIN LDAP SETTINGS
97 102 map.connect('ldap_settings', '/_admin/ldap', controller='admin/ldap_settings',
98 103 action='ldap_settings', conditions=dict(method=["POST"]))
99 104 map.connect('ldap_home', '/_admin/ldap', controller='admin/ldap_settings',)
100 105
101 106
102 107 #ADMIN SETTINGS REST ROUTES
103 108 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
104 109 m.connect("admin_settings", "/settings",
105 110 action="create", conditions=dict(method=["POST"]))
106 111 m.connect("admin_settings", "/settings",
107 112 action="index", conditions=dict(method=["GET"]))
108 113 m.connect("formatted_admin_settings", "/settings.{format}",
109 114 action="index", conditions=dict(method=["GET"]))
110 115 m.connect("admin_new_setting", "/settings/new",
111 116 action="new", conditions=dict(method=["GET"]))
112 117 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
113 118 action="new", conditions=dict(method=["GET"]))
114 119 m.connect("/settings/{setting_id}",
115 120 action="update", conditions=dict(method=["PUT"]))
116 121 m.connect("/settings/{setting_id}",
117 122 action="delete", conditions=dict(method=["DELETE"]))
118 123 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
119 124 action="edit", conditions=dict(method=["GET"]))
120 125 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
121 126 action="edit", conditions=dict(method=["GET"]))
122 127 m.connect("admin_setting", "/settings/{setting_id}",
123 128 action="show", conditions=dict(method=["GET"]))
124 129 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
125 130 action="show", conditions=dict(method=["GET"]))
126 131 m.connect("admin_settings_my_account", "/my_account",
127 132 action="my_account", conditions=dict(method=["GET"]))
128 133 m.connect("admin_settings_my_account_update", "/my_account_update",
129 134 action="my_account_update", conditions=dict(method=["PUT"]))
130 135 m.connect("admin_settings_create_repository", "/create_repository",
131 136 action="create_repository", conditions=dict(method=["GET"]))
132 137
133 138 #ADMIN MAIN PAGES
134 139 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
135 140 m.connect('admin_home', '', action='index')#main page
136 141 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
137 142 action='add_repo')
138 143
139 144
140 145 #USER JOURNAL
141 146 map.connect('journal', '/_admin/journal', controller='journal',)
142 147 map.connect('toggle_following', '/_admin/toggle_following', controller='journal',
143 148 action='toggle_following', conditions=dict(method=["POST"]))
144 149
145 150
146 151 #SEARCH
147 152 map.connect('search', '/_admin/search', controller='search',)
148 153 map.connect('search_repo', '/_admin/search/{search_repo:.*}', controller='search')
149 154
150 155 #LOGIN/LOGOUT/REGISTER/SIGN IN
151 156 map.connect('login_home', '/_admin/login', controller='login')
152 157 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
153 158 map.connect('register', '/_admin/register', controller='login', action='register')
154 159 map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
155 160
156 161 #FEEDS
157 162 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
158 163 controller='feed', action='rss',
159 164 conditions=dict(function=check_repo))
160 165 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
161 166 controller='feed', action='atom',
162 167 conditions=dict(function=check_repo))
163 168
164 169
165 170 #REPOSITORY ROUTES
166 171 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
167 172 controller='changeset', revision='tip',
168 173 conditions=dict(function=check_repo))
169 174 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
170 175 controller='changeset', action='raw_changeset', revision='tip',
171 176 conditions=dict(function=check_repo))
172 177 map.connect('summary_home', '/{repo_name:.*}',
173 178 controller='summary', conditions=dict(function=check_repo))
174 179 map.connect('summary_home', '/{repo_name:.*}/summary',
175 180 controller='summary', conditions=dict(function=check_repo))
176 181 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
177 182 controller='shortlog', conditions=dict(function=check_repo))
178 183 map.connect('branches_home', '/{repo_name:.*}/branches',
179 184 controller='branches', conditions=dict(function=check_repo))
180 185 map.connect('tags_home', '/{repo_name:.*}/tags',
181 186 controller='tags', conditions=dict(function=check_repo))
182 187 map.connect('changelog_home', '/{repo_name:.*}/changelog',
183 188 controller='changelog', conditions=dict(function=check_repo))
184 189 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
185 190 controller='files', revision='tip', f_path='',
186 191 conditions=dict(function=check_repo))
187 192 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
188 193 controller='files', action='diff', revision='tip', f_path='',
189 194 conditions=dict(function=check_repo))
190 195 map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
191 196 controller='files', action='rawfile', revision='tip', f_path='',
192 197 conditions=dict(function=check_repo))
193 198 map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
194 199 controller='files', action='raw', revision='tip', f_path='',
195 200 conditions=dict(function=check_repo))
196 201 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
197 202 controller='files', action='annotate', revision='tip', f_path='',
198 203 conditions=dict(function=check_repo))
199 204 map.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
200 205 controller='files', action='archivefile',
201 206 conditions=dict(function=check_repo))
202 207 map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
203 208 controller='settings', action="delete",
204 209 conditions=dict(method=["DELETE"], function=check_repo))
205 210 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
206 211 controller='settings', action="update",
207 212 conditions=dict(method=["PUT"], function=check_repo))
208 213 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
209 214 controller='settings', action='index',
210 215 conditions=dict(function=check_repo))
211 216
212 217 map.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
213 218 controller='settings', action='fork_create',
214 219 conditions=dict(function=check_repo, method=["POST"]))
215 220 map.connect('repo_fork_home', '/{repo_name:.*}/fork',
216 221 controller='settings', action='fork',
217 222 conditions=dict(function=check_repo))
218 223
219 224 return map
@@ -1,316 +1,341 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Admin controller for RhodeCode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import logging
29 29 import traceback
30 30 import formencode
31 31 from operator import itemgetter
32 32 from formencode import htmlfill
33 33
34 34 from paste.httpexceptions import HTTPInternalServerError
35 35 from pylons import request, response, session, tmpl_context as c, url
36 36 from pylons.controllers.util import abort, redirect
37 37 from pylons.i18n.translation import _
38 38
39 39 from rhodecode.lib import helpers as h
40 40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
41 41 HasPermissionAnyDecorator
42 42 from rhodecode.lib.base import BaseController, render
43 43 from rhodecode.lib.utils import invalidate_cache, action_logger
44 44 from rhodecode.model.db import User
45 45 from rhodecode.model.forms import RepoForm
46 46 from rhodecode.model.scm import ScmModel
47 47 from rhodecode.model.repo import RepoModel
48 48
49 49
50 50 log = logging.getLogger(__name__)
51 51
52 52 class ReposController(BaseController):
53 53 """REST Controller styled on the Atom Publishing Protocol"""
54 54 # To properly map this controller, ensure your config/routing.py
55 55 # file has a resource setup:
56 56 # map.resource('repo', 'repos')
57 57
58 58 @LoginRequired()
59 59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 60 def __before__(self):
61 61 c.admin_user = session.get('admin_user')
62 62 c.admin_username = session.get('admin_username')
63 63 super(ReposController, self).__before__()
64 64
65 65 @HasPermissionAllDecorator('hg.admin')
66 66 def index(self, format='html'):
67 67 """GET /repos: All items in the collection"""
68 68 # url('repos')
69 69 cached_repo_list = ScmModel().get_repos()
70 70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
71 71 return render('admin/repos/repos.html')
72 72
73 73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
74 74 def create(self):
75 75 """POST /repos: Create a new item"""
76 76 # url('repos')
77 77 repo_model = RepoModel()
78 78 _form = RepoForm()()
79 79 form_result = {}
80 80 try:
81 81 form_result = _form.to_python(dict(request.POST))
82 82 repo_model.create(form_result, c.rhodecode_user)
83 83 h.flash(_('created repository %s') % form_result['repo_name'],
84 84 category='success')
85 85
86 86 if request.POST.get('user_created'):
87 87 action_logger(self.rhodecode_user, 'user_created_repo',
88 88 form_result['repo_name'], '', self.sa)
89 89 else:
90 90 action_logger(self.rhodecode_user, 'admin_created_repo',
91 91 form_result['repo_name'], '', self.sa)
92 92
93 93 except formencode.Invalid, errors:
94 94 c.new_repo = errors.value['repo_name']
95 95
96 96 if request.POST.get('user_created'):
97 97 r = render('admin/repos/repo_add_create_repository.html')
98 98 else:
99 99 r = render('admin/repos/repo_add.html')
100 100
101 101 return htmlfill.render(
102 102 r,
103 103 defaults=errors.value,
104 104 errors=errors.error_dict or {},
105 105 prefix_error=False,
106 106 encoding="UTF-8")
107 107
108 108 except Exception:
109 109 log.error(traceback.format_exc())
110 110 msg = _('error occurred during creation of repository %s') \
111 111 % form_result.get('repo_name')
112 112 h.flash(msg, category='error')
113 113 if request.POST.get('user_created'):
114 114 return redirect(url('home'))
115 115 return redirect(url('repos'))
116 116
117 117 @HasPermissionAllDecorator('hg.admin')
118 118 def new(self, format='html'):
119 119 """GET /repos/new: Form to create a new item"""
120 120 new_repo = request.GET.get('repo', '')
121 121 c.new_repo = h.repo_name_slug(new_repo)
122 122
123 123 return render('admin/repos/repo_add.html')
124 124
125 125 @HasPermissionAllDecorator('hg.admin')
126 126 def update(self, repo_name):
127 127 """PUT /repos/repo_name: Update an existing item"""
128 128 # Forms posted to this method should contain a hidden field:
129 129 # <input type="hidden" name="_method" value="PUT" />
130 130 # Or using helpers:
131 131 # h.form(url('repo', repo_name=ID),
132 132 # method='put')
133 133 # url('repo', repo_name=ID)
134 134 repo_model = RepoModel()
135 135 changed_name = repo_name
136 136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
137 137
138 138 try:
139 139 form_result = _form.to_python(dict(request.POST))
140 140 repo_model.update(repo_name, form_result)
141 141 invalidate_cache('get_repo_cached_%s' % repo_name)
142 142 h.flash(_('Repository %s updated successfully' % repo_name),
143 143 category='success')
144 144 changed_name = form_result['repo_name']
145 145 action_logger(self.rhodecode_user, 'admin_updated_repo',
146 146 changed_name, '', self.sa)
147 147
148 148 except formencode.Invalid, errors:
149 149 c.repo_info = repo_model.get_by_repo_name(repo_name)
150 150 if c.repo_info.stats:
151 151 last_rev = c.repo_info.stats.stat_on_revision
152 152 else:
153 153 last_rev = 0
154 154 c.stats_revision = last_rev
155 155 r = ScmModel().get(repo_name)
156 156 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
157 157
158 158 if last_rev == 0:
159 159 c.stats_percentage = 0
160 160 else:
161 161 c.stats_percentage = '%.2f' % ((float((last_rev)) /
162 162 c.repo_last_rev) * 100)
163 163
164 164 c.users_array = repo_model.get_users_js()
165 165 c.users_groups_array = repo_model.get_users_groups_js()
166 166
167 167 errors.value.update({'user':c.repo_info.user.username})
168 168 return htmlfill.render(
169 169 render('admin/repos/repo_edit.html'),
170 170 defaults=errors.value,
171 171 errors=errors.error_dict or {},
172 172 prefix_error=False,
173 173 encoding="UTF-8")
174 174
175 175 except Exception:
176 176 log.error(traceback.format_exc())
177 177 h.flash(_('error occurred during update of repository %s') \
178 178 % repo_name, category='error')
179 179
180 180 return redirect(url('edit_repo', repo_name=changed_name))
181 181
182 182 @HasPermissionAllDecorator('hg.admin')
183 183 def delete(self, repo_name):
184 184 """DELETE /repos/repo_name: Delete an existing item"""
185 185 # Forms posted to this method should contain a hidden field:
186 186 # <input type="hidden" name="_method" value="DELETE" />
187 187 # Or using helpers:
188 188 # h.form(url('repo', repo_name=ID),
189 189 # method='delete')
190 190 # url('repo', repo_name=ID)
191 191
192 192 repo_model = RepoModel()
193 193 repo = repo_model.get_by_repo_name(repo_name)
194 194 if not repo:
195 195 h.flash(_('%s repository is not mapped to db perhaps'
196 196 ' it was moved or renamed from the filesystem'
197 197 ' please run the application again'
198 198 ' in order to rescan repositories') % repo_name,
199 199 category='error')
200 200
201 201 return redirect(url('repos'))
202 202 try:
203 203 action_logger(self.rhodecode_user, 'admin_deleted_repo',
204 204 repo_name, '', self.sa)
205 205 repo_model.delete(repo)
206 206 invalidate_cache('get_repo_cached_%s' % repo_name)
207 207 h.flash(_('deleted repository %s') % repo_name, category='success')
208 208
209 209 except Exception, e:
210 210 log.error(traceback.format_exc())
211 211 h.flash(_('An error occurred during deletion of %s') % repo_name,
212 212 category='error')
213 213
214 214 return redirect(url('repos'))
215 215
216 216 @HasPermissionAllDecorator('hg.admin')
217 217 def delete_perm_user(self, repo_name):
218 """
219 DELETE an existing repository permission user
218 """DELETE an existing repository permission user
219
220 220 :param repo_name:
221 221 """
222 222
223 223 try:
224 224 repo_model = RepoModel()
225 225 repo_model.delete_perm_user(request.POST, repo_name)
226 226 except Exception, e:
227 227 h.flash(_('An error occurred during deletion of repository user'),
228 228 category='error')
229 229 raise HTTPInternalServerError()
230 230
231 231 @HasPermissionAllDecorator('hg.admin')
232 def repo_stats(self, repo_name):
232 def delete_perm_users_group(self, repo_name):
233 """DELETE an existing repository permission users group
234
235 :param repo_name:
233 236 """
234 DELETE an existing repository statistics
237 try:
238 repo_model = RepoModel()
239 repo_model.delete_perm_users_group(request.POST, repo_name)
240 except Exception, e:
241 h.flash(_('An error occurred during deletion of repository'
242 ' users groups'),
243 category='error')
244 raise HTTPInternalServerError()
245
246 @HasPermissionAllDecorator('hg.admin')
247 def repo_stats(self, repo_name):
248 """DELETE an existing repository statistics
249
235 250 :param repo_name:
236 251 """
237 252
238 253 try:
239 254 repo_model = RepoModel()
240 255 repo_model.delete_stats(repo_name)
241 256 except Exception, e:
242 257 h.flash(_('An error occurred during deletion of repository stats'),
243 258 category='error')
244 259 return redirect(url('edit_repo', repo_name=repo_name))
245 260
246 261 @HasPermissionAllDecorator('hg.admin')
247 262 def repo_cache(self, repo_name):
248 """
249 INVALIDATE existing repository cache
263 """INVALIDATE existing repository cache
264
250 265 :param repo_name:
251 266 """
252 267
253 268 try:
254 269 ScmModel().mark_for_invalidation(repo_name)
255 270 except Exception, e:
256 271 h.flash(_('An error occurred during cache invalidation'),
257 272 category='error')
258 273 return redirect(url('edit_repo', repo_name=repo_name))
259 274
260 275 @HasPermissionAllDecorator('hg.admin')
261 276 def show(self, repo_name, format='html'):
262 277 """GET /repos/repo_name: Show a specific item"""
263 278 # url('repo', repo_name=ID)
264 279
265 280 @HasPermissionAllDecorator('hg.admin')
266 281 def edit(self, repo_name, format='html'):
267 282 """GET /repos/repo_name/edit: Form to edit an existing item"""
268 283 # url('edit_repo', repo_name=ID)
269 284 repo_model = RepoModel()
285 c.repo_info = repo_model.get_by_repo_name(repo_name)
286
270 287 r = ScmModel().get(repo_name)
271 c.repo_info = repo_model.get_by_repo_name(repo_name)
272 288
273 289 if c.repo_info is None:
274 290 h.flash(_('%s repository is not mapped to db perhaps'
275 291 ' it was created or renamed from the filesystem'
276 292 ' please run the application again'
277 293 ' in order to rescan repositories') % repo_name,
278 294 category='error')
279 295
280 296 return redirect(url('repos'))
281 297
282 298 if c.repo_info.stats:
283 299 last_rev = c.repo_info.stats.stat_on_revision
284 300 else:
285 301 last_rev = 0
286 302 c.stats_revision = last_rev
287 303
288 304 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
289 305
290 306 if last_rev == 0 or c.repo_last_rev == 0:
291 307 c.stats_percentage = 0
292 308 else:
293 309 c.stats_percentage = '%.2f' % ((float((last_rev)) /
294 310 c.repo_last_rev) * 100)
295 311
312 c.users_array = repo_model.get_users_js()
313 c.users_groups_array = repo_model.get_users_groups_js()
314
296 315 defaults = c.repo_info.get_dict()
316
317 #fill owner
297 318 if c.repo_info.user:
298 319 defaults.update({'user':c.repo_info.user.username})
299 320 else:
300 321 replacement_user = self.sa.query(User)\
301 322 .filter(User.admin == True).first().username
302 323 defaults.update({'user':replacement_user})
303 324
304 c.users_array = repo_model.get_users_js()
305 c.users_groups_array = repo_model.get_users_groups_js()
306 325
326 #fill repository users
307 327 for p in c.repo_info.repo_to_perm:
308 defaults.update({'perm_%s' % p.user.username:
328 defaults.update({'u_perm_%s' % p.user.username:
329 p.permission.permission_name})
330
331 #fill repository groups
332 for p in c.repo_info.users_group_to_perm:
333 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
309 334 p.permission.permission_name})
310 335
311 336 return htmlfill.render(
312 337 render('admin/repos/repo_edit.html'),
313 338 defaults=defaults,
314 339 encoding="UTF-8",
315 340 force_defaults=False
316 341 )
@@ -1,184 +1,201 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.settings
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Settings controller for rhodecode
7 7
8 8 :created_on: Jun 30, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import logging
29 29 import traceback
30 30
31 31 import formencode
32 32 from formencode import htmlfill
33 33
34 34 from pylons import tmpl_context as c, request, url
35 35 from pylons.controllers.util import redirect
36 36 from pylons.i18n.translation import _
37 37
38 38 import rhodecode.lib.helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
40 40 from rhodecode.lib.base import BaseController, render
41 41 from rhodecode.lib.utils import invalidate_cache, action_logger
42 42 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
43 43 from rhodecode.model.repo import RepoModel
44 from rhodecode.model.db import User
44 45
45 46 log = logging.getLogger(__name__)
46 47
47 48 class SettingsController(BaseController):
48 49
49 50 @LoginRequired()
50 51 @HasRepoPermissionAllDecorator('repository.admin')
51 52 def __before__(self):
52 53 super(SettingsController, self).__before__()
53 54
54 55 def index(self, repo_name):
55 56 repo_model = RepoModel()
56 57 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
57 58 if not repo:
58 59 h.flash(_('%s repository is not mapped to db perhaps'
59 60 ' it was created or renamed from the file system'
60 61 ' please run the application again'
61 62 ' in order to rescan repositories') % repo_name,
62 63 category='error')
63 64
64 65 return redirect(url('home'))
65 defaults = c.repo_info.get_dict()
66 defaults.update({'user':c.repo_info.user.username})
66
67 67 c.users_array = repo_model.get_users_js()
68 c.users_groups_array = repo_model.get_users_groups_js()
68 69
70 defaults = c.repo_info.get_dict()
71
72 #fill owner
73 if c.repo_info.user:
74 defaults.update({'user':c.repo_info.user.username})
75 else:
76 replacement_user = self.sa.query(User)\
77 .filter(User.admin == True).first().username
78 defaults.update({'user':replacement_user})
79
80 #fill repository users
69 81 for p in c.repo_info.repo_to_perm:
70 defaults.update({'perm_%s' % p.user.username:
82 defaults.update({'u_perm_%s' % p.user.username:
83 p.permission.permission_name})
84
85 #fill repository groups
86 for p in c.repo_info.users_group_to_perm:
87 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
71 88 p.permission.permission_name})
72 89
73 90 return htmlfill.render(
74 91 render('settings/repo_settings.html'),
75 92 defaults=defaults,
76 93 encoding="UTF-8",
77 94 force_defaults=False
78 95 )
79 96
80 97 def update(self, repo_name):
81 98 repo_model = RepoModel()
82 99 changed_name = repo_name
83 100 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
84 101 try:
85 102 form_result = _form.to_python(dict(request.POST))
86 103 repo_model.update(repo_name, form_result)
87 104 invalidate_cache('get_repo_cached_%s' % repo_name)
88 105 h.flash(_('Repository %s updated successfully' % repo_name),
89 106 category='success')
90 107 changed_name = form_result['repo_name']
91 108 action_logger(self.rhodecode_user, 'user_updated_repo',
92 109 changed_name, '', self.sa)
93 110 except formencode.Invalid, errors:
94 111 c.repo_info = repo_model.get_by_repo_name(repo_name)
95 112 c.users_array = repo_model.get_users_js()
96 113 errors.value.update({'user':c.repo_info.user.username})
97 114 return htmlfill.render(
98 115 render('settings/repo_settings.html'),
99 116 defaults=errors.value,
100 117 errors=errors.error_dict or {},
101 118 prefix_error=False,
102 119 encoding="UTF-8")
103 120 except Exception:
104 121 log.error(traceback.format_exc())
105 122 h.flash(_('error occurred during update of repository %s') \
106 123 % repo_name, category='error')
107 124
108 125 return redirect(url('repo_settings_home', repo_name=changed_name))
109 126
110 127
111 128
112 129 def delete(self, repo_name):
113 130 """DELETE /repos/repo_name: Delete an existing item"""
114 131 # Forms posted to this method should contain a hidden field:
115 132 # <input type="hidden" name="_method" value="DELETE" />
116 133 # Or using helpers:
117 134 # h.form(url('repo_settings_delete', repo_name=ID),
118 135 # method='delete')
119 136 # url('repo_settings_delete', repo_name=ID)
120 137
121 138 repo_model = RepoModel()
122 139 repo = repo_model.get_by_repo_name(repo_name)
123 140 if not repo:
124 141 h.flash(_('%s repository is not mapped to db perhaps'
125 142 ' it was moved or renamed from the filesystem'
126 143 ' please run the application again'
127 144 ' in order to rescan repositories') % repo_name,
128 145 category='error')
129 146
130 147 return redirect(url('home'))
131 148 try:
132 149 action_logger(self.rhodecode_user, 'user_deleted_repo',
133 150 repo_name, '', self.sa)
134 151 repo_model.delete(repo)
135 152 invalidate_cache('get_repo_cached_%s' % repo_name)
136 153 h.flash(_('deleted repository %s') % repo_name, category='success')
137 154 except Exception:
138 155 h.flash(_('An error occurred during deletion of %s') % repo_name,
139 156 category='error')
140 157
141 158 return redirect(url('home'))
142 159
143 160 def fork(self, repo_name):
144 161 repo_model = RepoModel()
145 162 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
146 163 if not repo:
147 164 h.flash(_('%s repository is not mapped to db perhaps'
148 165 ' it was created or renamed from the file system'
149 166 ' please run the application again'
150 167 ' in order to rescan repositories') % repo_name,
151 168 category='error')
152 169
153 170 return redirect(url('home'))
154 171
155 172 return render('settings/repo_fork.html')
156 173
157 174
158 175
159 176 def fork_create(self, repo_name):
160 177 repo_model = RepoModel()
161 178 c.repo_info = repo_model.get_by_repo_name(repo_name)
162 179 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
163 180 form_result = {}
164 181 try:
165 182 form_result = _form.to_python(dict(request.POST))
166 183 form_result.update({'repo_name':repo_name})
167 184 repo_model.create_fork(form_result, c.rhodecode_user)
168 185 h.flash(_('forked %s repository as %s') \
169 186 % (repo_name, form_result['fork_name']),
170 187 category='success')
171 188 action_logger(self.rhodecode_user,
172 189 'user_forked_repo:%s' % form_result['fork_name'],
173 190 repo_name, '', self.sa)
174 191 except formencode.Invalid, errors:
175 192 c.new_repo = errors.value['fork_name']
176 193 r = render('settings/repo_fork.html')
177 194
178 195 return htmlfill.render(
179 196 r,
180 197 defaults=errors.value,
181 198 errors=errors.error_dict or {},
182 199 prefix_error=False,
183 200 encoding="UTF-8")
184 201 return redirect(url('home'))
@@ -1,345 +1,346 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.db
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Database Models for RhodeCode
7 7
8 8 :created_on: Apr 08, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27 import logging
28 28 import datetime
29 29 from datetime import date
30 30
31 31 from sqlalchemy import *
32 32 from sqlalchemy.exc import DatabaseError
33 33 from sqlalchemy.orm import relationship, backref, class_mapper
34 34 from sqlalchemy.orm.session import Session
35 35
36 36 from rhodecode.model.meta import Base
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40 class BaseModel(object):
41 41
42 42 @classmethod
43 43 def _get_keys(cls):
44 44 """return column names for this model """
45 45 return class_mapper(cls).c.keys()
46 46
47 47 def get_dict(self):
48 48 """return dict with keys and values corresponding
49 49 to this model data """
50 50
51 51 d = {}
52 52 for k in self._get_keys():
53 53 d[k] = getattr(self, k)
54 54 return d
55 55
56 56 def get_appstruct(self):
57 57 """return list with keys and values tupples corresponding
58 58 to this model data """
59 59
60 60 l = []
61 61 for k in self._get_keys():
62 62 l.append((k, getattr(self, k),))
63 63 return l
64 64
65 65 def populate_obj(self, populate_dict):
66 66 """populate model with data from given populate_dict"""
67 67
68 68 for k in self._get_keys():
69 69 if k in populate_dict:
70 70 setattr(self, k, populate_dict[k])
71 71
72 72 class RhodeCodeSettings(Base, BaseModel):
73 73 __tablename__ = 'rhodecode_settings'
74 74 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
75 75 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
76 76 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
77 77 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
78 78
79 79 def __init__(self, k='', v=''):
80 80 self.app_settings_name = k
81 81 self.app_settings_value = v
82 82
83 83 def __repr__(self):
84 84 return "<%s('%s:%s')>" % (self.__class__.__name__,
85 85 self.app_settings_name, self.app_settings_value)
86 86
87 87 class RhodeCodeUi(Base, BaseModel):
88 88 __tablename__ = 'rhodecode_ui'
89 89 __table_args__ = {'useexisting':True}
90 90 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
91 91 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 92 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
93 93 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
94 94 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
95 95
96 96
97 97 class User(Base, BaseModel):
98 98 __tablename__ = 'users'
99 99 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
100 100 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
101 101 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 102 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
103 103 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
104 104 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
105 105 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 106 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 107 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
108 108 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
109 109 ldap_dn = Column("ldap_dn", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
110 110
111 111 user_log = relationship('UserLog', cascade='all')
112 112 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
113 113
114 114 repositories = relationship('Repository')
115 115 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
116 116
117 117 @property
118 118 def full_contact(self):
119 119 return '%s %s <%s>' % (self.name, self.lastname, self.email)
120 120
121 121
122 122 @property
123 123 def is_admin(self):
124 124 return self.admin
125 125
126 126 def __repr__(self):
127 127 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
128 128 self.user_id, self.username)
129 129
130 130 def update_lastlogin(self):
131 131 """Update user lastlogin"""
132 132
133 133 try:
134 134 session = Session.object_session(self)
135 135 self.last_login = datetime.datetime.now()
136 136 session.add(self)
137 137 session.commit()
138 138 log.debug('updated user %s lastlogin', self.username)
139 139 except (DatabaseError,):
140 140 session.rollback()
141 141
142 142
143 143 class UserLog(Base, BaseModel):
144 144 __tablename__ = 'user_logs'
145 145 __table_args__ = {'useexisting':True}
146 146 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
147 147 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
148 148 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
149 149 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 150 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 151 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
152 152 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
153 153
154 154 @property
155 155 def action_as_day(self):
156 156 return date(*self.action_date.timetuple()[:3])
157 157
158 158 user = relationship('User')
159 159 repository = relationship('Repository')
160 160
161 161
162 162 class UsersGroup(Base, BaseModel):
163 163 __tablename__ = 'users_groups'
164 164 __table_args__ = {'useexisting':True}
165 165
166 166 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
167 167 users_group_name = Column("users_group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
168 168 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
169 169
170 170 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
171 171
172 172 class UsersGroupMember(Base, BaseModel):
173 173 __tablename__ = 'users_groups_members'
174 174 __table_args__ = {'useexisting':True}
175 175
176 176 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
177 177 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
178 178 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
179 179
180 180 user = relationship('User', lazy='joined')
181 181 users_group = relationship('UsersGroup')
182 182
183 183 def __init__(self, gr_id, u_id):
184 184 self.users_group_id = gr_id
185 185 self.user_id = u_id
186 186
187 187 class Repository(Base, BaseModel):
188 188 __tablename__ = 'repositories'
189 189 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
190 190 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
191 191 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
192 192 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
193 193 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
194 194 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
195 195 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
196 196 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
197 197 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
198 198 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
199 199 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
200 200
201 201 user = relationship('User')
202 202 fork = relationship('Repository', remote_side=repo_id)
203 203 group = relationship('Group')
204 204 repo_to_perm = relationship('RepoToPerm', cascade='all')
205 users_group_to_perm = relationship('UsersGroupToPerm', cascade='all')
205 206 stats = relationship('Statistics', cascade='all', uselist=False)
206 207
207 208 repo_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
208 209
209 210 logs = relationship('UserLog', cascade='all')
210 211
211 212 def __repr__(self):
212 213 return "<%s('%s:%s')>" % (self.__class__.__name__,
213 214 self.repo_id, self.repo_name)
214 215
215 216 class Group(Base, BaseModel):
216 217 __tablename__ = 'groups'
217 218 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
218 219
219 220 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
220 221 group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
221 222 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
222 223
223 224 parent_group = relationship('Group', remote_side=group_id)
224 225
225 226
226 227 def __init__(self, group_name='', parent_group=None):
227 228 self.group_name = group_name
228 229 self.parent_group = parent_group
229 230
230 231 def __repr__(self):
231 232 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
232 233 self.group_name)
233 234
234 235 class Permission(Base, BaseModel):
235 236 __tablename__ = 'permissions'
236 237 __table_args__ = {'useexisting':True}
237 238 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
238 239 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
239 240 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
240 241
241 242 def __repr__(self):
242 243 return "<%s('%s:%s')>" % (self.__class__.__name__,
243 244 self.permission_id, self.permission_name)
244 245
245 246 class RepoToPerm(Base, BaseModel):
246 247 __tablename__ = 'repo_to_perm'
247 248 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
248 249 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
249 250 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
250 251 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
251 252 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
252 253
253 254 user = relationship('User')
254 255 permission = relationship('Permission')
255 256 repository = relationship('Repository')
256 257
257 258 class UserToPerm(Base, BaseModel):
258 259 __tablename__ = 'user_to_perm'
259 260 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
260 261 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
261 262 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
262 263 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
263 264
264 265 user = relationship('User')
265 266 permission = relationship('Permission')
266 267
267 268
268 269 class UsersGroupToPerm(Base, BaseModel):
269 270 __tablename__ = 'users_group_to_perm'
270 271 __table_args__ = (UniqueConstraint('users_group_id', 'permission_id'), {'useexisting':True})
271 272 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
272 273 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
273 274 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
274 275 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
275 276
276 277 users_group = relationship('UsersGroup')
277 278 permission = relationship('Permission')
278 279 repository = relationship('Repository')
279 280
280 281 class GroupToPerm(Base, BaseModel):
281 282 __tablename__ = 'group_to_perm'
282 283 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
283 284
284 285 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
285 286 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
286 287 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
287 288 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
288 289
289 290 user = relationship('User')
290 291 permission = relationship('Permission')
291 292 group = relationship('Group')
292 293
293 294 class Statistics(Base, BaseModel):
294 295 __tablename__ = 'statistics'
295 296 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
296 297 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
297 298 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
298 299 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
299 300 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
300 301 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
301 302 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
302 303
303 304 repository = relationship('Repository', single_parent=True)
304 305
305 306 class UserFollowing(Base, BaseModel):
306 307 __tablename__ = 'user_followings'
307 308 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
308 309 UniqueConstraint('user_id', 'follows_user_id')
309 310 , {'useexisting':True})
310 311
311 312 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
312 313 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
313 314 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
314 315 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
315 316
316 317 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
317 318
318 319 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
319 320 follows_repository = relationship('Repository', order_by='Repository.repo_name')
320 321
321 322 class CacheInvalidation(Base, BaseModel):
322 323 __tablename__ = 'cache_invalidation'
323 324 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
324 325 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 326 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 327 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 328 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
328 329
329 330
330 331 def __init__(self, cache_key, cache_args=''):
331 332 self.cache_key = cache_key
332 333 self.cache_args = cache_args
333 334 self.cache_active = False
334 335
335 336 def __repr__(self):
336 337 return "<%s('%s:%s')>" % (self.__class__.__name__,
337 338 self.cache_id, self.cache_key)
338 339
339 340 class DbMigrateVersion(Base, BaseModel):
340 341 __tablename__ = 'db_migrate_version'
341 342 __table_args__ = {'useexisting':True}
342 343 repository_id = Column('repository_id', String(250), primary_key=True)
343 344 repository_path = Column('repository_path', Text)
344 345 version = Column('version', Integer)
345 346
@@ -1,549 +1,552 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 import os
23 23 import re
24 24 import logging
25 25
26 26 import formencode
27 27 from formencode import All
28 28 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 29 Email, Bool, StringBoolean, Set
30 30
31 31 from pylons.i18n.translation import _
32 32
33 33 import rhodecode.lib.helpers as h
34 34 from rhodecode.lib.auth import authenticate, get_crypt_password
35 35 from rhodecode.lib.exceptions import LdapImportError
36 36 from rhodecode.model import meta
37 37 from rhodecode.model.user import UserModel
38 38 from rhodecode.model.repo import RepoModel
39 39 from rhodecode.model.users_group import UsersGroupModel
40 40 from rhodecode.model.db import User, UsersGroup
41 41 from rhodecode import BACKENDS
42 42
43 43 from webhelpers.pylonslib.secure_form import authentication_token
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47 #this is needed to translate the messages using _() in validators
48 48 class State_obj(object):
49 49 _ = staticmethod(_)
50 50
51 51 #===============================================================================
52 52 # VALIDATORS
53 53 #===============================================================================
54 54 class ValidAuthToken(formencode.validators.FancyValidator):
55 55 messages = {'invalid_token':_('Token mismatch')}
56 56
57 57 def validate_python(self, value, state):
58 58
59 59 if value != authentication_token():
60 60 raise formencode.Invalid(self.message('invalid_token', state,
61 61 search_number=value), value, state)
62 62
63 63 def ValidUsername(edit, old_data):
64 64 class _ValidUsername(formencode.validators.FancyValidator):
65 65
66 66 def validate_python(self, value, state):
67 67 if value in ['default', 'new_user']:
68 68 raise formencode.Invalid(_('Invalid username'), value, state)
69 69 #check if user is unique
70 70 old_un = None
71 71 if edit:
72 72 old_un = UserModel().get(old_data.get('user_id')).username
73 73
74 74 if old_un != value or not edit:
75 75 if UserModel().get_by_username(value, cache=False,
76 76 case_insensitive=True):
77 77 raise formencode.Invalid(_('This username already exists') ,
78 78 value, state)
79 79
80 80
81 81 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
82 82 raise formencode.Invalid(_('Username may only contain '
83 83 'alphanumeric characters underscores, '
84 84 'periods or dashes and must begin with '
85 85 'alphanumeric character'),
86 86 value, state)
87 87
88 88
89 89
90 90 return _ValidUsername
91 91
92 92
93 93
94 94 def ValidUsersGroup(edit, old_data):
95 95
96 96 class _ValidUsersGroup(formencode.validators.FancyValidator):
97 97
98 98 def validate_python(self, value, state):
99 99 if value in ['default']:
100 100 raise formencode.Invalid(_('Invalid group name'), value, state)
101 101 #check if group is unique
102 102 old_ugname = None
103 103 if edit:
104 104 old_ugname = UsersGroupModel()\
105 105 .get(old_data.get('users_group_id')).users_group_name
106 106
107 107 if old_ugname != value or not edit:
108 108 if UsersGroupModel().get_by_groupname(value, cache=False,
109 109 case_insensitive=True):
110 110 raise formencode.Invalid(_('This users group already exists') ,
111 111 value, state)
112 112
113 113
114 114 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
115 115 raise formencode.Invalid(_('Group name may only contain '
116 116 'alphanumeric characters underscores, '
117 117 'periods or dashes and must begin with '
118 118 'alphanumeric character'),
119 119 value, state)
120 120
121 121 return _ValidUsersGroup
122 122
123 123
124 124
125 125 class ValidPassword(formencode.validators.FancyValidator):
126 126
127 127 def to_python(self, value, state):
128 128
129 129 if value:
130 130
131 131 if value.get('password'):
132 132 try:
133 133 value['password'] = get_crypt_password(value['password'])
134 134 except UnicodeEncodeError:
135 135 e_dict = {'password':_('Invalid characters in password')}
136 136 raise formencode.Invalid('', value, state, error_dict=e_dict)
137 137
138 138 if value.get('password_confirmation'):
139 139 try:
140 140 value['password_confirmation'] = \
141 141 get_crypt_password(value['password_confirmation'])
142 142 except UnicodeEncodeError:
143 143 e_dict = {'password_confirmation':_('Invalid characters in password')}
144 144 raise formencode.Invalid('', value, state, error_dict=e_dict)
145 145
146 146 if value.get('new_password'):
147 147 try:
148 148 value['new_password'] = \
149 149 get_crypt_password(value['new_password'])
150 150 except UnicodeEncodeError:
151 151 e_dict = {'new_password':_('Invalid characters in password')}
152 152 raise formencode.Invalid('', value, state, error_dict=e_dict)
153 153
154 154 return value
155 155
156 156 class ValidPasswordsMatch(formencode.validators.FancyValidator):
157 157
158 158 def validate_python(self, value, state):
159 159
160 160 if value['password'] != value['password_confirmation']:
161 161 e_dict = {'password_confirmation':
162 162 _('Password do not match')}
163 163 raise formencode.Invalid('', value, state, error_dict=e_dict)
164 164
165 165 class ValidAuth(formencode.validators.FancyValidator):
166 166 messages = {
167 167 'invalid_password':_('invalid password'),
168 168 'invalid_login':_('invalid user name'),
169 169 'disabled_account':_('Your account is disabled')
170 170
171 171 }
172 172 #error mapping
173 173 e_dict = {'username':messages['invalid_login'],
174 174 'password':messages['invalid_password']}
175 175 e_dict_disable = {'username':messages['disabled_account']}
176 176
177 177 def validate_python(self, value, state):
178 178 password = value['password']
179 179 username = value['username']
180 180 user = UserModel().get_by_username(username)
181 181
182 182 if authenticate(username, password):
183 183 return value
184 184 else:
185 185 if user and user.active is False:
186 186 log.warning('user %s is disabled', username)
187 187 raise formencode.Invalid(self.message('disabled_account',
188 188 state=State_obj),
189 189 value, state,
190 190 error_dict=self.e_dict_disable)
191 191 else:
192 192 log.warning('user %s not authenticated', username)
193 193 raise formencode.Invalid(self.message('invalid_password',
194 194 state=State_obj), value, state,
195 195 error_dict=self.e_dict)
196 196
197 197 class ValidRepoUser(formencode.validators.FancyValidator):
198 198
199 199 def to_python(self, value, state):
200 200 sa = meta.Session()
201 201 try:
202 202 self.user_db = sa.query(User)\
203 203 .filter(User.active == True)\
204 204 .filter(User.username == value).one()
205 205 except Exception:
206 206 raise formencode.Invalid(_('This username is not valid'),
207 207 value, state)
208 208 finally:
209 209 meta.Session.remove()
210 210
211 211 return self.user_db.user_id
212 212
213 213 def ValidRepoName(edit, old_data):
214 214 class _ValidRepoName(formencode.validators.FancyValidator):
215 215
216 216 def to_python(self, value, state):
217 217 slug = h.repo_name_slug(value)
218 218 if slug in ['_admin']:
219 219 raise formencode.Invalid(_('This repository name is disallowed'),
220 220 value, state)
221 221 if old_data.get('repo_name') != value or not edit:
222 222 if RepoModel().get_by_repo_name(slug, cache=False):
223 223 raise formencode.Invalid(_('This repository already exists') ,
224 224 value, state)
225 225 return slug
226 226
227 227
228 228 return _ValidRepoName
229 229
230 230 def ValidForkType(old_data):
231 231 class _ValidForkType(formencode.validators.FancyValidator):
232 232
233 233 def to_python(self, value, state):
234 234 if old_data['repo_type'] != value:
235 235 raise formencode.Invalid(_('Fork have to be the same type as original'),
236 236 value, state)
237 237 return value
238 238 return _ValidForkType
239 239
240 240 class ValidPerms(formencode.validators.FancyValidator):
241 241 messages = {'perm_new_member_name':_('This username or users group name'
242 242 ' is not valid')}
243 243
244 244 def to_python(self, value, state):
245 245 perms_update = []
246 246 perms_new = []
247 247 #build a list of permission to update and new permission to create
248 248 for k, v in value.items():
249 if k.startswith('perm_'):
249 #means new added member to permissions
250 250 if k.startswith('perm_new_member'):
251 #means new added member to permissions
252 251 new_perm = value.get('perm_new_member', False)
253 252 new_member = value.get('perm_new_member_name', False)
254 253 new_type = value.get('perm_new_member_type')
255 254
256 255 if new_member and new_perm:
257 256 if (new_member, new_perm, new_type) not in perms_new:
258 257 perms_new.append((new_member, new_perm, new_type))
259 else:
260 usr = k[5:]
261 t = 'user'
262 if usr == 'default':
258 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
259 member = k[7:]
260 t = {'u':'user',
261 'g':'users_group'}[k[0]]
262 if member == 'default':
263 263 if value['private']:
264 264 #set none for default when updating to private repo
265 265 v = 'repository.none'
266 perms_update.append((usr, v, t))
266 perms_update.append((member, v, t))
267
267 268 value['perms_updates'] = perms_update
268 269 value['perms_new'] = perms_new
269 270
270 271 #update permissions
271 272 sa = meta.Session
272 273 for k, v, t in perms_new:
273 274 try:
274 275 if t is 'user':
275 276 self.user_db = sa.query(User)\
276 277 .filter(User.active == True)\
277 278 .filter(User.username == k).one()
278 279 if t is 'users_group':
279 280 self.user_db = sa.query(UsersGroup)\
280 281 .filter(UsersGroup.users_group_active == True)\
281 282 .filter(UsersGroup.users_group_name == k).one()
282 283
283 284 except Exception:
284 285 msg = self.message('perm_new_member_name',
285 286 state=State_obj)
286 287 raise formencode.Invalid(msg, value, state,
287 288 error_dict={'perm_new_member_name':msg})
288 289 return value
289 290
290 291 class ValidSettings(formencode.validators.FancyValidator):
291 292
292 293 def to_python(self, value, state):
293 294 #settings form can't edit user
294 295 if value.has_key('user'):
295 296 del['value']['user']
296 297
297 298 return value
298 299
299 300 class ValidPath(formencode.validators.FancyValidator):
300 301 def to_python(self, value, state):
301 302
302 303 if not os.path.isdir(value):
303 304 msg = _('This is not a valid path')
304 305 raise formencode.Invalid(msg, value, state,
305 306 error_dict={'paths_root_path':msg})
306 307 return value
307 308
308 309 def UniqSystemEmail(old_data):
309 310 class _UniqSystemEmail(formencode.validators.FancyValidator):
310 311 def to_python(self, value, state):
311 312 value = value.lower()
312 313 if old_data.get('email') != value:
313 314 sa = meta.Session()
314 315 try:
315 316 user = sa.query(User).filter(User.email == value).scalar()
316 317 if user:
317 318 raise formencode.Invalid(_("This e-mail address is already taken") ,
318 319 value, state)
319 320 finally:
320 321 meta.Session.remove()
321 322
322 323 return value
323 324
324 325 return _UniqSystemEmail
325 326
326 327 class ValidSystemEmail(formencode.validators.FancyValidator):
327 328 def to_python(self, value, state):
328 329 value = value.lower()
329 330 sa = meta.Session
330 331 try:
331 332 user = sa.query(User).filter(User.email == value).scalar()
332 333 if user is None:
333 334 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
334 335 value, state)
335 336 finally:
336 337 meta.Session.remove()
337 338
338 339 return value
339 340
340 341 class LdapLibValidator(formencode.validators.FancyValidator):
341 342
342 343 def to_python(self, value, state):
343 344
344 345 try:
345 346 import ldap
346 347 except ImportError:
347 348 raise LdapImportError
348 349 return value
349 350
350 351 class AttrLoginValidator(formencode.validators.FancyValidator):
351 352
352 353 def to_python(self, value, state):
353 354
354 355 if not value or not isinstance(value, (str, unicode)):
355 raise formencode.Invalid(_("The LDAP Login attribute of the CN must be specified "
356 "- this is the name of the attribute that is equivalent to 'username'"),
356 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
357 "must be specified - this is the name "
358 "of the attribute that is equivalent "
359 "to 'username'"),
357 360 value, state)
358 361
359 362 return value
360 363
361 364 #===============================================================================
362 365 # FORMS
363 366 #===============================================================================
364 367 class LoginForm(formencode.Schema):
365 368 allow_extra_fields = True
366 369 filter_extra_fields = True
367 370 username = UnicodeString(
368 371 strip=True,
369 372 min=1,
370 373 not_empty=True,
371 374 messages={
372 375 'empty':_('Please enter a login'),
373 376 'tooShort':_('Enter a value %(min)i characters long or more')}
374 377 )
375 378
376 379 password = UnicodeString(
377 380 strip=True,
378 381 min=6,
379 382 not_empty=True,
380 383 messages={
381 384 'empty':_('Please enter a password'),
382 385 'tooShort':_('Enter %(min)i characters or more')}
383 386 )
384 387
385 388
386 389 #chained validators have access to all data
387 390 chained_validators = [ValidAuth]
388 391
389 392 def UserForm(edit=False, old_data={}):
390 393 class _UserForm(formencode.Schema):
391 394 allow_extra_fields = True
392 395 filter_extra_fields = True
393 396 username = All(UnicodeString(strip=True, min=1, not_empty=True),
394 397 ValidUsername(edit, old_data))
395 398 if edit:
396 399 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
397 400 admin = StringBoolean(if_missing=False)
398 401 else:
399 402 password = All(UnicodeString(strip=True, min=6, not_empty=True))
400 403 active = StringBoolean(if_missing=False)
401 404 name = UnicodeString(strip=True, min=1, not_empty=True)
402 405 lastname = UnicodeString(strip=True, min=1, not_empty=True)
403 406 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
404 407
405 408 chained_validators = [ValidPassword]
406 409
407 410 return _UserForm
408 411
409 412
410 413 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
411 414 class _UsersGroupForm(formencode.Schema):
412 415 allow_extra_fields = True
413 416 filter_extra_fields = True
414 417
415 418 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
416 419 ValidUsersGroup(edit, old_data))
417 420
418 421 users_group_active = StringBoolean(if_missing=False)
419 422
420 423 if edit:
421 424 users_group_members = OneOf(available_members, hideList=False,
422 425 testValueList=True,
423 426 if_missing=None, not_empty=False)
424 427
425 428 return _UsersGroupForm
426 429
427 430 def RegisterForm(edit=False, old_data={}):
428 431 class _RegisterForm(formencode.Schema):
429 432 allow_extra_fields = True
430 433 filter_extra_fields = True
431 434 username = All(ValidUsername(edit, old_data),
432 435 UnicodeString(strip=True, min=1, not_empty=True))
433 436 password = All(UnicodeString(strip=True, min=6, not_empty=True))
434 437 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
435 438 active = StringBoolean(if_missing=False)
436 439 name = UnicodeString(strip=True, min=1, not_empty=True)
437 440 lastname = UnicodeString(strip=True, min=1, not_empty=True)
438 441 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
439 442
440 443 chained_validators = [ValidPasswordsMatch, ValidPassword]
441 444
442 445 return _RegisterForm
443 446
444 447 def PasswordResetForm():
445 448 class _PasswordResetForm(formencode.Schema):
446 449 allow_extra_fields = True
447 450 filter_extra_fields = True
448 451 email = All(ValidSystemEmail(), Email(not_empty=True))
449 452 return _PasswordResetForm
450 453
451 454 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
452 455 class _RepoForm(formencode.Schema):
453 456 allow_extra_fields = True
454 457 filter_extra_fields = False
455 458 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
456 459 ValidRepoName(edit, old_data))
457 460 description = UnicodeString(strip=True, min=1, not_empty=True)
458 461 private = StringBoolean(if_missing=False)
459 462 enable_statistics = StringBoolean(if_missing=False)
460 463 enable_downloads = StringBoolean(if_missing=False)
461 464 repo_type = OneOf(supported_backends)
462 465 if edit:
463 466 #this is repo owner
464 467 user = All(Int(not_empty=True), ValidRepoUser)
465 468
466 469 chained_validators = [ValidPerms]
467 470 return _RepoForm
468 471
469 472 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
470 473 class _RepoForkForm(formencode.Schema):
471 474 allow_extra_fields = True
472 475 filter_extra_fields = False
473 476 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
474 477 ValidRepoName(edit, old_data))
475 478 description = UnicodeString(strip=True, min=1, not_empty=True)
476 479 private = StringBoolean(if_missing=False)
477 480 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
478 481 return _RepoForkForm
479 482
480 483 def RepoSettingsForm(edit=False, old_data={}):
481 484 class _RepoForm(formencode.Schema):
482 485 allow_extra_fields = True
483 486 filter_extra_fields = False
484 487 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
485 488 ValidRepoName(edit, old_data))
486 489 description = UnicodeString(strip=True, min=1, not_empty=True)
487 490 private = StringBoolean(if_missing=False)
488 491
489 492 chained_validators = [ValidPerms, ValidSettings]
490 493 return _RepoForm
491 494
492 495
493 496 def ApplicationSettingsForm():
494 497 class _ApplicationSettingsForm(formencode.Schema):
495 498 allow_extra_fields = True
496 499 filter_extra_fields = False
497 500 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
498 501 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
499 502 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
500 503
501 504 return _ApplicationSettingsForm
502 505
503 506 def ApplicationUiSettingsForm():
504 507 class _ApplicationUiSettingsForm(formencode.Schema):
505 508 allow_extra_fields = True
506 509 filter_extra_fields = False
507 510 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
508 511 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
509 512 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
510 513 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
511 514 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
512 515 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
513 516
514 517 return _ApplicationUiSettingsForm
515 518
516 519 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
517 520 class _DefaultPermissionsForm(formencode.Schema):
518 521 allow_extra_fields = True
519 522 filter_extra_fields = True
520 523 overwrite_default = StringBoolean(if_missing=False)
521 524 anonymous = OneOf(['True', 'False'], if_missing=False)
522 525 default_perm = OneOf(perms_choices)
523 526 default_register = OneOf(register_choices)
524 527 default_create = OneOf(create_choices)
525 528
526 529 return _DefaultPermissionsForm
527 530
528 531
529 532 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices):
530 533 class _LdapSettingsForm(formencode.Schema):
531 534 allow_extra_fields = True
532 535 filter_extra_fields = True
533 536 pre_validators = [LdapLibValidator]
534 537 ldap_active = StringBoolean(if_missing=False)
535 538 ldap_host = UnicodeString(strip=True,)
536 539 ldap_port = Number(strip=True,)
537 540 ldap_ldaps = StringBoolean(if_missing=False)
538 541 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
539 542 ldap_dn_user = UnicodeString(strip=True,)
540 543 ldap_dn_pass = UnicodeString(strip=True,)
541 544 ldap_base_dn = UnicodeString(strip=True,)
542 545 ldap_filter = UnicodeString(strip=True,)
543 546 ldap_search_scope = OneOf(search_scope_choices)
544 547 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
545 548 ldap_attr_firstname = UnicodeString(strip=True,)
546 549 ldap_attr_lastname = UnicodeString(strip=True,)
547 550 ldap_attr_email = UnicodeString(strip=True,)
548 551
549 552 return _LdapSettingsForm
@@ -1,314 +1,327 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.repo
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repository model for rhodecode
7 7
8 8 :created_on: Jun 5, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27 import os
28 28 import shutil
29 29 import logging
30 30 import traceback
31 31 from datetime import datetime
32 32
33 33 from rhodecode.model import BaseModel
34 34 from rhodecode.model.caching_query import FromCache
35 35 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
36 36 Statistics, UsersGroup, UsersGroupToPerm
37 37 from rhodecode.model.user import UserModel
38 38 from rhodecode.model.users_group import UsersGroupMember, UsersGroupModel
39 39
40 40 from vcs.backends import get_backend
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44 class RepoModel(BaseModel):
45 45
46 46 def __init__(self, sa=None):
47 47 try:
48 48 from pylons import app_globals
49 49 self._base_path = app_globals.base_path
50 50 except:
51 51 self._base_path = None
52 52
53 53 @property
54 54 def base_path():
55 55 if self._base_path is None:
56 56 raise Exception('Base Path is empty, try set this after'
57 57 'class initialization when not having '
58 58 'app_globals available')
59 59 return self._base_path
60 60
61 61 super(RepoModel, self).__init__()
62 62
63 63
64 64 def get(self, repo_id, cache=False):
65 65 repo = self.sa.query(Repository)\
66 66 .filter(Repository.repo_id == repo_id)
67 67
68 68 if cache:
69 69 repo = repo.options(FromCache("sql_cache_short",
70 70 "get_repo_%s" % repo_id))
71 71 return repo.scalar()
72 72
73 73
74 74 def get_by_repo_name(self, repo_name, cache=False):
75 75 repo = self.sa.query(Repository)\
76 76 .filter(Repository.repo_name == repo_name)
77 77
78 78 if cache:
79 79 repo = repo.options(FromCache("sql_cache_short",
80 80 "get_repo_%s" % repo_name))
81 81 return repo.scalar()
82 82
83 83 def get_users_js(self):
84 84
85 85 users = self.sa.query(User).filter(User.active == True).all()
86 86 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
87 87 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
88 88 u.lastname, u.username)
89 89 for u in users])
90 90 return users_array
91 91
92 92
93 93 def get_users_groups_js(self):
94 94 users_groups = self.sa.query(UsersGroup)\
95 95 .filter(UsersGroup.users_group_active == True).all()
96 96
97 97 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
98 98
99 99 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
100 100 (gr.users_group_id, gr.users_group_name,
101 101 len(gr.members))
102 102 for gr in users_groups])
103 103 return users_groups_array
104 104
105 105 def update(self, repo_name, form_data):
106 106 try:
107 107 cur_repo = self.get_by_repo_name(repo_name, cache=False)
108 108 user_model = UserModel(self.sa)
109 109 users_group_model = UsersGroupModel(self.sa)
110 110
111 111 #update permissions
112 112 for member, perm, member_type in form_data['perms_updates']:
113 113 if member_type == 'user':
114 114 r2p = self.sa.query(RepoToPerm)\
115 115 .filter(RepoToPerm.user == user_model.get_by_username(member))\
116 116 .filter(RepoToPerm.repository == cur_repo)\
117 117 .one()
118 118
119 119 r2p.permission = self.sa.query(Permission)\
120 120 .filter(Permission.permission_name == perm)\
121 121 .scalar()
122 122 self.sa.add(r2p)
123 123 else:
124 124 g2p = self.sa.query(UsersGroupToPerm)\
125 125 .filter(UsersGroupToPerm.users_group == users_group_model.get_by_groupname(member))\
126 126 .filter(UsersGroupToPerm.repository == cur_repo)\
127 127 .one()
128 128
129 129 g2p.permission = self.sa.query(Permission)\
130 130 .filter(Permission.permission_name == perm)\
131 131 .scalar()
132 132 self.sa.add(g2p)
133 133
134 134 #set new permissions
135 135 for member, perm, member_type in form_data['perms_new']:
136 136 if member_type == 'user':
137 137 r2p = RepoToPerm()
138 138 r2p.repository = cur_repo
139 139 r2p.user = user_model.get_by_username(member)
140 140
141 141 r2p.permission = self.sa.query(Permission)\
142 142 .filter(Permission.permission_name == perm)\
143 143 .scalar()
144 144 self.sa.add(r2p)
145 145 else:
146 146 g2p = UsersGroupToPerm()
147 147 g2p.repository = cur_repo
148 148 g2p.users_group = users_group_model.get_by_groupname(member)
149 149
150 150 g2p.permission = self.sa.query(Permission)\
151 151 .filter(Permission.permission_name == perm)\
152 152 .scalar()
153 153 self.sa.add(g2p)
154 154
155 155 #update current repo
156 156 for k, v in form_data.items():
157 157 if k == 'user':
158 158 cur_repo.user = user_model.get(v)
159 159 else:
160 160 setattr(cur_repo, k, v)
161 161
162 162 self.sa.add(cur_repo)
163 163
164 164 if repo_name != form_data['repo_name']:
165 165 #rename our data
166 166 self.__rename_repo(repo_name, form_data['repo_name'])
167 167
168 168 self.sa.commit()
169 169 except:
170 170 log.error(traceback.format_exc())
171 171 self.sa.rollback()
172 172 raise
173 173
174 174 def create(self, form_data, cur_user, just_db=False, fork=False):
175 175 try:
176 176 if fork:
177 177 #force str since hg doesn't go with unicode
178 178 repo_name = str(form_data['fork_name'])
179 179 org_name = str(form_data['repo_name'])
180 180
181 181 else:
182 182 org_name = repo_name = str(form_data['repo_name'])
183 183 new_repo = Repository()
184 184 new_repo.enable_statistics = True
185 185 for k, v in form_data.items():
186 186 if k == 'repo_name':
187 187 v = repo_name
188 188 setattr(new_repo, k, v)
189 189
190 190 if fork:
191 191 parent_repo = self.sa.query(Repository)\
192 192 .filter(Repository.repo_name == org_name).scalar()
193 193 new_repo.fork = parent_repo
194 194
195 195 new_repo.user_id = cur_user.user_id
196 196 self.sa.add(new_repo)
197 197
198 198 #create default permission
199 199 repo_to_perm = RepoToPerm()
200 200 default = 'repository.read'
201 201 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
202 202 if p.permission.permission_name.startswith('repository.'):
203 203 default = p.permission.permission_name
204 204 break
205 205
206 206 default_perm = 'repository.none' if form_data['private'] else default
207 207
208 208 repo_to_perm.permission_id = self.sa.query(Permission)\
209 209 .filter(Permission.permission_name == default_perm)\
210 210 .one().permission_id
211 211
212 212 repo_to_perm.repository_id = new_repo.repo_id
213 213 repo_to_perm.user_id = UserModel(self.sa)\
214 214 .get_by_username('default', cache=False).user_id
215 215
216 216 self.sa.add(repo_to_perm)
217 217 self.sa.commit()
218 218
219 219
220 220 #now automatically start following this repository as owner
221 221 from rhodecode.model.scm import ScmModel
222 222 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
223 223 cur_user.user_id)
224 224
225 225 if not just_db:
226 226 self.__create_repo(repo_name, form_data['repo_type'])
227 227 except:
228 228 log.error(traceback.format_exc())
229 229 self.sa.rollback()
230 230 raise
231 231
232 232 def create_fork(self, form_data, cur_user):
233 233 from rhodecode.lib.celerylib import tasks, run_task
234 234 run_task(tasks.create_repo_fork, form_data, cur_user)
235 235
236 236 def delete(self, repo):
237 237 try:
238 238 self.sa.delete(repo)
239 239 self.__delete_repo(repo)
240 240 self.sa.commit()
241 241 except:
242 242 log.error(traceback.format_exc())
243 243 self.sa.rollback()
244 244 raise
245 245
246 246 def delete_perm_user(self, form_data, repo_name):
247 247 try:
248 248 self.sa.query(RepoToPerm)\
249 249 .filter(RepoToPerm.repository \
250 250 == self.get_by_repo_name(repo_name))\
251 251 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
252 252 self.sa.commit()
253 253 except:
254 254 log.error(traceback.format_exc())
255 255 self.sa.rollback()
256 256 raise
257 257
258 def delete_perm_users_group(self, form_data, repo_name):
259 try:
260 self.sa.query(UsersGroupToPerm)\
261 .filter(UsersGroupToPerm.repository \
262 == self.get_by_repo_name(repo_name))\
263 .filter(UsersGroupToPerm.users_group_id \
264 == form_data['users_group_id']).delete()
265 self.sa.commit()
266 except:
267 log.error(traceback.format_exc())
268 self.sa.rollback()
269 raise
270
258 271 def delete_stats(self, repo_name):
259 272 try:
260 273 self.sa.query(Statistics)\
261 274 .filter(Statistics.repository == \
262 275 self.get_by_repo_name(repo_name)).delete()
263 276 self.sa.commit()
264 277 except:
265 278 log.error(traceback.format_exc())
266 279 self.sa.rollback()
267 280 raise
268 281
269 282
270 283 def __create_repo(self, repo_name, alias):
271 284 """
272 285 makes repository on filesystem
273 286 :param repo_name:
274 287 :param alias:
275 288 """
276 289 from rhodecode.lib.utils import check_repo
277 290 repo_path = os.path.join(self.base_path, repo_name)
278 291 if check_repo(repo_name, self.base_path):
279 292 log.info('creating repo %s in %s', repo_name, repo_path)
280 293 backend = get_backend(alias)
281 294 backend(repo_path, create=True)
282 295
283 296 def __rename_repo(self, old, new):
284 297 """
285 298 renames repository on filesystem
286 299 :param old: old name
287 300 :param new: new name
288 301 """
289 302 log.info('renaming repo from %s to %s', old, new)
290 303
291 304 old_path = os.path.join(self.base_path, old)
292 305 new_path = os.path.join(self.base_path, new)
293 306 if os.path.isdir(new_path):
294 307 raise Exception('Was trying to rename to already existing dir %s',
295 308 new_path)
296 309 shutil.move(old_path, new_path)
297 310
298 311 def __delete_repo(self, repo):
299 312 """
300 313 removes repo from filesystem, the removal is acctually made by
301 314 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
302 315 repository is no longer valid for rhodecode, can be undeleted later on
303 316 by reverting the renames on this repository
304 317 :param repo: repo object
305 318 """
306 319 rm_path = os.path.join(self.base_path, repo.repo_name)
307 320 log.info("Removing %s", rm_path)
308 321 #disable hg/git
309 322 alias = repo.repo_type
310 323 shutil.move(os.path.join(rm_path, '.%s' % alias),
311 324 os.path.join(rm_path, 'rm__.%s' % alias))
312 325 #disable repo
313 326 shutil.move(rm_path, os.path.join(self.base_path, 'rm__%s__%s' \
314 327 % (datetime.today(), repo.repo_name)))
@@ -1,407 +1,339 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 10 &raquo;
11 11 ${h.link_to(_('Repositories'),h.url('repos'))}
12 12 &raquo;
13 13 ${_('edit')} "${c.repo_name}"
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('admin')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box box-left">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
27 27 <div class="form">
28 28 <!-- fields -->
29 29 <div class="fields">
30 30 <div class="field">
31 31 <div class="label">
32 32 <label for="repo_name">${_('Name')}:</label>
33 33 </div>
34 34 <div class="input">
35 35 ${h.text('repo_name',class_="medium")}
36 36 </div>
37 37 </div>
38 38 <div class="field">
39 39 <div class="label">
40 40 <label for="repo_type">${_('Type')}:</label>
41 41 </div>
42 42 <div class="input">
43 43 ${h.select('repo_type','hg',c.backends,class_="medium")}
44 44 </div>
45 45 </div>
46 46 <div class="field">
47 47 <div class="label label-textarea">
48 48 <label for="description">${_('Description')}:</label>
49 49 </div>
50 50 <div class="textarea text-area editor">
51 51 ${h.textarea('description',cols=23,rows=5)}
52 52 </div>
53 53 </div>
54 54
55 55 <div class="field">
56 56 <div class="label label-checkbox">
57 57 <label for="private">${_('Private')}:</label>
58 58 </div>
59 59 <div class="checkboxes">
60 60 ${h.checkbox('private',value="True")}
61 61 </div>
62 62 </div>
63 63 <div class="field">
64 64 <div class="label label-checkbox">
65 65 <label for="enable_statistics">${_('Enable statistics')}:</label>
66 66 </div>
67 67 <div class="checkboxes">
68 68 ${h.checkbox('enable_statistics',value="True")}
69 69 </div>
70 70 </div>
71 71 <div class="field">
72 72 <div class="label label-checkbox">
73 73 <label for="enable_downloads">${_('Enable downloads')}:</label>
74 74 </div>
75 75 <div class="checkboxes">
76 76 ${h.checkbox('enable_downloads',value="True")}
77 77 </div>
78 78 </div>
79 79 <div class="field">
80 80 <div class="label">
81 81 <label for="user">${_('Owner')}:</label>
82 82 </div>
83 83 <div class="input input-small ac">
84 84 <div class="perm_ac">
85 85 ${h.text('user',class_='yui-ac-input')}
86 86 <div id="owner_container"></div>
87 87 </div>
88 88 </div>
89 89 </div>
90 90
91 91 <div class="field">
92 92 <div class="label">
93 93 <label for="input">${_('Permissions')}:</label>
94 94 </div>
95 95 <div class="input">
96 <table id="permissions_manage">
97 <tr>
98 <td>${_('none')}</td>
99 <td>${_('read')}</td>
100 <td>${_('write')}</td>
101 <td>${_('admin')}</td>
102 <td>${_('member')}</td>
103 <td></td>
104 </tr>
105
106 %for r2p in c.repo_info.repo_to_perm:
107 %if r2p.user.username =='default' and c.repo_info.private:
108 <tr>
109 <td colspan="4">
110 <span class="private_repo_msg">
111 ${_('private repository')}
112 </span>
113 </td>
114 <td class="private_repo_msg">${r2p.user.username}</td>
115 </tr>
116 %else:
117 <tr id="id${id(r2p.user.username)}">
118 <td>${h.radio('perm_%s' % r2p.user.username,'repository.none')}</td>
119 <td>${h.radio('perm_%s' % r2p.user.username,'repository.read')}</td>
120 <td>${h.radio('perm_%s' % r2p.user.username,'repository.write')}</td>
121 <td>${h.radio('perm_%s' % r2p.user.username,'repository.admin')}</td>
122 <td>${r2p.user.username}</td>
123 <td>
124 %if r2p.user.username !='default':
125 <span class="delete_icon action_button" onclick="ajaxAction(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
126 <script type="text/javascript">
127 function ajaxAction(user_id,field_id){
128 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
129 var callback = { success:function(o){
130 var tr = YAHOO.util.Dom.get(String(field_id));
131 tr.parentNode.removeChild(tr);},failure:function(o){
132 alert("${_('Failed to remove user')}");},};
133 var postData = '_method=delete&user_id='+user_id;
134 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);};
135 </script>
136 </span>
137 %endif
138 </td>
139 </tr>
140 %endif
141 %endfor
142
143 <tr id="add_perm_input">
144 <td>${h.radio('perm_new_member','repository.none')}</td>
145 <td>${h.radio('perm_new_member','repository.read')}</td>
146 <td>${h.radio('perm_new_member','repository.write')}</td>
147 <td>${h.radio('perm_new_member','repository.admin')}</td>
148 <td class='ac'>
149 <div class="perm_ac" id="perm_ac">
150 ${h.text('perm_new_member_name',class_='yui-ac-input')}
151 ${h.hidden('perm_new_member_type')}
152 <div id="perm_container"></div>
153 </div>
154 </td>
155 <td></td>
156 </tr>
157 <tr>
158 <td colspan="6">
159 <span id="add_perm" class="add_icon" style="cursor: pointer;">
160 ${_('Add another member')}
161 </span>
162 </td>
163 </tr>
164 </table>
96 <%include file="repo_edit_perms.html"/>
165 97 </div>
166 98
167 99 <div class="buttons">
168 100 ${h.submit('save','Save',class_="ui-button")}
169 101 ${h.reset('reset','Reset',class_="ui-button")}
170 102 </div>
171 103 </div>
172 104 </div>
173 105 </div>
174 106 ${h.end_form()}
175 107 <script type="text/javascript">
176 108 YAHOO.util.Event.onDOMReady(function(){
177 109 var D = YAHOO.util.Dom;
178 110 if(!D.hasClass('perm_new_member_name','error')){
179 111 D.setStyle('add_perm_input','display','none');
180 112 }
181 113 YAHOO.util.Event.addListener('add_perm','click',function(){
182 114 D.setStyle('add_perm_input','display','');
183 115 D.setStyle('add_perm','opacity','0.6');
184 116 D.setStyle('add_perm','cursor','default');
185 117 });
186 118 });
187 119 </script>
188 120 <script type="text/javascript">
189 121 YAHOO.example.FnMultipleFields = function(){
190 122 var myUsers = ${c.users_array|n};
191 123 var myGroups = ${c.users_groups_array|n};
192 124
193 125 // Define a custom search function for the DataSource of users
194 126 var matchUsers = function(sQuery) {
195 127 // Case insensitive matching
196 128 var query = sQuery.toLowerCase();
197 129 var i=0;
198 130 var l=myUsers.length;
199 131 var matches = [];
200 132
201 133 // Match against each name of each contact
202 134 for(; i<l; i++) {
203 135 contact = myUsers[i];
204 136 if((contact.fname.toLowerCase().indexOf(query) > -1) ||
205 137 (contact.lname.toLowerCase().indexOf(query) > -1) ||
206 138 (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
207 139 matches[matches.length] = contact;
208 140 }
209 141 }
210 142 return matches;
211 143 };
212 144
213 145 // Define a custom search function for the DataSource of usersGroups
214 146 var matchGroups = function(sQuery) {
215 147 // Case insensitive matching
216 148 var query = sQuery.toLowerCase();
217 149 var i=0;
218 150 var l=myGroups.length;
219 151 var matches = [];
220 152
221 153 // Match against each name of each contact
222 154 for(; i<l; i++) {
223 155 matched_group = myGroups[i];
224 156 if(matched_group.grname.toLowerCase().indexOf(query) > -1) {
225 157 matches[matches.length] = matched_group;
226 158 }
227 159 }
228 160 return matches;
229 161 };
230 162
231 163 //match all
232 164 var matchAll = function(sQuery){
233 165 u = matchUsers(sQuery);
234 166 g = matchGroups(sQuery);
235 167 return u.concat(g);
236 168 };
237 169
238 170 // DataScheme for members
239 171 var memberDS = new YAHOO.util.FunctionDataSource(matchAll);
240 172 memberDS.responseSchema = {
241 173 fields: ["id", "fname", "lname", "nname", "grname", "grmembers"]
242 174 };
243 175
244 176 // DataScheme for owner
245 177 var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers);
246 178 ownerDS.responseSchema = {
247 179 fields: ["id", "fname", "lname", "nname"]
248 180 };
249 181
250 182 // Instantiate AutoComplete for perms
251 183 var membersAC = new YAHOO.widget.AutoComplete("perm_new_member_name", "perm_container", memberDS);
252 184 membersAC.useShadow = false;
253 185 membersAC.resultTypeList = false;
254 186
255 187 // Instantiate AutoComplete for owner
256 188 var ownerAC = new YAHOO.widget.AutoComplete("user", "owner_container", ownerDS);
257 189 ownerAC.useShadow = false;
258 190 ownerAC.resultTypeList = false;
259 191
260 192
261 193 // Helper highlight function for the formatter
262 194 var highlightMatch = function(full, snippet, matchindex) {
263 195 return full.substring(0, matchindex) +
264 196 "<span class='match'>" +
265 197 full.substr(matchindex, snippet.length) +
266 198 "</span>" +
267 199 full.substring(matchindex + snippet.length);
268 200 };
269 201
270 202 // Custom formatter to highlight the matching letters
271 203 var custom_formatter = function(oResultData, sQuery, sResultMatch) {
272 204 var query = sQuery.toLowerCase();
273 205
274 206 if (oResultData.grname != undefined){
275 207 var grname = oResultData.grname;
276 208 var grmembers = oResultData.grmembers;
277 209 var grnameMatchIndex = grname.toLowerCase().indexOf(query);
278 210 var grprefix = "${_('Group')}: ";
279 211 var grsuffix = " ("+grmembers+" ${_('members')})";
280 212
281 213 if (grnameMatchIndex > -1){
282 214 return grprefix+highlightMatch(grname,query,grnameMatchIndex)+grsuffix;
283 215 }
284 216
285 217 return grprefix+oResultData.grname+grsuffix;
286 218 }
287 219 else if(oResultData.fname != undefined){
288 220
289 221 var fname = oResultData.fname,
290 222 lname = oResultData.lname,
291 223 nname = oResultData.nname || "", // Guard against null value
292 224 fnameMatchIndex = fname.toLowerCase().indexOf(query),
293 225 lnameMatchIndex = lname.toLowerCase().indexOf(query),
294 226 nnameMatchIndex = nname.toLowerCase().indexOf(query),
295 227 displayfname, displaylname, displaynname;
296 228
297 229 if(fnameMatchIndex > -1) {
298 230 displayfname = highlightMatch(fname, query, fnameMatchIndex);
299 231 }
300 232 else {
301 233 displayfname = fname;
302 234 }
303 235
304 236 if(lnameMatchIndex > -1) {
305 237 displaylname = highlightMatch(lname, query, lnameMatchIndex);
306 238 }
307 239 else {
308 240 displaylname = lname;
309 241 }
310 242
311 243 if(nnameMatchIndex > -1) {
312 244 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
313 245 }
314 246 else {
315 247 displaynname = nname ? "(" + nname + ")" : "";
316 248 }
317 249
318 250 return displayfname + " " + displaylname + " " + displaynname;
319 251 }
320 252 else{
321 253 return '';
322 254 }
323 255 };
324 256 membersAC.formatResult = custom_formatter;
325 257 ownerAC.formatResult = custom_formatter;
326 258
327 259 var myHandler = function(sType, aArgs) {
328 260
329 261 var myAC = aArgs[0]; // reference back to the AC instance
330 262 var elLI = aArgs[1]; // reference to the selected LI element
331 263 var oData = aArgs[2]; // object literal of selected item's result data
332 264
333 265 //fill the autocomplete with value
334 266 if(oData.nname != undefined){
335 267 //users
336 268 myAC.getInputEl().value = oData.nname;
337 269 YUD.get('perm_new_member_type').value = 'user';
338 270 }
339 271 else{
340 272 //groups
341 273 myAC.getInputEl().value = oData.grname;
342 274 YUD.get('perm_new_member_type').value = 'users_group';
343 275 }
344 276
345 277 };
346 278
347 279 membersAC.itemSelectEvent.subscribe(myHandler);
348 280 ownerAC.itemSelectEvent.subscribe(myHandler);
349 281
350 282 return {
351 283 memberDS: memberDS,
352 284 ownerDS: ownerDS,
353 285 membersAC: membersAC,
354 286 ownerAC: ownerAC,
355 287 };
356 288 }();
357 289
358 290 </script>
359 291
360 292 </div>
361 293
362 294 <div class="box box-right">
363 295 <div class="title">
364 296 <h5>${_('Administration')}</h5>
365 297 </div>
366 298
367 299 <h3>${_('Statistics')}</h3>
368 300
369 301 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
370 302 <div class="form">
371 303 <div class="fields">
372 304 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="refresh_icon action_button",onclick="return confirm('Confirm to remove current statistics');")}
373 305
374 306 <div class="field">
375 307 <ul>
376 308 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
377 309 <li>${_('Percentage of stats gathered')}: ${c.stats_percentage} %</li>
378 310 </ul>
379 311 </div>
380 312
381 313 </div>
382 314 </div>
383 315 ${h.end_form()}
384 316
385 317 <h3>${_('Cache')}</h3>
386 318 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
387 319 <div class="form">
388 320 <div class="fields">
389 321 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="refresh_icon action_button",onclick="return confirm('Confirm to invalidate repository cache');")}
390 322 </div>
391 323 </div>
392 324 ${h.end_form()}
393 325
394 326
395 327 <h3>${_('Delete')}</h3>
396 328 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
397 329 <div class="form">
398 330 <div class="fields">
399 331 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
400 332 </div>
401 333 </div>
402 334 ${h.end_form()}
403 335
404 336 </div>
405 337
406 338
407 339 </%def> No newline at end of file
@@ -1,392 +1,392 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 4 <head>
5 5 <title>${next.title()}</title>
6 6 <link rel="icon" href="/images/icons/database_gear.png" type="image/png" />
7 7 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9 <!-- stylesheets -->
10 10 ${self.css()}
11 11 <!-- scripts -->
12 12 ${self.js()}
13 13 %if c.ga_code:
14 14 <script type="text/javascript">
15 15
16 16 var _gaq = _gaq || [];
17 17 _gaq.push(['_setAccount', '${c.ga_code}']);
18 18 _gaq.push(['_trackPageview']);
19 19
20 20 (function() {
21 21 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
22 22 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
23 23 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
24 24 })();
25 25
26 26
27 27 </script>
28 28 %endif
29 29 </head>
30 30 <body>
31 31 <!-- header -->
32 32 <div id="header">
33 33 <!-- user -->
34 34 <ul id="logged-user">
35 35 <li class="first">
36 36 <div class="gravatar">
37 37 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,20)}" />
38 38 </div>
39 39 <div class="account">
40 40 %if c.rhodecode_user.username == 'default':
41 41 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
42 42 ${h.link_to('anonymous',h.url('register'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
43 43 %else:
44 44 ${h.link_to('anonymous',h.url('#'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
45 45 %endif
46 46
47 47 %else:
48 48 ${h.link_to(c.rhodecode_user.username,h.url('admin_settings_my_account'),title='%s %s'%(c.rhodecode_user.name,c.rhodecode_user.lastname))}
49 49 %endif
50 50 </div>
51 51 </li>
52 52 <li>
53 53 <a href="${h.url('home')}">${_('Home')}</a>
54 54 </li>
55 55 %if c.rhodecode_user.username != 'default':
56 56 <li>
57 57 <a href="${h.url('journal')}">${_('Journal')}</a>
58 58 ##(${c.unread_journal})</a>
59 59 </li>
60 60 %endif
61 61 %if c.rhodecode_user.username == 'default':
62 62 <li class="last highlight">${h.link_to(u'Login',h.url('login_home'))}</li>
63 63 %else:
64 64 <li class="last highlight">${h.link_to(u'Log Out',h.url('logout_home'))}</li>
65 65 %endif
66 66 </ul>
67 67 <!-- end user -->
68 68 <div id="header-inner" class="title top-left-rounded-corner top-right-rounded-corner">
69 69 <!-- logo -->
70 70 <div id="logo">
71 71 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
72 72 </div>
73 73 <!-- end logo -->
74 74 <!-- menu -->
75 75 ${self.page_nav()}
76 76 <!-- quick -->
77 77 </div>
78 78 </div>
79 79 <!-- end header -->
80 80
81 81 <!-- CONTENT -->
82 82 <div id="content">
83 83 <div class="flash_msg">
84 84 <% messages = h.flash.pop_messages() %>
85 85 % if messages:
86 86 <ul id="flash-messages">
87 87 % for message in messages:
88 88 <li class="${message.category}_msg">${message}</li>
89 89 % endfor
90 90 </ul>
91 91 % endif
92 92 </div>
93 93 <div id="main">
94 94 ${next.main()}
95 95 </div>
96 96 </div>
97 97 <!-- END CONTENT -->
98 98
99 99 <!-- footer -->
100 100 <div id="footer">
101 101 <div id="footer-inner" class="title bottom-left-rounded-corner bottom-right-rounded-corner">
102 102 <div>
103 103 <p class="footer-link">${h.link_to(_('Submit a bug'),h.url('bugtracker'))}</p>
104 104 <p class="footer-link">${h.link_to(_('GPL license'),h.url('gpl_license'))}</p>
105 <p>RhodeCode ${c.rhodecode_version} &copy; 2010 by Marcin Kuzminski</p>
105 <p>RhodeCode ${c.rhodecode_version} &copy; 2010-2011 by Marcin Kuzminski</p>
106 106 </div>
107 107 </div>
108 108 <script type="text/javascript">
109 109 function tooltip_activate(){
110 110 ${h.tooltip.activate()}
111 111 }
112 112 tooltip_activate();
113 113 </script>
114 114 </div>
115 115 <!-- end footer -->
116 116 </body>
117 117
118 118 </html>
119 119
120 120 ### MAKO DEFS ###
121 121 <%def name="page_nav()">
122 122 ${self.menu()}
123 123 </%def>
124 124
125 125 <%def name="menu(current=None)">
126 126 <%
127 127 def is_current(selected):
128 128 if selected == current:
129 129 return h.literal('class="current"')
130 130 %>
131 131 %if current not in ['home','admin']:
132 132 ##REGULAR MENU
133 133 <ul id="quick">
134 134 <!-- repo switcher -->
135 135 <li>
136 136 <a id="repo_switcher" title="${_('Switch repository')}" href="#">
137 137 <span class="icon">
138 138 <img src="/images/icons/database.png" alt="${_('Products')}" />
139 139 </span>
140 140 <span>&darr;</span>
141 141 </a>
142 142 <ul class="repo_switcher">
143 143 %for repo in c.cached_repo_list:
144 144
145 145 %if repo['repo'].dbrepo.private:
146 146 <li><img src="/images/icons/lock.png" alt="${_('Private repository')}" class="repo_switcher_type"/>${h.link_to(repo['repo'].name,h.url('summary_home',repo_name=repo['repo'].name),class_="%s" % repo['repo'].dbrepo.repo_type)}</li>
147 147 %else:
148 148 <li><img src="/images/icons/lock_open.png" alt="${_('Public repository')}" class="repo_switcher_type" />${h.link_to(repo['repo'].name,h.url('summary_home',repo_name=repo['repo'].name),class_="%s" % repo['repo'].dbrepo.repo_type)}</li>
149 149 %endif
150 150 %endfor
151 151 </ul>
152 152 </li>
153 153
154 154 <li ${is_current('summary')}>
155 155 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=c.repo_name)}">
156 156 <span class="icon">
157 157 <img src="/images/icons/clipboard_16.png" alt="${_('Summary')}" />
158 158 </span>
159 159 <span>${_('Summary')}</span>
160 160 </a>
161 161 </li>
162 162 ##<li ${is_current('shortlog')}>
163 163 ## <a title="${_('Shortlog')}" href="${h.url('shortlog_home',repo_name=c.repo_name)}">
164 164 ## <span class="icon">
165 165 ## <img src="/images/icons/application_view_list.png" alt="${_('Shortlog')}" />
166 166 ## </span>
167 167 ## <span>${_('Shortlog')}</span>
168 168 ## </a>
169 169 ##</li>
170 170 <li ${is_current('changelog')}>
171 171 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=c.repo_name)}">
172 172 <span class="icon">
173 173 <img src="/images/icons/time.png" alt="${_('Changelog')}" />
174 174 </span>
175 175 <span>${_('Changelog')}</span>
176 176 </a>
177 177 </li>
178 178
179 179 <li ${is_current('switch_to')}>
180 180 <a title="${_('Switch to')}" href="#">
181 181 <span class="icon">
182 182 <img src="/images/icons/arrow_switch.png" alt="${_('Switch to')}" />
183 183 </span>
184 184 <span>${_('Switch to')}</span>
185 185 </a>
186 186 <ul>
187 187 <li>
188 188 ${h.link_to('%s (%s)' % (_('branches'),len(c.repository_branches.values()),),h.url('branches_home',repo_name=c.repo_name),class_='branches childs')}
189 189 <ul>
190 190 %if c.repository_branches.values():
191 191 %for cnt,branch in enumerate(c.repository_branches.items()):
192 192 <li>${h.link_to('%s - %s' % (branch[0],h.short_id(branch[1])),h.url('files_home',repo_name=c.repo_name,revision=branch[1]))}</li>
193 193 %endfor
194 194 %else:
195 195 <li>${h.link_to(_('There are no branches yet'),'#')}</li>
196 196 %endif
197 197 </ul>
198 198 </li>
199 199 <li>
200 200 ${h.link_to('%s (%s)' % (_('tags'),len(c.repository_tags.values()),),h.url('tags_home',repo_name=c.repo_name),class_='tags childs')}
201 201 <ul>
202 202 %if c.repository_tags.values():
203 203 %for cnt,tag in enumerate(c.repository_tags.items()):
204 204 <li>${h.link_to('%s - %s' % (tag[0],h.short_id(tag[1])),h.url('files_home',repo_name=c.repo_name,revision=tag[1]))}</li>
205 205 %endfor
206 206 %else:
207 207 <li>${h.link_to(_('There are no tags yet'),'#')}</li>
208 208 %endif
209 209 </ul>
210 210 </li>
211 211 </ul>
212 212 </li>
213 213 <li ${is_current('files')}>
214 214 <a title="${_('Files')}" href="${h.url('files_home',repo_name=c.repo_name)}">
215 215 <span class="icon">
216 216 <img src="/images/icons/file.png" alt="${_('Files')}" />
217 217 </span>
218 218 <span>${_('Files')}</span>
219 219 </a>
220 220 </li>
221 221
222 222 <li ${is_current('options')}>
223 223 <a title="${_('Options')}" href="#">
224 224 <span class="icon">
225 225 <img src="/images/icons/table_gear.png" alt="${_('Admin')}" />
226 226 </span>
227 227 <span>${_('Options')}</span>
228 228 </a>
229 229 <ul>
230 230 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
231 231 %if h.HasPermissionAll('hg.admin')('access settings on repository'):
232 232 <li>${h.link_to(_('settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
233 233 %else:
234 234 <li>${h.link_to(_('settings'),h.url('repo_settings_home',repo_name=c.repo_name),class_='settings')}</li>
235 235 %endif
236 236 <li>${h.link_to(_('fork'),h.url('repo_fork_home',repo_name=c.repo_name),class_='fork')}</li>
237 237 %endif
238 238 <li>${h.link_to(_('search'),h.url('search_repo',search_repo=c.repo_name),class_='search')}</li>
239 239
240 240 %if h.HasPermissionAll('hg.admin')('access admin main page'):
241 241 <li>
242 242 ${h.link_to(_('admin'),h.url('admin_home'),class_='admin')}
243 243 <%def name="admin_menu()">
244 244 <ul>
245 245 <li>${h.link_to(_('journal'),h.url('admin_home'),class_='journal')}</li>
246 246 <li>${h.link_to(_('repositories'),h.url('repos'),class_='repos')}</li>
247 247 <li>${h.link_to(_('users'),h.url('users'),class_='users')}</li>
248 248 <li>${h.link_to(_('users groups'),h.url('users_groups'),class_='groups')}</li>
249 249 <li>${h.link_to(_('permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
250 250 <li>${h.link_to(_('ldap'),h.url('ldap_home'),class_='ldap')}</li>
251 251 <li class="last">${h.link_to(_('settings'),h.url('admin_settings'),class_='settings')}</li>
252 252 </ul>
253 253 </%def>
254 254
255 255 ${admin_menu()}
256 256 </li>
257 257 %endif
258 258
259 259 </ul>
260 260 </li>
261 261
262 262 <li>
263 263 <a title="${_('Followers')}" href="#">
264 264 <span class="icon_short">
265 265 <img src="/images/icons/heart.png" alt="${_('Followers')}" />
266 266 </span>
267 267 <span class="short">${c.repository_followers}</span>
268 268 </a>
269 269 </li>
270 270 <li>
271 271 <a title="${_('Forks')}" href="#">
272 272 <span class="icon_short">
273 273 <img src="/images/icons/arrow_divide.png" alt="${_('Forks')}" />
274 274 </span>
275 275 <span class="short">${c.repository_forks}</span>
276 276 </a>
277 277 </li>
278 278
279 279
280 280
281 281 </ul>
282 282 %else:
283 283 ##ROOT MENU
284 284 <ul id="quick">
285 285 <li>
286 286 <a title="${_('Home')}" href="${h.url('home')}">
287 287 <span class="icon">
288 288 <img src="/images/icons/home_16.png" alt="${_('Home')}" />
289 289 </span>
290 290 <span>${_('Home')}</span>
291 291 </a>
292 292 </li>
293 293 %if c.rhodecode_user.username != 'default':
294 294 <li>
295 295 <a title="${_('Journal')}" href="${h.url('journal')}">
296 296 <span class="icon">
297 297 <img src="/images/icons/book.png" alt="${_('Journal')}" />
298 298 </span>
299 299 <span>${_('Journal')}</span>
300 300 </a>
301 301 </li>
302 302 %endif
303 303 <li>
304 304 <a title="${_('Search')}" href="${h.url('search')}">
305 305 <span class="icon">
306 306 <img src="/images/icons/search_16.png" alt="${_('Search')}" />
307 307 </span>
308 308 <span>${_('Search')}</span>
309 309 </a>
310 310 </li>
311 311
312 312 %if h.HasPermissionAll('hg.admin')('access admin main page'):
313 313 <li ${is_current('admin')}>
314 314 <a title="${_('Admin')}" href="${h.url('admin_home')}">
315 315 <span class="icon">
316 316 <img src="/images/icons/cog_edit.png" alt="${_('Admin')}" />
317 317 </span>
318 318 <span>${_('Admin')}</span>
319 319 </a>
320 320 ${admin_menu()}
321 321 </li>
322 322 %endif
323 323 </ul>
324 324 %endif
325 325 </%def>
326 326
327 327
328 328 <%def name="css()">
329 329 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
330 330 <link rel="stylesheet" type="text/css" href="/css/pygments.css" />
331 331 <link rel="stylesheet" type="text/css" href="/css/diff.css" />
332 332 </%def>
333 333
334 334 <%def name="js()">
335 335 ##<script type="text/javascript" src="/js/yui/utilities/utilities.js"></script>
336 336 ##<script type="text/javascript" src="/js/yui/container/container.js"></script>
337 337 ##<script type="text/javascript" src="/js/yui/datasource/datasource.js"></script>
338 338 ##<script type="text/javascript" src="/js/yui/autocomplete/autocomplete.js"></script>
339 339 ##<script type="text/javascript" src="/js/yui/selector/selector-min.js"></script>
340 340
341 341 <script type="text/javascript" src="/js/yui2a.js"></script>
342 342 <!--[if IE]><script language="javascript" type="text/javascript" src="/js/excanvas.min.js"></script><![endif]-->
343 343 <script type="text/javascript" src="/js/yui.flot.js"></script>
344 344
345 345 <script type="text/javascript">
346 346 var base_url ='/_admin/toggle_following';
347 347 var YUC = YAHOO.util.Connect;
348 348 var YUD = YAHOO.util.Dom;
349 349 var YUE = YAHOO.util.Event;
350 350
351 351 function onSuccess(target){
352 352
353 353 var f = YUD.get(target.id);
354 354 if(f.getAttribute('class')=='follow'){
355 355 f.setAttribute('class','following');
356 356 f.setAttribute('title',"${_('Stop following this repository')}");
357 357 }
358 358 else{
359 359 f.setAttribute('class','follow');
360 360 f.setAttribute('title',"${_('Start following this repository')}");
361 361 }
362 362 }
363 363
364 364 function toggleFollowingUser(fallows_user_id,token){
365 365 args = 'follows_user_id='+fallows_user_id;
366 366 args+= '&amp;auth_token='+token;
367 367 YUC.asyncRequest('POST',base_url,{
368 368 success:function(o){
369 369 onSuccess();
370 370 }
371 371 },args); return false;
372 372 }
373 373
374 374 function toggleFollowingRepo(target,fallows_repo_id,token){
375 375
376 376 args = 'follows_repo_id='+fallows_repo_id;
377 377 args+= '&amp;auth_token='+token;
378 378 YUC.asyncRequest('POST',base_url,{
379 379 success:function(o){
380 380 onSuccess(target);
381 381 }
382 382 },args); return false;
383 383 }
384 384 </script>
385 385
386 386 </%def>
387 387
388 388 <%def name="breadcrumbs()">
389 389 <div class="breadcrumbs">
390 390 ${self.breadcrumbs_links()}
391 391 </div>
392 392 </%def> No newline at end of file
@@ -1,263 +1,195 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${c.repo_name} ${_('Settings')} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))}
10 10 &raquo;
11 11 ${_('Settings')}
12 12 </%def>
13 13
14 14 <%def name="page_nav()">
15 15 ${self.menu('settings')}
16 16 </%def>
17 17 <%def name="main()">
18 18 <div class="box">
19 19 <!-- box / title -->
20 20 <div class="title">
21 21 ${self.breadcrumbs()}
22 22 </div>
23 23 ${h.form(url('repo_settings_update', repo_name=c.repo_info.repo_name),method='put')}
24 24 <div class="form">
25 25 <!-- fields -->
26 26 <div class="fields">
27 27 <div class="field">
28 28 <div class="label">
29 29 <label for="repo_name">${_('Name')}:</label>
30 30 </div>
31 31 <div class="input input-medium">
32 32 ${h.text('repo_name',class_="small")}
33 33 </div>
34 34 </div>
35 35
36 36 <div class="field">
37 37 <div class="label label-textarea">
38 38 <label for="description">${_('Description')}:</label>
39 39 </div>
40 40 <div class="textarea text-area editor">
41 41 ${h.textarea('description',cols=23,rows=5)}
42 42 </div>
43 43 </div>
44 44
45 45 <div class="field">
46 46 <div class="label label-checkbox">
47 47 <label for="private">${_('Private')}:</label>
48 48 </div>
49 49 <div class="checkboxes">
50 50 ${h.checkbox('private',value="True")}
51 51 </div>
52 52 </div>
53 53
54 54 <div class="field">
55 55 <div class="label">
56 56 <label for="">${_('Permissions')}:</label>
57 57 </div>
58 58 <div class="input">
59 <table id="permissions_manage">
60 <tr>
61 <td>${_('none')}</td>
62 <td>${_('read')}</td>
63 <td>${_('write')}</td>
64 <td>${_('admin')}</td>
65 <td>${_('user')}</td>
66 <td></td>
67 </tr>
68
69 %for r2p in c.repo_info.repo_to_perm:
70 %if r2p.user.username =='default' and c.repo_info.private:
71 <tr>
72 <td colspan="4">
73 <span class="private_repo_msg">
74 ${_('private repository')}
75 </span>
76 </td>
77 <td class="private_repo_msg">${r2p.user.username}</td>
78 </tr>
79 %else:
80 <tr id="id${id(r2p.user.username)}">
81 <td>${h.radio('perm_%s' % r2p.user.username,'repository.none')}</td>
82 <td>${h.radio('perm_%s' % r2p.user.username,'repository.read')}</td>
83 <td>${h.radio('perm_%s' % r2p.user.username,'repository.write')}</td>
84 <td>${h.radio('perm_%s' % r2p.user.username,'repository.admin')}</td>
85 <td>${r2p.user.username}</td>
86 <td>
87 %if r2p.user.username !='default':
88 <span class="delete_icon action_button" onclick="ajaxAction(${r2p.user.user_id},'${'id%s'%id(r2p.user.username)}')">
89 <script type="text/javascript">
90 function ajaxAction(user_id,field_id){
91 var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}";
92 var callback = { success:function(o){
93 var tr = YAHOO.util.Dom.get(String(field_id));
94 tr.parentNode.removeChild(tr);},failure:function(o){
95 alert("${_('Failed to remove user')}");},};
96 var postData = '_method=delete&user_id='+user_id;
97 var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData);};
98 </script>
99 </span>
100 %endif
101 </td>
102 </tr>
103 %endif
104 %endfor
105
106
107 <tr id="add_perm_input">
108 <td>${h.radio('perm_new_user','repository.none')}</td>
109 <td>${h.radio('perm_new_user','repository.read')}</td>
110 <td>${h.radio('perm_new_user','repository.write')}</td>
111 <td>${h.radio('perm_new_user','repository.admin')}</td>
112 <td class='ac'>
113 <div class="perm_ac" id="perm_ac">
114 ${h.text('perm_new_user_name',class_='yui-ac-input')}
115 <div id="perm_container"></div>
116 </div>
117 </td>
118 <td></td>
119 </tr>
120 <tr>
121 <td colspan="6">
122 <span id="add_perm" class="add_icon" style="cursor: pointer;">
123 ${_('Add another user')}
124 </span>
125 </td>
126 </tr>
127 </table>
59 <%include file="../admin/repos/repo_edit_perms.html"/>
128 60 </div>
129 61
130 62 <div class="buttons">
131 63 ${h.submit('update','Update',class_="ui-button")}
132 64 ${h.reset('reset','Reset',class_="ui-button")}
133 65 </div>
134 66 </div>
135 67 </div>
136 68 ${h.end_form()}
137 69 <script type="text/javascript">
138 70 YAHOO.util.Event.onDOMReady(function(){
139 71 var D = YAHOO.util.Dom;
140 72 if(!D.hasClass('perm_new_user_name','error')){
141 73 D.setStyle('add_perm_input','display','none');
142 74 }
143 75 YAHOO.util.Event.addListener('add_perm','click',function(){
144 76 D.setStyle('add_perm_input','display','');
145 77 D.setStyle('add_perm','opacity','0.6');
146 78 D.setStyle('add_perm','cursor','default');
147 79 });
148 80 });
149 81 </script>
150 82 <script type="text/javascript">
151 83 YAHOO.example.FnMultipleFields = function(){
152 84 var myContacts = ${c.users_array|n}
153 85
154 86 // Define a custom search function for the DataSource
155 87 var matchNames = function(sQuery) {
156 88 // Case insensitive matching
157 89 var query = sQuery.toLowerCase(),
158 90 contact,
159 91 i=0,
160 92 l=myContacts.length,
161 93 matches = [];
162 94
163 95 // Match against each name of each contact
164 96 for(; i<l; i++) {
165 97 contact = myContacts[i];
166 98 if((contact.fname.toLowerCase().indexOf(query) > -1) ||
167 99 (contact.lname.toLowerCase().indexOf(query) > -1) ||
168 100 (contact.nname && (contact.nname.toLowerCase().indexOf(query) > -1))) {
169 101 matches[matches.length] = contact;
170 102 }
171 103 }
172 104
173 105 return matches;
174 106 };
175 107
176 108 // Use a FunctionDataSource
177 109 var oDS = new YAHOO.util.FunctionDataSource(matchNames);
178 110 oDS.responseSchema = {
179 111 fields: ["id", "fname", "lname", "nname"]
180 112 }
181 113
182 114 // Instantiate AutoComplete for perms
183 115 var oAC_perms = new YAHOO.widget.AutoComplete("perm_new_user_name", "perm_container", oDS);
184 116 oAC_perms.useShadow = false;
185 117 oAC_perms.resultTypeList = false;
186 118
187 119 // Instantiate AutoComplete for owner
188 120 var oAC_owner = new YAHOO.widget.AutoComplete("user", "owner_container", oDS);
189 121 oAC_owner.useShadow = false;
190 122 oAC_owner.resultTypeList = false;
191 123
192 124
193 125 // Custom formatter to highlight the matching letters
194 126 var custom_formatter = function(oResultData, sQuery, sResultMatch) {
195 127 var query = sQuery.toLowerCase(),
196 128 fname = oResultData.fname,
197 129 lname = oResultData.lname,
198 130 nname = oResultData.nname || "", // Guard against null value
199 131 query = sQuery.toLowerCase(),
200 132 fnameMatchIndex = fname.toLowerCase().indexOf(query),
201 133 lnameMatchIndex = lname.toLowerCase().indexOf(query),
202 134 nnameMatchIndex = nname.toLowerCase().indexOf(query),
203 135 displayfname, displaylname, displaynname;
204 136
205 137 if(fnameMatchIndex > -1) {
206 138 displayfname = highlightMatch(fname, query, fnameMatchIndex);
207 139 }
208 140 else {
209 141 displayfname = fname;
210 142 }
211 143
212 144 if(lnameMatchIndex > -1) {
213 145 displaylname = highlightMatch(lname, query, lnameMatchIndex);
214 146 }
215 147 else {
216 148 displaylname = lname;
217 149 }
218 150
219 151 if(nnameMatchIndex > -1) {
220 152 displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")";
221 153 }
222 154 else {
223 155 displaynname = nname ? "(" + nname + ")" : "";
224 156 }
225 157
226 158 return displayfname + " " + displaylname + " " + displaynname;
227 159
228 160 };
229 161 oAC_perms.formatResult = custom_formatter;
230 162 oAC_owner.formatResult = custom_formatter;
231 163
232 164 // Helper function for the formatter
233 165 var highlightMatch = function(full, snippet, matchindex) {
234 166 return full.substring(0, matchindex) +
235 167 "<span class='match'>" +
236 168 full.substr(matchindex, snippet.length) +
237 169 "</span>" +
238 170 full.substring(matchindex + snippet.length);
239 171 };
240 172
241 173 var myHandler = function(sType, aArgs) {
242 174 var myAC = aArgs[0]; // reference back to the AC instance
243 175 var elLI = aArgs[1]; // reference to the selected LI element
244 176 var oData = aArgs[2]; // object literal of selected item's result data
245 177 myAC.getInputEl().value = oData.nname;
246 178 };
247 179
248 180 oAC_perms.itemSelectEvent.subscribe(myHandler);
249 181 //oAC_owner.itemSelectEvent.subscribe(myHandler);
250 182
251 183 return {
252 184 oDS: oDS,
253 185 oAC_perms: oAC_perms,
254 186 oAC_owner: oAC_owner,
255 187 };
256 188 }();
257 189
258 190 </script>
259 191 </div>
260 192 </div>
261 193 </%def>
262 194
263 195
General Comments 0
You need to be logged in to leave comments. Login now