##// END OF EJS Templates
#48 rewrote action logger, translated action logger messages, added some extra messages. Linked and showed pushed revisions in logs
marcink -
r660:df613780 beta
parent child Browse files
Show More
@@ -1,245 +1,248 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # repos controller for pylons
3 # repos controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 7, 2010
21 Created on April 7, 2010
22 admin controller for pylons
22 admin controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from operator import itemgetter
26 from operator import itemgetter
27 from paste.httpexceptions import HTTPInternalServerError
27 from paste.httpexceptions import HTTPInternalServerError
28 from pylons import request, response, session, tmpl_context as c, url
28 from pylons import request, response, session, tmpl_context as c, url
29 from pylons.controllers.util import abort, redirect
29 from pylons.controllers.util import abort, redirect
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
33 HasPermissionAnyDecorator
33 HasPermissionAnyDecorator
34 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.utils import invalidate_cache, action_logger
35 from rhodecode.lib.utils import invalidate_cache, action_logger
36 from rhodecode.model.db import User
36 from rhodecode.model.db import User
37 from rhodecode.model.forms import RepoForm
37 from rhodecode.model.forms import RepoForm
38 from rhodecode.model.hg import HgModel
38 from rhodecode.model.hg import HgModel
39 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
40 import formencode
40 import formencode
41 import logging
41 import logging
42 import traceback
42 import traceback
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class ReposController(BaseController):
46 class ReposController(BaseController):
47 """REST Controller styled on the Atom Publishing Protocol"""
47 """REST Controller styled on the Atom Publishing Protocol"""
48 # To properly map this controller, ensure your config/routing.py
48 # To properly map this controller, ensure your config/routing.py
49 # file has a resource setup:
49 # file has a resource setup:
50 # map.resource('repo', 'repos')
50 # map.resource('repo', 'repos')
51
51
52 @LoginRequired()
52 @LoginRequired()
53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
54 def __before__(self):
54 def __before__(self):
55 c.admin_user = session.get('admin_user')
55 c.admin_user = session.get('admin_user')
56 c.admin_username = session.get('admin_username')
56 c.admin_username = session.get('admin_username')
57 super(ReposController, self).__before__()
57 super(ReposController, self).__before__()
58
58
59 @HasPermissionAllDecorator('hg.admin')
59 @HasPermissionAllDecorator('hg.admin')
60 def index(self, format='html'):
60 def index(self, format='html'):
61 """GET /repos: All items in the collection"""
61 """GET /repos: All items in the collection"""
62 # url('repos')
62 # url('repos')
63 cached_repo_list = HgModel().get_repos()
63 cached_repo_list = HgModel().get_repos()
64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
65 return render('admin/repos/repos.html')
65 return render('admin/repos/repos.html')
66
66
67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
68 def create(self):
68 def create(self):
69 """POST /repos: Create a new item"""
69 """POST /repos: Create a new item"""
70 # url('repos')
70 # url('repos')
71 repo_model = RepoModel()
71 repo_model = RepoModel()
72 _form = RepoForm()()
72 _form = RepoForm()()
73 form_result = {}
73 form_result = {}
74 try:
74 try:
75 form_result = _form.to_python(dict(request.POST))
75 form_result = _form.to_python(dict(request.POST))
76 repo_model.create(form_result, c.rhodecode_user)
76 repo_model.create(form_result, c.rhodecode_user)
77 invalidate_cache('cached_repo_list')
77 invalidate_cache('cached_repo_list')
78 h.flash(_('created repository %s') % form_result['repo_name'],
78 h.flash(_('created repository %s') % form_result['repo_name'],
79 category='success')
79 category='success')
80
80
81 if request.POST.get('user_created'):
81 if request.POST.get('user_created'):
82 action_logger(self.rhodecode_user, 'user_created_repo',
82 action_logger(self.rhodecode_user, 'user_created_repo',
83 form_result['repo_name'], '', self.sa)
83 form_result['repo_name'], '', self.sa)
84 else:
84 else:
85 action_logger(self.rhodecode_user, 'admin_created_repo',
85 action_logger(self.rhodecode_user, 'admin_created_repo',
86 form_result['repo_name'], '', self.sa)
86 form_result['repo_name'], '', self.sa)
87
87
88 except formencode.Invalid, errors:
88 except formencode.Invalid, errors:
89 c.new_repo = errors.value['repo_name']
89 c.new_repo = errors.value['repo_name']
90
90
91 if request.POST.get('user_created'):
91 if request.POST.get('user_created'):
92 r = render('admin/repos/repo_add_create_repository.html')
92 r = render('admin/repos/repo_add_create_repository.html')
93 else:
93 else:
94 r = render('admin/repos/repo_add.html')
94 r = render('admin/repos/repo_add.html')
95
95
96 return htmlfill.render(
96 return htmlfill.render(
97 r,
97 r,
98 defaults=errors.value,
98 defaults=errors.value,
99 errors=errors.error_dict or {},
99 errors=errors.error_dict or {},
100 prefix_error=False,
100 prefix_error=False,
101 encoding="UTF-8")
101 encoding="UTF-8")
102
102
103 except Exception:
103 except Exception:
104 log.error(traceback.format_exc())
104 log.error(traceback.format_exc())
105 msg = _('error occured during creation of repository %s') \
105 msg = _('error occured during creation of repository %s') \
106 % form_result.get('repo_name')
106 % form_result.get('repo_name')
107 h.flash(msg, category='error')
107 h.flash(msg, category='error')
108 if request.POST.get('user_created'):
108 if request.POST.get('user_created'):
109 return redirect(url('home'))
109 return redirect(url('home'))
110 return redirect(url('repos'))
110 return redirect(url('repos'))
111
111
112 @HasPermissionAllDecorator('hg.admin')
112 @HasPermissionAllDecorator('hg.admin')
113 def new(self, format='html'):
113 def new(self, format='html'):
114 """GET /repos/new: Form to create a new item"""
114 """GET /repos/new: Form to create a new item"""
115 new_repo = request.GET.get('repo', '')
115 new_repo = request.GET.get('repo', '')
116 c.new_repo = h.repo_name_slug(new_repo)
116 c.new_repo = h.repo_name_slug(new_repo)
117
117
118 return render('admin/repos/repo_add.html')
118 return render('admin/repos/repo_add.html')
119
119
120 @HasPermissionAllDecorator('hg.admin')
120 @HasPermissionAllDecorator('hg.admin')
121 def update(self, repo_name):
121 def update(self, repo_name):
122 """PUT /repos/repo_name: Update an existing item"""
122 """PUT /repos/repo_name: Update an existing item"""
123 # Forms posted to this method should contain a hidden field:
123 # Forms posted to this method should contain a hidden field:
124 # <input type="hidden" name="_method" value="PUT" />
124 # <input type="hidden" name="_method" value="PUT" />
125 # Or using helpers:
125 # Or using helpers:
126 # h.form(url('repo', repo_name=ID),
126 # h.form(url('repo', repo_name=ID),
127 # method='put')
127 # method='put')
128 # url('repo', repo_name=ID)
128 # url('repo', repo_name=ID)
129 repo_model = RepoModel()
129 repo_model = RepoModel()
130 changed_name = repo_name
130 changed_name = repo_name
131 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
131 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
132
132
133 try:
133 try:
134 form_result = _form.to_python(dict(request.POST))
134 form_result = _form.to_python(dict(request.POST))
135 repo_model.update(repo_name, form_result)
135 repo_model.update(repo_name, form_result)
136 invalidate_cache('cached_repo_list')
136 invalidate_cache('cached_repo_list')
137 h.flash(_('Repository %s updated succesfully' % repo_name),
137 h.flash(_('Repository %s updated succesfully' % repo_name),
138 category='success')
138 category='success')
139 changed_name = form_result['repo_name']
139 changed_name = form_result['repo_name']
140 action_logger(self.rhodecode_user, 'admin_updated_repo',
141 changed_name, '', self.sa)
142
140 except formencode.Invalid, errors:
143 except formencode.Invalid, errors:
141 c.repo_info = repo_model.get(repo_name)
144 c.repo_info = repo_model.get(repo_name)
142 c.users_array = repo_model.get_users_js()
145 c.users_array = repo_model.get_users_js()
143 errors.value.update({'user':c.repo_info.user.username})
146 errors.value.update({'user':c.repo_info.user.username})
144 return htmlfill.render(
147 return htmlfill.render(
145 render('admin/repos/repo_edit.html'),
148 render('admin/repos/repo_edit.html'),
146 defaults=errors.value,
149 defaults=errors.value,
147 errors=errors.error_dict or {},
150 errors=errors.error_dict or {},
148 prefix_error=False,
151 prefix_error=False,
149 encoding="UTF-8")
152 encoding="UTF-8")
150
153
151 except Exception:
154 except Exception:
152 log.error(traceback.format_exc())
155 log.error(traceback.format_exc())
153 h.flash(_('error occured during update of repository %s') \
156 h.flash(_('error occured during update of repository %s') \
154 % repo_name, category='error')
157 % repo_name, category='error')
155
158
156 return redirect(url('edit_repo', repo_name=changed_name))
159 return redirect(url('edit_repo', repo_name=changed_name))
157
160
158 @HasPermissionAllDecorator('hg.admin')
161 @HasPermissionAllDecorator('hg.admin')
159 def delete(self, repo_name):
162 def delete(self, repo_name):
160 """DELETE /repos/repo_name: Delete an existing item"""
163 """DELETE /repos/repo_name: Delete an existing item"""
161 # Forms posted to this method should contain a hidden field:
164 # Forms posted to this method should contain a hidden field:
162 # <input type="hidden" name="_method" value="DELETE" />
165 # <input type="hidden" name="_method" value="DELETE" />
163 # Or using helpers:
166 # Or using helpers:
164 # h.form(url('repo', repo_name=ID),
167 # h.form(url('repo', repo_name=ID),
165 # method='delete')
168 # method='delete')
166 # url('repo', repo_name=ID)
169 # url('repo', repo_name=ID)
167
170
168 repo_model = RepoModel()
171 repo_model = RepoModel()
169 repo = repo_model.get(repo_name)
172 repo = repo_model.get(repo_name)
170 if not repo:
173 if not repo:
171 h.flash(_('%s repository is not mapped to db perhaps'
174 h.flash(_('%s repository is not mapped to db perhaps'
172 ' it was moved or renamed from the filesystem'
175 ' it was moved or renamed from the filesystem'
173 ' please run the application again'
176 ' please run the application again'
174 ' in order to rescan repositories') % repo_name,
177 ' in order to rescan repositories') % repo_name,
175 category='error')
178 category='error')
176
179
177 return redirect(url('repos'))
180 return redirect(url('repos'))
178 try:
181 try:
179 action_logger(self.rhodecode_user, 'admin_deleted_repo',
182 action_logger(self.rhodecode_user, 'admin_deleted_repo',
180 repo_name, '', self.sa)
183 repo_name, '', self.sa)
181 repo_model.delete(repo)
184 repo_model.delete(repo)
182 invalidate_cache('cached_repo_list')
185 invalidate_cache('cached_repo_list')
183 h.flash(_('deleted repository %s') % repo_name, category='success')
186 h.flash(_('deleted repository %s') % repo_name, category='success')
184
187
185 except Exception, e:
188 except Exception, e:
186 log.error(traceback.format_exc())
189 log.error(traceback.format_exc())
187 h.flash(_('An error occured during deletion of %s') % repo_name,
190 h.flash(_('An error occured during deletion of %s') % repo_name,
188 category='error')
191 category='error')
189
192
190 return redirect(url('repos'))
193 return redirect(url('repos'))
191
194
192 @HasPermissionAllDecorator('hg.admin')
195 @HasPermissionAllDecorator('hg.admin')
193 def delete_perm_user(self, repo_name):
196 def delete_perm_user(self, repo_name):
194 """
197 """
195 DELETE an existing repository permission user
198 DELETE an existing repository permission user
196 :param repo_name:
199 :param repo_name:
197 """
200 """
198
201
199 try:
202 try:
200 repo_model = RepoModel()
203 repo_model = RepoModel()
201 repo_model.delete_perm_user(request.POST, repo_name)
204 repo_model.delete_perm_user(request.POST, repo_name)
202 except Exception, e:
205 except Exception, e:
203 h.flash(_('An error occured during deletion of repository user'),
206 h.flash(_('An error occured during deletion of repository user'),
204 category='error')
207 category='error')
205 raise HTTPInternalServerError()
208 raise HTTPInternalServerError()
206
209
207 @HasPermissionAllDecorator('hg.admin')
210 @HasPermissionAllDecorator('hg.admin')
208 def show(self, repo_name, format='html'):
211 def show(self, repo_name, format='html'):
209 """GET /repos/repo_name: Show a specific item"""
212 """GET /repos/repo_name: Show a specific item"""
210 # url('repo', repo_name=ID)
213 # url('repo', repo_name=ID)
211
214
212 @HasPermissionAllDecorator('hg.admin')
215 @HasPermissionAllDecorator('hg.admin')
213 def edit(self, repo_name, format='html'):
216 def edit(self, repo_name, format='html'):
214 """GET /repos/repo_name/edit: Form to edit an existing item"""
217 """GET /repos/repo_name/edit: Form to edit an existing item"""
215 # url('edit_repo', repo_name=ID)
218 # url('edit_repo', repo_name=ID)
216 repo_model = RepoModel()
219 repo_model = RepoModel()
217 c.repo_info = repo = repo_model.get(repo_name)
220 c.repo_info = repo = repo_model.get(repo_name)
218 if not repo:
221 if not repo:
219 h.flash(_('%s repository is not mapped to db perhaps'
222 h.flash(_('%s repository is not mapped to db perhaps'
220 ' it was created or renamed from the filesystem'
223 ' it was created or renamed from the filesystem'
221 ' please run the application again'
224 ' please run the application again'
222 ' in order to rescan repositories') % repo_name,
225 ' in order to rescan repositories') % repo_name,
223 category='error')
226 category='error')
224
227
225 return redirect(url('repos'))
228 return redirect(url('repos'))
226 defaults = c.repo_info.__dict__
229 defaults = c.repo_info.__dict__
227 if c.repo_info.user:
230 if c.repo_info.user:
228 defaults.update({'user':c.repo_info.user.username})
231 defaults.update({'user':c.repo_info.user.username})
229 else:
232 else:
230 replacement_user = self.sa.query(User)\
233 replacement_user = self.sa.query(User)\
231 .filter(User.admin == True).first().username
234 .filter(User.admin == True).first().username
232 defaults.update({'user':replacement_user})
235 defaults.update({'user':replacement_user})
233
236
234 c.users_array = repo_model.get_users_js()
237 c.users_array = repo_model.get_users_js()
235
238
236 for p in c.repo_info.repo_to_perm:
239 for p in c.repo_info.repo_to_perm:
237 defaults.update({'perm_%s' % p.user.username:
240 defaults.update({'perm_%s' % p.user.username:
238 p.permission.permission_name})
241 p.permission.permission_name})
239
242
240 return htmlfill.render(
243 return htmlfill.render(
241 render('admin/repos/repo_edit.html'),
244 render('admin/repos/repo_edit.html'),
242 defaults=defaults,
245 defaults=defaults,
243 encoding="UTF-8",
246 encoding="UTF-8",
244 force_defaults=False
247 force_defaults=False
245 )
248 )
@@ -1,175 +1,177 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # settings controller for pylons
3 # settings controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on June 30, 2010
21 Created on June 30, 2010
22 settings controller for pylons
22 settings controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from pylons import tmpl_context as c, request, url
26 from pylons import tmpl_context as c, request, url
27 from pylons.controllers.util import redirect
27 from pylons.controllers.util import redirect
28 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 from rhodecode.lib.base import BaseController, render
30 from rhodecode.lib.base import BaseController, render
31 from rhodecode.lib.utils import invalidate_cache, action_logger
31 from rhodecode.lib.utils import invalidate_cache, action_logger
32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
33 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo import RepoModel
34 import formencode
34 import formencode
35 import logging
35 import logging
36 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
37 import traceback
37 import traceback
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 class SettingsController(BaseController):
41 class SettingsController(BaseController):
42
42
43 @LoginRequired()
43 @LoginRequired()
44 @HasRepoPermissionAllDecorator('repository.admin')
44 @HasRepoPermissionAllDecorator('repository.admin')
45 def __before__(self):
45 def __before__(self):
46 super(SettingsController, self).__before__()
46 super(SettingsController, self).__before__()
47
47
48 def index(self, repo_name):
48 def index(self, repo_name):
49 repo_model = RepoModel()
49 repo_model = RepoModel()
50 c.repo_info = repo = repo_model.get(repo_name)
50 c.repo_info = repo = repo_model.get(repo_name)
51 if not repo:
51 if not repo:
52 h.flash(_('%s repository is not mapped to db perhaps'
52 h.flash(_('%s repository is not mapped to db perhaps'
53 ' it was created or renamed from the filesystem'
53 ' it was created or renamed from the filesystem'
54 ' please run the application again'
54 ' please run the application again'
55 ' in order to rescan repositories') % repo_name,
55 ' in order to rescan repositories') % repo_name,
56 category='error')
56 category='error')
57
57
58 return redirect(url('home'))
58 return redirect(url('home'))
59 defaults = c.repo_info.__dict__
59 defaults = c.repo_info.__dict__
60 defaults.update({'user':c.repo_info.user.username})
60 defaults.update({'user':c.repo_info.user.username})
61 c.users_array = repo_model.get_users_js()
61 c.users_array = repo_model.get_users_js()
62
62
63 for p in c.repo_info.repo_to_perm:
63 for p in c.repo_info.repo_to_perm:
64 defaults.update({'perm_%s' % p.user.username:
64 defaults.update({'perm_%s' % p.user.username:
65 p.permission.permission_name})
65 p.permission.permission_name})
66
66
67 return htmlfill.render(
67 return htmlfill.render(
68 render('settings/repo_settings.html'),
68 render('settings/repo_settings.html'),
69 defaults=defaults,
69 defaults=defaults,
70 encoding="UTF-8",
70 encoding="UTF-8",
71 force_defaults=False
71 force_defaults=False
72 )
72 )
73
73
74 def update(self, repo_name):
74 def update(self, repo_name):
75 repo_model = RepoModel()
75 repo_model = RepoModel()
76 changed_name = repo_name
76 changed_name = repo_name
77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
78 try:
78 try:
79 form_result = _form.to_python(dict(request.POST))
79 form_result = _form.to_python(dict(request.POST))
80 repo_model.update(repo_name, form_result)
80 repo_model.update(repo_name, form_result)
81 invalidate_cache('cached_repo_list')
81 invalidate_cache('cached_repo_list')
82 h.flash(_('Repository %s updated successfully' % repo_name),
82 h.flash(_('Repository %s updated successfully' % repo_name),
83 category='success')
83 category='success')
84 changed_name = form_result['repo_name']
84 changed_name = form_result['repo_name']
85 action_logger(self.rhodecode_user, 'user_updated_repo',
86 changed_name, '', self.sa)
85 except formencode.Invalid, errors:
87 except formencode.Invalid, errors:
86 c.repo_info = repo_model.get(repo_name)
88 c.repo_info = repo_model.get(repo_name)
87 c.users_array = repo_model.get_users_js()
89 c.users_array = repo_model.get_users_js()
88 errors.value.update({'user':c.repo_info.user.username})
90 errors.value.update({'user':c.repo_info.user.username})
89 return htmlfill.render(
91 return htmlfill.render(
90 render('settings/repo_settings.html'),
92 render('settings/repo_settings.html'),
91 defaults=errors.value,
93 defaults=errors.value,
92 errors=errors.error_dict or {},
94 errors=errors.error_dict or {},
93 prefix_error=False,
95 prefix_error=False,
94 encoding="UTF-8")
96 encoding="UTF-8")
95 except Exception:
97 except Exception:
96 log.error(traceback.format_exc())
98 log.error(traceback.format_exc())
97 h.flash(_('error occured during update of repository %s') \
99 h.flash(_('error occured during update of repository %s') \
98 % repo_name, category='error')
100 % repo_name, category='error')
99
101
100 return redirect(url('repo_settings_home', repo_name=changed_name))
102 return redirect(url('repo_settings_home', repo_name=changed_name))
101
103
102
104
103
105
104 def delete(self, repo_name):
106 def delete(self, repo_name):
105 """DELETE /repos/repo_name: Delete an existing item"""
107 """DELETE /repos/repo_name: Delete an existing item"""
106 # Forms posted to this method should contain a hidden field:
108 # Forms posted to this method should contain a hidden field:
107 # <input type="hidden" name="_method" value="DELETE" />
109 # <input type="hidden" name="_method" value="DELETE" />
108 # Or using helpers:
110 # Or using helpers:
109 # h.form(url('repo_settings_delete', repo_name=ID),
111 # h.form(url('repo_settings_delete', repo_name=ID),
110 # method='delete')
112 # method='delete')
111 # url('repo_settings_delete', repo_name=ID)
113 # url('repo_settings_delete', repo_name=ID)
112
114
113 repo_model = RepoModel()
115 repo_model = RepoModel()
114 repo = repo_model.get(repo_name)
116 repo = repo_model.get(repo_name)
115 if not repo:
117 if not repo:
116 h.flash(_('%s repository is not mapped to db perhaps'
118 h.flash(_('%s repository is not mapped to db perhaps'
117 ' it was moved or renamed from the filesystem'
119 ' it was moved or renamed from the filesystem'
118 ' please run the application again'
120 ' please run the application again'
119 ' in order to rescan repositories') % repo_name,
121 ' in order to rescan repositories') % repo_name,
120 category='error')
122 category='error')
121
123
122 return redirect(url('home'))
124 return redirect(url('home'))
123 try:
125 try:
124 action_logger(self.rhodecode_user, 'user_deleted_repo',
126 action_logger(self.rhodecode_user, 'user_deleted_repo',
125 repo_name, '', self.sa)
127 repo_name, '', self.sa)
126 repo_model.delete(repo)
128 repo_model.delete(repo)
127 invalidate_cache('cached_repo_list')
129 invalidate_cache('cached_repo_list')
128 h.flash(_('deleted repository %s') % repo_name, category='success')
130 h.flash(_('deleted repository %s') % repo_name, category='success')
129 except Exception:
131 except Exception:
130 h.flash(_('An error occurred during deletion of %s') % repo_name,
132 h.flash(_('An error occurred during deletion of %s') % repo_name,
131 category='error')
133 category='error')
132
134
133 return redirect(url('home'))
135 return redirect(url('home'))
134
136
135 def fork(self, repo_name):
137 def fork(self, repo_name):
136 repo_model = RepoModel()
138 repo_model = RepoModel()
137 c.repo_info = repo = repo_model.get(repo_name)
139 c.repo_info = repo = repo_model.get(repo_name)
138 if not repo:
140 if not repo:
139 h.flash(_('%s repository is not mapped to db perhaps'
141 h.flash(_('%s repository is not mapped to db perhaps'
140 ' it was created or renamed from the filesystem'
142 ' it was created or renamed from the filesystem'
141 ' please run the application again'
143 ' please run the application again'
142 ' in order to rescan repositories') % repo_name,
144 ' in order to rescan repositories') % repo_name,
143 category='error')
145 category='error')
144
146
145 return redirect(url('home'))
147 return redirect(url('home'))
146
148
147 return render('settings/repo_fork.html')
149 return render('settings/repo_fork.html')
148
150
149
151
150
152
151 def fork_create(self, repo_name):
153 def fork_create(self, repo_name):
152 repo_model = RepoModel()
154 repo_model = RepoModel()
153 c.repo_info = repo_model.get(repo_name)
155 c.repo_info = repo_model.get(repo_name)
154 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
156 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
155 form_result = {}
157 form_result = {}
156 try:
158 try:
157 form_result = _form.to_python(dict(request.POST))
159 form_result = _form.to_python(dict(request.POST))
158 form_result.update({'repo_name':repo_name})
160 form_result.update({'repo_name':repo_name})
159 repo_model.create_fork(form_result, c.rhodecode_user)
161 repo_model.create_fork(form_result, c.rhodecode_user)
160 h.flash(_('fork %s repository as %s task added') \
162 h.flash(_('fork %s repository as %s task added') \
161 % (repo_name, form_result['fork_name']),
163 % (repo_name, form_result['fork_name']),
162 category='success')
164 category='success')
163 action_logger(self.rhodecode_user, 'user_forked_repo',
165 action_logger(self.rhodecode_user, 'user_forked_repo',
164 repo_name, '', self.sa)
166 repo_name, '', self.sa)
165 except formencode.Invalid, errors:
167 except formencode.Invalid, errors:
166 c.new_repo = errors.value['fork_name']
168 c.new_repo = errors.value['fork_name']
167 r = render('settings/repo_fork.html')
169 r = render('settings/repo_fork.html')
168
170
169 return htmlfill.render(
171 return htmlfill.render(
170 r,
172 r,
171 defaults=errors.value,
173 defaults=errors.value,
172 errors=errors.error_dict or {},
174 errors=errors.error_dict or {},
173 prefix_error=False,
175 prefix_error=False,
174 encoding="UTF-8")
176 encoding="UTF-8")
175 return redirect(url('home'))
177 return redirect(url('home'))
@@ -1,401 +1,445 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 from pygments.formatters import HtmlFormatter
6 from pygments.formatters import HtmlFormatter
7 from pygments import highlight as code_highlight
7 from pygments import highlight as code_highlight
8 from pylons import url, app_globals as g
8 from pylons import url, app_globals as g
9 from pylons.i18n.translation import _, ungettext
9 from pylons.i18n.translation import _, ungettext
10 from vcs.utils.annotate import annotate_highlight
10 from vcs.utils.annotate import annotate_highlight
11 from webhelpers.html import literal, HTML, escape
11 from webhelpers.html import literal, HTML, escape
12 from webhelpers.html.tools import *
12 from webhelpers.html.tools import *
13 from webhelpers.html.builder import make_tag
13 from webhelpers.html.builder import make_tag
14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
17 password, textarea, title, ul, xml_declaration, radio
17 password, textarea, title, ul, xml_declaration, radio
18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
19 mail_to, strip_links, strip_tags, tag_re
19 mail_to, strip_links, strip_tags, tag_re
20 from webhelpers.number import format_byte_size, format_bit_size
20 from webhelpers.number import format_byte_size, format_bit_size
21 from webhelpers.pylonslib import Flash as _Flash
21 from webhelpers.pylonslib import Flash as _Flash
22 from webhelpers.pylonslib.secure_form import secure_form
22 from webhelpers.pylonslib.secure_form import secure_form
23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
25 replace_whitespace, urlify, truncate, wrap_paragraphs
25 replace_whitespace, urlify, truncate, wrap_paragraphs
26 from webhelpers.date import time_ago_in_words
26 from webhelpers.date import time_ago_in_words
27
27
28 #Custom helpers here :)
28 #Custom helpers here :)
29 class _Link(object):
29 class _Link(object):
30 '''
30 '''
31 Make a url based on label and url with help of url_for
31 Make a url based on label and url with help of url_for
32 :param label:name of link if not defined url is used
32 :param label:name of link if not defined url is used
33 :param url: the url for link
33 :param url: the url for link
34 '''
34 '''
35
35
36 def __call__(self, label='', *url_, **urlargs):
36 def __call__(self, label='', *url_, **urlargs):
37 if label is None or '':
37 if label is None or '':
38 label = url
38 label = url
39 link_fn = link_to(label, url(*url_, **urlargs))
39 link_fn = link_to(label, url(*url_, **urlargs))
40 return link_fn
40 return link_fn
41
41
42 link = _Link()
42 link = _Link()
43
43
44 class _GetError(object):
44 class _GetError(object):
45
45
46 def __call__(self, field_name, form_errors):
46 def __call__(self, field_name, form_errors):
47 tmpl = """<span class="error_msg">%s</span>"""
47 tmpl = """<span class="error_msg">%s</span>"""
48 if form_errors and form_errors.has_key(field_name):
48 if form_errors and form_errors.has_key(field_name):
49 return literal(tmpl % form_errors.get(field_name))
49 return literal(tmpl % form_errors.get(field_name))
50
50
51 get_error = _GetError()
51 get_error = _GetError()
52
52
53 def recursive_replace(str, replace=' '):
53 def recursive_replace(str, replace=' '):
54 """
54 """
55 Recursive replace of given sign to just one instance
55 Recursive replace of given sign to just one instance
56 :param str: given string
56 :param str: given string
57 :param replace:char to find and replace multiple instances
57 :param replace:char to find and replace multiple instances
58
58
59 Examples::
59 Examples::
60 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
60 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
61 'Mighty-Mighty-Bo-sstones'
61 'Mighty-Mighty-Bo-sstones'
62 """
62 """
63
63
64 if str.find(replace * 2) == -1:
64 if str.find(replace * 2) == -1:
65 return str
65 return str
66 else:
66 else:
67 str = str.replace(replace * 2, replace)
67 str = str.replace(replace * 2, replace)
68 return recursive_replace(str, replace)
68 return recursive_replace(str, replace)
69
69
70 class _ToolTip(object):
70 class _ToolTip(object):
71
71
72 def __call__(self, tooltip_title, trim_at=50):
72 def __call__(self, tooltip_title, trim_at=50):
73 """
73 """
74 Special function just to wrap our text into nice formatted autowrapped
74 Special function just to wrap our text into nice formatted autowrapped
75 text
75 text
76 :param tooltip_title:
76 :param tooltip_title:
77 """
77 """
78
78
79 return wrap_paragraphs(escape(tooltip_title), trim_at)\
79 return wrap_paragraphs(escape(tooltip_title), trim_at)\
80 .replace('\n', '<br/>')
80 .replace('\n', '<br/>')
81
81
82 def activate(self):
82 def activate(self):
83 """
83 """
84 Adds tooltip mechanism to the given Html all tooltips have to have
84 Adds tooltip mechanism to the given Html all tooltips have to have
85 set class tooltip and set attribute tooltip_title.
85 set class tooltip and set attribute tooltip_title.
86 Then a tooltip will be generated based on that
86 Then a tooltip will be generated based on that
87 All with yui js tooltip
87 All with yui js tooltip
88 """
88 """
89
89
90 js = '''
90 js = '''
91 YAHOO.util.Event.onDOMReady(function(){
91 YAHOO.util.Event.onDOMReady(function(){
92 function toolTipsId(){
92 function toolTipsId(){
93 var ids = [];
93 var ids = [];
94 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
94 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
95
95
96 for (var i = 0; i < tts.length; i++) {
96 for (var i = 0; i < tts.length; i++) {
97 //if element doesn not have and id autgenerate one for tooltip
97 //if element doesn not have and id autgenerate one for tooltip
98
98
99 if (!tts[i].id){
99 if (!tts[i].id){
100 tts[i].id='tt'+i*100;
100 tts[i].id='tt'+i*100;
101 }
101 }
102 ids.push(tts[i].id);
102 ids.push(tts[i].id);
103 }
103 }
104 return ids
104 return ids
105 };
105 };
106 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
106 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
107 context: toolTipsId(),
107 context: toolTipsId(),
108 monitorresize:false,
108 monitorresize:false,
109 xyoffset :[0,0],
109 xyoffset :[0,0],
110 autodismissdelay:300000,
110 autodismissdelay:300000,
111 hidedelay:5,
111 hidedelay:5,
112 showdelay:20,
112 showdelay:20,
113 });
113 });
114
114
115 //Mouse Over event disabled for new repositories since they dont
115 //Mouse Over event disabled for new repositories since they dont
116 //have last commit message
116 //have last commit message
117 myToolTips.contextMouseOverEvent.subscribe(
117 myToolTips.contextMouseOverEvent.subscribe(
118 function(type, args) {
118 function(type, args) {
119 var context = args[0];
119 var context = args[0];
120 var txt = context.getAttribute('tooltip_title');
120 var txt = context.getAttribute('tooltip_title');
121 if(txt){
121 if(txt){
122 return true;
122 return true;
123 }
123 }
124 else{
124 else{
125 return false;
125 return false;
126 }
126 }
127 });
127 });
128
128
129
129
130 // Set the text for the tooltip just before we display it. Lazy method
130 // Set the text for the tooltip just before we display it. Lazy method
131 myToolTips.contextTriggerEvent.subscribe(
131 myToolTips.contextTriggerEvent.subscribe(
132 function(type, args) {
132 function(type, args) {
133
133
134
134
135 var context = args[0];
135 var context = args[0];
136
136
137 var txt = context.getAttribute('tooltip_title');
137 var txt = context.getAttribute('tooltip_title');
138 this.cfg.setProperty("text", txt);
138 this.cfg.setProperty("text", txt);
139
139
140
140
141 // positioning of tooltip
141 // positioning of tooltip
142 var tt_w = this.element.clientWidth;
142 var tt_w = this.element.clientWidth;
143 var tt_h = this.element.clientHeight;
143 var tt_h = this.element.clientHeight;
144
144
145 var context_w = context.offsetWidth;
145 var context_w = context.offsetWidth;
146 var context_h = context.offsetHeight;
146 var context_h = context.offsetHeight;
147
147
148 var pos_x = YAHOO.util.Dom.getX(context);
148 var pos_x = YAHOO.util.Dom.getX(context);
149 var pos_y = YAHOO.util.Dom.getY(context);
149 var pos_y = YAHOO.util.Dom.getY(context);
150
150
151 var display_strategy = 'top';
151 var display_strategy = 'top';
152 var xy_pos = [0,0];
152 var xy_pos = [0,0];
153 switch (display_strategy){
153 switch (display_strategy){
154
154
155 case 'top':
155 case 'top':
156 var cur_x = (pos_x+context_w/2)-(tt_w/2);
156 var cur_x = (pos_x+context_w/2)-(tt_w/2);
157 var cur_y = pos_y-tt_h-4;
157 var cur_y = pos_y-tt_h-4;
158 xy_pos = [cur_x,cur_y];
158 xy_pos = [cur_x,cur_y];
159 break;
159 break;
160 case 'bottom':
160 case 'bottom':
161 var cur_x = (pos_x+context_w/2)-(tt_w/2);
161 var cur_x = (pos_x+context_w/2)-(tt_w/2);
162 var cur_y = pos_y+context_h+4;
162 var cur_y = pos_y+context_h+4;
163 xy_pos = [cur_x,cur_y];
163 xy_pos = [cur_x,cur_y];
164 break;
164 break;
165 case 'left':
165 case 'left':
166 var cur_x = (pos_x-tt_w-4);
166 var cur_x = (pos_x-tt_w-4);
167 var cur_y = pos_y-((tt_h/2)-context_h/2);
167 var cur_y = pos_y-((tt_h/2)-context_h/2);
168 xy_pos = [cur_x,cur_y];
168 xy_pos = [cur_x,cur_y];
169 break;
169 break;
170 case 'right':
170 case 'right':
171 var cur_x = (pos_x+context_w+4);
171 var cur_x = (pos_x+context_w+4);
172 var cur_y = pos_y-((tt_h/2)-context_h/2);
172 var cur_y = pos_y-((tt_h/2)-context_h/2);
173 xy_pos = [cur_x,cur_y];
173 xy_pos = [cur_x,cur_y];
174 break;
174 break;
175 default:
175 default:
176 var cur_x = (pos_x+context_w/2)-(tt_w/2);
176 var cur_x = (pos_x+context_w/2)-(tt_w/2);
177 var cur_y = pos_y-tt_h-4;
177 var cur_y = pos_y-tt_h-4;
178 xy_pos = [cur_x,cur_y];
178 xy_pos = [cur_x,cur_y];
179 break;
179 break;
180
180
181 }
181 }
182
182
183 this.cfg.setProperty("xy",xy_pos);
183 this.cfg.setProperty("xy",xy_pos);
184
184
185 });
185 });
186
186
187 //Mouse out
187 //Mouse out
188 myToolTips.contextMouseOutEvent.subscribe(
188 myToolTips.contextMouseOutEvent.subscribe(
189 function(type, args) {
189 function(type, args) {
190 var context = args[0];
190 var context = args[0];
191
191
192 });
192 });
193 });
193 });
194 '''
194 '''
195 return literal(js)
195 return literal(js)
196
196
197 tooltip = _ToolTip()
197 tooltip = _ToolTip()
198
198
199 class _FilesBreadCrumbs(object):
199 class _FilesBreadCrumbs(object):
200
200
201 def __call__(self, repo_name, rev, paths):
201 def __call__(self, repo_name, rev, paths):
202 url_l = [link_to(repo_name, url('files_home',
202 url_l = [link_to(repo_name, url('files_home',
203 repo_name=repo_name,
203 repo_name=repo_name,
204 revision=rev, f_path=''))]
204 revision=rev, f_path=''))]
205 paths_l = paths.split('/')
205 paths_l = paths.split('/')
206
206
207 for cnt, p in enumerate(paths_l, 1):
207 for cnt, p in enumerate(paths_l, 1):
208 if p != '':
208 if p != '':
209 url_l.append(link_to(p, url('files_home',
209 url_l.append(link_to(p, url('files_home',
210 repo_name=repo_name,
210 repo_name=repo_name,
211 revision=rev,
211 revision=rev,
212 f_path='/'.join(paths_l[:cnt]))))
212 f_path='/'.join(paths_l[:cnt]))))
213
213
214 return literal('/'.join(url_l))
214 return literal('/'.join(url_l))
215
215
216 files_breadcrumbs = _FilesBreadCrumbs()
216 files_breadcrumbs = _FilesBreadCrumbs()
217 class CodeHtmlFormatter(HtmlFormatter):
217 class CodeHtmlFormatter(HtmlFormatter):
218
218
219 def wrap(self, source, outfile):
219 def wrap(self, source, outfile):
220 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
220 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
221
221
222 def _wrap_code(self, source):
222 def _wrap_code(self, source):
223 for cnt, it in enumerate(source, 1):
223 for cnt, it in enumerate(source, 1):
224 i, t = it
224 i, t = it
225 t = '<div id="#S-%s">%s</div>' % (cnt, t)
225 t = '<div id="#S-%s">%s</div>' % (cnt, t)
226 yield i, t
226 yield i, t
227 def pygmentize(filenode, **kwargs):
227 def pygmentize(filenode, **kwargs):
228 """
228 """
229 pygmentize function using pygments
229 pygmentize function using pygments
230 :param filenode:
230 :param filenode:
231 """
231 """
232 return literal(code_highlight(filenode.content,
232 return literal(code_highlight(filenode.content,
233 filenode.lexer, CodeHtmlFormatter(**kwargs)))
233 filenode.lexer, CodeHtmlFormatter(**kwargs)))
234
234
235 def pygmentize_annotation(filenode, **kwargs):
235 def pygmentize_annotation(filenode, **kwargs):
236 """
236 """
237 pygmentize function for annotation
237 pygmentize function for annotation
238 :param filenode:
238 :param filenode:
239 """
239 """
240
240
241 color_dict = {}
241 color_dict = {}
242 def gen_color():
242 def gen_color():
243 """generator for getting 10k of evenly distibuted colors using hsv color
243 """generator for getting 10k of evenly distibuted colors using hsv color
244 and golden ratio.
244 and golden ratio.
245 """
245 """
246 import colorsys
246 import colorsys
247 n = 10000
247 n = 10000
248 golden_ratio = 0.618033988749895
248 golden_ratio = 0.618033988749895
249 h = 0.22717784590367374
249 h = 0.22717784590367374
250 #generate 10k nice web friendly colors in the same order
250 #generate 10k nice web friendly colors in the same order
251 for c in xrange(n):
251 for c in xrange(n):
252 h += golden_ratio
252 h += golden_ratio
253 h %= 1
253 h %= 1
254 HSV_tuple = [h, 0.95, 0.95]
254 HSV_tuple = [h, 0.95, 0.95]
255 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
255 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
256 yield map(lambda x:str(int(x * 256)), RGB_tuple)
256 yield map(lambda x:str(int(x * 256)), RGB_tuple)
257
257
258 cgenerator = gen_color()
258 cgenerator = gen_color()
259
259
260 def get_color_string(cs):
260 def get_color_string(cs):
261 if color_dict.has_key(cs):
261 if color_dict.has_key(cs):
262 col = color_dict[cs]
262 col = color_dict[cs]
263 else:
263 else:
264 col = color_dict[cs] = cgenerator.next()
264 col = color_dict[cs] = cgenerator.next()
265 return "color: rgb(%s)! important;" % (', '.join(col))
265 return "color: rgb(%s)! important;" % (', '.join(col))
266
266
267 def url_func(changeset):
267 def url_func(changeset):
268 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
268 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
269 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
269 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
270
270
271 tooltip_html = tooltip_html % (changeset.author,
271 tooltip_html = tooltip_html % (changeset.author,
272 changeset.date,
272 changeset.date,
273 tooltip(changeset.message))
273 tooltip(changeset.message))
274 lnk_format = 'r%-5s:%s' % (changeset.revision,
274 lnk_format = 'r%-5s:%s' % (changeset.revision,
275 changeset.raw_id)
275 changeset.raw_id)
276 uri = link_to(
276 uri = link_to(
277 lnk_format,
277 lnk_format,
278 url('changeset_home', repo_name=changeset.repository.name,
278 url('changeset_home', repo_name=changeset.repository.name,
279 revision=changeset.raw_id),
279 revision=changeset.raw_id),
280 style=get_color_string(changeset.raw_id),
280 style=get_color_string(changeset.raw_id),
281 class_='tooltip',
281 class_='tooltip',
282 tooltip_title=tooltip_html
282 tooltip_title=tooltip_html
283 )
283 )
284
284
285 uri += '\n'
285 uri += '\n'
286 return uri
286 return uri
287 return literal(annotate_highlight(filenode, url_func, **kwargs))
287 return literal(annotate_highlight(filenode, url_func, **kwargs))
288
288
289 def repo_name_slug(value):
289 def repo_name_slug(value):
290 """Return slug of name of repository
290 """Return slug of name of repository
291 This function is called on each creation/modification
291 This function is called on each creation/modification
292 of repository to prevent bad names in repo
292 of repository to prevent bad names in repo
293 """
293 """
294 slug = remove_formatting(value)
294 slug = remove_formatting(value)
295 slug = strip_tags(slug)
295 slug = strip_tags(slug)
296
296
297 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
297 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
298 slug = slug.replace(c, '-')
298 slug = slug.replace(c, '-')
299 slug = recursive_replace(slug, '-')
299 slug = recursive_replace(slug, '-')
300 slug = collapse(slug, '-')
300 slug = collapse(slug, '-')
301 return slug
301 return slug
302
302
303 def get_changeset_safe(repo, rev):
303 def get_changeset_safe(repo, rev):
304 from vcs.backends.base import BaseRepository
304 from vcs.backends.base import BaseRepository
305 from vcs.exceptions import RepositoryError
305 from vcs.exceptions import RepositoryError
306 if not isinstance(repo, BaseRepository):
306 if not isinstance(repo, BaseRepository):
307 raise Exception('You must pass an Repository '
307 raise Exception('You must pass an Repository '
308 'object as first argument got %s', type(repo))
308 'object as first argument got %s', type(repo))
309
309
310 try:
310 try:
311 cs = repo.get_changeset(rev)
311 cs = repo.get_changeset(rev)
312 except RepositoryError:
312 except RepositoryError:
313 from rhodecode.lib.utils import EmptyChangeset
313 from rhodecode.lib.utils import EmptyChangeset
314 cs = EmptyChangeset()
314 cs = EmptyChangeset()
315 return cs
315 return cs
316
316
317
317
318 flash = _Flash()
318 flash = _Flash()
319
319
320
320
321 #==============================================================================
321 #==============================================================================
322 # MERCURIAL FILTERS available via h.
322 # MERCURIAL FILTERS available via h.
323 #==============================================================================
323 #==============================================================================
324 from mercurial import util
324 from mercurial import util
325 from mercurial.templatefilters import person as _person
325 from mercurial.templatefilters import person as _person
326
326
327
327
328
328
329 def _age(curdate):
329 def _age(curdate):
330 """turns a datetime into an age string."""
330 """turns a datetime into an age string."""
331
331
332 if not curdate:
332 if not curdate:
333 return ''
333 return ''
334
334
335 from datetime import timedelta, datetime
335 from datetime import timedelta, datetime
336
336
337 agescales = [("year", 3600 * 24 * 365),
337 agescales = [("year", 3600 * 24 * 365),
338 ("month", 3600 * 24 * 30),
338 ("month", 3600 * 24 * 30),
339 ("day", 3600 * 24),
339 ("day", 3600 * 24),
340 ("hour", 3600),
340 ("hour", 3600),
341 ("minute", 60),
341 ("minute", 60),
342 ("second", 1), ]
342 ("second", 1), ]
343
343
344 age = datetime.now() - curdate
344 age = datetime.now() - curdate
345 age_seconds = (age.days * agescales[2][1]) + age.seconds
345 age_seconds = (age.days * agescales[2][1]) + age.seconds
346 pos = 1
346 pos = 1
347 for scale in agescales:
347 for scale in agescales:
348 if scale[1] <= age_seconds:
348 if scale[1] <= age_seconds:
349 if pos == 6:pos = 5
349 if pos == 6:pos = 5
350 return time_ago_in_words(curdate, agescales[pos][0])
350 return time_ago_in_words(curdate, agescales[pos][0])
351 pos += 1
351 pos += 1
352
352
353 age = lambda x:_age(x)
353 age = lambda x:_age(x)
354 capitalize = lambda x: x.capitalize()
354 capitalize = lambda x: x.capitalize()
355 email = util.email
355 email = util.email
356 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
356 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
357 person = lambda x: _person(x)
357 person = lambda x: _person(x)
358 short_id = lambda x: x[:12]
358 short_id = lambda x: x[:12]
359
359
360
361 def action_parser(user_log):
362 """
363 This helper will map the specified string action into translated
364 fancy names with icons and links
365
366 @param action:
367 """
368 action = user_log.action
369 action_params = None
370 cs_links = ''
371
372 x = action.split(':')
373
374 if len(x) > 1:
375 action, action_params = x
376
377 if action == 'push':
378 revs_limit = 5
379 revs = action_params.split(',')
380 cs_links = " " + ', '.join ([link(rev,
381 url('changeset_home',
382 repo_name=user_log.repository.repo_name,
383 revision=rev)) for rev in revs[:revs_limit] ])
384 if len(revs) > revs_limit:
385 html_tmpl = '<span title="%s"> %s </span>'
386 cs_links += html_tmpl % (', '.join(r for r in revs[revs_limit:]),
387 _('and %s more revisions') % (len(revs) - revs_limit))
388
389 map = {'user_deleted_repo':_('User deleted repository'),
390 'user_created_repo':_('User created repository'),
391 'user_forked_repo':_('User forked repository'),
392 'user_updated_repo':_('User updated repository'),
393 'admin_deleted_repo':_('Admin delete repository'),
394 'admin_created_repo':_('Admin created repository'),
395 'admin_forked_repo':_('Admin forked repository'),
396 'admin_updated_repo':_('Admin updated repository'),
397 'push':_('Pushed') + literal(cs_links),
398 'pull':_('Pulled'), }
399
400 print action, action_params
401 return map.get(action, action)
402
403
360 #==============================================================================
404 #==============================================================================
361 # PERMS
405 # PERMS
362 #==============================================================================
406 #==============================================================================
363 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
407 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
364 HasRepoPermissionAny, HasRepoPermissionAll
408 HasRepoPermissionAny, HasRepoPermissionAll
365
409
366 #==============================================================================
410 #==============================================================================
367 # GRAVATAR URL
411 # GRAVATAR URL
368 #==============================================================================
412 #==============================================================================
369 import hashlib
413 import hashlib
370 import urllib
414 import urllib
371 from pylons import request
415 from pylons import request
372
416
373 def gravatar_url(email_address, size=30):
417 def gravatar_url(email_address, size=30):
374 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
418 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
375 default = 'identicon'
419 default = 'identicon'
376 baseurl_nossl = "http://www.gravatar.com/avatar/"
420 baseurl_nossl = "http://www.gravatar.com/avatar/"
377 baseurl_ssl = "https://secure.gravatar.com/avatar/"
421 baseurl_ssl = "https://secure.gravatar.com/avatar/"
378 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
422 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
379
423
380
424
381 # construct the url
425 # construct the url
382 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
426 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
383 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
427 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
384
428
385 return gravatar_url
429 return gravatar_url
386
430
387 def safe_unicode(str):
431 def safe_unicode(str):
388 """safe unicode function. In case of UnicodeDecode error we try to return
432 """safe unicode function. In case of UnicodeDecode error we try to return
389 unicode with errors replace, if this failes we return unicode with
433 unicode with errors replace, if this failes we return unicode with
390 string_escape decoding """
434 string_escape decoding """
391
435
392 try:
436 try:
393 u_str = unicode(str)
437 u_str = unicode(str)
394 except UnicodeDecodeError:
438 except UnicodeDecodeError:
395 try:
439 try:
396 u_str = unicode(str, 'utf-8', 'replace')
440 u_str = unicode(str, 'utf-8', 'replace')
397 except UnicodeDecodeError:
441 except UnicodeDecodeError:
398 #incase we have a decode error just represent as byte string
442 #incase we have a decode error just represent as byte string
399 u_str = unicode(str(str).encode('string_escape'))
443 u_str = unicode(str(str).encode('string_escape'))
400
444
401 return u_str
445 return u_str
@@ -1,54 +1,48 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 %if c.users_log:
2 %if c.users_log:
3 <table>
3 <table>
4 <tr>
4 <tr>
5 <th class="left">${_('Username')}</th>
5 <th class="left">${_('Username')}</th>
6 <th class="left">${_('Action')}</th>
6 <th class="left">${_('Repository')}</th>
7 <th class="left">${_('Repository')}</th>
7 <th class="left">${_('Action')}</th>
8 <th class="left">${_('Date')}</th>
8 <th class="left">${_('Date')}</th>
9 <th class="left">${_('From IP')}</th>
9 <th class="left">${_('From IP')}</th>
10 </tr>
10 </tr>
11
11
12 %for cnt,l in enumerate(c.users_log):
12 %for cnt,l in enumerate(c.users_log):
13 <tr class="parity${cnt%2}">
13 <tr class="parity${cnt%2}">
14 <td>${h.link_to(l.user.username,h.url('edit_user', id=l.user.user_id))}</td>
14 <td>${h.link_to(l.user.username,h.url('edit_user', id=l.user.user_id))}</td>
15 <td>${h.action_parser(l)}</td>
15 <td>
16 <td>
16 %if l.repository:
17 %if l.repository:
17 ${h.link_to(l.repository.repo_name,h.url('summary_home',repo_name=l.repository.repo_name))}
18 ${h.link_to(l.repository.repo_name,h.url('summary_home',repo_name=l.repository.repo_name))}
18 %else:
19 %else:
19 ${l.repository_name}
20 ${l.repository_name}
20 %endif
21 %endif
21 </td>
22 </td>
22 <td>
23
23 % if l.action == 'push' and l.revision:
24 ${h.link_to('%s - %s' % (l.action,l.revision),
25 h.url('changeset_home',repo_name=l.repository.repo_name,revision=l.revision))}
26 %else:
27 ${l.action}
28 %endif
29 </td>
30 <td>${l.action_date}</td>
24 <td>${l.action_date}</td>
31 <td>${l.user_ip}</td>
25 <td>${l.user_ip}</td>
32 </tr>
26 </tr>
33 %endfor
27 %endfor
34 </table>
28 </table>
35
29
36 <script type="text/javascript">
30 <script type="text/javascript">
37 var data_div = 'user_log';
31 var data_div = 'user_log';
38 YAHOO.util.Event.onDOMReady(function(){
32 YAHOO.util.Event.onDOMReady(function(){
39 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
33 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
40 YAHOO.util.Dom.setStyle('shortlog_data','opacity','0.3');});});
34 YAHOO.util.Dom.setStyle('shortlog_data','opacity','0.3');});});
41 </script>
35 </script>
42
36
43
37
44 <div class="pagination-wh pagination-left">
38 <div class="pagination-wh pagination-left">
45 ${c.users_log.pager('$link_previous ~2~ $link_next',
39 ${c.users_log.pager('$link_previous ~2~ $link_next',
46 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
40 onclick="""YAHOO.util.Connect.asyncRequest('GET','$partial_url',{
47 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
41 success:function(o){YAHOO.util.Dom.get(data_div).innerHTML=o.responseText;
48 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
42 YAHOO.util.Event.addListener(YAHOO.util.Dom.getElementsByClassName('pager_link'),"click",function(){
49 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
43 YAHOO.util.Dom.setStyle(data_div,'opacity','0.3');});
50 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
44 YAHOO.util.Dom.setStyle(data_div,'opacity','1');}},null); return false;""")}
51 </div>
45 </div>
52 %else:
46 %else:
53 ${_('No actions yet')}
47 ${_('No actions yet')}
54 %endif
48 %endif
General Comments 0
You need to be logged in to leave comments. Login now