##// END OF EJS Templates
fixes fixes fixes ! optimized queries on journal...
marcink -
r1040:8e49b6ce beta
parent child Browse files
Show More
@@ -1,53 +1,60
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.admin
3 rhodecode.controllers.admin.admin
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Controller for Admin panel of Rhodecode
6 Controller for Admin panel of Rhodecode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29
29 from pylons import request, tmpl_context as c
30 from pylons import request, tmpl_context as c
31 from sqlalchemy.orm import joinedload
32 from webhelpers.paginate import Page
33
34 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
30 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
31 from rhodecode.model.db import UserLog
36 from rhodecode.model.db import UserLog
32 from webhelpers.paginate import Page
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
34
37
35 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
36
39
37 class AdminController(BaseController):
40 class AdminController(BaseController):
38
41
39 @LoginRequired()
42 @LoginRequired()
40 def __before__(self):
43 def __before__(self):
41 super(AdminController, self).__before__()
44 super(AdminController, self).__before__()
42
45
43 @HasPermissionAllDecorator('hg.admin')
46 @HasPermissionAllDecorator('hg.admin')
44 def index(self):
47 def index(self):
45
48
46 users_log = self.sa.query(UserLog).order_by(UserLog.action_date.desc())
49 users_log = self.sa.query(UserLog)\
50 .options(joinedload(UserLog.user))\
51 .options(joinedload(UserLog.repository))\
52 .order_by(UserLog.action_date.desc())
53
47 p = int(request.params.get('page', 1))
54 p = int(request.params.get('page', 1))
48 c.users_log = Page(users_log, page=p, items_per_page=10)
55 c.users_log = Page(users_log, page=p, items_per_page=10)
49 c.log_data = render('admin/admin_log.html')
56 c.log_data = render('admin/admin_log.html')
50 if request.params.get('partial'):
57 if request.params.get('partial'):
51 return c.log_data
58 return c.log_data
52 return render('admin/admin.html')
59 return render('admin/admin.html')
53
60
@@ -1,609 +1,609
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
11 """
12 # This program is free software; you can redistribute it and/or
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; version 2
14 # as published by the Free Software Foundation; version 2
15 # of the License or (at your opinion) any later version of the license.
15 # of the License or (at your opinion) any later version of the license.
16 #
16 #
17 # This program is distributed in the hope that it will be useful,
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
20 # GNU General Public License for more details.
21 #
21 #
22 # You should have received a copy of the GNU General Public License
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
25 # MA 02110-1301, USA.
26
26
27 import bcrypt
27 import bcrypt
28 import random
28 import random
29 import logging
29 import logging
30 import traceback
30 import traceback
31
31
32 from decorator import decorator
32 from decorator import decorator
33
33
34 from pylons import config, session, url, request
34 from pylons import config, session, url, request
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36
36
37 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
37 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
38 from rhodecode.lib.utils import get_repo_slug
38 from rhodecode.lib.utils import get_repo_slug
39 from rhodecode.lib.auth_ldap import AuthLdap
39 from rhodecode.lib.auth_ldap import AuthLdap
40
40
41 from rhodecode.model import meta
41 from rhodecode.model import meta
42 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
43 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
43 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
44 UserToPerm, UsersGroupToPerm, UsersGroupMember
44 UserToPerm, UsersGroupToPerm, UsersGroupMember
45
45
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 PERM_WEIGHTS = {'repository.none':0,
50 PERM_WEIGHTS = {'repository.none':0,
51 'repository.read':1,
51 'repository.read':1,
52 'repository.write':3,
52 'repository.write':3,
53 'repository.admin':3}
53 'repository.admin':3}
54
54
55
55
56 class PasswordGenerator(object):
56 class PasswordGenerator(object):
57 """This is a simple class for generating password from
57 """This is a simple class for generating password from
58 different sets of characters
58 different sets of characters
59 usage:
59 usage:
60 passwd_gen = PasswordGenerator()
60 passwd_gen = PasswordGenerator()
61 #print 8-letter password containing only big and small letters of alphabet
61 #print 8-letter password containing only big and small letters of alphabet
62 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
62 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
63 """
63 """
64 ALPHABETS_NUM = r'''1234567890'''#[0]
64 ALPHABETS_NUM = r'''1234567890'''#[0]
65 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
65 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
66 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
66 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
67 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
67 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
68 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
68 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
69 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
69 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
70 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
70 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
71 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
71 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
72 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
72 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
73
73
74 def __init__(self, passwd=''):
74 def __init__(self, passwd=''):
75 self.passwd = passwd
75 self.passwd = passwd
76
76
77 def gen_password(self, len, type):
77 def gen_password(self, len, type):
78 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
78 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
79 return self.passwd
79 return self.passwd
80
80
81
81
82 def get_crypt_password(password):
82 def get_crypt_password(password):
83 """Cryptographic function used for password hashing based on pybcrypt
83 """Cryptographic function used for password hashing based on pybcrypt
84
84
85 :param password: password to hash
85 :param password: password to hash
86 """
86 """
87 return bcrypt.hashpw(password, bcrypt.gensalt(10))
87 return bcrypt.hashpw(password, bcrypt.gensalt(10))
88
88
89 def check_password(password, hashed):
89 def check_password(password, hashed):
90 return bcrypt.hashpw(password, hashed) == hashed
90 return bcrypt.hashpw(password, hashed) == hashed
91
91
92 def authfunc(environ, username, password):
92 def authfunc(environ, username, password):
93 """Dummy authentication function used in Mercurial/Git/ and access control,
93 """Dummy authentication function used in Mercurial/Git/ and access control,
94
94
95 :param environ: needed only for using in Basic auth
95 :param environ: needed only for using in Basic auth
96 """
96 """
97 return authenticate(username, password)
97 return authenticate(username, password)
98
98
99
99
100 def authenticate(username, password):
100 def authenticate(username, password):
101 """Authentication function used for access control,
101 """Authentication function used for access control,
102 firstly checks for db authentication then if ldap is enabled for ldap
102 firstly checks for db authentication then if ldap is enabled for ldap
103 authentication, also creates ldap user if not in database
103 authentication, also creates ldap user if not in database
104
104
105 :param username: username
105 :param username: username
106 :param password: password
106 :param password: password
107 """
107 """
108 user_model = UserModel()
108 user_model = UserModel()
109 user = user_model.get_by_username(username, cache=False)
109 user = user_model.get_by_username(username, cache=False)
110
110
111 log.debug('Authenticating user using RhodeCode account')
111 log.debug('Authenticating user using RhodeCode account')
112 if user is not None and not user.ldap_dn:
112 if user is not None and not user.ldap_dn:
113 if user.active:
113 if user.active:
114
114
115 if user.username == 'default' and user.active:
115 if user.username == 'default' and user.active:
116 log.info('user %s authenticated correctly as anonymous user',
116 log.info('user %s authenticated correctly as anonymous user',
117 username)
117 username)
118 return True
118 return True
119
119
120 elif user.username == username and check_password(password, user.password):
120 elif user.username == username and check_password(password, user.password):
121 log.info('user %s authenticated correctly', username)
121 log.info('user %s authenticated correctly', username)
122 return True
122 return True
123 else:
123 else:
124 log.warning('user %s is disabled', username)
124 log.warning('user %s is disabled', username)
125
125
126 else:
126 else:
127 log.debug('Regular authentication failed')
127 log.debug('Regular authentication failed')
128 user_obj = user_model.get_by_username(username, cache=False,
128 user_obj = user_model.get_by_username(username, cache=False,
129 case_insensitive=True)
129 case_insensitive=True)
130
130
131 if user_obj is not None and not user_obj.ldap_dn:
131 if user_obj is not None and not user_obj.ldap_dn:
132 log.debug('this user already exists as non ldap')
132 log.debug('this user already exists as non ldap')
133 return False
133 return False
134
134
135 from rhodecode.model.settings import SettingsModel
135 from rhodecode.model.settings import SettingsModel
136 ldap_settings = SettingsModel().get_ldap_settings()
136 ldap_settings = SettingsModel().get_ldap_settings()
137
137
138 #======================================================================
138 #======================================================================
139 # FALLBACK TO LDAP AUTH IF ENABLE
139 # FALLBACK TO LDAP AUTH IF ENABLE
140 #======================================================================
140 #======================================================================
141 if ldap_settings.get('ldap_active', False):
141 if ldap_settings.get('ldap_active', False):
142 log.debug("Authenticating user using ldap")
142 log.debug("Authenticating user using ldap")
143 kwargs = {
143 kwargs = {
144 'server':ldap_settings.get('ldap_host', ''),
144 'server':ldap_settings.get('ldap_host', ''),
145 'base_dn':ldap_settings.get('ldap_base_dn', ''),
145 'base_dn':ldap_settings.get('ldap_base_dn', ''),
146 'port':ldap_settings.get('ldap_port'),
146 'port':ldap_settings.get('ldap_port'),
147 'bind_dn':ldap_settings.get('ldap_dn_user'),
147 'bind_dn':ldap_settings.get('ldap_dn_user'),
148 'bind_pass':ldap_settings.get('ldap_dn_pass'),
148 'bind_pass':ldap_settings.get('ldap_dn_pass'),
149 'use_ldaps':ldap_settings.get('ldap_ldaps'),
149 'use_ldaps':ldap_settings.get('ldap_ldaps'),
150 'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
150 'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
151 'ldap_filter':ldap_settings.get('ldap_filter'),
151 'ldap_filter':ldap_settings.get('ldap_filter'),
152 'search_scope':ldap_settings.get('ldap_search_scope'),
152 'search_scope':ldap_settings.get('ldap_search_scope'),
153 'attr_login':ldap_settings.get('ldap_attr_login'),
153 'attr_login':ldap_settings.get('ldap_attr_login'),
154 'ldap_version':3,
154 'ldap_version':3,
155 }
155 }
156 log.debug('Checking for ldap authentication')
156 log.debug('Checking for ldap authentication')
157 try:
157 try:
158 aldap = AuthLdap(**kwargs)
158 aldap = AuthLdap(**kwargs)
159 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
159 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
160 log.debug('Got ldap DN response %s', user_dn)
160 log.debug('Got ldap DN response %s', user_dn)
161
161
162 user_attrs = {
162 user_attrs = {
163 'name' : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
163 'name' : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
164 'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
164 'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
165 'email' : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
165 'email' : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
166 }
166 }
167
167
168 if user_model.create_ldap(username, password, user_dn, user_attrs):
168 if user_model.create_ldap(username, password, user_dn, user_attrs):
169 log.info('created new ldap user %s', username)
169 log.info('created new ldap user %s', username)
170
170
171 return True
171 return True
172 except (LdapUsernameError, LdapPasswordError,):
172 except (LdapUsernameError, LdapPasswordError,):
173 pass
173 pass
174 except (Exception,):
174 except (Exception,):
175 log.error(traceback.format_exc())
175 log.error(traceback.format_exc())
176 pass
176 pass
177 return False
177 return False
178
178
179 class AuthUser(object):
179 class AuthUser(object):
180 """A simple object that handles a mercurial username for authentication
180 """A simple object that handles a mercurial username for authentication
181 """
181 """
182
182
183 def __init__(self):
183 def __init__(self):
184 self.username = 'None'
184 self.username = 'None'
185 self.name = ''
185 self.name = ''
186 self.lastname = ''
186 self.lastname = ''
187 self.email = ''
187 self.email = ''
188 self.user_id = None
188 self.user_id = None
189 self.is_authenticated = False
189 self.is_authenticated = False
190 self.is_admin = False
190 self.is_admin = False
191 self.permissions = {}
191 self.permissions = {}
192
192
193 def __repr__(self):
193 def __repr__(self):
194 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
194 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
195
195
196 def set_available_permissions(config):
196 def set_available_permissions(config):
197 """This function will propagate pylons globals with all available defined
197 """This function will propagate pylons globals with all available defined
198 permission given in db. We don't want to check each time from db for new
198 permission given in db. We don't want to check each time from db for new
199 permissions since adding a new permission also requires application restart
199 permissions since adding a new permission also requires application restart
200 ie. to decorate new views with the newly created permission
200 ie. to decorate new views with the newly created permission
201
201
202 :param config: current pylons config instance
202 :param config: current pylons config instance
203
203
204 """
204 """
205 log.info('getting information about all available permissions')
205 log.info('getting information about all available permissions')
206 try:
206 try:
207 sa = meta.Session()
207 sa = meta.Session()
208 all_perms = sa.query(Permission).all()
208 all_perms = sa.query(Permission).all()
209 except:
209 except:
210 pass
210 pass
211 finally:
211 finally:
212 meta.Session.remove()
212 meta.Session.remove()
213
213
214 config['available_permissions'] = [x.permission_name for x in all_perms]
214 config['available_permissions'] = [x.permission_name for x in all_perms]
215
215
216 def fill_perms(user):
216 def fill_perms(user):
217 """Fills user permission attribute with permissions taken from database
217 """Fills user permission attribute with permissions taken from database
218 works for permissions given for repositories, and for permissions that
218 works for permissions given for repositories, and for permissions that
219 as part of beeing group member
219 as part of beeing group member
220
220
221 :param user: user instance to fill his perms
221 :param user: user instance to fill his perms
222 """
222 """
223
223
224 sa = meta.Session()
224 sa = meta.Session()
225 user.permissions['repositories'] = {}
225 user.permissions['repositories'] = {}
226 user.permissions['global'] = set()
226 user.permissions['global'] = set()
227
227
228 #===========================================================================
228 #===========================================================================
229 # fetch default permissions
229 # fetch default permissions
230 #===========================================================================
230 #===========================================================================
231 default_user = UserModel().get_by_username('default', cache=True)
231 default_user = UserModel().get_by_username('default', cache=True)
232
232
233 default_perms = sa.query(RepoToPerm, Repository, Permission)\
233 default_perms = sa.query(RepoToPerm, Repository, Permission)\
234 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
234 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
235 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
235 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
236 .filter(RepoToPerm.user == default_user).all()
236 .filter(RepoToPerm.user == default_user).all()
237
237
238 if user.is_admin:
238 if user.is_admin:
239 #=======================================================================
239 #=======================================================================
240 # #admin have all default rights set to admin
240 # #admin have all default rights set to admin
241 #=======================================================================
241 #=======================================================================
242 user.permissions['global'].add('hg.admin')
242 user.permissions['global'].add('hg.admin')
243
243
244 for perm in default_perms:
244 for perm in default_perms:
245 p = 'repository.admin'
245 p = 'repository.admin'
246 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
246 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
247
247
248 else:
248 else:
249 #=======================================================================
249 #=======================================================================
250 # set default permissions
250 # set default permissions
251 #=======================================================================
251 #=======================================================================
252
252
253 #default global
253 #default global
254 default_global_perms = sa.query(UserToPerm)\
254 default_global_perms = sa.query(UserToPerm)\
255 .filter(UserToPerm.user == sa.query(User)\
255 .filter(UserToPerm.user == sa.query(User)\
256 .filter(User.username == 'default').one())
256 .filter(User.username == 'default').one())
257
257
258 for perm in default_global_perms:
258 for perm in default_global_perms:
259 user.permissions['global'].add(perm.permission.permission_name)
259 user.permissions['global'].add(perm.permission.permission_name)
260
260
261 #default for repositories
261 #default for repositories
262 for perm in default_perms:
262 for perm in default_perms:
263 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
263 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
264 #disable defaults for private repos,
264 #disable defaults for private repos,
265 p = 'repository.none'
265 p = 'repository.none'
266 elif perm.Repository.user_id == user.user_id:
266 elif perm.Repository.user_id == user.user_id:
267 #set admin if owner
267 #set admin if owner
268 p = 'repository.admin'
268 p = 'repository.admin'
269 else:
269 else:
270 p = perm.Permission.permission_name
270 p = perm.Permission.permission_name
271
271
272 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
272 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
273
273
274 #=======================================================================
274 #=======================================================================
275 # overwrite default with user permissions if any
275 # overwrite default with user permissions if any
276 #=======================================================================
276 #=======================================================================
277 user_perms = sa.query(RepoToPerm, Permission, Repository)\
277 user_perms = sa.query(RepoToPerm, Permission, Repository)\
278 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
278 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
279 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
279 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
280 .filter(RepoToPerm.user_id == user.user_id).all()
280 .filter(RepoToPerm.user_id == user.user_id).all()
281
281
282 for perm in user_perms:
282 for perm in user_perms:
283 if perm.Repository.user_id == user.user_id:#set admin if owner
283 if perm.Repository.user_id == user.user_id:#set admin if owner
284 p = 'repository.admin'
284 p = 'repository.admin'
285 else:
285 else:
286 p = perm.Permission.permission_name
286 p = perm.Permission.permission_name
287 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
287 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
288
288
289
289
290 #=======================================================================
290 #=======================================================================
291 # check if user is part of groups for this repository and fill in
291 # check if user is part of groups for this repository and fill in
292 # (or replace with higher) permissions
292 # (or replace with higher) permissions
293 #=======================================================================
293 #=======================================================================
294 user_perms_from_users_groups = sa.query(UsersGroupToPerm, Permission, Repository,)\
294 user_perms_from_users_groups = sa.query(UsersGroupToPerm, Permission, Repository,)\
295 .join((Repository, UsersGroupToPerm.repository_id == Repository.repo_id))\
295 .join((Repository, UsersGroupToPerm.repository_id == Repository.repo_id))\
296 .join((Permission, UsersGroupToPerm.permission_id == Permission.permission_id))\
296 .join((Permission, UsersGroupToPerm.permission_id == Permission.permission_id))\
297 .join((UsersGroupMember, UsersGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
297 .join((UsersGroupMember, UsersGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
298 .filter(UsersGroupMember.user_id == user.user_id).all()
298 .filter(UsersGroupMember.user_id == user.user_id).all()
299
299
300 for perm in user_perms_from_users_groups:
300 for perm in user_perms_from_users_groups:
301 p = perm.Permission.permission_name
301 p = perm.Permission.permission_name
302 cur_perm = user.permissions['repositories'][perm.UsersGroupToPerm.repository.repo_name]
302 cur_perm = user.permissions['repositories'][perm.UsersGroupToPerm.repository.repo_name]
303 #overwrite permission only if it's greater than permission given from other sources
303 #overwrite permission only if it's greater than permission given from other sources
304 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
304 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
305 user.permissions['repositories'][perm.UsersGroupToPerm.repository.repo_name] = p
305 user.permissions['repositories'][perm.UsersGroupToPerm.repository.repo_name] = p
306
306
307 meta.Session.remove()
307 meta.Session.remove()
308 return user
308 return user
309
309
310 def get_user(session):
310 def get_user(session):
311 """Gets user from session, and wraps permissions into user
311 """Gets user from session, and wraps permissions into user
312
312
313 :param session:
313 :param session:
314 """
314 """
315 user = session.get('rhodecode_user', AuthUser())
315 user = session.get('rhodecode_user', AuthUser())
316 #if the user is not logged in we check for anonymous access
316 #if the user is not logged in we check for anonymous access
317 #if user is logged and it's a default user check if we still have anonymous
317 #if user is logged and it's a default user check if we still have anonymous
318 #access enabled
318 #access enabled
319 if user.user_id is None or user.username == 'default':
319 if user.user_id is None or user.username == 'default':
320 anonymous_user = UserModel().get_by_username('default', cache=True)
320 anonymous_user = UserModel().get_by_username('default', cache=True)
321 if anonymous_user.active is True:
321 if anonymous_user.active is True:
322 #then we set this user is logged in
322 #then we set this user is logged in
323 user.is_authenticated = True
323 user.is_authenticated = True
324 user.user_id = anonymous_user.user_id
324 user.user_id = anonymous_user.user_id
325 else:
325 else:
326 user.is_authenticated = False
326 user.is_authenticated = False
327
327
328 if user.is_authenticated:
328 if user.is_authenticated:
329 user = UserModel().fill_data(user)
329 user = UserModel().fill_data(user)
330
330
331 user = fill_perms(user)
331 user = fill_perms(user)
332 session['rhodecode_user'] = user
332 session['rhodecode_user'] = user
333 session.save()
333 session.save()
334 return user
334 return user
335
335
336 #===============================================================================
336 #===============================================================================
337 # CHECK DECORATORS
337 # CHECK DECORATORS
338 #===============================================================================
338 #===============================================================================
339 class LoginRequired(object):
339 class LoginRequired(object):
340 """Must be logged in to execute this function else
340 """Must be logged in to execute this function else
341 redirect to login page"""
341 redirect to login page"""
342
342
343 def __call__(self, func):
343 def __call__(self, func):
344 return decorator(self.__wrapper, func)
344 return decorator(self.__wrapper, func)
345
345
346 def __wrapper(self, func, *fargs, **fkwargs):
346 def __wrapper(self, func, *fargs, **fkwargs):
347 user = session.get('rhodecode_user', AuthUser())
347 user = session.get('rhodecode_user', AuthUser())
348 log.debug('Checking login required for user:%s', user.username)
348 log.debug('Checking login required for user:%s', user.username)
349 if user.is_authenticated:
349 if user.is_authenticated:
350 log.debug('user %s is authenticated', user.username)
350 log.debug('user %s is authenticated', user.username)
351 return func(*fargs, **fkwargs)
351 return func(*fargs, **fkwargs)
352 else:
352 else:
353 log.warn('user %s not authenticated', user.username)
353 log.warn('user %s not authenticated', user.username)
354
354
355 p = ''
355 p = ''
356 if request.environ.get('SCRIPT_NAME') != '/':
356 if request.environ.get('SCRIPT_NAME') != '/':
357 p += request.environ.get('SCRIPT_NAME')
357 p += request.environ.get('SCRIPT_NAME')
358
358
359 p += request.environ.get('PATH_INFO')
359 p += request.environ.get('PATH_INFO')
360 if request.environ.get('QUERY_STRING'):
360 if request.environ.get('QUERY_STRING'):
361 p += '?' + request.environ.get('QUERY_STRING')
361 p += '?' + request.environ.get('QUERY_STRING')
362
362
363 log.debug('redirecting to login page with %s', p)
363 log.debug('redirecting to login page with %s', p)
364 return redirect(url('login_home', came_from=p))
364 return redirect(url('login_home', came_from=p))
365
365
366 class NotAnonymous(object):
366 class NotAnonymous(object):
367 """Must be logged in to execute this function else
367 """Must be logged in to execute this function else
368 redirect to login page"""
368 redirect to login page"""
369
369
370 def __call__(self, func):
370 def __call__(self, func):
371 return decorator(self.__wrapper, func)
371 return decorator(self.__wrapper, func)
372
372
373 def __wrapper(self, func, *fargs, **fkwargs):
373 def __wrapper(self, func, *fargs, **fkwargs):
374 user = session.get('rhodecode_user', AuthUser())
374 user = session.get('rhodecode_user', AuthUser())
375 log.debug('Checking if user is not anonymous')
375 log.debug('Checking if user is not anonymous')
376
376
377 anonymous = user.username == 'default'
377 anonymous = user.username == 'default'
378
378
379 if anonymous:
379 if anonymous:
380 p = ''
380 p = ''
381 if request.environ.get('SCRIPT_NAME') != '/':
381 if request.environ.get('SCRIPT_NAME') != '/':
382 p += request.environ.get('SCRIPT_NAME')
382 p += request.environ.get('SCRIPT_NAME')
383
383
384 p += request.environ.get('PATH_INFO')
384 p += request.environ.get('PATH_INFO')
385 if request.environ.get('QUERY_STRING'):
385 if request.environ.get('QUERY_STRING'):
386 p += '?' + request.environ.get('QUERY_STRING')
386 p += '?' + request.environ.get('QUERY_STRING')
387 return redirect(url('login_home', came_from=p))
387 return redirect(url('login_home', came_from=p))
388 else:
388 else:
389 return func(*fargs, **fkwargs)
389 return func(*fargs, **fkwargs)
390
390
391 class PermsDecorator(object):
391 class PermsDecorator(object):
392 """Base class for decorators"""
392 """Base class for decorators"""
393
393
394 def __init__(self, *required_perms):
394 def __init__(self, *required_perms):
395 available_perms = config['available_permissions']
395 available_perms = config['available_permissions']
396 for perm in required_perms:
396 for perm in required_perms:
397 if perm not in available_perms:
397 if perm not in available_perms:
398 raise Exception("'%s' permission is not defined" % perm)
398 raise Exception("'%s' permission is not defined" % perm)
399 self.required_perms = set(required_perms)
399 self.required_perms = set(required_perms)
400 self.user_perms = None
400 self.user_perms = None
401
401
402 def __call__(self, func):
402 def __call__(self, func):
403 return decorator(self.__wrapper, func)
403 return decorator(self.__wrapper, func)
404
404
405
405
406 def __wrapper(self, func, *fargs, **fkwargs):
406 def __wrapper(self, func, *fargs, **fkwargs):
407 # _wrapper.__name__ = func.__name__
407 # _wrapper.__name__ = func.__name__
408 # _wrapper.__dict__.update(func.__dict__)
408 # _wrapper.__dict__.update(func.__dict__)
409 # _wrapper.__doc__ = func.__doc__
409 # _wrapper.__doc__ = func.__doc__
410 self.user = session.get('rhodecode_user', AuthUser())
410 self.user = session.get('rhodecode_user', AuthUser())
411 self.user_perms = self.user.permissions
411 self.user_perms = self.user.permissions
412 log.debug('checking %s permissions %s for %s %s',
412 log.debug('checking %s permissions %s for %s %s',
413 self.__class__.__name__, self.required_perms, func.__name__,
413 self.__class__.__name__, self.required_perms, func.__name__,
414 self.user)
414 self.user)
415
415
416 if self.check_permissions():
416 if self.check_permissions():
417 log.debug('Permission granted for %s %s', func.__name__, self.user)
417 log.debug('Permission granted for %s %s', func.__name__, self.user)
418
418
419 return func(*fargs, **fkwargs)
419 return func(*fargs, **fkwargs)
420
420
421 else:
421 else:
422 log.warning('Permission denied for %s %s', func.__name__, self.user)
422 log.warning('Permission denied for %s %s', func.__name__, self.user)
423 #redirect with forbidden ret code
423 #redirect with forbidden ret code
424 return abort(403)
424 return abort(403)
425
425
426
426
427
427
428 def check_permissions(self):
428 def check_permissions(self):
429 """Dummy function for overriding"""
429 """Dummy function for overriding"""
430 raise Exception('You have to write this function in child class')
430 raise Exception('You have to write this function in child class')
431
431
432 class HasPermissionAllDecorator(PermsDecorator):
432 class HasPermissionAllDecorator(PermsDecorator):
433 """Checks for access permission for all given predicates. All of them
433 """Checks for access permission for all given predicates. All of them
434 have to be meet in order to fulfill the request
434 have to be meet in order to fulfill the request
435 """
435 """
436
436
437 def check_permissions(self):
437 def check_permissions(self):
438 if self.required_perms.issubset(self.user_perms.get('global')):
438 if self.required_perms.issubset(self.user_perms.get('global')):
439 return True
439 return True
440 return False
440 return False
441
441
442
442
443 class HasPermissionAnyDecorator(PermsDecorator):
443 class HasPermissionAnyDecorator(PermsDecorator):
444 """Checks for access permission for any of given predicates. In order to
444 """Checks for access permission for any of given predicates. In order to
445 fulfill the request any of predicates must be meet
445 fulfill the request any of predicates must be meet
446 """
446 """
447
447
448 def check_permissions(self):
448 def check_permissions(self):
449 if self.required_perms.intersection(self.user_perms.get('global')):
449 if self.required_perms.intersection(self.user_perms.get('global')):
450 return True
450 return True
451 return False
451 return False
452
452
453 class HasRepoPermissionAllDecorator(PermsDecorator):
453 class HasRepoPermissionAllDecorator(PermsDecorator):
454 """Checks for access permission for all given predicates for specific
454 """Checks for access permission for all given predicates for specific
455 repository. All of them have to be meet in order to fulfill the request
455 repository. All of them have to be meet in order to fulfill the request
456 """
456 """
457
457
458 def check_permissions(self):
458 def check_permissions(self):
459 repo_name = get_repo_slug(request)
459 repo_name = get_repo_slug(request)
460 try:
460 try:
461 user_perms = set([self.user_perms['repositories'][repo_name]])
461 user_perms = set([self.user_perms['repositories'][repo_name]])
462 except KeyError:
462 except KeyError:
463 return False
463 return False
464 if self.required_perms.issubset(user_perms):
464 if self.required_perms.issubset(user_perms):
465 return True
465 return True
466 return False
466 return False
467
467
468
468
469 class HasRepoPermissionAnyDecorator(PermsDecorator):
469 class HasRepoPermissionAnyDecorator(PermsDecorator):
470 """Checks for access permission for any of given predicates for specific
470 """Checks for access permission for any of given predicates for specific
471 repository. In order to fulfill the request any of predicates must be meet
471 repository. In order to fulfill the request any of predicates must be meet
472 """
472 """
473
473
474 def check_permissions(self):
474 def check_permissions(self):
475 repo_name = get_repo_slug(request)
475 repo_name = get_repo_slug(request)
476
476
477 try:
477 try:
478 user_perms = set([self.user_perms['repositories'][repo_name]])
478 user_perms = set([self.user_perms['repositories'][repo_name]])
479 except KeyError:
479 except KeyError:
480 return False
480 return False
481 if self.required_perms.intersection(user_perms):
481 if self.required_perms.intersection(user_perms):
482 return True
482 return True
483 return False
483 return False
484 #===============================================================================
484 #===============================================================================
485 # CHECK FUNCTIONS
485 # CHECK FUNCTIONS
486 #===============================================================================
486 #===============================================================================
487
487
488 class PermsFunction(object):
488 class PermsFunction(object):
489 """Base function for other check functions"""
489 """Base function for other check functions"""
490
490
491 def __init__(self, *perms):
491 def __init__(self, *perms):
492 available_perms = config['available_permissions']
492 available_perms = config['available_permissions']
493
493
494 for perm in perms:
494 for perm in perms:
495 if perm not in available_perms:
495 if perm not in available_perms:
496 raise Exception("'%s' permission in not defined" % perm)
496 raise Exception("'%s' permission in not defined" % perm)
497 self.required_perms = set(perms)
497 self.required_perms = set(perms)
498 self.user_perms = None
498 self.user_perms = None
499 self.granted_for = ''
499 self.granted_for = ''
500 self.repo_name = None
500 self.repo_name = None
501
501
502 def __call__(self, check_Location=''):
502 def __call__(self, check_Location=''):
503 user = session.get('rhodecode_user', False)
503 user = session.get('rhodecode_user', False)
504 if not user:
504 if not user:
505 return False
505 return False
506 self.user_perms = user.permissions
506 self.user_perms = user.permissions
507 self.granted_for = user.username
507 self.granted_for = user.username
508 log.debug('checking %s %s %s', self.__class__.__name__,
508 log.debug('checking %s %s %s', self.__class__.__name__,
509 self.required_perms, user)
509 self.required_perms, user)
510
510
511 if self.check_permissions():
511 if self.check_permissions():
512 log.debug('Permission granted for %s @ %s %s', self.granted_for,
512 log.debug('Permission granted for %s @ %s %s', self.granted_for,
513 check_Location, user)
513 check_Location, user)
514 return True
514 return True
515
515
516 else:
516 else:
517 log.warning('Permission denied for %s @ %s %s', self.granted_for,
517 log.warning('Permission denied for %s @ %s %s', self.granted_for,
518 check_Location, user)
518 check_Location, user)
519 return False
519 return False
520
520
521 def check_permissions(self):
521 def check_permissions(self):
522 """Dummy function for overriding"""
522 """Dummy function for overriding"""
523 raise Exception('You have to write this function in child class')
523 raise Exception('You have to write this function in child class')
524
524
525 class HasPermissionAll(PermsFunction):
525 class HasPermissionAll(PermsFunction):
526 def check_permissions(self):
526 def check_permissions(self):
527 if self.required_perms.issubset(self.user_perms.get('global')):
527 if self.required_perms.issubset(self.user_perms.get('global')):
528 return True
528 return True
529 return False
529 return False
530
530
531 class HasPermissionAny(PermsFunction):
531 class HasPermissionAny(PermsFunction):
532 def check_permissions(self):
532 def check_permissions(self):
533 if self.required_perms.intersection(self.user_perms.get('global')):
533 if self.required_perms.intersection(self.user_perms.get('global')):
534 return True
534 return True
535 return False
535 return False
536
536
537 class HasRepoPermissionAll(PermsFunction):
537 class HasRepoPermissionAll(PermsFunction):
538
538
539 def __call__(self, repo_name=None, check_Location=''):
539 def __call__(self, repo_name=None, check_Location=''):
540 self.repo_name = repo_name
540 self.repo_name = repo_name
541 return super(HasRepoPermissionAll, self).__call__(check_Location)
541 return super(HasRepoPermissionAll, self).__call__(check_Location)
542
542
543 def check_permissions(self):
543 def check_permissions(self):
544 if not self.repo_name:
544 if not self.repo_name:
545 self.repo_name = get_repo_slug(request)
545 self.repo_name = get_repo_slug(request)
546
546
547 try:
547 try:
548 self.user_perms = set([self.user_perms['repositories']\
548 self.user_perms = set([self.user_perms['repositories']\
549 [self.repo_name]])
549 [self.repo_name]])
550 except KeyError:
550 except KeyError:
551 return False
551 return False
552 self.granted_for = self.repo_name
552 self.granted_for = self.repo_name
553 if self.required_perms.issubset(self.user_perms):
553 if self.required_perms.issubset(self.user_perms):
554 return True
554 return True
555 return False
555 return False
556
556
557 class HasRepoPermissionAny(PermsFunction):
557 class HasRepoPermissionAny(PermsFunction):
558
558
559 def __call__(self, repo_name=None, check_Location=''):
559 def __call__(self, repo_name=None, check_Location=''):
560 self.repo_name = repo_name
560 self.repo_name = repo_name
561 return super(HasRepoPermissionAny, self).__call__(check_Location)
561 return super(HasRepoPermissionAny, self).__call__(check_Location)
562
562
563 def check_permissions(self):
563 def check_permissions(self):
564 if not self.repo_name:
564 if not self.repo_name:
565 self.repo_name = get_repo_slug(request)
565 self.repo_name = get_repo_slug(request)
566
566
567 try:
567 try:
568 self.user_perms = set([self.user_perms['repositories']\
568 self.user_perms = set([self.user_perms['repositories']\
569 [self.repo_name]])
569 [self.repo_name]])
570 except KeyError:
570 except KeyError:
571 return False
571 return False
572 self.granted_for = self.repo_name
572 self.granted_for = self.repo_name
573 if self.required_perms.intersection(self.user_perms):
573 if self.required_perms.intersection(self.user_perms):
574 return True
574 return True
575 return False
575 return False
576
576
577 #===============================================================================
577 #===============================================================================
578 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
578 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
579 #===============================================================================
579 #===============================================================================
580
580
581 class HasPermissionAnyMiddleware(object):
581 class HasPermissionAnyMiddleware(object):
582 def __init__(self, *perms):
582 def __init__(self, *perms):
583 self.required_perms = set(perms)
583 self.required_perms = set(perms)
584
584
585 def __call__(self, user, repo_name):
585 def __call__(self, user, repo_name):
586 usr = AuthUser()
586 usr = AuthUser()
587 usr.user_id = user.user_id
587 usr.user_id = user.user_id
588 usr.username = user.username
588 usr.username = user.username
589 usr.is_admin = user.admin
589 usr.is_admin = user.admin
590
590
591 try:
591 try:
592 self.user_perms = set([fill_perms(usr)\
592 self.user_perms = set([fill_perms(usr)\
593 .permissions['repositories'][repo_name]])
593 .permissions['repositories'][repo_name]])
594 except:
594 except:
595 self.user_perms = set()
595 self.user_perms = set()
596 self.granted_for = ''
596 self.granted_for = ''
597 self.username = user.username
597 self.username = user.username
598 self.repo_name = repo_name
598 self.repo_name = repo_name
599 return self.check_permissions()
599 return self.check_permissions()
600
600
601 def check_permissions(self):
601 def check_permissions(self):
602 log.debug('checking mercurial protocol '
602 log.debug('checking mercurial protocol '
603 'permissions for user:%s repository:%s',
603 'permissions %s for user:%s repository:%s', self.user_perms,
604 self.username, self.repo_name)
604 self.username, self.repo_name)
605 if self.required_perms.intersection(self.user_perms):
605 if self.required_perms.intersection(self.user_perms):
606 log.debug('permission granted')
606 log.debug('permission granted')
607 return True
607 return True
608 log.debug('permission denied')
608 log.debug('permission denied')
609 return False
609 return False
@@ -1,591 +1,590
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 import random
6 import random
7 import hashlib
7 import hashlib
8 import StringIO
8 import StringIO
9 from pygments.formatters import HtmlFormatter
9 from pygments.formatters import HtmlFormatter
10 from pygments import highlight as code_highlight
10 from pygments import highlight as code_highlight
11 from pylons import url
11 from pylons import url
12 from pylons.i18n.translation import _, ungettext
12 from pylons.i18n.translation import _, ungettext
13 from vcs.utils.annotate import annotate_highlight
13 from vcs.utils.annotate import annotate_highlight
14 from rhodecode.lib.utils import repo_name_slug
14 from rhodecode.lib.utils import repo_name_slug
15
15
16 from webhelpers.html import literal, HTML, escape
16 from webhelpers.html import literal, HTML, escape
17 from webhelpers.html.tools import *
17 from webhelpers.html.tools import *
18 from webhelpers.html.builder import make_tag
18 from webhelpers.html.builder import make_tag
19 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
19 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
20 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
20 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
21 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
21 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
22 password, textarea, title, ul, xml_declaration, radio
22 password, textarea, title, ul, xml_declaration, radio
23 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
23 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
24 mail_to, strip_links, strip_tags, tag_re
24 mail_to, strip_links, strip_tags, tag_re
25 from webhelpers.number import format_byte_size, format_bit_size
25 from webhelpers.number import format_byte_size, format_bit_size
26 from webhelpers.pylonslib import Flash as _Flash
26 from webhelpers.pylonslib import Flash as _Flash
27 from webhelpers.pylonslib.secure_form import secure_form
27 from webhelpers.pylonslib.secure_form import secure_form
28 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
28 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
29 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
29 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
30 replace_whitespace, urlify, truncate, wrap_paragraphs
30 replace_whitespace, urlify, truncate, wrap_paragraphs
31 from webhelpers.date import time_ago_in_words
31 from webhelpers.date import time_ago_in_words
32
32
33 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
33 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
34 convert_boolean_attrs, NotGiven
34 convert_boolean_attrs, NotGiven
35
35
36 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
36 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
37 """Reset button
37 """Reset button
38 """
38 """
39 _set_input_attrs(attrs, type, name, value)
39 _set_input_attrs(attrs, type, name, value)
40 _set_id_attr(attrs, id, name)
40 _set_id_attr(attrs, id, name)
41 convert_boolean_attrs(attrs, ["disabled"])
41 convert_boolean_attrs(attrs, ["disabled"])
42 return HTML.input(**attrs)
42 return HTML.input(**attrs)
43
43
44 reset = _reset
44 reset = _reset
45
45
46
46
47 def get_token():
47 def get_token():
48 """Return the current authentication token, creating one if one doesn't
48 """Return the current authentication token, creating one if one doesn't
49 already exist.
49 already exist.
50 """
50 """
51 token_key = "_authentication_token"
51 token_key = "_authentication_token"
52 from pylons import session
52 from pylons import session
53 if not token_key in session:
53 if not token_key in session:
54 try:
54 try:
55 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
55 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
56 except AttributeError: # Python < 2.4
56 except AttributeError: # Python < 2.4
57 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
57 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
58 session[token_key] = token
58 session[token_key] = token
59 if hasattr(session, 'save'):
59 if hasattr(session, 'save'):
60 session.save()
60 session.save()
61 return session[token_key]
61 return session[token_key]
62
62
63 class _GetError(object):
63 class _GetError(object):
64 """Get error from form_errors, and represent it as span wrapped error
64 """Get error from form_errors, and represent it as span wrapped error
65 message
65 message
66
66
67 :param field_name: field to fetch errors for
67 :param field_name: field to fetch errors for
68 :param form_errors: form errors dict
68 :param form_errors: form errors dict
69 """
69 """
70
70
71 def __call__(self, field_name, form_errors):
71 def __call__(self, field_name, form_errors):
72 tmpl = """<span class="error_msg">%s</span>"""
72 tmpl = """<span class="error_msg">%s</span>"""
73 if form_errors and form_errors.has_key(field_name):
73 if form_errors and form_errors.has_key(field_name):
74 return literal(tmpl % form_errors.get(field_name))
74 return literal(tmpl % form_errors.get(field_name))
75
75
76 get_error = _GetError()
76 get_error = _GetError()
77
77
78 class _ToolTip(object):
78 class _ToolTip(object):
79
79
80 def __call__(self, tooltip_title, trim_at=50):
80 def __call__(self, tooltip_title, trim_at=50):
81 """Special function just to wrap our text into nice formatted
81 """Special function just to wrap our text into nice formatted
82 autowrapped text
82 autowrapped text
83
83
84 :param tooltip_title:
84 :param tooltip_title:
85 """
85 """
86
86
87 return wrap_paragraphs(escape(tooltip_title), trim_at)\
87 return wrap_paragraphs(escape(tooltip_title), trim_at)\
88 .replace('\n', '<br/>')
88 .replace('\n', '<br/>')
89
89
90 def activate(self):
90 def activate(self):
91 """Adds tooltip mechanism to the given Html all tooltips have to have
91 """Adds tooltip mechanism to the given Html all tooltips have to have
92 set class `tooltip` and set attribute `tooltip_title`.
92 set class `tooltip` and set attribute `tooltip_title`.
93 Then a tooltip will be generated based on that. All with yui js tooltip
93 Then a tooltip will be generated based on that. All with yui js tooltip
94 """
94 """
95
95
96 js = '''
96 js = '''
97 YAHOO.util.Event.onDOMReady(function(){
97 YAHOO.util.Event.onDOMReady(function(){
98 function toolTipsId(){
98 function toolTipsId(){
99 var ids = [];
99 var ids = [];
100 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
100 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
101
101
102 for (var i = 0; i < tts.length; i++) {
102 for (var i = 0; i < tts.length; i++) {
103 //if element doesn't not have and id autogenerate one for tooltip
103 //if element doesn't not have and id autogenerate one for tooltip
104
104
105 if (!tts[i].id){
105 if (!tts[i].id){
106 tts[i].id='tt'+i*100;
106 tts[i].id='tt'+i*100;
107 }
107 }
108 ids.push(tts[i].id);
108 ids.push(tts[i].id);
109 }
109 }
110 return ids
110 return ids
111 };
111 };
112 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
112 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
113 context: toolTipsId(),
113 context: toolTipsId(),
114 monitorresize:false,
114 monitorresize:false,
115 xyoffset :[0,0],
115 xyoffset :[0,0],
116 autodismissdelay:300000,
116 autodismissdelay:300000,
117 hidedelay:5,
117 hidedelay:5,
118 showdelay:20,
118 showdelay:20,
119 });
119 });
120
120
121 // Set the text for the tooltip just before we display it. Lazy method
121 // Set the text for the tooltip just before we display it. Lazy method
122 myToolTips.contextTriggerEvent.subscribe(
122 myToolTips.contextTriggerEvent.subscribe(
123 function(type, args) {
123 function(type, args) {
124
124
125 var context = args[0];
125 var context = args[0];
126
126
127 //positioning of tooltip
127 //positioning of tooltip
128 var tt_w = this.element.clientWidth;//tooltip width
128 var tt_w = this.element.clientWidth;//tooltip width
129 var tt_h = this.element.clientHeight;//tooltip height
129 var tt_h = this.element.clientHeight;//tooltip height
130
130
131 var context_w = context.offsetWidth;
131 var context_w = context.offsetWidth;
132 var context_h = context.offsetHeight;
132 var context_h = context.offsetHeight;
133
133
134 var pos_x = YAHOO.util.Dom.getX(context);
134 var pos_x = YAHOO.util.Dom.getX(context);
135 var pos_y = YAHOO.util.Dom.getY(context);
135 var pos_y = YAHOO.util.Dom.getY(context);
136
136
137 var display_strategy = 'right';
137 var display_strategy = 'right';
138 var xy_pos = [0,0];
138 var xy_pos = [0,0];
139 switch (display_strategy){
139 switch (display_strategy){
140
140
141 case 'top':
141 case 'top':
142 var cur_x = (pos_x+context_w/2)-(tt_w/2);
142 var cur_x = (pos_x+context_w/2)-(tt_w/2);
143 var cur_y = (pos_y-tt_h-4);
143 var cur_y = (pos_y-tt_h-4);
144 xy_pos = [cur_x,cur_y];
144 xy_pos = [cur_x,cur_y];
145 break;
145 break;
146 case 'bottom':
146 case 'bottom':
147 var cur_x = (pos_x+context_w/2)-(tt_w/2);
147 var cur_x = (pos_x+context_w/2)-(tt_w/2);
148 var cur_y = pos_y+context_h+4;
148 var cur_y = pos_y+context_h+4;
149 xy_pos = [cur_x,cur_y];
149 xy_pos = [cur_x,cur_y];
150 break;
150 break;
151 case 'left':
151 case 'left':
152 var cur_x = (pos_x-tt_w-4);
152 var cur_x = (pos_x-tt_w-4);
153 var cur_y = pos_y-((tt_h/2)-context_h/2);
153 var cur_y = pos_y-((tt_h/2)-context_h/2);
154 xy_pos = [cur_x,cur_y];
154 xy_pos = [cur_x,cur_y];
155 break;
155 break;
156 case 'right':
156 case 'right':
157 var cur_x = (pos_x+context_w+4);
157 var cur_x = (pos_x+context_w+4);
158 var cur_y = pos_y-((tt_h/2)-context_h/2);
158 var cur_y = pos_y-((tt_h/2)-context_h/2);
159 xy_pos = [cur_x,cur_y];
159 xy_pos = [cur_x,cur_y];
160 break;
160 break;
161 default:
161 default:
162 var cur_x = (pos_x+context_w/2)-(tt_w/2);
162 var cur_x = (pos_x+context_w/2)-(tt_w/2);
163 var cur_y = pos_y-tt_h-4;
163 var cur_y = pos_y-tt_h-4;
164 xy_pos = [cur_x,cur_y];
164 xy_pos = [cur_x,cur_y];
165 break;
165 break;
166
166
167 }
167 }
168
168
169 this.cfg.setProperty("xy",xy_pos);
169 this.cfg.setProperty("xy",xy_pos);
170
170
171 });
171 });
172
172
173 //Mouse out
173 //Mouse out
174 myToolTips.contextMouseOutEvent.subscribe(
174 myToolTips.contextMouseOutEvent.subscribe(
175 function(type, args) {
175 function(type, args) {
176 var context = args[0];
176 var context = args[0];
177
177
178 });
178 });
179 });
179 });
180 '''
180 '''
181 return literal(js)
181 return literal(js)
182
182
183 tooltip = _ToolTip()
183 tooltip = _ToolTip()
184
184
185 class _FilesBreadCrumbs(object):
185 class _FilesBreadCrumbs(object):
186
186
187 def __call__(self, repo_name, rev, paths):
187 def __call__(self, repo_name, rev, paths):
188 if isinstance(paths, str):
188 if isinstance(paths, str):
189 paths = paths.decode('utf-8')
189 paths = paths.decode('utf-8')
190 url_l = [link_to(repo_name, url('files_home',
190 url_l = [link_to(repo_name, url('files_home',
191 repo_name=repo_name,
191 repo_name=repo_name,
192 revision=rev, f_path=''))]
192 revision=rev, f_path=''))]
193 paths_l = paths.split('/')
193 paths_l = paths.split('/')
194 for cnt, p in enumerate(paths_l):
194 for cnt, p in enumerate(paths_l):
195 if p != '':
195 if p != '':
196 url_l.append(link_to(p, url('files_home',
196 url_l.append(link_to(p, url('files_home',
197 repo_name=repo_name,
197 repo_name=repo_name,
198 revision=rev,
198 revision=rev,
199 f_path='/'.join(paths_l[:cnt + 1]))))
199 f_path='/'.join(paths_l[:cnt + 1]))))
200
200
201 return literal('/'.join(url_l))
201 return literal('/'.join(url_l))
202
202
203 files_breadcrumbs = _FilesBreadCrumbs()
203 files_breadcrumbs = _FilesBreadCrumbs()
204
204
205 class CodeHtmlFormatter(HtmlFormatter):
205 class CodeHtmlFormatter(HtmlFormatter):
206 """My code Html Formatter for source codes
206 """My code Html Formatter for source codes
207 """
207 """
208
208
209 def wrap(self, source, outfile):
209 def wrap(self, source, outfile):
210 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
210 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
211
211
212 def _wrap_code(self, source):
212 def _wrap_code(self, source):
213 for cnt, it in enumerate(source):
213 for cnt, it in enumerate(source):
214 i, t = it
214 i, t = it
215 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
215 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
216 yield i, t
216 yield i, t
217
217
218 def _wrap_tablelinenos(self, inner):
218 def _wrap_tablelinenos(self, inner):
219 dummyoutfile = StringIO.StringIO()
219 dummyoutfile = StringIO.StringIO()
220 lncount = 0
220 lncount = 0
221 for t, line in inner:
221 for t, line in inner:
222 if t:
222 if t:
223 lncount += 1
223 lncount += 1
224 dummyoutfile.write(line)
224 dummyoutfile.write(line)
225
225
226 fl = self.linenostart
226 fl = self.linenostart
227 mw = len(str(lncount + fl - 1))
227 mw = len(str(lncount + fl - 1))
228 sp = self.linenospecial
228 sp = self.linenospecial
229 st = self.linenostep
229 st = self.linenostep
230 la = self.lineanchors
230 la = self.lineanchors
231 aln = self.anchorlinenos
231 aln = self.anchorlinenos
232 nocls = self.noclasses
232 nocls = self.noclasses
233 if sp:
233 if sp:
234 lines = []
234 lines = []
235
235
236 for i in range(fl, fl + lncount):
236 for i in range(fl, fl + lncount):
237 if i % st == 0:
237 if i % st == 0:
238 if i % sp == 0:
238 if i % sp == 0:
239 if aln:
239 if aln:
240 lines.append('<a href="#%s%d" class="special">%*d</a>' %
240 lines.append('<a href="#%s%d" class="special">%*d</a>' %
241 (la, i, mw, i))
241 (la, i, mw, i))
242 else:
242 else:
243 lines.append('<span class="special">%*d</span>' % (mw, i))
243 lines.append('<span class="special">%*d</span>' % (mw, i))
244 else:
244 else:
245 if aln:
245 if aln:
246 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
246 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
247 else:
247 else:
248 lines.append('%*d' % (mw, i))
248 lines.append('%*d' % (mw, i))
249 else:
249 else:
250 lines.append('')
250 lines.append('')
251 ls = '\n'.join(lines)
251 ls = '\n'.join(lines)
252 else:
252 else:
253 lines = []
253 lines = []
254 for i in range(fl, fl + lncount):
254 for i in range(fl, fl + lncount):
255 if i % st == 0:
255 if i % st == 0:
256 if aln:
256 if aln:
257 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
257 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
258 else:
258 else:
259 lines.append('%*d' % (mw, i))
259 lines.append('%*d' % (mw, i))
260 else:
260 else:
261 lines.append('')
261 lines.append('')
262 ls = '\n'.join(lines)
262 ls = '\n'.join(lines)
263
263
264 # in case you wonder about the seemingly redundant <div> here: since the
264 # in case you wonder about the seemingly redundant <div> here: since the
265 # content in the other cell also is wrapped in a div, some browsers in
265 # content in the other cell also is wrapped in a div, some browsers in
266 # some configurations seem to mess up the formatting...
266 # some configurations seem to mess up the formatting...
267 if nocls:
267 if nocls:
268 yield 0, ('<table class="%stable">' % self.cssclass +
268 yield 0, ('<table class="%stable">' % self.cssclass +
269 '<tr><td><div class="linenodiv" '
269 '<tr><td><div class="linenodiv" '
270 'style="background-color: #f0f0f0; padding-right: 10px">'
270 'style="background-color: #f0f0f0; padding-right: 10px">'
271 '<pre style="line-height: 125%">' +
271 '<pre style="line-height: 125%">' +
272 ls + '</pre></div></td><td class="code">')
272 ls + '</pre></div></td><td class="code">')
273 else:
273 else:
274 yield 0, ('<table class="%stable">' % self.cssclass +
274 yield 0, ('<table class="%stable">' % self.cssclass +
275 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
275 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
276 ls + '</pre></div></td><td class="code">')
276 ls + '</pre></div></td><td class="code">')
277 yield 0, dummyoutfile.getvalue()
277 yield 0, dummyoutfile.getvalue()
278 yield 0, '</td></tr></table>'
278 yield 0, '</td></tr></table>'
279
279
280
280
281 def pygmentize(filenode, **kwargs):
281 def pygmentize(filenode, **kwargs):
282 """pygmentize function using pygments
282 """pygmentize function using pygments
283
283
284 :param filenode:
284 :param filenode:
285 """
285 """
286
286
287 return literal(code_highlight(filenode.content,
287 return literal(code_highlight(filenode.content,
288 filenode.lexer, CodeHtmlFormatter(**kwargs)))
288 filenode.lexer, CodeHtmlFormatter(**kwargs)))
289
289
290 def pygmentize_annotation(filenode, **kwargs):
290 def pygmentize_annotation(filenode, **kwargs):
291 """pygmentize function for annotation
291 """pygmentize function for annotation
292
292
293 :param filenode:
293 :param filenode:
294 """
294 """
295
295
296 color_dict = {}
296 color_dict = {}
297 def gen_color(n=10000):
297 def gen_color(n=10000):
298 """generator for getting n of evenly distributed colors using
298 """generator for getting n of evenly distributed colors using
299 hsv color and golden ratio. It always return same order of colors
299 hsv color and golden ratio. It always return same order of colors
300
300
301 :returns: RGB tuple
301 :returns: RGB tuple
302 """
302 """
303 import colorsys
303 import colorsys
304 golden_ratio = 0.618033988749895
304 golden_ratio = 0.618033988749895
305 h = 0.22717784590367374
305 h = 0.22717784590367374
306
306
307 for c in xrange(n):
307 for c in xrange(n):
308 h += golden_ratio
308 h += golden_ratio
309 h %= 1
309 h %= 1
310 HSV_tuple = [h, 0.95, 0.95]
310 HSV_tuple = [h, 0.95, 0.95]
311 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
311 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
312 yield map(lambda x:str(int(x * 256)), RGB_tuple)
312 yield map(lambda x:str(int(x * 256)), RGB_tuple)
313
313
314 cgenerator = gen_color()
314 cgenerator = gen_color()
315
315
316 def get_color_string(cs):
316 def get_color_string(cs):
317 if color_dict.has_key(cs):
317 if color_dict.has_key(cs):
318 col = color_dict[cs]
318 col = color_dict[cs]
319 else:
319 else:
320 col = color_dict[cs] = cgenerator.next()
320 col = color_dict[cs] = cgenerator.next()
321 return "color: rgb(%s)! important;" % (', '.join(col))
321 return "color: rgb(%s)! important;" % (', '.join(col))
322
322
323 def url_func(changeset):
323 def url_func(changeset):
324 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
324 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
325 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
325 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
326
326
327 tooltip_html = tooltip_html % (changeset.author,
327 tooltip_html = tooltip_html % (changeset.author,
328 changeset.date,
328 changeset.date,
329 tooltip(changeset.message))
329 tooltip(changeset.message))
330 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
330 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
331 short_id(changeset.raw_id))
331 short_id(changeset.raw_id))
332 uri = link_to(
332 uri = link_to(
333 lnk_format,
333 lnk_format,
334 url('changeset_home', repo_name=changeset.repository.name,
334 url('changeset_home', repo_name=changeset.repository.name,
335 revision=changeset.raw_id),
335 revision=changeset.raw_id),
336 style=get_color_string(changeset.raw_id),
336 style=get_color_string(changeset.raw_id),
337 class_='tooltip',
337 class_='tooltip',
338 title=tooltip_html
338 title=tooltip_html
339 )
339 )
340
340
341 uri += '\n'
341 uri += '\n'
342 return uri
342 return uri
343 return literal(annotate_highlight(filenode, url_func, **kwargs))
343 return literal(annotate_highlight(filenode, url_func, **kwargs))
344
344
345 def get_changeset_safe(repo, rev):
345 def get_changeset_safe(repo, rev):
346 from vcs.backends.base import BaseRepository
346 from vcs.backends.base import BaseRepository
347 from vcs.exceptions import RepositoryError
347 from vcs.exceptions import RepositoryError
348 if not isinstance(repo, BaseRepository):
348 if not isinstance(repo, BaseRepository):
349 raise Exception('You must pass an Repository '
349 raise Exception('You must pass an Repository '
350 'object as first argument got %s', type(repo))
350 'object as first argument got %s', type(repo))
351
351
352 try:
352 try:
353 cs = repo.get_changeset(rev)
353 cs = repo.get_changeset(rev)
354 except RepositoryError:
354 except RepositoryError:
355 from rhodecode.lib.utils import EmptyChangeset
355 from rhodecode.lib.utils import EmptyChangeset
356 cs = EmptyChangeset()
356 cs = EmptyChangeset()
357 return cs
357 return cs
358
358
359
359
360 def is_following_repo(repo_name, user_id):
360 def is_following_repo(repo_name, user_id):
361 from rhodecode.model.scm import ScmModel
361 from rhodecode.model.scm import ScmModel
362 return ScmModel().is_following_repo(repo_name, user_id)
362 return ScmModel().is_following_repo(repo_name, user_id)
363
363
364 flash = _Flash()
364 flash = _Flash()
365
365
366
366
367 #==============================================================================
367 #==============================================================================
368 # MERCURIAL FILTERS available via h.
368 # MERCURIAL FILTERS available via h.
369 #==============================================================================
369 #==============================================================================
370 from mercurial import util
370 from mercurial import util
371 from mercurial.templatefilters import person as _person
371 from mercurial.templatefilters import person as _person
372
372
373 def _age(curdate):
373 def _age(curdate):
374 """turns a datetime into an age string."""
374 """turns a datetime into an age string."""
375
375
376 if not curdate:
376 if not curdate:
377 return ''
377 return ''
378
378
379 from datetime import timedelta, datetime
379 from datetime import timedelta, datetime
380
380
381 agescales = [("year", 3600 * 24 * 365),
381 agescales = [("year", 3600 * 24 * 365),
382 ("month", 3600 * 24 * 30),
382 ("month", 3600 * 24 * 30),
383 ("day", 3600 * 24),
383 ("day", 3600 * 24),
384 ("hour", 3600),
384 ("hour", 3600),
385 ("minute", 60),
385 ("minute", 60),
386 ("second", 1), ]
386 ("second", 1), ]
387
387
388 age = datetime.now() - curdate
388 age = datetime.now() - curdate
389 age_seconds = (age.days * agescales[2][1]) + age.seconds
389 age_seconds = (age.days * agescales[2][1]) + age.seconds
390 pos = 1
390 pos = 1
391 for scale in agescales:
391 for scale in agescales:
392 if scale[1] <= age_seconds:
392 if scale[1] <= age_seconds:
393 if pos == 6:pos = 5
393 if pos == 6:pos = 5
394 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
394 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
395 pos += 1
395 pos += 1
396
396
397 return _('just now')
397 return _('just now')
398
398
399 age = lambda x:_age(x)
399 age = lambda x:_age(x)
400 capitalize = lambda x: x.capitalize()
400 capitalize = lambda x: x.capitalize()
401 email = util.email
401 email = util.email
402 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
402 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
403 person = lambda x: _person(x)
403 person = lambda x: _person(x)
404 short_id = lambda x: x[:12]
404 short_id = lambda x: x[:12]
405
405
406
406
407 def bool2icon(value):
407 def bool2icon(value):
408 """Returns True/False values represented as small html image of true/false
408 """Returns True/False values represented as small html image of true/false
409 icons
409 icons
410
410
411 :param value: bool value
411 :param value: bool value
412 """
412 """
413
413
414 if value is True:
414 if value is True:
415 return HTML.tag('img', src="/images/icons/accept.png", alt=_('True'))
415 return HTML.tag('img', src="/images/icons/accept.png", alt=_('True'))
416
416
417 if value is False:
417 if value is False:
418 return HTML.tag('img', src="/images/icons/cancel.png", alt=_('False'))
418 return HTML.tag('img', src="/images/icons/cancel.png", alt=_('False'))
419
419
420 return value
420 return value
421
421
422
422
423 def action_parser(user_log):
423 def action_parser(user_log):
424 """This helper will map the specified string action into translated
424 """This helper will map the specified string action into translated
425 fancy names with icons and links
425 fancy names with icons and links
426
426
427 :param user_log: user log instance
427 :param user_log: user log instance
428 """
428 """
429
429
430 action = user_log.action
430 action = user_log.action
431 action_params = ' '
431 action_params = ' '
432
432
433 x = action.split(':')
433 x = action.split(':')
434
434
435 if len(x) > 1:
435 if len(x) > 1:
436 action, action_params = x
436 action, action_params = x
437
437
438 def get_cs_links():
438 def get_cs_links():
439 revs_limit = 5 #display this amount always
439 revs_limit = 5 #display this amount always
440 revs_top_limit = 50 #show upto this amount of changesets hidden
440 revs_top_limit = 50 #show upto this amount of changesets hidden
441 revs = action_params.split(',')
441 revs = action_params.split(',')
442 repo_name = user_log.repository.repo_name
442 repo_name = user_log.repository.repo_name
443 from rhodecode.model.scm import ScmModel
443 from rhodecode.model.scm import ScmModel
444
444 repo, dbrepo = ScmModel().get(repo_name, retval='repo')
445 message = lambda rev: get_changeset_safe(ScmModel().get(repo_name),
445 message = lambda rev: get_changeset_safe(repo, rev).message
446 rev).message
447
446
448 cs_links = " " + ', '.join ([link_to(rev,
447 cs_links = " " + ', '.join ([link_to(rev,
449 url('changeset_home',
448 url('changeset_home',
450 repo_name=repo_name,
449 repo_name=repo_name,
451 revision=rev), title=tooltip(message(rev)),
450 revision=rev), title=tooltip(message(rev)),
452 class_='tooltip') for rev in revs[:revs_limit] ])
451 class_='tooltip') for rev in revs[:revs_limit] ])
453
452
454 compare_view = (' <div class="compare_view tooltip" title="%s">'
453 compare_view = (' <div class="compare_view tooltip" title="%s">'
455 '<a href="%s">%s</a> '
454 '<a href="%s">%s</a> '
456 '</div>' % (_('Show all combined changesets %s->%s' \
455 '</div>' % (_('Show all combined changesets %s->%s' \
457 % (revs[0], revs[-1])),
456 % (revs[0], revs[-1])),
458 url('changeset_home', repo_name=repo_name,
457 url('changeset_home', repo_name=repo_name,
459 revision='%s...%s' % (revs[0], revs[-1])
458 revision='%s...%s' % (revs[0], revs[-1])
460 ),
459 ),
461 _('compare view'))
460 _('compare view'))
462 )
461 )
463
462
464 if len(revs) > revs_limit:
463 if len(revs) > revs_limit:
465 uniq_id = revs[0]
464 uniq_id = revs[0]
466 html_tmpl = ('<span> %s '
465 html_tmpl = ('<span> %s '
467 '<a class="show_more" id="_%s" href="#more">%s</a> '
466 '<a class="show_more" id="_%s" href="#more">%s</a> '
468 '%s</span>')
467 '%s</span>')
469 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
468 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
470 % (len(revs) - revs_limit),
469 % (len(revs) - revs_limit),
471 _('revisions'))
470 _('revisions'))
472
471
473 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
472 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
474 cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
473 cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
475 url('changeset_home',
474 url('changeset_home',
476 repo_name=repo_name, revision=rev),
475 repo_name=repo_name, revision=rev),
477 title=message(rev), class_='tooltip')
476 title=message(rev), class_='tooltip')
478 for rev in revs[revs_limit:revs_top_limit]]))
477 for rev in revs[revs_limit:revs_top_limit]]))
479 if len(revs) > 1:
478 if len(revs) > 1:
480 cs_links += compare_view
479 cs_links += compare_view
481 return cs_links
480 return cs_links
482
481
483 def get_fork_name():
482 def get_fork_name():
484 from rhodecode.model.scm import ScmModel
483 from rhodecode.model.scm import ScmModel
485 repo_name = action_params
484 repo_name = action_params
486 repo, dbrepo = ScmModel().get(repo_name)
485 repo, dbrepo = ScmModel().get(repo_name)
487 if repo is None:
486 if repo is None:
488 return repo_name
487 return repo_name
489 return link_to(action_params, url('summary_home',
488 return link_to(action_params, url('summary_home',
490 repo_name=repo.name,),
489 repo_name=repo.name,),
491 title=dbrepo.description)
490 title=dbrepo.description)
492
491
493 map = {'user_deleted_repo':(_('User [deleted] repository'), None),
492 map = {'user_deleted_repo':(_('User [deleted] repository'), None),
494 'user_created_repo':(_('User [created] repository'), None),
493 'user_created_repo':(_('User [created] repository'), None),
495 'user_forked_repo':(_('User [forked] repository as:'), get_fork_name),
494 'user_forked_repo':(_('User [forked] repository as:'), get_fork_name),
496 'user_updated_repo':(_('User [updated] repository'), None),
495 'user_updated_repo':(_('User [updated] repository'), None),
497 'admin_deleted_repo':(_('Admin [delete] repository'), None),
496 'admin_deleted_repo':(_('Admin [delete] repository'), None),
498 'admin_created_repo':(_('Admin [created] repository'), None),
497 'admin_created_repo':(_('Admin [created] repository'), None),
499 'admin_forked_repo':(_('Admin [forked] repository'), None),
498 'admin_forked_repo':(_('Admin [forked] repository'), None),
500 'admin_updated_repo':(_('Admin [updated] repository'), None),
499 'admin_updated_repo':(_('Admin [updated] repository'), None),
501 'push':(_('[Pushed]'), get_cs_links),
500 'push':(_('[Pushed]'), get_cs_links),
502 'pull':(_('[Pulled]'), None),
501 'pull':(_('[Pulled]'), None),
503 'started_following_repo':(_('User [started following] repository'), None),
502 'started_following_repo':(_('User [started following] repository'), None),
504 'stopped_following_repo':(_('User [stopped following] repository'), None),
503 'stopped_following_repo':(_('User [stopped following] repository'), None),
505 }
504 }
506
505
507 action_str = map.get(action, action)
506 action_str = map.get(action, action)
508 action = action_str[0].replace('[', '<span class="journal_highlight">')\
507 action = action_str[0].replace('[', '<span class="journal_highlight">')\
509 .replace(']', '</span>')
508 .replace(']', '</span>')
510 if action_str[1] is not None:
509 if action_str[1] is not None:
511 action = action + " " + action_str[1]()
510 action = action + " " + action_str[1]()
512
511
513 return literal(action)
512 return literal(action)
514
513
515 def action_parser_icon(user_log):
514 def action_parser_icon(user_log):
516 action = user_log.action
515 action = user_log.action
517 action_params = None
516 action_params = None
518 x = action.split(':')
517 x = action.split(':')
519
518
520 if len(x) > 1:
519 if len(x) > 1:
521 action, action_params = x
520 action, action_params = x
522
521
523 tmpl = """<img src="/images/icons/%s" alt="%s"/>"""
522 tmpl = """<img src="/images/icons/%s" alt="%s"/>"""
524 map = {'user_deleted_repo':'database_delete.png',
523 map = {'user_deleted_repo':'database_delete.png',
525 'user_created_repo':'database_add.png',
524 'user_created_repo':'database_add.png',
526 'user_forked_repo':'arrow_divide.png',
525 'user_forked_repo':'arrow_divide.png',
527 'user_updated_repo':'database_edit.png',
526 'user_updated_repo':'database_edit.png',
528 'admin_deleted_repo':'database_delete.png',
527 'admin_deleted_repo':'database_delete.png',
529 'admin_created_repo':'database_add.png',
528 'admin_created_repo':'database_add.png',
530 'admin_forked_repo':'arrow_divide.png',
529 'admin_forked_repo':'arrow_divide.png',
531 'admin_updated_repo':'database_edit.png',
530 'admin_updated_repo':'database_edit.png',
532 'push':'script_add.png',
531 'push':'script_add.png',
533 'pull':'down_16.png',
532 'pull':'down_16.png',
534 'started_following_repo':'heart_add.png',
533 'started_following_repo':'heart_add.png',
535 'stopped_following_repo':'heart_delete.png',
534 'stopped_following_repo':'heart_delete.png',
536 }
535 }
537 return literal(tmpl % (map.get(action, action), action))
536 return literal(tmpl % (map.get(action, action), action))
538
537
539
538
540 #==============================================================================
539 #==============================================================================
541 # PERMS
540 # PERMS
542 #==============================================================================
541 #==============================================================================
543 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
542 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
544 HasRepoPermissionAny, HasRepoPermissionAll
543 HasRepoPermissionAny, HasRepoPermissionAll
545
544
546 #==============================================================================
545 #==============================================================================
547 # GRAVATAR URL
546 # GRAVATAR URL
548 #==============================================================================
547 #==============================================================================
549 import hashlib
548 import hashlib
550 import urllib
549 import urllib
551 from pylons import request
550 from pylons import request
552
551
553 def gravatar_url(email_address, size=30):
552 def gravatar_url(email_address, size=30):
554 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
553 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
555 default = 'identicon'
554 default = 'identicon'
556 baseurl_nossl = "http://www.gravatar.com/avatar/"
555 baseurl_nossl = "http://www.gravatar.com/avatar/"
557 baseurl_ssl = "https://secure.gravatar.com/avatar/"
556 baseurl_ssl = "https://secure.gravatar.com/avatar/"
558 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
557 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
559
558
560
559
561 # construct the url
560 # construct the url
562 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
561 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
563 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
562 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
564
563
565 return gravatar_url
564 return gravatar_url
566
565
567 def safe_unicode(str):
566 def safe_unicode(str):
568 """safe unicode function. In case of UnicodeDecode error we try to return
567 """safe unicode function. In case of UnicodeDecode error we try to return
569 unicode with errors replace, if this failes we return unicode with
568 unicode with errors replace, if this failes we return unicode with
570 string_escape decoding """
569 string_escape decoding """
571
570
572 try:
571 try:
573 u_str = unicode(str)
572 u_str = unicode(str)
574 except UnicodeDecodeError:
573 except UnicodeDecodeError:
575 try:
574 try:
576 u_str = unicode(str, 'utf-8', 'replace')
575 u_str = unicode(str, 'utf-8', 'replace')
577 except UnicodeDecodeError:
576 except UnicodeDecodeError:
578 #incase we have a decode error just represent as byte string
577 #incase we have a decode error just represent as byte string
579 u_str = unicode(str(str).encode('string_escape'))
578 u_str = unicode(str(str).encode('string_escape'))
580
579
581 return u_str
580 return u_str
582
581
583 def changed_tooltip(nodes):
582 def changed_tooltip(nodes):
584 if nodes:
583 if nodes:
585 pref = ': <br/> '
584 pref = ': <br/> '
586 suf = ''
585 suf = ''
587 if len(nodes) > 30:
586 if len(nodes) > 30:
588 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
587 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
589 return literal(pref + '<br/> '.join([x.path for x in nodes[:30]]) + suf)
588 return literal(pref + '<br/> '.join([x.path for x in nodes[:30]]) + suf)
590 else:
589 else:
591 return ': ' + _('No Files')
590 return ': ' + _('No Files')
General Comments 0
You need to be logged in to leave comments. Login now