##// END OF EJS Templates
Fixes #18, removing user, who owns some repositories
marcink -
r376:7fbf8144 default
parent child Browse files
Show More
@@ -1,211 +1,218 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # repos controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 7, 2010
22 22 admin controller for pylons
23 23 @author: marcink
24 24 """
25 25 from formencode import htmlfill
26 26 from operator import itemgetter
27 from paste.httpexceptions import HTTPInternalServerError
27 28 from pylons import request, response, session, tmpl_context as c, url
28 29 from pylons.controllers.util import abort, redirect
29 30 from pylons.i18n.translation import _
30 31 from pylons_app.lib import helpers as h
31 32 from pylons_app.lib.auth import LoginRequired, HasPermissionAllDecorator
32 33 from pylons_app.lib.base import BaseController, render
33 34 from pylons_app.lib.utils import invalidate_cache
35 from pylons_app.model.db import User
34 36 from pylons_app.model.forms import RepoForm
35 37 from pylons_app.model.hg_model import HgModel
36 38 from pylons_app.model.repo_model import RepoModel
37 39 import formencode
38 40 import logging
39 41 import traceback
40 from paste.httpexceptions import HTTPInternalServerError
41 42
42 43 log = logging.getLogger(__name__)
43 44
44 45 class ReposController(BaseController):
45 46 """REST Controller styled on the Atom Publishing Protocol"""
46 47 # To properly map this controller, ensure your config/routing.py
47 48 # file has a resource setup:
48 49 # map.resource('repo', 'repos')
49 50
50 51 @LoginRequired()
51 52 @HasPermissionAllDecorator('hg.admin')
52 53 def __before__(self):
53 54 c.admin_user = session.get('admin_user')
54 55 c.admin_username = session.get('admin_username')
55 56 super(ReposController, self).__before__()
56 57
57 58 def index(self, format='html'):
58 59 """GET /repos: All items in the collection"""
59 60 # url('repos')
60 61 cached_repo_list = HgModel().get_repos()
61 62 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
62 63 return render('admin/repos/repos.html')
63 64
64 65 def create(self):
65 66 """POST /repos: Create a new item"""
66 67 # url('repos')
67 68 repo_model = RepoModel()
68 69 _form = RepoForm()()
69 70 form_result = {}
70 71 try:
71 72 form_result = _form.to_python(dict(request.POST))
72 73 repo_model.create(form_result, c.hg_app_user)
73 74 invalidate_cache('cached_repo_list')
74 75 h.flash(_('created repository %s') % form_result['repo_name'],
75 76 category='success')
76 77
77 78 except formencode.Invalid as errors:
78 79 c.new_repo = errors.value['repo_name']
79 80 return htmlfill.render(
80 81 render('admin/repos/repo_add.html'),
81 82 defaults=errors.value,
82 83 errors=errors.error_dict or {},
83 84 prefix_error=False,
84 85 encoding="UTF-8")
85 86
86 87 except Exception:
87 88 log.error(traceback.format_exc())
88 89 msg = _('error occured during creation of repository %s') \
89 90 % form_result.get('repo_name')
90 91 h.flash(msg, category='error')
91 92
92 93 return redirect('repos')
93 94
94 95 def new(self, format='html'):
95 96 """GET /repos/new: Form to create a new item"""
96 97 new_repo = request.GET.get('repo', '')
97 98 c.new_repo = h.repo_name_slug(new_repo)
98 99
99 100 return render('admin/repos/repo_add.html')
100 101
101 102 def update(self, repo_name):
102 103 """PUT /repos/repo_name: Update an existing item"""
103 104 # Forms posted to this method should contain a hidden field:
104 105 # <input type="hidden" name="_method" value="PUT" />
105 106 # Or using helpers:
106 107 # h.form(url('repo', repo_name=ID),
107 108 # method='put')
108 109 # url('repo', repo_name=ID)
109 110 repo_model = RepoModel()
110 111 changed_name = repo_name
111 112 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
112 113
113 114 try:
114 115 form_result = _form.to_python(dict(request.POST))
115 116 repo_model.update(repo_name, form_result)
116 117 invalidate_cache('cached_repo_list')
117 118 h.flash(_('Repository %s updated succesfully' % repo_name),
118 119 category='success')
119 120 changed_name = form_result['repo_name']
120 121 except formencode.Invalid as errors:
121 122 c.repo_info = repo_model.get(repo_name)
122 123 c.users_array = repo_model.get_users_js()
123 124 errors.value.update({'user':c.repo_info.user.username})
124 125 return htmlfill.render(
125 126 render('admin/repos/repo_edit.html'),
126 127 defaults=errors.value,
127 128 errors=errors.error_dict or {},
128 129 prefix_error=False,
129 130 encoding="UTF-8")
130 131
131 132 except Exception:
132 133 log.error(traceback.format_exc())
133 134 h.flash(_('error occured during update of repository %s') \
134 135 % repo_name, category='error')
135 136
136 137 return redirect(url('edit_repo', repo_name=changed_name))
137 138
138 139 def delete(self, repo_name):
139 140 """DELETE /repos/repo_name: Delete an existing item"""
140 141 # Forms posted to this method should contain a hidden field:
141 142 # <input type="hidden" name="_method" value="DELETE" />
142 143 # Or using helpers:
143 144 # h.form(url('repo', repo_name=ID),
144 145 # method='delete')
145 146 # url('repo', repo_name=ID)
146 147
147 148 repo_model = RepoModel()
148 149 repo = repo_model.get(repo_name)
149 150 if not repo:
150 151 h.flash(_('%s repository is not mapped to db perhaps'
151 152 ' it was moved or renamed from the filesystem'
152 153 ' please run the application again'
153 154 ' in order to rescan repositories') % repo_name,
154 155 category='error')
155 156
156 157 return redirect(url('repos'))
157 158 try:
158 159 repo_model.delete(repo)
159 160 invalidate_cache('cached_repo_list')
160 161 h.flash(_('deleted repository %s') % repo_name, category='success')
161 162 except Exception:
162 163 h.flash(_('An error occured during deletion of %s') % repo_name,
163 164 category='error')
164 165
165 166 return redirect(url('repos'))
166 167
167 168 def delete_perm_user(self, repo_name):
168 169 """
169 170 DELETE an existing repository permission user
170 171 @param repo_name:
171 172 """
172 173
173 174 try:
174 175 repo_model = RepoModel()
175 176 repo_model.delete_perm_user(request.POST, repo_name)
176 177 except Exception as e:
177 178 h.flash(_('An error occured during deletion of repository user'),
178 179 category='error')
179 180 raise HTTPInternalServerError()
180 181
181 182 def show(self, repo_name, format='html'):
182 183 """GET /repos/repo_name: Show a specific item"""
183 184 # url('repo', repo_name=ID)
184 185
185 186 def edit(self, repo_name, format='html'):
186 187 """GET /repos/repo_name/edit: Form to edit an existing item"""
187 188 # url('edit_repo', repo_name=ID)
188 189 repo_model = RepoModel()
189 190 c.repo_info = repo = repo_model.get(repo_name)
190 191 if not repo:
191 192 h.flash(_('%s repository is not mapped to db perhaps'
192 193 ' it was created or renamed from the filesystem'
193 194 ' please run the application again'
194 195 ' in order to rescan repositories') % repo_name,
195 196 category='error')
196 197
197 198 return redirect(url('repos'))
198 199 defaults = c.repo_info.__dict__
199 defaults.update({'user':c.repo_info.user.username})
200 if c.repo_info.user:
201 defaults.update({'user':c.repo_info.user.username})
202 else:
203 replacement_user = self.sa.query(User)\
204 .filter(User.admin == True).first().username
205 defaults.update({'user':replacement_user})
206
200 207 c.users_array = repo_model.get_users_js()
201 208
202 209 for p in c.repo_info.repo2perm:
203 210 defaults.update({'perm_%s' % p.user.username:
204 211 p.permission.permission_name})
205 212
206 213 return htmlfill.render(
207 214 render('admin/repos/repo_edit.html'),
208 215 defaults=defaults,
209 216 encoding="UTF-8",
210 217 force_defaults=False
211 218 )
@@ -1,174 +1,178 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # Model for hg app
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 9, 2010
22 22 Model for hg app
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region
26 26 from mercurial import ui
27 27 from mercurial.hgweb.hgwebdir_mod import findrepos
28 28 from pylons.i18n.translation import _
29 29 from pylons_app.lib.auth import HasRepoPermissionAny
30 30 from pylons_app.model import meta
31 from pylons_app.model.db import Repository
31 from pylons_app.model.db import Repository, User
32 32 from sqlalchemy.orm import joinedload
33 33 from vcs.exceptions import RepositoryError, VCSError
34 34 import logging
35 35 import os
36 36 import sys
37 37 log = logging.getLogger(__name__)
38 38
39 39 try:
40 40 from vcs.backends.hg import MercurialRepository
41 41 except ImportError:
42 42 sys.stderr.write('You have to import vcs module')
43 43 raise Exception('Unable to import vcs')
44 44
45 45 def _get_repos_cached_initial(app_globals, initial):
46 46 """
47 47 return cached dict with repos
48 48 """
49 49 g = app_globals
50 50 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui, initial)
51 51
52 52 @cache_region('long_term', 'cached_repo_list')
53 53 def _get_repos_cached():
54 54 """
55 55 return cached dict with repos
56 56 """
57 57 log.info('getting all repositories list')
58 58 from pylons import app_globals as g
59 59 return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
60 60
61 61 @cache_region('super_short_term', 'cached_repos_switcher_list')
62 62 def _get_repos_switcher_cached(cached_repo_list):
63 63 repos_lst = []
64 64 for repo in sorted(x.name.lower() for x in cached_repo_list.values()):
65 65 if HasRepoPermissionAny('repository.write', 'repository.read', 'repository.admin')(repo, 'main page check'):
66 66 repos_lst.append(repo)
67 67
68 68 return repos_lst
69 69
70 70 @cache_region('long_term', 'full_changelog')
71 71 def _full_changelog_cached(repo_name):
72 72 log.info('getting full changelog for %s', repo_name)
73 73 return list(reversed(list(HgModel().get_repo(repo_name))))
74 74
75 75 class HgModel(object):
76 76 """
77 77 Mercurial Model
78 78 """
79 79
80 80 def __init__(self):
81 81 """
82 82 Constructor
83 83 """
84 84
85 85 @staticmethod
86 86 def repo_scan(repos_prefix, repos_path, baseui, initial=False):
87 87 """
88 88 Listing of repositories in given path. This path should not be a
89 89 repository itself. Return a dictionary of repository objects
90 90 :param repos_path: path to directory it could take syntax with
91 91 * or ** for deep recursive displaying repositories
92 92 """
93 93 sa = meta.Session()
94 94 def check_repo_dir(path):
95 95 """
96 96 Checks the repository
97 97 :param path:
98 98 """
99 99 repos_path = path.split('/')
100 100 if repos_path[-1] in ['*', '**']:
101 101 repos_path = repos_path[:-1]
102 102 if repos_path[0] != '/':
103 103 repos_path[0] = '/'
104 104 if not os.path.isdir(os.path.join(*repos_path)):
105 105 raise RepositoryError('Not a valid repository in %s' % path[0][1])
106 106 if not repos_path.endswith('*'):
107 107 raise VCSError('You need to specify * or ** at the end of path '
108 108 'for recursive scanning')
109 109
110 110 check_repo_dir(repos_path)
111 111 log.info('scanning for repositories in %s', repos_path)
112 112 repos = findrepos([(repos_prefix, repos_path)])
113 113 if not isinstance(baseui, ui.ui):
114 114 baseui = ui.ui()
115 115
116 116 repos_list = {}
117 117 for name, path in repos:
118 118 try:
119 119 #name = name.split('/')[-1]
120 120 if repos_list.has_key(name):
121 121 raise RepositoryError('Duplicate repository name %s found in'
122 122 ' %s' % (name, path))
123 123 else:
124 124
125 125 repos_list[name] = MercurialRepository(path, baseui=baseui)
126 126 repos_list[name].name = name
127 127
128 128 dbrepo = None
129 129 if not initial:
130 130 dbrepo = sa.query(Repository)\
131 131 .filter(Repository.repo_name == name).scalar()
132 132
133 133 if dbrepo:
134 134 log.info('Adding db instance to cached list')
135 135 repos_list[name].dbrepo = dbrepo
136 136 repos_list[name].description = dbrepo.description
137 repos_list[name].contact = dbrepo.user.full_contact
137 if dbrepo.user:
138 repos_list[name].contact = dbrepo.user.full_contact
139 else:
140 repos_list[name].contact = sa.query(User)\
141 .filter(User.admin == True).first().full_contact
138 142 except OSError:
139 143 continue
140 144 meta.Session.remove()
141 145 return repos_list
142 146
143 147 def get_repos(self):
144 148 for name, repo in _get_repos_cached().items():
145 149 if repo._get_hidden():
146 150 #skip hidden web repository
147 151 continue
148 152
149 153 last_change = repo.last_change
150 154 try:
151 155 tip = repo.get_changeset('tip')
152 156 except RepositoryError:
153 157 from pylons_app.lib.utils import EmptyChangeset
154 158 tip = EmptyChangeset()
155 159
156 160 tmp_d = {}
157 161 tmp_d['name'] = repo.name
158 162 tmp_d['name_sort'] = tmp_d['name'].lower()
159 163 tmp_d['description'] = repo.description
160 164 tmp_d['description_sort'] = tmp_d['description']
161 165 tmp_d['last_change'] = last_change
162 166 tmp_d['last_change_sort'] = last_change[1] - last_change[0]
163 167 tmp_d['tip'] = tip.raw_id
164 168 tmp_d['tip_sort'] = tip.revision
165 169 tmp_d['rev'] = tip.revision
166 170 tmp_d['contact'] = repo.contact
167 171 tmp_d['contact_sort'] = tmp_d['contact']
168 172 tmp_d['repo_archives'] = list(repo._get_archives())
169 173 tmp_d['last_msg'] = tip.message
170 174 tmp_d['repo'] = repo
171 175 yield tmp_d
172 176
173 177 def get_repo(self, repo_name):
174 178 return _get_repos_cached()[repo_name]
General Comments 0
You need to be logged in to leave comments. Login now