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