##// END OF EJS Templates
fixed @repo into :repo for docs...
marcink -
r604:5cc96df7 default
parent child Browse files
Show More
@@ -1,181 +1,189 b''
1 """Routes configuration
1 """
2 Routes configuration
2
3
3 The more specific and detailed routes should be defined first so they
4 The more specific and detailed routes should be defined first so they
4 may take precedent over the more generic routes. For more information
5 may take precedent over the more generic routes. For more information
5 refer to the routes manual at http://routes.groovie.org/docs/
6 refer to the routes manual at http://routes.groovie.org/docs/
6 """
7 """
7 from __future__ import with_statement
8 from __future__ import with_statement
8 from routes import Mapper
9 from routes import Mapper
9 from rhodecode.lib.utils import check_repo_fast as cr
10 from rhodecode.lib.utils import check_repo_fast as cr
10
11
11 def make_map(config):
12 def make_map(config):
12 """Create, configure and return the routes Mapper"""
13 """Create, configure and return the routes Mapper"""
13 map = Mapper(directory=config['pylons.paths']['controllers'],
14 map = Mapper(directory=config['pylons.paths']['controllers'],
14 always_scan=config['debug'])
15 always_scan=config['debug'])
15 map.minimization = False
16 map.minimization = False
16 map.explicit = False
17 map.explicit = False
17
18
19 def check_repo(environ, match_dict):
20 """
21 check for valid repository for proper 404 handling
22 :param environ:
23 :param match_dict:
24 """
25 repo_name = match_dict.get('repo_name')
26 return not cr(repo_name, config['base_path'])
27
18 # The ErrorController route (handles 404/500 error pages); it should
28 # The ErrorController route (handles 404/500 error pages); it should
19 # likely stay at the top, ensuring it can always be resolved
29 # likely stay at the top, ensuring it can always be resolved
20 map.connect('/error/{action}', controller='error')
30 map.connect('/error/{action}', controller='error')
21 map.connect('/error/{action}/{id}', controller='error')
31 map.connect('/error/{action}/{id}', controller='error')
22
32
33 #==========================================================================
23 # CUSTOM ROUTES HERE
34 # CUSTOM ROUTES HERE
35 #==========================================================================
36
37 #MAIN PAGE
24 map.connect('hg_home', '/', controller='hg', action='index')
38 map.connect('hg_home', '/', controller='hg', action='index')
25
39
26 def check_repo(environ, match_dict):
40 #ADMIN REPOSITORY REST ROUTES
27 """
28 check for valid repository for proper 404 handling
29 @param environ:
30 @param match_dict:
31 """
32 repo_name = match_dict.get('repo_name')
33 return not cr(repo_name, config['base_path'])
34
35 #REST REPO MAP
36 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
41 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
37 m.connect("repos", "/repos",
42 m.connect("repos", "/repos",
38 action="create", conditions=dict(method=["POST"]))
43 action="create", conditions=dict(method=["POST"]))
39 m.connect("repos", "/repos",
44 m.connect("repos", "/repos",
40 action="index", conditions=dict(method=["GET"]))
45 action="index", conditions=dict(method=["GET"]))
41 m.connect("formatted_repos", "/repos.{format}",
46 m.connect("formatted_repos", "/repos.{format}",
42 action="index",
47 action="index",
43 conditions=dict(method=["GET"]))
48 conditions=dict(method=["GET"]))
44 m.connect("new_repo", "/repos/new",
49 m.connect("new_repo", "/repos/new",
45 action="new", conditions=dict(method=["GET"]))
50 action="new", conditions=dict(method=["GET"]))
46 m.connect("formatted_new_repo", "/repos/new.{format}",
51 m.connect("formatted_new_repo", "/repos/new.{format}",
47 action="new", conditions=dict(method=["GET"]))
52 action="new", conditions=dict(method=["GET"]))
48 m.connect("/repos/{repo_name:.*}",
53 m.connect("/repos/{repo_name:.*}",
49 action="update", conditions=dict(method=["PUT"],
54 action="update", conditions=dict(method=["PUT"],
50 function=check_repo))
55 function=check_repo))
51 m.connect("/repos/{repo_name:.*}",
56 m.connect("/repos/{repo_name:.*}",
52 action="delete", conditions=dict(method=["DELETE"],
57 action="delete", conditions=dict(method=["DELETE"],
53 function=check_repo))
58 function=check_repo))
54 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
59 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
55 action="edit", conditions=dict(method=["GET"],
60 action="edit", conditions=dict(method=["GET"],
56 function=check_repo))
61 function=check_repo))
57 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
62 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
58 action="edit", conditions=dict(method=["GET"],
63 action="edit", conditions=dict(method=["GET"],
59 function=check_repo))
64 function=check_repo))
60 m.connect("repo", "/repos/{repo_name:.*}",
65 m.connect("repo", "/repos/{repo_name:.*}",
61 action="show", conditions=dict(method=["GET"],
66 action="show", conditions=dict(method=["GET"],
62 function=check_repo))
67 function=check_repo))
63 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
68 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
64 action="show", conditions=dict(method=["GET"],
69 action="show", conditions=dict(method=["GET"],
65 function=check_repo))
70 function=check_repo))
66 #ajax delete repo perm user
71 #ajax delete repo perm user
67 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
72 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
68 action="delete_perm_user", conditions=dict(method=["DELETE"],
73 action="delete_perm_user", conditions=dict(method=["DELETE"],
69 function=check_repo))
74 function=check_repo))
70
75
76 #ADMIN USER REST ROUTES
71 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
77 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
78
79 #ADMIN PERMISSIONS REST ROUTES
72 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
80 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
73
81
74 #REST SETTINGS MAP
82 #ADMIN SETTINGS REST ROUTES
75 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
83 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
76 m.connect("admin_settings", "/settings",
84 m.connect("admin_settings", "/settings",
77 action="create", conditions=dict(method=["POST"]))
85 action="create", conditions=dict(method=["POST"]))
78 m.connect("admin_settings", "/settings",
86 m.connect("admin_settings", "/settings",
79 action="index", conditions=dict(method=["GET"]))
87 action="index", conditions=dict(method=["GET"]))
80 m.connect("formatted_admin_settings", "/settings.{format}",
88 m.connect("formatted_admin_settings", "/settings.{format}",
81 action="index", conditions=dict(method=["GET"]))
89 action="index", conditions=dict(method=["GET"]))
82 m.connect("admin_new_setting", "/settings/new",
90 m.connect("admin_new_setting", "/settings/new",
83 action="new", conditions=dict(method=["GET"]))
91 action="new", conditions=dict(method=["GET"]))
84 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
92 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
85 action="new", conditions=dict(method=["GET"]))
93 action="new", conditions=dict(method=["GET"]))
86 m.connect("/settings/{setting_id}",
94 m.connect("/settings/{setting_id}",
87 action="update", conditions=dict(method=["PUT"]))
95 action="update", conditions=dict(method=["PUT"]))
88 m.connect("/settings/{setting_id}",
96 m.connect("/settings/{setting_id}",
89 action="delete", conditions=dict(method=["DELETE"]))
97 action="delete", conditions=dict(method=["DELETE"]))
90 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
98 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
91 action="edit", conditions=dict(method=["GET"]))
99 action="edit", conditions=dict(method=["GET"]))
92 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
100 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
93 action="edit", conditions=dict(method=["GET"]))
101 action="edit", conditions=dict(method=["GET"]))
94 m.connect("admin_setting", "/settings/{setting_id}",
102 m.connect("admin_setting", "/settings/{setting_id}",
95 action="show", conditions=dict(method=["GET"]))
103 action="show", conditions=dict(method=["GET"]))
96 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
104 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
97 action="show", conditions=dict(method=["GET"]))
105 action="show", conditions=dict(method=["GET"]))
98 m.connect("admin_settings_my_account", "/my_account",
106 m.connect("admin_settings_my_account", "/my_account",
99 action="my_account", conditions=dict(method=["GET"]))
107 action="my_account", conditions=dict(method=["GET"]))
100 m.connect("admin_settings_my_account_update", "/my_account_update",
108 m.connect("admin_settings_my_account_update", "/my_account_update",
101 action="my_account_update", conditions=dict(method=["PUT"]))
109 action="my_account_update", conditions=dict(method=["PUT"]))
102 m.connect("admin_settings_create_repository", "/create_repository",
110 m.connect("admin_settings_create_repository", "/create_repository",
103 action="create_repository", conditions=dict(method=["GET"]))
111 action="create_repository", conditions=dict(method=["GET"]))
104
112
105 #ADMIN
113 #ADMIN MAIN PAGES
106 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
114 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
107 m.connect('admin_home', '', action='index')#main page
115 m.connect('admin_home', '', action='index')#main page
108 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
116 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
109 action='add_repo')
117 action='add_repo')
110 #SEARCH
118 #SEARCH
111 map.connect('search', '/_admin/search', controller='search',)
119 map.connect('search', '/_admin/search', controller='search',)
112 map.connect('search_repo', '/_admin/search/{search_repo:.*}', controller='search')
120 map.connect('search_repo', '/_admin/search/{search_repo:.*}', controller='search')
113
121
114 #LOGIN/LOGOUT/REGISTER/SIGN IN
122 #LOGIN/LOGOUT/REGISTER/SIGN IN
115 map.connect('login_home', '/_admin/login', controller='login')
123 map.connect('login_home', '/_admin/login', controller='login')
116 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
124 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
117 map.connect('register', '/_admin/register', controller='login', action='register')
125 map.connect('register', '/_admin/register', controller='login', action='register')
118 map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
126 map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
119
127
120 #FEEDS
128 #FEEDS
121 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
129 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
122 controller='feed', action='rss',
130 controller='feed', action='rss',
123 conditions=dict(function=check_repo))
131 conditions=dict(function=check_repo))
124 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
132 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
125 controller='feed', action='atom',
133 controller='feed', action='atom',
126 conditions=dict(function=check_repo))
134 conditions=dict(function=check_repo))
127
135
128
136
129 #OTHERS
137 #REPOSITORY ROUTES
130 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
138 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
131 controller='changeset', revision='tip',
139 controller='changeset', revision='tip',
132 conditions=dict(function=check_repo))
140 conditions=dict(function=check_repo))
133 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
141 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
134 controller='changeset', action='raw_changeset', revision='tip',
142 controller='changeset', action='raw_changeset', revision='tip',
135 conditions=dict(function=check_repo))
143 conditions=dict(function=check_repo))
136 map.connect('summary_home', '/{repo_name:.*}/summary',
144 map.connect('summary_home', '/{repo_name:.*}/summary',
137 controller='summary', conditions=dict(function=check_repo))
145 controller='summary', conditions=dict(function=check_repo))
138 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
146 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
139 controller='shortlog', conditions=dict(function=check_repo))
147 controller='shortlog', conditions=dict(function=check_repo))
140 map.connect('branches_home', '/{repo_name:.*}/branches',
148 map.connect('branches_home', '/{repo_name:.*}/branches',
141 controller='branches', conditions=dict(function=check_repo))
149 controller='branches', conditions=dict(function=check_repo))
142 map.connect('tags_home', '/{repo_name:.*}/tags',
150 map.connect('tags_home', '/{repo_name:.*}/tags',
143 controller='tags', conditions=dict(function=check_repo))
151 controller='tags', conditions=dict(function=check_repo))
144 map.connect('changelog_home', '/{repo_name:.*}/changelog',
152 map.connect('changelog_home', '/{repo_name:.*}/changelog',
145 controller='changelog', conditions=dict(function=check_repo))
153 controller='changelog', conditions=dict(function=check_repo))
146 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
154 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
147 controller='files', revision='tip', f_path='',
155 controller='files', revision='tip', f_path='',
148 conditions=dict(function=check_repo))
156 conditions=dict(function=check_repo))
149 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
157 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
150 controller='files', action='diff', revision='tip', f_path='',
158 controller='files', action='diff', revision='tip', f_path='',
151 conditions=dict(function=check_repo))
159 conditions=dict(function=check_repo))
152 map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
160 map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
153 controller='files', action='rawfile', revision='tip', f_path='',
161 controller='files', action='rawfile', revision='tip', f_path='',
154 conditions=dict(function=check_repo))
162 conditions=dict(function=check_repo))
155 map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
163 map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
156 controller='files', action='raw', revision='tip', f_path='',
164 controller='files', action='raw', revision='tip', f_path='',
157 conditions=dict(function=check_repo))
165 conditions=dict(function=check_repo))
158 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
166 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
159 controller='files', action='annotate', revision='tip', f_path='',
167 controller='files', action='annotate', revision='tip', f_path='',
160 conditions=dict(function=check_repo))
168 conditions=dict(function=check_repo))
161 map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
169 map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
162 controller='files', action='archivefile', revision='tip',
170 controller='files', action='archivefile', revision='tip',
163 conditions=dict(function=check_repo))
171 conditions=dict(function=check_repo))
164 map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
172 map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
165 controller='settings', action="delete",
173 controller='settings', action="delete",
166 conditions=dict(method=["DELETE"], function=check_repo))
174 conditions=dict(method=["DELETE"], function=check_repo))
167 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
175 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
168 controller='settings', action="update",
176 controller='settings', action="update",
169 conditions=dict(method=["PUT"], function=check_repo))
177 conditions=dict(method=["PUT"], function=check_repo))
170 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
178 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
171 controller='settings', action='index',
179 controller='settings', action='index',
172 conditions=dict(function=check_repo))
180 conditions=dict(function=check_repo))
173
181
174 map.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
182 map.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
175 controller='settings', action='fork_create',
183 controller='settings', action='fork_create',
176 conditions=dict(function=check_repo, method=["POST"]))
184 conditions=dict(function=check_repo, method=["POST"]))
177 map.connect('repo_fork_home', '/{repo_name:.*}/fork',
185 map.connect('repo_fork_home', '/{repo_name:.*}/fork',
178 controller='settings', action='fork',
186 controller='settings', action='fork',
179 conditions=dict(function=check_repo))
187 conditions=dict(function=check_repo))
180
188
181 return map
189 return map
@@ -1,245 +1,245 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # repos controller for pylons
3 # repos controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 7, 2010
21 Created on April 7, 2010
22 admin controller for pylons
22 admin controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from operator import itemgetter
26 from operator import itemgetter
27 from paste.httpexceptions import HTTPInternalServerError
27 from paste.httpexceptions import HTTPInternalServerError
28 from pylons import request, response, session, tmpl_context as c, url
28 from pylons import request, response, session, tmpl_context as c, url
29 from pylons.controllers.util import abort, redirect
29 from pylons.controllers.util import abort, redirect
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
33 HasPermissionAnyDecorator
33 HasPermissionAnyDecorator
34 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.utils import invalidate_cache, action_logger
35 from rhodecode.lib.utils import invalidate_cache, action_logger
36 from rhodecode.model.db import User
36 from rhodecode.model.db import User
37 from rhodecode.model.forms import RepoForm
37 from rhodecode.model.forms import RepoForm
38 from rhodecode.model.hg_model import HgModel
38 from rhodecode.model.hg_model import HgModel
39 from rhodecode.model.repo_model import RepoModel
39 from rhodecode.model.repo_model import RepoModel
40 import formencode
40 import formencode
41 import logging
41 import logging
42 import traceback
42 import traceback
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class ReposController(BaseController):
46 class ReposController(BaseController):
47 """REST Controller styled on the Atom Publishing Protocol"""
47 """REST Controller styled on the Atom Publishing Protocol"""
48 # To properly map this controller, ensure your config/routing.py
48 # To properly map this controller, ensure your config/routing.py
49 # file has a resource setup:
49 # file has a resource setup:
50 # map.resource('repo', 'repos')
50 # map.resource('repo', 'repos')
51
51
52 @LoginRequired()
52 @LoginRequired()
53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
54 def __before__(self):
54 def __before__(self):
55 c.admin_user = session.get('admin_user')
55 c.admin_user = session.get('admin_user')
56 c.admin_username = session.get('admin_username')
56 c.admin_username = session.get('admin_username')
57 super(ReposController, self).__before__()
57 super(ReposController, self).__before__()
58
58
59 @HasPermissionAllDecorator('hg.admin')
59 @HasPermissionAllDecorator('hg.admin')
60 def index(self, format='html'):
60 def index(self, format='html'):
61 """GET /repos: All items in the collection"""
61 """GET /repos: All items in the collection"""
62 # url('repos')
62 # url('repos')
63 cached_repo_list = HgModel().get_repos()
63 cached_repo_list = HgModel().get_repos()
64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
65 return render('admin/repos/repos.html')
65 return render('admin/repos/repos.html')
66
66
67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
68 def create(self):
68 def create(self):
69 """POST /repos: Create a new item"""
69 """POST /repos: Create a new item"""
70 # url('repos')
70 # url('repos')
71 repo_model = RepoModel()
71 repo_model = RepoModel()
72 _form = RepoForm()()
72 _form = RepoForm()()
73 form_result = {}
73 form_result = {}
74 try:
74 try:
75 form_result = _form.to_python(dict(request.POST))
75 form_result = _form.to_python(dict(request.POST))
76 repo_model.create(form_result, c.rhodecode_user)
76 repo_model.create(form_result, c.rhodecode_user)
77 invalidate_cache('cached_repo_list')
77 invalidate_cache('cached_repo_list')
78 h.flash(_('created repository %s') % form_result['repo_name'],
78 h.flash(_('created repository %s') % form_result['repo_name'],
79 category='success')
79 category='success')
80
80
81 if request.POST.get('user_created'):
81 if request.POST.get('user_created'):
82 action_logger(self.rhodecode_user, 'user_created_repo',
82 action_logger(self.rhodecode_user, 'user_created_repo',
83 form_result['repo_name'], '', self.sa)
83 form_result['repo_name'], '', self.sa)
84 else:
84 else:
85 action_logger(self.rhodecode_user, 'admin_created_repo',
85 action_logger(self.rhodecode_user, 'admin_created_repo',
86 form_result['repo_name'], '', self.sa)
86 form_result['repo_name'], '', self.sa)
87
87
88 except formencode.Invalid, errors:
88 except formencode.Invalid, errors:
89 c.new_repo = errors.value['repo_name']
89 c.new_repo = errors.value['repo_name']
90
90
91 if request.POST.get('user_created'):
91 if request.POST.get('user_created'):
92 r = render('admin/repos/repo_add_create_repository.html')
92 r = render('admin/repos/repo_add_create_repository.html')
93 else:
93 else:
94 r = render('admin/repos/repo_add.html')
94 r = render('admin/repos/repo_add.html')
95
95
96 return htmlfill.render(
96 return htmlfill.render(
97 r,
97 r,
98 defaults=errors.value,
98 defaults=errors.value,
99 errors=errors.error_dict or {},
99 errors=errors.error_dict or {},
100 prefix_error=False,
100 prefix_error=False,
101 encoding="UTF-8")
101 encoding="UTF-8")
102
102
103 except Exception:
103 except Exception:
104 log.error(traceback.format_exc())
104 log.error(traceback.format_exc())
105 msg = _('error occured during creation of repository %s') \
105 msg = _('error occured during creation of repository %s') \
106 % form_result.get('repo_name')
106 % form_result.get('repo_name')
107 h.flash(msg, category='error')
107 h.flash(msg, category='error')
108 if request.POST.get('user_created'):
108 if request.POST.get('user_created'):
109 return redirect(url('hg_home'))
109 return redirect(url('hg_home'))
110 return redirect(url('repos'))
110 return redirect(url('repos'))
111
111
112 @HasPermissionAllDecorator('hg.admin')
112 @HasPermissionAllDecorator('hg.admin')
113 def new(self, format='html'):
113 def new(self, format='html'):
114 """GET /repos/new: Form to create a new item"""
114 """GET /repos/new: Form to create a new item"""
115 new_repo = request.GET.get('repo', '')
115 new_repo = request.GET.get('repo', '')
116 c.new_repo = h.repo_name_slug(new_repo)
116 c.new_repo = h.repo_name_slug(new_repo)
117
117
118 return render('admin/repos/repo_add.html')
118 return render('admin/repos/repo_add.html')
119
119
120 @HasPermissionAllDecorator('hg.admin')
120 @HasPermissionAllDecorator('hg.admin')
121 def update(self, repo_name):
121 def update(self, repo_name):
122 """PUT /repos/repo_name: Update an existing item"""
122 """PUT /repos/repo_name: Update an existing item"""
123 # Forms posted to this method should contain a hidden field:
123 # Forms posted to this method should contain a hidden field:
124 # <input type="hidden" name="_method" value="PUT" />
124 # <input type="hidden" name="_method" value="PUT" />
125 # Or using helpers:
125 # Or using helpers:
126 # h.form(url('repo', repo_name=ID),
126 # h.form(url('repo', repo_name=ID),
127 # method='put')
127 # method='put')
128 # url('repo', repo_name=ID)
128 # url('repo', repo_name=ID)
129 repo_model = RepoModel()
129 repo_model = RepoModel()
130 changed_name = repo_name
130 changed_name = repo_name
131 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
131 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
132
132
133 try:
133 try:
134 form_result = _form.to_python(dict(request.POST))
134 form_result = _form.to_python(dict(request.POST))
135 repo_model.update(repo_name, form_result)
135 repo_model.update(repo_name, form_result)
136 invalidate_cache('cached_repo_list')
136 invalidate_cache('cached_repo_list')
137 h.flash(_('Repository %s updated succesfully' % repo_name),
137 h.flash(_('Repository %s updated succesfully' % repo_name),
138 category='success')
138 category='success')
139 changed_name = form_result['repo_name']
139 changed_name = form_result['repo_name']
140 except formencode.Invalid, errors:
140 except formencode.Invalid, errors:
141 c.repo_info = repo_model.get(repo_name)
141 c.repo_info = repo_model.get(repo_name)
142 c.users_array = repo_model.get_users_js()
142 c.users_array = repo_model.get_users_js()
143 errors.value.update({'user':c.repo_info.user.username})
143 errors.value.update({'user':c.repo_info.user.username})
144 return htmlfill.render(
144 return htmlfill.render(
145 render('admin/repos/repo_edit.html'),
145 render('admin/repos/repo_edit.html'),
146 defaults=errors.value,
146 defaults=errors.value,
147 errors=errors.error_dict or {},
147 errors=errors.error_dict or {},
148 prefix_error=False,
148 prefix_error=False,
149 encoding="UTF-8")
149 encoding="UTF-8")
150
150
151 except Exception:
151 except Exception:
152 log.error(traceback.format_exc())
152 log.error(traceback.format_exc())
153 h.flash(_('error occured during update of repository %s') \
153 h.flash(_('error occured during update of repository %s') \
154 % repo_name, category='error')
154 % repo_name, category='error')
155
155
156 return redirect(url('edit_repo', repo_name=changed_name))
156 return redirect(url('edit_repo', repo_name=changed_name))
157
157
158 @HasPermissionAllDecorator('hg.admin')
158 @HasPermissionAllDecorator('hg.admin')
159 def delete(self, repo_name):
159 def delete(self, repo_name):
160 """DELETE /repos/repo_name: Delete an existing item"""
160 """DELETE /repos/repo_name: Delete an existing item"""
161 # Forms posted to this method should contain a hidden field:
161 # Forms posted to this method should contain a hidden field:
162 # <input type="hidden" name="_method" value="DELETE" />
162 # <input type="hidden" name="_method" value="DELETE" />
163 # Or using helpers:
163 # Or using helpers:
164 # h.form(url('repo', repo_name=ID),
164 # h.form(url('repo', repo_name=ID),
165 # method='delete')
165 # method='delete')
166 # url('repo', repo_name=ID)
166 # url('repo', repo_name=ID)
167
167
168 repo_model = RepoModel()
168 repo_model = RepoModel()
169 repo = repo_model.get(repo_name)
169 repo = repo_model.get(repo_name)
170 if not repo:
170 if not repo:
171 h.flash(_('%s repository is not mapped to db perhaps'
171 h.flash(_('%s repository is not mapped to db perhaps'
172 ' it was moved or renamed from the filesystem'
172 ' it was moved or renamed from the filesystem'
173 ' please run the application again'
173 ' please run the application again'
174 ' in order to rescan repositories') % repo_name,
174 ' in order to rescan repositories') % repo_name,
175 category='error')
175 category='error')
176
176
177 return redirect(url('repos'))
177 return redirect(url('repos'))
178 try:
178 try:
179 action_logger(self.rhodecode_user, 'admin_deleted_repo',
179 action_logger(self.rhodecode_user, 'admin_deleted_repo',
180 repo_name, '', self.sa)
180 repo_name, '', self.sa)
181 repo_model.delete(repo)
181 repo_model.delete(repo)
182 invalidate_cache('cached_repo_list')
182 invalidate_cache('cached_repo_list')
183 h.flash(_('deleted repository %s') % repo_name, category='success')
183 h.flash(_('deleted repository %s') % repo_name, category='success')
184
184
185 except Exception, e:
185 except Exception, e:
186 log.error(traceback.format_exc())
186 log.error(traceback.format_exc())
187 h.flash(_('An error occured during deletion of %s') % repo_name,
187 h.flash(_('An error occured during deletion of %s') % repo_name,
188 category='error')
188 category='error')
189
189
190 return redirect(url('repos'))
190 return redirect(url('repos'))
191
191
192 @HasPermissionAllDecorator('hg.admin')
192 @HasPermissionAllDecorator('hg.admin')
193 def delete_perm_user(self, repo_name):
193 def delete_perm_user(self, repo_name):
194 """
194 """
195 DELETE an existing repository permission user
195 DELETE an existing repository permission user
196 @param repo_name:
196 :param repo_name:
197 """
197 """
198
198
199 try:
199 try:
200 repo_model = RepoModel()
200 repo_model = RepoModel()
201 repo_model.delete_perm_user(request.POST, repo_name)
201 repo_model.delete_perm_user(request.POST, repo_name)
202 except Exception, e:
202 except Exception, e:
203 h.flash(_('An error occured during deletion of repository user'),
203 h.flash(_('An error occured during deletion of repository user'),
204 category='error')
204 category='error')
205 raise HTTPInternalServerError()
205 raise HTTPInternalServerError()
206
206
207 @HasPermissionAllDecorator('hg.admin')
207 @HasPermissionAllDecorator('hg.admin')
208 def show(self, repo_name, format='html'):
208 def show(self, repo_name, format='html'):
209 """GET /repos/repo_name: Show a specific item"""
209 """GET /repos/repo_name: Show a specific item"""
210 # url('repo', repo_name=ID)
210 # url('repo', repo_name=ID)
211
211
212 @HasPermissionAllDecorator('hg.admin')
212 @HasPermissionAllDecorator('hg.admin')
213 def edit(self, repo_name, format='html'):
213 def edit(self, repo_name, format='html'):
214 """GET /repos/repo_name/edit: Form to edit an existing item"""
214 """GET /repos/repo_name/edit: Form to edit an existing item"""
215 # url('edit_repo', repo_name=ID)
215 # url('edit_repo', repo_name=ID)
216 repo_model = RepoModel()
216 repo_model = RepoModel()
217 c.repo_info = repo = repo_model.get(repo_name)
217 c.repo_info = repo = repo_model.get(repo_name)
218 if not repo:
218 if not repo:
219 h.flash(_('%s repository is not mapped to db perhaps'
219 h.flash(_('%s repository is not mapped to db perhaps'
220 ' it was created or renamed from the filesystem'
220 ' it was created or renamed from the filesystem'
221 ' please run the application again'
221 ' please run the application again'
222 ' in order to rescan repositories') % repo_name,
222 ' in order to rescan repositories') % repo_name,
223 category='error')
223 category='error')
224
224
225 return redirect(url('repos'))
225 return redirect(url('repos'))
226 defaults = c.repo_info.__dict__
226 defaults = c.repo_info.__dict__
227 if c.repo_info.user:
227 if c.repo_info.user:
228 defaults.update({'user':c.repo_info.user.username})
228 defaults.update({'user':c.repo_info.user.username})
229 else:
229 else:
230 replacement_user = self.sa.query(User)\
230 replacement_user = self.sa.query(User)\
231 .filter(User.admin == True).first().username
231 .filter(User.admin == True).first().username
232 defaults.update({'user':replacement_user})
232 defaults.update({'user':replacement_user})
233
233
234 c.users_array = repo_model.get_users_js()
234 c.users_array = repo_model.get_users_js()
235
235
236 for p in c.repo_info.repo_to_perm:
236 for p in c.repo_info.repo_to_perm:
237 defaults.update({'perm_%s' % p.user.username:
237 defaults.update({'perm_%s' % p.user.username:
238 p.permission.permission_name})
238 p.permission.permission_name})
239
239
240 return htmlfill.render(
240 return htmlfill.render(
241 render('admin/repos/repo_edit.html'),
241 render('admin/repos/repo_edit.html'),
242 defaults=defaults,
242 defaults=defaults,
243 encoding="UTF-8",
243 encoding="UTF-8",
244 force_defaults=False
244 force_defaults=False
245 )
245 )
@@ -1,486 +1,486 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # authentication and permission libraries
3 # authentication and permission libraries
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 4, 2010
21 Created on April 4, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from pylons import config, session, url, request
26 from pylons import config, session, url, request
27 from pylons.controllers.util import abort, redirect
27 from pylons.controllers.util import abort, redirect
28 from rhodecode.lib.utils import get_repo_slug
28 from rhodecode.lib.utils import get_repo_slug
29 from rhodecode.model import meta
29 from rhodecode.model import meta
30 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
30 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
31 UserToPerm
31 UserToPerm
32 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.exc import OperationalError
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
34 import bcrypt
34 import bcrypt
35 from decorator import decorator
35 from decorator import decorator
36 import logging
36 import logging
37 import random
37 import random
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 class PasswordGenerator(object):
41 class PasswordGenerator(object):
42 """This is a simple class for generating password from
42 """This is a simple class for generating password from
43 different sets of characters
43 different sets of characters
44 usage:
44 usage:
45 passwd_gen = PasswordGenerator()
45 passwd_gen = PasswordGenerator()
46 #print 8-letter password containing only big and small letters of alphabet
46 #print 8-letter password containing only big and small letters of alphabet
47 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
47 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
48 """
48 """
49 ALPHABETS_NUM = r'''1234567890'''#[0]
49 ALPHABETS_NUM = r'''1234567890'''#[0]
50 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
50 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
51 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
51 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
52 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
52 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
53 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
53 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
54 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
54 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
55 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
55 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
56 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
56 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
57 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
57 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
58
58
59 def __init__(self, passwd=''):
59 def __init__(self, passwd=''):
60 self.passwd = passwd
60 self.passwd = passwd
61
61
62 def gen_password(self, len, type):
62 def gen_password(self, len, type):
63 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
63 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
64 return self.passwd
64 return self.passwd
65
65
66
66
67 def get_crypt_password(password):
67 def get_crypt_password(password):
68 """Cryptographic function used for password hashing based on sha1
68 """Cryptographic function used for password hashing based on sha1
69 @param password: password to hash
69 :param password: password to hash
70 """
70 """
71 return bcrypt.hashpw(password, bcrypt.gensalt(10))
71 return bcrypt.hashpw(password, bcrypt.gensalt(10))
72
72
73 def check_password(password, hashed):
73 def check_password(password, hashed):
74 return bcrypt.hashpw(password, hashed) == hashed
74 return bcrypt.hashpw(password, hashed) == hashed
75
75
76 @cache_region('super_short_term', 'cached_user')
76 @cache_region('super_short_term', 'cached_user')
77 def get_user_cached(username):
77 def get_user_cached(username):
78 sa = meta.Session
78 sa = meta.Session
79 try:
79 try:
80 user = sa.query(User).filter(User.username == username).one()
80 user = sa.query(User).filter(User.username == username).one()
81 finally:
81 finally:
82 meta.Session.remove()
82 meta.Session.remove()
83 return user
83 return user
84
84
85 def authfunc(environ, username, password):
85 def authfunc(environ, username, password):
86 try:
86 try:
87 user = get_user_cached(username)
87 user = get_user_cached(username)
88 except (NoResultFound, MultipleResultsFound, OperationalError), e:
88 except (NoResultFound, MultipleResultsFound, OperationalError), e:
89 log.error(e)
89 log.error(e)
90 user = None
90 user = None
91
91
92 if user:
92 if user:
93 if user.active:
93 if user.active:
94 if user.username == username and check_password(password, user.password):
94 if user.username == username and check_password(password, user.password):
95 log.info('user %s authenticated correctly', username)
95 log.info('user %s authenticated correctly', username)
96 return True
96 return True
97 else:
97 else:
98 log.error('user %s is disabled', username)
98 log.error('user %s is disabled', username)
99
99
100 return False
100 return False
101
101
102 class AuthUser(object):
102 class AuthUser(object):
103 """
103 """
104 A simple object that handles a mercurial username for authentication
104 A simple object that handles a mercurial username for authentication
105 """
105 """
106 def __init__(self):
106 def __init__(self):
107 self.username = 'None'
107 self.username = 'None'
108 self.name = ''
108 self.name = ''
109 self.lastname = ''
109 self.lastname = ''
110 self.email = ''
110 self.email = ''
111 self.user_id = None
111 self.user_id = None
112 self.is_authenticated = False
112 self.is_authenticated = False
113 self.is_admin = False
113 self.is_admin = False
114 self.permissions = {}
114 self.permissions = {}
115
115
116
116
117 def set_available_permissions(config):
117 def set_available_permissions(config):
118 """
118 """
119 This function will propagate pylons globals with all available defined
119 This function will propagate pylons globals with all available defined
120 permission given in db. We don't wannt to check each time from db for new
120 permission given in db. We don't wannt to check each time from db for new
121 permissions since adding a new permission also requires application restart
121 permissions since adding a new permission also requires application restart
122 ie. to decorate new views with the newly created permission
122 ie. to decorate new views with the newly created permission
123 @param config:
123 :param config:
124 """
124 """
125 log.info('getting information about all available permissions')
125 log.info('getting information about all available permissions')
126 try:
126 try:
127 sa = meta.Session
127 sa = meta.Session
128 all_perms = sa.query(Permission).all()
128 all_perms = sa.query(Permission).all()
129 finally:
129 finally:
130 meta.Session.remove()
130 meta.Session.remove()
131
131
132 config['available_permissions'] = [x.permission_name for x in all_perms]
132 config['available_permissions'] = [x.permission_name for x in all_perms]
133
133
134 def set_base_path(config):
134 def set_base_path(config):
135 config['base_path'] = config['pylons.app_globals'].base_path
135 config['base_path'] = config['pylons.app_globals'].base_path
136
136
137 def fill_data(user):
137 def fill_data(user):
138 """
138 """
139 Fills user data with those from database and log out user if not present
139 Fills user data with those from database and log out user if not present
140 in database
140 in database
141 @param user:
141 :param user:
142 """
142 """
143 sa = meta.Session
143 sa = meta.Session
144 dbuser = sa.query(User).get(user.user_id)
144 dbuser = sa.query(User).get(user.user_id)
145 if dbuser:
145 if dbuser:
146 user.username = dbuser.username
146 user.username = dbuser.username
147 user.is_admin = dbuser.admin
147 user.is_admin = dbuser.admin
148 user.name = dbuser.name
148 user.name = dbuser.name
149 user.lastname = dbuser.lastname
149 user.lastname = dbuser.lastname
150 user.email = dbuser.email
150 user.email = dbuser.email
151 else:
151 else:
152 user.is_authenticated = False
152 user.is_authenticated = False
153 meta.Session.remove()
153 meta.Session.remove()
154 return user
154 return user
155
155
156 def fill_perms(user):
156 def fill_perms(user):
157 """
157 """
158 Fills user permission attribute with permissions taken from database
158 Fills user permission attribute with permissions taken from database
159 @param user:
159 :param user:
160 """
160 """
161
161
162 sa = meta.Session
162 sa = meta.Session
163 user.permissions['repositories'] = {}
163 user.permissions['repositories'] = {}
164 user.permissions['global'] = set()
164 user.permissions['global'] = set()
165
165
166 #===========================================================================
166 #===========================================================================
167 # fetch default permissions
167 # fetch default permissions
168 #===========================================================================
168 #===========================================================================
169 default_perms = sa.query(RepoToPerm, Repository, Permission)\
169 default_perms = sa.query(RepoToPerm, Repository, Permission)\
170 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
170 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
171 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
171 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
172 .filter(RepoToPerm.user == sa.query(User).filter(User.username ==
172 .filter(RepoToPerm.user == sa.query(User).filter(User.username ==
173 'default').scalar()).all()
173 'default').scalar()).all()
174
174
175 if user.is_admin:
175 if user.is_admin:
176 #=======================================================================
176 #=======================================================================
177 # #admin have all default rights set to admin
177 # #admin have all default rights set to admin
178 #=======================================================================
178 #=======================================================================
179 user.permissions['global'].add('hg.admin')
179 user.permissions['global'].add('hg.admin')
180
180
181 for perm in default_perms:
181 for perm in default_perms:
182 p = 'repository.admin'
182 p = 'repository.admin'
183 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
183 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
184
184
185 else:
185 else:
186 #=======================================================================
186 #=======================================================================
187 # set default permissions
187 # set default permissions
188 #=======================================================================
188 #=======================================================================
189
189
190 #default global
190 #default global
191 default_global_perms = sa.query(UserToPerm)\
191 default_global_perms = sa.query(UserToPerm)\
192 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
192 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
193 'default').one())
193 'default').one())
194
194
195 for perm in default_global_perms:
195 for perm in default_global_perms:
196 user.permissions['global'].add(perm.permission.permission_name)
196 user.permissions['global'].add(perm.permission.permission_name)
197
197
198 #default repositories
198 #default repositories
199 for perm in default_perms:
199 for perm in default_perms:
200 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
200 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
201 #disable defaults for private repos,
201 #disable defaults for private repos,
202 p = 'repository.none'
202 p = 'repository.none'
203 elif perm.Repository.user_id == user.user_id:
203 elif perm.Repository.user_id == user.user_id:
204 #set admin if owner
204 #set admin if owner
205 p = 'repository.admin'
205 p = 'repository.admin'
206 else:
206 else:
207 p = perm.Permission.permission_name
207 p = perm.Permission.permission_name
208
208
209 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
209 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
210
210
211 #=======================================================================
211 #=======================================================================
212 # #overwrite default with user permissions if any
212 # #overwrite default with user permissions if any
213 #=======================================================================
213 #=======================================================================
214 user_perms = sa.query(RepoToPerm, Permission, Repository)\
214 user_perms = sa.query(RepoToPerm, Permission, Repository)\
215 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
215 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
216 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
216 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
217 .filter(RepoToPerm.user_id == user.user_id).all()
217 .filter(RepoToPerm.user_id == user.user_id).all()
218
218
219 for perm in user_perms:
219 for perm in user_perms:
220 if perm.Repository.user_id == user.user_id:#set admin if owner
220 if perm.Repository.user_id == user.user_id:#set admin if owner
221 p = 'repository.admin'
221 p = 'repository.admin'
222 else:
222 else:
223 p = perm.Permission.permission_name
223 p = perm.Permission.permission_name
224 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
224 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
225 meta.Session.remove()
225 meta.Session.remove()
226 return user
226 return user
227
227
228 def get_user(session):
228 def get_user(session):
229 """
229 """
230 Gets user from session, and wraps permissions into user
230 Gets user from session, and wraps permissions into user
231 @param session:
231 :param session:
232 """
232 """
233 user = session.get('rhodecode_user', AuthUser())
233 user = session.get('rhodecode_user', AuthUser())
234 if user.is_authenticated:
234 if user.is_authenticated:
235 user = fill_data(user)
235 user = fill_data(user)
236 user = fill_perms(user)
236 user = fill_perms(user)
237 session['rhodecode_user'] = user
237 session['rhodecode_user'] = user
238 session.save()
238 session.save()
239 return user
239 return user
240
240
241 #===============================================================================
241 #===============================================================================
242 # CHECK DECORATORS
242 # CHECK DECORATORS
243 #===============================================================================
243 #===============================================================================
244 class LoginRequired(object):
244 class LoginRequired(object):
245 """Must be logged in to execute this function else redirect to login page"""
245 """Must be logged in to execute this function else redirect to login page"""
246
246
247 def __call__(self, func):
247 def __call__(self, func):
248 return decorator(self.__wrapper, func)
248 return decorator(self.__wrapper, func)
249
249
250 def __wrapper(self, func, *fargs, **fkwargs):
250 def __wrapper(self, func, *fargs, **fkwargs):
251 user = session.get('rhodecode_user', AuthUser())
251 user = session.get('rhodecode_user', AuthUser())
252 log.debug('Checking login required for user:%s', user.username)
252 log.debug('Checking login required for user:%s', user.username)
253 if user.is_authenticated:
253 if user.is_authenticated:
254 log.debug('user %s is authenticated', user.username)
254 log.debug('user %s is authenticated', user.username)
255 return func(*fargs, **fkwargs)
255 return func(*fargs, **fkwargs)
256 else:
256 else:
257 log.warn('user %s not authenticated', user.username)
257 log.warn('user %s not authenticated', user.username)
258
258
259 p = ''
259 p = ''
260 if request.environ.get('SCRIPT_NAME') != '/':
260 if request.environ.get('SCRIPT_NAME') != '/':
261 p += request.environ.get('SCRIPT_NAME')
261 p += request.environ.get('SCRIPT_NAME')
262
262
263 p += request.environ.get('PATH_INFO')
263 p += request.environ.get('PATH_INFO')
264 if request.environ.get('QUERY_STRING'):
264 if request.environ.get('QUERY_STRING'):
265 p += '?' + request.environ.get('QUERY_STRING')
265 p += '?' + request.environ.get('QUERY_STRING')
266
266
267 log.debug('redirecting to login page with %s', p)
267 log.debug('redirecting to login page with %s', p)
268 return redirect(url('login_home', came_from=p))
268 return redirect(url('login_home', came_from=p))
269
269
270 class PermsDecorator(object):
270 class PermsDecorator(object):
271 """Base class for decorators"""
271 """Base class for decorators"""
272
272
273 def __init__(self, *required_perms):
273 def __init__(self, *required_perms):
274 available_perms = config['available_permissions']
274 available_perms = config['available_permissions']
275 for perm in required_perms:
275 for perm in required_perms:
276 if perm not in available_perms:
276 if perm not in available_perms:
277 raise Exception("'%s' permission is not defined" % perm)
277 raise Exception("'%s' permission is not defined" % perm)
278 self.required_perms = set(required_perms)
278 self.required_perms = set(required_perms)
279 self.user_perms = None
279 self.user_perms = None
280
280
281 def __call__(self, func):
281 def __call__(self, func):
282 return decorator(self.__wrapper, func)
282 return decorator(self.__wrapper, func)
283
283
284
284
285 def __wrapper(self, func, *fargs, **fkwargs):
285 def __wrapper(self, func, *fargs, **fkwargs):
286 # _wrapper.__name__ = func.__name__
286 # _wrapper.__name__ = func.__name__
287 # _wrapper.__dict__.update(func.__dict__)
287 # _wrapper.__dict__.update(func.__dict__)
288 # _wrapper.__doc__ = func.__doc__
288 # _wrapper.__doc__ = func.__doc__
289
289
290 self.user_perms = session.get('rhodecode_user', AuthUser()).permissions
290 self.user_perms = session.get('rhodecode_user', AuthUser()).permissions
291 log.debug('checking %s permissions %s for %s',
291 log.debug('checking %s permissions %s for %s',
292 self.__class__.__name__, self.required_perms, func.__name__)
292 self.__class__.__name__, self.required_perms, func.__name__)
293
293
294 if self.check_permissions():
294 if self.check_permissions():
295 log.debug('Permission granted for %s', func.__name__)
295 log.debug('Permission granted for %s', func.__name__)
296
296
297 return func(*fargs, **fkwargs)
297 return func(*fargs, **fkwargs)
298
298
299 else:
299 else:
300 log.warning('Permission denied for %s', func.__name__)
300 log.warning('Permission denied for %s', func.__name__)
301 #redirect with forbidden ret code
301 #redirect with forbidden ret code
302 return abort(403)
302 return abort(403)
303
303
304
304
305
305
306 def check_permissions(self):
306 def check_permissions(self):
307 """Dummy function for overriding"""
307 """Dummy function for overriding"""
308 raise Exception('You have to write this function in child class')
308 raise Exception('You have to write this function in child class')
309
309
310 class HasPermissionAllDecorator(PermsDecorator):
310 class HasPermissionAllDecorator(PermsDecorator):
311 """Checks for access permission for all given predicates. All of them
311 """Checks for access permission for all given predicates. All of them
312 have to be meet in order to fulfill the request
312 have to be meet in order to fulfill the request
313 """
313 """
314
314
315 def check_permissions(self):
315 def check_permissions(self):
316 if self.required_perms.issubset(self.user_perms.get('global')):
316 if self.required_perms.issubset(self.user_perms.get('global')):
317 return True
317 return True
318 return False
318 return False
319
319
320
320
321 class HasPermissionAnyDecorator(PermsDecorator):
321 class HasPermissionAnyDecorator(PermsDecorator):
322 """Checks for access permission for any of given predicates. In order to
322 """Checks for access permission for any of given predicates. In order to
323 fulfill the request any of predicates must be meet
323 fulfill the request any of predicates must be meet
324 """
324 """
325
325
326 def check_permissions(self):
326 def check_permissions(self):
327 if self.required_perms.intersection(self.user_perms.get('global')):
327 if self.required_perms.intersection(self.user_perms.get('global')):
328 return True
328 return True
329 return False
329 return False
330
330
331 class HasRepoPermissionAllDecorator(PermsDecorator):
331 class HasRepoPermissionAllDecorator(PermsDecorator):
332 """Checks for access permission for all given predicates for specific
332 """Checks for access permission for all given predicates for specific
333 repository. All of them have to be meet in order to fulfill the request
333 repository. All of them have to be meet in order to fulfill the request
334 """
334 """
335
335
336 def check_permissions(self):
336 def check_permissions(self):
337 repo_name = get_repo_slug(request)
337 repo_name = get_repo_slug(request)
338 try:
338 try:
339 user_perms = set([self.user_perms['repositories'][repo_name]])
339 user_perms = set([self.user_perms['repositories'][repo_name]])
340 except KeyError:
340 except KeyError:
341 return False
341 return False
342 if self.required_perms.issubset(user_perms):
342 if self.required_perms.issubset(user_perms):
343 return True
343 return True
344 return False
344 return False
345
345
346
346
347 class HasRepoPermissionAnyDecorator(PermsDecorator):
347 class HasRepoPermissionAnyDecorator(PermsDecorator):
348 """Checks for access permission for any of given predicates for specific
348 """Checks for access permission for any of given predicates for specific
349 repository. In order to fulfill the request any of predicates must be meet
349 repository. In order to fulfill the request any of predicates must be meet
350 """
350 """
351
351
352 def check_permissions(self):
352 def check_permissions(self):
353 repo_name = get_repo_slug(request)
353 repo_name = get_repo_slug(request)
354
354
355 try:
355 try:
356 user_perms = set([self.user_perms['repositories'][repo_name]])
356 user_perms = set([self.user_perms['repositories'][repo_name]])
357 except KeyError:
357 except KeyError:
358 return False
358 return False
359 if self.required_perms.intersection(user_perms):
359 if self.required_perms.intersection(user_perms):
360 return True
360 return True
361 return False
361 return False
362 #===============================================================================
362 #===============================================================================
363 # CHECK FUNCTIONS
363 # CHECK FUNCTIONS
364 #===============================================================================
364 #===============================================================================
365
365
366 class PermsFunction(object):
366 class PermsFunction(object):
367 """Base function for other check functions"""
367 """Base function for other check functions"""
368
368
369 def __init__(self, *perms):
369 def __init__(self, *perms):
370 available_perms = config['available_permissions']
370 available_perms = config['available_permissions']
371
371
372 for perm in perms:
372 for perm in perms:
373 if perm not in available_perms:
373 if perm not in available_perms:
374 raise Exception("'%s' permission in not defined" % perm)
374 raise Exception("'%s' permission in not defined" % perm)
375 self.required_perms = set(perms)
375 self.required_perms = set(perms)
376 self.user_perms = None
376 self.user_perms = None
377 self.granted_for = ''
377 self.granted_for = ''
378 self.repo_name = None
378 self.repo_name = None
379
379
380 def __call__(self, check_Location=''):
380 def __call__(self, check_Location=''):
381 user = session.get('rhodecode_user', False)
381 user = session.get('rhodecode_user', False)
382 if not user:
382 if not user:
383 return False
383 return False
384 self.user_perms = user.permissions
384 self.user_perms = user.permissions
385 self.granted_for = user.username
385 self.granted_for = user.username
386 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
386 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
387
387
388 if self.check_permissions():
388 if self.check_permissions():
389 log.debug('Permission granted for %s @%s', self.granted_for,
389 log.debug('Permission granted for %s @%s', self.granted_for,
390 check_Location)
390 check_Location)
391 return True
391 return True
392
392
393 else:
393 else:
394 log.warning('Permission denied for %s @%s', self.granted_for,
394 log.warning('Permission denied for %s @%s', self.granted_for,
395 check_Location)
395 check_Location)
396 return False
396 return False
397
397
398 def check_permissions(self):
398 def check_permissions(self):
399 """Dummy function for overriding"""
399 """Dummy function for overriding"""
400 raise Exception('You have to write this function in child class')
400 raise Exception('You have to write this function in child class')
401
401
402 class HasPermissionAll(PermsFunction):
402 class HasPermissionAll(PermsFunction):
403 def check_permissions(self):
403 def check_permissions(self):
404 if self.required_perms.issubset(self.user_perms.get('global')):
404 if self.required_perms.issubset(self.user_perms.get('global')):
405 return True
405 return True
406 return False
406 return False
407
407
408 class HasPermissionAny(PermsFunction):
408 class HasPermissionAny(PermsFunction):
409 def check_permissions(self):
409 def check_permissions(self):
410 if self.required_perms.intersection(self.user_perms.get('global')):
410 if self.required_perms.intersection(self.user_perms.get('global')):
411 return True
411 return True
412 return False
412 return False
413
413
414 class HasRepoPermissionAll(PermsFunction):
414 class HasRepoPermissionAll(PermsFunction):
415
415
416 def __call__(self, repo_name=None, check_Location=''):
416 def __call__(self, repo_name=None, check_Location=''):
417 self.repo_name = repo_name
417 self.repo_name = repo_name
418 return super(HasRepoPermissionAll, self).__call__(check_Location)
418 return super(HasRepoPermissionAll, self).__call__(check_Location)
419
419
420 def check_permissions(self):
420 def check_permissions(self):
421 if not self.repo_name:
421 if not self.repo_name:
422 self.repo_name = get_repo_slug(request)
422 self.repo_name = get_repo_slug(request)
423
423
424 try:
424 try:
425 self.user_perms = set([self.user_perms['repositories']\
425 self.user_perms = set([self.user_perms['repositories']\
426 [self.repo_name]])
426 [self.repo_name]])
427 except KeyError:
427 except KeyError:
428 return False
428 return False
429 self.granted_for = self.repo_name
429 self.granted_for = self.repo_name
430 if self.required_perms.issubset(self.user_perms):
430 if self.required_perms.issubset(self.user_perms):
431 return True
431 return True
432 return False
432 return False
433
433
434 class HasRepoPermissionAny(PermsFunction):
434 class HasRepoPermissionAny(PermsFunction):
435
435
436 def __call__(self, repo_name=None, check_Location=''):
436 def __call__(self, repo_name=None, check_Location=''):
437 self.repo_name = repo_name
437 self.repo_name = repo_name
438 return super(HasRepoPermissionAny, self).__call__(check_Location)
438 return super(HasRepoPermissionAny, self).__call__(check_Location)
439
439
440 def check_permissions(self):
440 def check_permissions(self):
441 if not self.repo_name:
441 if not self.repo_name:
442 self.repo_name = get_repo_slug(request)
442 self.repo_name = get_repo_slug(request)
443
443
444 try:
444 try:
445 self.user_perms = set([self.user_perms['repositories']\
445 self.user_perms = set([self.user_perms['repositories']\
446 [self.repo_name]])
446 [self.repo_name]])
447 except KeyError:
447 except KeyError:
448 return False
448 return False
449 self.granted_for = self.repo_name
449 self.granted_for = self.repo_name
450 if self.required_perms.intersection(self.user_perms):
450 if self.required_perms.intersection(self.user_perms):
451 return True
451 return True
452 return False
452 return False
453
453
454 #===============================================================================
454 #===============================================================================
455 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
455 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
456 #===============================================================================
456 #===============================================================================
457
457
458 class HasPermissionAnyMiddleware(object):
458 class HasPermissionAnyMiddleware(object):
459 def __init__(self, *perms):
459 def __init__(self, *perms):
460 self.required_perms = set(perms)
460 self.required_perms = set(perms)
461
461
462 def __call__(self, user, repo_name):
462 def __call__(self, user, repo_name):
463 usr = AuthUser()
463 usr = AuthUser()
464 usr.user_id = user.user_id
464 usr.user_id = user.user_id
465 usr.username = user.username
465 usr.username = user.username
466 usr.is_admin = user.admin
466 usr.is_admin = user.admin
467
467
468 try:
468 try:
469 self.user_perms = set([fill_perms(usr)\
469 self.user_perms = set([fill_perms(usr)\
470 .permissions['repositories'][repo_name]])
470 .permissions['repositories'][repo_name]])
471 except:
471 except:
472 self.user_perms = set()
472 self.user_perms = set()
473 self.granted_for = ''
473 self.granted_for = ''
474 self.username = user.username
474 self.username = user.username
475 self.repo_name = repo_name
475 self.repo_name = repo_name
476 return self.check_permissions()
476 return self.check_permissions()
477
477
478 def check_permissions(self):
478 def check_permissions(self):
479 log.debug('checking mercurial protocol '
479 log.debug('checking mercurial protocol '
480 'permissions for user:%s repository:%s',
480 'permissions for user:%s repository:%s',
481 self.username, self.repo_name)
481 self.username, self.repo_name)
482 if self.required_perms.intersection(self.user_perms):
482 if self.required_perms.intersection(self.user_perms):
483 log.debug('permission granted')
483 log.debug('permission granted')
484 return True
484 return True
485 log.debug('permission denied')
485 log.debug('permission denied')
486 return False
486 return False
@@ -1,383 +1,383 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 from pygments.formatters import HtmlFormatter
6 from pygments.formatters import HtmlFormatter
7 from pygments import highlight as code_highlight
7 from pygments import highlight as code_highlight
8 from pylons import url, app_globals as g
8 from pylons import url, app_globals as g
9 from pylons.i18n.translation import _, ungettext
9 from pylons.i18n.translation import _, ungettext
10 from vcs.utils.annotate import annotate_highlight
10 from vcs.utils.annotate import annotate_highlight
11 from webhelpers.html import literal, HTML, escape
11 from webhelpers.html import literal, HTML, escape
12 from webhelpers.html.tools import *
12 from webhelpers.html.tools import *
13 from webhelpers.html.builder import make_tag
13 from webhelpers.html.builder import make_tag
14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
14 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
15 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
16 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
17 password, textarea, title, ul, xml_declaration, radio
17 password, textarea, title, ul, xml_declaration, radio
18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
18 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
19 mail_to, strip_links, strip_tags, tag_re
19 mail_to, strip_links, strip_tags, tag_re
20 from webhelpers.number import format_byte_size, format_bit_size
20 from webhelpers.number import format_byte_size, format_bit_size
21 from webhelpers.pylonslib import Flash as _Flash
21 from webhelpers.pylonslib import Flash as _Flash
22 from webhelpers.pylonslib.secure_form import secure_form
22 from webhelpers.pylonslib.secure_form import secure_form
23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
23 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
24 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
25 replace_whitespace, urlify, truncate, wrap_paragraphs
25 replace_whitespace, urlify, truncate, wrap_paragraphs
26
26
27 #Custom helpers here :)
27 #Custom helpers here :)
28 class _Link(object):
28 class _Link(object):
29 '''
29 '''
30 Make a url based on label and url with help of url_for
30 Make a url based on label and url with help of url_for
31 @param label:name of link if not defined url is used
31 :param label:name of link if not defined url is used
32 @param url: the url for link
32 :param url: the url for link
33 '''
33 '''
34
34
35 def __call__(self, label='', *url_, **urlargs):
35 def __call__(self, label='', *url_, **urlargs):
36 if label is None or '':
36 if label is None or '':
37 label = url
37 label = url
38 link_fn = link_to(label, url(*url_, **urlargs))
38 link_fn = link_to(label, url(*url_, **urlargs))
39 return link_fn
39 return link_fn
40
40
41 link = _Link()
41 link = _Link()
42
42
43 class _GetError(object):
43 class _GetError(object):
44
44
45 def __call__(self, field_name, form_errors):
45 def __call__(self, field_name, form_errors):
46 tmpl = """<span class="error_msg">%s</span>"""
46 tmpl = """<span class="error_msg">%s</span>"""
47 if form_errors and form_errors.has_key(field_name):
47 if form_errors and form_errors.has_key(field_name):
48 return literal(tmpl % form_errors.get(field_name))
48 return literal(tmpl % form_errors.get(field_name))
49
49
50 get_error = _GetError()
50 get_error = _GetError()
51
51
52 def recursive_replace(str, replace=' '):
52 def recursive_replace(str, replace=' '):
53 """
53 """
54 Recursive replace of given sign to just one instance
54 Recursive replace of given sign to just one instance
55 @param str: given string
55 :param str: given string
56 @param replace:char to find and replace multiple instances
56 :param replace:char to find and replace multiple instances
57
57
58 Examples::
58 Examples::
59 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
59 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
60 'Mighty-Mighty-Bo-sstones'
60 'Mighty-Mighty-Bo-sstones'
61 """
61 """
62
62
63 if str.find(replace * 2) == -1:
63 if str.find(replace * 2) == -1:
64 return str
64 return str
65 else:
65 else:
66 str = str.replace(replace * 2, replace)
66 str = str.replace(replace * 2, replace)
67 return recursive_replace(str, replace)
67 return recursive_replace(str, replace)
68
68
69 class _ToolTip(object):
69 class _ToolTip(object):
70
70
71 def __call__(self, tooltip_title, trim_at=50):
71 def __call__(self, tooltip_title, trim_at=50):
72 """
72 """
73 Special function just to wrap our text into nice formatted autowrapped
73 Special function just to wrap our text into nice formatted autowrapped
74 text
74 text
75 @param tooltip_title:
75 :param tooltip_title:
76 """
76 """
77
77
78 return wrap_paragraphs(escape(tooltip_title), trim_at)\
78 return wrap_paragraphs(escape(tooltip_title), trim_at)\
79 .replace('\n', '<br/>')
79 .replace('\n', '<br/>')
80
80
81 def activate(self):
81 def activate(self):
82 """
82 """
83 Adds tooltip mechanism to the given Html all tooltips have to have
83 Adds tooltip mechanism to the given Html all tooltips have to have
84 set class tooltip and set attribute tooltip_title.
84 set class tooltip and set attribute tooltip_title.
85 Then a tooltip will be generated based on that
85 Then a tooltip will be generated based on that
86 All with yui js tooltip
86 All with yui js tooltip
87 """
87 """
88
88
89 js = '''
89 js = '''
90 YAHOO.util.Event.onDOMReady(function(){
90 YAHOO.util.Event.onDOMReady(function(){
91 function toolTipsId(){
91 function toolTipsId(){
92 var ids = [];
92 var ids = [];
93 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
93 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
94
94
95 for (var i = 0; i < tts.length; i++) {
95 for (var i = 0; i < tts.length; i++) {
96 //if element doesn not have and id autgenerate one for tooltip
96 //if element doesn not have and id autgenerate one for tooltip
97
97
98 if (!tts[i].id){
98 if (!tts[i].id){
99 tts[i].id='tt'+i*100;
99 tts[i].id='tt'+i*100;
100 }
100 }
101 ids.push(tts[i].id);
101 ids.push(tts[i].id);
102 }
102 }
103 return ids
103 return ids
104 };
104 };
105 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
105 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
106 context: toolTipsId(),
106 context: toolTipsId(),
107 monitorresize:false,
107 monitorresize:false,
108 xyoffset :[0,0],
108 xyoffset :[0,0],
109 autodismissdelay:300000,
109 autodismissdelay:300000,
110 hidedelay:5,
110 hidedelay:5,
111 showdelay:20,
111 showdelay:20,
112 });
112 });
113
113
114 //Mouse Over event disabled for new repositories since they dont
114 //Mouse Over event disabled for new repositories since they dont
115 //have last commit message
115 //have last commit message
116 myToolTips.contextMouseOverEvent.subscribe(
116 myToolTips.contextMouseOverEvent.subscribe(
117 function(type, args) {
117 function(type, args) {
118 var context = args[0];
118 var context = args[0];
119 var txt = context.getAttribute('tooltip_title');
119 var txt = context.getAttribute('tooltip_title');
120 if(txt){
120 if(txt){
121 return true;
121 return true;
122 }
122 }
123 else{
123 else{
124 return false;
124 return false;
125 }
125 }
126 });
126 });
127
127
128
128
129 // Set the text for the tooltip just before we display it. Lazy method
129 // Set the text for the tooltip just before we display it. Lazy method
130 myToolTips.contextTriggerEvent.subscribe(
130 myToolTips.contextTriggerEvent.subscribe(
131 function(type, args) {
131 function(type, args) {
132
132
133
133
134 var context = args[0];
134 var context = args[0];
135
135
136 var txt = context.getAttribute('tooltip_title');
136 var txt = context.getAttribute('tooltip_title');
137 this.cfg.setProperty("text", txt);
137 this.cfg.setProperty("text", txt);
138
138
139
139
140 // positioning of tooltip
140 // positioning of tooltip
141 var tt_w = this.element.clientWidth;
141 var tt_w = this.element.clientWidth;
142 var tt_h = this.element.clientHeight;
142 var tt_h = this.element.clientHeight;
143
143
144 var context_w = context.offsetWidth;
144 var context_w = context.offsetWidth;
145 var context_h = context.offsetHeight;
145 var context_h = context.offsetHeight;
146
146
147 var pos_x = YAHOO.util.Dom.getX(context);
147 var pos_x = YAHOO.util.Dom.getX(context);
148 var pos_y = YAHOO.util.Dom.getY(context);
148 var pos_y = YAHOO.util.Dom.getY(context);
149
149
150 var display_strategy = 'top';
150 var display_strategy = 'top';
151 var xy_pos = [0,0];
151 var xy_pos = [0,0];
152 switch (display_strategy){
152 switch (display_strategy){
153
153
154 case 'top':
154 case 'top':
155 var cur_x = (pos_x+context_w/2)-(tt_w/2);
155 var cur_x = (pos_x+context_w/2)-(tt_w/2);
156 var cur_y = pos_y-tt_h-4;
156 var cur_y = pos_y-tt_h-4;
157 xy_pos = [cur_x,cur_y];
157 xy_pos = [cur_x,cur_y];
158 break;
158 break;
159 case 'bottom':
159 case 'bottom':
160 var cur_x = (pos_x+context_w/2)-(tt_w/2);
160 var cur_x = (pos_x+context_w/2)-(tt_w/2);
161 var cur_y = pos_y+context_h+4;
161 var cur_y = pos_y+context_h+4;
162 xy_pos = [cur_x,cur_y];
162 xy_pos = [cur_x,cur_y];
163 break;
163 break;
164 case 'left':
164 case 'left':
165 var cur_x = (pos_x-tt_w-4);
165 var cur_x = (pos_x-tt_w-4);
166 var cur_y = pos_y-((tt_h/2)-context_h/2);
166 var cur_y = pos_y-((tt_h/2)-context_h/2);
167 xy_pos = [cur_x,cur_y];
167 xy_pos = [cur_x,cur_y];
168 break;
168 break;
169 case 'right':
169 case 'right':
170 var cur_x = (pos_x+context_w+4);
170 var cur_x = (pos_x+context_w+4);
171 var cur_y = pos_y-((tt_h/2)-context_h/2);
171 var cur_y = pos_y-((tt_h/2)-context_h/2);
172 xy_pos = [cur_x,cur_y];
172 xy_pos = [cur_x,cur_y];
173 break;
173 break;
174 default:
174 default:
175 var cur_x = (pos_x+context_w/2)-(tt_w/2);
175 var cur_x = (pos_x+context_w/2)-(tt_w/2);
176 var cur_y = pos_y-tt_h-4;
176 var cur_y = pos_y-tt_h-4;
177 xy_pos = [cur_x,cur_y];
177 xy_pos = [cur_x,cur_y];
178 break;
178 break;
179
179
180 }
180 }
181
181
182 this.cfg.setProperty("xy",xy_pos);
182 this.cfg.setProperty("xy",xy_pos);
183
183
184 });
184 });
185
185
186 //Mouse out
186 //Mouse out
187 myToolTips.contextMouseOutEvent.subscribe(
187 myToolTips.contextMouseOutEvent.subscribe(
188 function(type, args) {
188 function(type, args) {
189 var context = args[0];
189 var context = args[0];
190
190
191 });
191 });
192 });
192 });
193 '''
193 '''
194 return literal(js)
194 return literal(js)
195
195
196 tooltip = _ToolTip()
196 tooltip = _ToolTip()
197
197
198 class _FilesBreadCrumbs(object):
198 class _FilesBreadCrumbs(object):
199
199
200 def __call__(self, repo_name, rev, paths):
200 def __call__(self, repo_name, rev, paths):
201 url_l = [link_to(repo_name, url('files_home',
201 url_l = [link_to(repo_name, url('files_home',
202 repo_name=repo_name,
202 repo_name=repo_name,
203 revision=rev, f_path=''))]
203 revision=rev, f_path=''))]
204 paths_l = paths.split('/')
204 paths_l = paths.split('/')
205
205
206 for cnt, p in enumerate(paths_l, 1):
206 for cnt, p in enumerate(paths_l, 1):
207 if p != '':
207 if p != '':
208 url_l.append(link_to(p, url('files_home',
208 url_l.append(link_to(p, url('files_home',
209 repo_name=repo_name,
209 repo_name=repo_name,
210 revision=rev,
210 revision=rev,
211 f_path='/'.join(paths_l[:cnt]))))
211 f_path='/'.join(paths_l[:cnt]))))
212
212
213 return literal('/'.join(url_l))
213 return literal('/'.join(url_l))
214
214
215 files_breadcrumbs = _FilesBreadCrumbs()
215 files_breadcrumbs = _FilesBreadCrumbs()
216 class CodeHtmlFormatter(HtmlFormatter):
216 class CodeHtmlFormatter(HtmlFormatter):
217
217
218 def wrap(self, source, outfile):
218 def wrap(self, source, outfile):
219 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
219 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
220
220
221 def _wrap_code(self, source):
221 def _wrap_code(self, source):
222 for cnt, it in enumerate(source, 1):
222 for cnt, it in enumerate(source, 1):
223 i, t = it
223 i, t = it
224 t = '<div id="#S-%s">%s</div>' % (cnt, t)
224 t = '<div id="#S-%s">%s</div>' % (cnt, t)
225 yield i, t
225 yield i, t
226 def pygmentize(filenode, **kwargs):
226 def pygmentize(filenode, **kwargs):
227 """
227 """
228 pygmentize function using pygments
228 pygmentize function using pygments
229 @param filenode:
229 :param filenode:
230 """
230 """
231 return literal(code_highlight(filenode.content,
231 return literal(code_highlight(filenode.content,
232 filenode.lexer, CodeHtmlFormatter(**kwargs)))
232 filenode.lexer, CodeHtmlFormatter(**kwargs)))
233
233
234 def pygmentize_annotation(filenode, **kwargs):
234 def pygmentize_annotation(filenode, **kwargs):
235 """
235 """
236 pygmentize function for annotation
236 pygmentize function for annotation
237 @param filenode:
237 :param filenode:
238 """
238 """
239
239
240 color_dict = {}
240 color_dict = {}
241 def gen_color():
241 def gen_color():
242 """generator for getting 10k of evenly distibuted colors using hsv color
242 """generator for getting 10k of evenly distibuted colors using hsv color
243 and golden ratio.
243 and golden ratio.
244 """
244 """
245 import colorsys
245 import colorsys
246 n = 10000
246 n = 10000
247 golden_ratio = 0.618033988749895
247 golden_ratio = 0.618033988749895
248 h = 0.22717784590367374
248 h = 0.22717784590367374
249 #generate 10k nice web friendly colors in the same order
249 #generate 10k nice web friendly colors in the same order
250 for c in xrange(n):
250 for c in xrange(n):
251 h += golden_ratio
251 h += golden_ratio
252 h %= 1
252 h %= 1
253 HSV_tuple = [h, 0.95, 0.95]
253 HSV_tuple = [h, 0.95, 0.95]
254 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
254 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
255 yield map(lambda x:str(int(x * 256)), RGB_tuple)
255 yield map(lambda x:str(int(x * 256)), RGB_tuple)
256
256
257 cgenerator = gen_color()
257 cgenerator = gen_color()
258
258
259 def get_color_string(cs):
259 def get_color_string(cs):
260 if color_dict.has_key(cs):
260 if color_dict.has_key(cs):
261 col = color_dict[cs]
261 col = color_dict[cs]
262 else:
262 else:
263 col = color_dict[cs] = cgenerator.next()
263 col = color_dict[cs] = cgenerator.next()
264 return "color: rgb(%s)! important;" % (', '.join(col))
264 return "color: rgb(%s)! important;" % (', '.join(col))
265
265
266 def url_func(changeset):
266 def url_func(changeset):
267 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
267 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
268 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
268 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
269
269
270 tooltip_html = tooltip_html % (changeset.author,
270 tooltip_html = tooltip_html % (changeset.author,
271 changeset.date,
271 changeset.date,
272 tooltip(changeset.message))
272 tooltip(changeset.message))
273 lnk_format = 'r%-5s:%s' % (changeset.revision,
273 lnk_format = 'r%-5s:%s' % (changeset.revision,
274 changeset.short_id)
274 changeset.short_id)
275 uri = link_to(
275 uri = link_to(
276 lnk_format,
276 lnk_format,
277 url('changeset_home', repo_name=changeset.repository.name,
277 url('changeset_home', repo_name=changeset.repository.name,
278 revision=changeset.short_id),
278 revision=changeset.short_id),
279 style=get_color_string(changeset.short_id),
279 style=get_color_string(changeset.short_id),
280 class_='tooltip',
280 class_='tooltip',
281 tooltip_title=tooltip_html
281 tooltip_title=tooltip_html
282 )
282 )
283
283
284 uri += '\n'
284 uri += '\n'
285 return uri
285 return uri
286 return literal(annotate_highlight(filenode, url_func, **kwargs))
286 return literal(annotate_highlight(filenode, url_func, **kwargs))
287
287
288 def repo_name_slug(value):
288 def repo_name_slug(value):
289 """Return slug of name of repository
289 """Return slug of name of repository
290 This function is called on each creation/modification
290 This function is called on each creation/modification
291 of repository to prevent bad names in repo
291 of repository to prevent bad names in repo
292 """
292 """
293 slug = remove_formatting(value)
293 slug = remove_formatting(value)
294 slug = strip_tags(slug)
294 slug = strip_tags(slug)
295
295
296 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
296 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
297 slug = slug.replace(c, '-')
297 slug = slug.replace(c, '-')
298 slug = recursive_replace(slug, '-')
298 slug = recursive_replace(slug, '-')
299 slug = collapse(slug, '-')
299 slug = collapse(slug, '-')
300 return slug
300 return slug
301
301
302 def get_changeset_safe(repo, rev):
302 def get_changeset_safe(repo, rev):
303 from vcs.backends.base import BaseRepository
303 from vcs.backends.base import BaseRepository
304 from vcs.exceptions import RepositoryError
304 from vcs.exceptions import RepositoryError
305 if not isinstance(repo, BaseRepository):
305 if not isinstance(repo, BaseRepository):
306 raise Exception('You must pass an Repository '
306 raise Exception('You must pass an Repository '
307 'object as first argument got %s', type(repo))
307 'object as first argument got %s', type(repo))
308
308
309 try:
309 try:
310 cs = repo.get_changeset(rev)
310 cs = repo.get_changeset(rev)
311 except RepositoryError:
311 except RepositoryError:
312 from rhodecode.lib.utils import EmptyChangeset
312 from rhodecode.lib.utils import EmptyChangeset
313 cs = EmptyChangeset()
313 cs = EmptyChangeset()
314 return cs
314 return cs
315
315
316
316
317 flash = _Flash()
317 flash = _Flash()
318
318
319
319
320 #===============================================================================
320 #===============================================================================
321 # MERCURIAL FILTERS available via h.
321 # MERCURIAL FILTERS available via h.
322 #===============================================================================
322 #===============================================================================
323 from mercurial import util
323 from mercurial import util
324 from mercurial.templatefilters import age as _age, person as _person
324 from mercurial.templatefilters import age as _age, person as _person
325
325
326 age = lambda x:_age(x)
326 age = lambda x:_age(x)
327 capitalize = lambda x: x.capitalize()
327 capitalize = lambda x: x.capitalize()
328 date = lambda x: util.datestr(x)
328 date = lambda x: util.datestr(x)
329 email = util.email
329 email = util.email
330 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
330 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
331 person = lambda x: _person(x)
331 person = lambda x: _person(x)
332 hgdate = lambda x: "%d %d" % x
332 hgdate = lambda x: "%d %d" % x
333 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
333 isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2')
334 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
334 isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2')
335 localdate = lambda x: (x[0], util.makedate()[1])
335 localdate = lambda x: (x[0], util.makedate()[1])
336 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
336 rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2")
337 rfc822date_notz = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S")
337 rfc822date_notz = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S")
338 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
338 rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2")
339 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
339 time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2")
340
340
341
341
342 #===============================================================================
342 #===============================================================================
343 # PERMS
343 # PERMS
344 #===============================================================================
344 #===============================================================================
345 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
345 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
346 HasRepoPermissionAny, HasRepoPermissionAll
346 HasRepoPermissionAny, HasRepoPermissionAll
347
347
348 #===============================================================================
348 #===============================================================================
349 # GRAVATAR URL
349 # GRAVATAR URL
350 #===============================================================================
350 #===============================================================================
351 import hashlib
351 import hashlib
352 import urllib
352 import urllib
353 from pylons import request
353 from pylons import request
354
354
355 def gravatar_url(email_address, size=30):
355 def gravatar_url(email_address, size=30):
356 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
356 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
357 default = 'identicon'
357 default = 'identicon'
358 baseurl_nossl = "http://www.gravatar.com/avatar/"
358 baseurl_nossl = "http://www.gravatar.com/avatar/"
359 baseurl_ssl = "https://secure.gravatar.com/avatar/"
359 baseurl_ssl = "https://secure.gravatar.com/avatar/"
360 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
360 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
361
361
362
362
363 # construct the url
363 # construct the url
364 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
364 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
365 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
365 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
366
366
367 return gravatar_url
367 return gravatar_url
368
368
369 def safe_unicode(str):
369 def safe_unicode(str):
370 """safe unicode function. In case of UnicodeDecode error we try to return
370 """safe unicode function. In case of UnicodeDecode error we try to return
371 unicode with errors replace, if this failes we return unicode with
371 unicode with errors replace, if this failes we return unicode with
372 string_escape decoding """
372 string_escape decoding """
373
373
374 try:
374 try:
375 u_str = unicode(str)
375 u_str = unicode(str)
376 except UnicodeDecodeError:
376 except UnicodeDecodeError:
377 try:
377 try:
378 u_str = unicode(str, 'utf-8', 'replace')
378 u_str = unicode(str, 'utf-8', 'replace')
379 except UnicodeDecodeError:
379 except UnicodeDecodeError:
380 #incase we have a decode error just represent as byte string
380 #incase we have a decode error just represent as byte string
381 u_str = unicode(str(str).encode('string_escape'))
381 u_str = unicode(str(str).encode('string_escape'))
382
382
383 return u_str
383 return u_str
@@ -1,78 +1,78 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # custom hooks for application
3 # custom hooks for application
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on Aug 6, 2010
21 Created on Aug 6, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 import sys
26 import sys
27 import os
27 import os
28 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
29 from rhodecode.model import meta
29 from rhodecode.model import meta
30 from rhodecode.model.db import UserLog, User
30 from rhodecode.model.db import UserLog, User
31
31
32 def repo_size(ui, repo, hooktype=None, **kwargs):
32 def repo_size(ui, repo, hooktype=None, **kwargs):
33
33
34 if hooktype != 'changegroup':
34 if hooktype != 'changegroup':
35 return False
35 return False
36 size_hg, size_root = 0, 0
36 size_hg, size_root = 0, 0
37 for path, dirs, files in os.walk(repo.root):
37 for path, dirs, files in os.walk(repo.root):
38 if path.find('.hg') != -1:
38 if path.find('.hg') != -1:
39 for f in files:
39 for f in files:
40 size_hg += os.path.getsize(os.path.join(path, f))
40 size_hg += os.path.getsize(os.path.join(path, f))
41 else:
41 else:
42 for f in files:
42 for f in files:
43 size_root += os.path.getsize(os.path.join(path, f))
43 size_root += os.path.getsize(os.path.join(path, f))
44
44
45 size_hg_f = h.format_byte_size(size_hg)
45 size_hg_f = h.format_byte_size(size_hg)
46 size_root_f = h.format_byte_size(size_root)
46 size_root_f = h.format_byte_size(size_root)
47 size_total_f = h.format_byte_size(size_root + size_hg)
47 size_total_f = h.format_byte_size(size_root + size_hg)
48 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
48 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
49 % (size_hg_f, size_root_f, size_total_f))
49 % (size_hg_f, size_root_f, size_total_f))
50
50
51 user_action_mapper(ui, repo, hooktype, **kwargs)
51 user_action_mapper(ui, repo, hooktype, **kwargs)
52
52
53 def user_action_mapper(ui, repo, hooktype=None, **kwargs):
53 def user_action_mapper(ui, repo, hooktype=None, **kwargs):
54 """
54 """
55 Maps user last push action to new changeset id, from mercurial
55 Maps user last push action to new changeset id, from mercurial
56 @param ui:
56 :param ui:
57 @param repo:
57 :param repo:
58 @param hooktype:
58 :param hooktype:
59 """
59 """
60
60
61 try:
61 try:
62 sa = meta.Session
62 sa = meta.Session
63 username = kwargs['url'].split(':')[-1]
63 username = kwargs['url'].split(':')[-1]
64 user_log = sa.query(UserLog)\
64 user_log = sa.query(UserLog)\
65 .filter(UserLog.user == sa.query(User)\
65 .filter(UserLog.user == sa.query(User)\
66 .filter(User.username == username).one())\
66 .filter(User.username == username).one())\
67 .order_by(UserLog.user_log_id.desc()).first()
67 .order_by(UserLog.user_log_id.desc()).first()
68
68
69 if user_log and not user_log.revision:
69 if user_log and not user_log.revision:
70 user_log.revision = str(repo['tip'])
70 user_log.revision = str(repo['tip'])
71 sa.add(user_log)
71 sa.add(user_log)
72 sa.commit()
72 sa.commit()
73
73
74 except Exception, e:
74 except Exception, e:
75 sa.rollback()
75 sa.rollback()
76 raise
76 raise
77 finally:
77 finally:
78 meta.Session.remove()
78 meta.Session.remove()
@@ -1,142 +1,142 b''
1 from os.path import dirname as dn, join as jn
1 from os.path import dirname as dn, join as jn
2 from rhodecode.config.environment import load_environment
2 from rhodecode.config.environment import load_environment
3 from rhodecode.model.hg_model import HgModel
3 from rhodecode.model.hg_model import HgModel
4 from shutil import rmtree
4 from shutil import rmtree
5 from webhelpers.html.builder import escape
5 from webhelpers.html.builder import escape
6 from vcs.utils.lazy import LazyProperty
6 from vcs.utils.lazy import LazyProperty
7
7
8 from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter
8 from whoosh.analysis import RegexTokenizer, LowercaseFilter, StopFilter
9 from whoosh.fields import TEXT, ID, STORED, Schema, FieldType
9 from whoosh.fields import TEXT, ID, STORED, Schema, FieldType
10 from whoosh.index import create_in, open_dir
10 from whoosh.index import create_in, open_dir
11 from whoosh.formats import Characters
11 from whoosh.formats import Characters
12 from whoosh.highlight import highlight, SimpleFragmenter, HtmlFormatter
12 from whoosh.highlight import highlight, SimpleFragmenter, HtmlFormatter
13
13
14 import os
14 import os
15 import sys
15 import sys
16 import traceback
16 import traceback
17
17
18 #to get the rhodecode import
18 #to get the rhodecode import
19 sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
19 sys.path.append(dn(dn(dn(os.path.realpath(__file__)))))
20
20
21
21
22 #LOCATION WE KEEP THE INDEX
22 #LOCATION WE KEEP THE INDEX
23 IDX_LOCATION = jn(dn(dn(dn(dn(os.path.abspath(__file__))))), 'data', 'index')
23 IDX_LOCATION = jn(dn(dn(dn(dn(os.path.abspath(__file__))))), 'data', 'index')
24
24
25 #EXTENSIONS WE WANT TO INDEX CONTENT OFF
25 #EXTENSIONS WE WANT TO INDEX CONTENT OFF
26 INDEX_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
26 INDEX_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
27 'cfg', 'cfm', 'cpp', 'cs', 'css', 'diff', 'do', 'el', 'erl',
27 'cfg', 'cfm', 'cpp', 'cs', 'css', 'diff', 'do', 'el', 'erl',
28 'h', 'htm', 'html', 'ini', 'java', 'js', 'jsp', 'jspx', 'lisp',
28 'h', 'htm', 'html', 'ini', 'java', 'js', 'jsp', 'jspx', 'lisp',
29 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
29 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
30 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh', 'sql',
30 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh', 'sql',
31 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
31 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
32 'yaws']
32 'yaws']
33
33
34 #CUSTOM ANALYZER wordsplit + lowercase filter
34 #CUSTOM ANALYZER wordsplit + lowercase filter
35 ANALYZER = RegexTokenizer(expression=r"\w+") | LowercaseFilter()
35 ANALYZER = RegexTokenizer(expression=r"\w+") | LowercaseFilter()
36
36
37
37
38 #INDEX SCHEMA DEFINITION
38 #INDEX SCHEMA DEFINITION
39 SCHEMA = Schema(owner=TEXT(),
39 SCHEMA = Schema(owner=TEXT(),
40 repository=TEXT(stored=True),
40 repository=TEXT(stored=True),
41 path=TEXT(stored=True),
41 path=TEXT(stored=True),
42 content=FieldType(format=Characters(ANALYZER),
42 content=FieldType(format=Characters(ANALYZER),
43 scorable=True, stored=True),
43 scorable=True, stored=True),
44 modtime=STORED(), extension=TEXT(stored=True))
44 modtime=STORED(), extension=TEXT(stored=True))
45
45
46
46
47 IDX_NAME = 'HG_INDEX'
47 IDX_NAME = 'HG_INDEX'
48 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
48 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
49 FRAGMENTER = SimpleFragmenter(200)
49 FRAGMENTER = SimpleFragmenter(200)
50
50
51 class ResultWrapper(object):
51 class ResultWrapper(object):
52 def __init__(self, search_type, searcher, matcher, highlight_items):
52 def __init__(self, search_type, searcher, matcher, highlight_items):
53 self.search_type = search_type
53 self.search_type = search_type
54 self.searcher = searcher
54 self.searcher = searcher
55 self.matcher = matcher
55 self.matcher = matcher
56 self.highlight_items = highlight_items
56 self.highlight_items = highlight_items
57 self.fragment_size = 200 / 2
57 self.fragment_size = 200 / 2
58
58
59 @LazyProperty
59 @LazyProperty
60 def doc_ids(self):
60 def doc_ids(self):
61 docs_id = []
61 docs_id = []
62 while self.matcher.is_active():
62 while self.matcher.is_active():
63 docnum = self.matcher.id()
63 docnum = self.matcher.id()
64 chunks = [offsets for offsets in self.get_chunks()]
64 chunks = [offsets for offsets in self.get_chunks()]
65 docs_id.append([docnum, chunks])
65 docs_id.append([docnum, chunks])
66 self.matcher.next()
66 self.matcher.next()
67 return docs_id
67 return docs_id
68
68
69 def __str__(self):
69 def __str__(self):
70 return '<%s at %s>' % (self.__class__.__name__, len(self.doc_ids))
70 return '<%s at %s>' % (self.__class__.__name__, len(self.doc_ids))
71
71
72 def __repr__(self):
72 def __repr__(self):
73 return self.__str__()
73 return self.__str__()
74
74
75 def __len__(self):
75 def __len__(self):
76 return len(self.doc_ids)
76 return len(self.doc_ids)
77
77
78 def __iter__(self):
78 def __iter__(self):
79 """
79 """
80 Allows Iteration over results,and lazy generate content
80 Allows Iteration over results,and lazy generate content
81
81
82 *Requires* implementation of ``__getitem__`` method.
82 *Requires* implementation of ``__getitem__`` method.
83 """
83 """
84 for docid in self.doc_ids:
84 for docid in self.doc_ids:
85 yield self.get_full_content(docid)
85 yield self.get_full_content(docid)
86
86
87 def __getslice__(self, i, j):
87 def __getslice__(self, i, j):
88 """
88 """
89 Slicing of resultWrapper
89 Slicing of resultWrapper
90 """
90 """
91 slice = []
91 slice = []
92 for docid in self.doc_ids[i:j]:
92 for docid in self.doc_ids[i:j]:
93 slice.append(self.get_full_content(docid))
93 slice.append(self.get_full_content(docid))
94 return slice
94 return slice
95
95
96
96
97 def get_full_content(self, docid):
97 def get_full_content(self, docid):
98 res = self.searcher.stored_fields(docid[0])
98 res = self.searcher.stored_fields(docid[0])
99 f_path = res['path'][res['path'].find(res['repository']) \
99 f_path = res['path'][res['path'].find(res['repository']) \
100 + len(res['repository']):].lstrip('/')
100 + len(res['repository']):].lstrip('/')
101
101
102 content_short = self.get_short_content(res, docid[1])
102 content_short = self.get_short_content(res, docid[1])
103 res.update({'content_short':content_short,
103 res.update({'content_short':content_short,
104 'content_short_hl':self.highlight(content_short),
104 'content_short_hl':self.highlight(content_short),
105 'f_path':f_path})
105 'f_path':f_path})
106
106
107 return res
107 return res
108
108
109 def get_short_content(self, res, chunks):
109 def get_short_content(self, res, chunks):
110
110
111 return ''.join([res['content'][chunk[0]:chunk[1]] for chunk in chunks])
111 return ''.join([res['content'][chunk[0]:chunk[1]] for chunk in chunks])
112
112
113 def get_chunks(self):
113 def get_chunks(self):
114 """
114 """
115 Smart function that implements chunking the content
115 Smart function that implements chunking the content
116 but not overlap chunks so it doesn't highlight the same
116 but not overlap chunks so it doesn't highlight the same
117 close occurrences twice.
117 close occurrences twice.
118 @param matcher:
118 :param matcher:
119 @param size:
119 :param size:
120 """
120 """
121 memory = [(0, 0)]
121 memory = [(0, 0)]
122 for span in self.matcher.spans():
122 for span in self.matcher.spans():
123 start = span.startchar or 0
123 start = span.startchar or 0
124 end = span.endchar or 0
124 end = span.endchar or 0
125 start_offseted = max(0, start - self.fragment_size)
125 start_offseted = max(0, start - self.fragment_size)
126 end_offseted = end + self.fragment_size
126 end_offseted = end + self.fragment_size
127
127
128 if start_offseted < memory[-1][1]:
128 if start_offseted < memory[-1][1]:
129 start_offseted = memory[-1][1]
129 start_offseted = memory[-1][1]
130 memory.append((start_offseted, end_offseted,))
130 memory.append((start_offseted, end_offseted,))
131 yield (start_offseted, end_offseted,)
131 yield (start_offseted, end_offseted,)
132
132
133 def highlight(self, content, top=5):
133 def highlight(self, content, top=5):
134 if self.search_type != 'content':
134 if self.search_type != 'content':
135 return ''
135 return ''
136 hl = highlight(escape(content),
136 hl = highlight(escape(content),
137 self.highlight_items,
137 self.highlight_items,
138 analyzer=ANALYZER,
138 analyzer=ANALYZER,
139 fragmenter=FRAGMENTER,
139 fragmenter=FRAGMENTER,
140 formatter=FORMATTER,
140 formatter=FORMATTER,
141 top=top)
141 top=top)
142 return hl
142 return hl
@@ -1,120 +1,120 b''
1 import os, time
1 import os, time
2 import sys
2 import sys
3 from warnings import warn
3 from warnings import warn
4 from multiprocessing.util import Finalize
4 from multiprocessing.util import Finalize
5 import errno
5 import errno
6
6
7 class LockHeld(Exception):pass
7 class LockHeld(Exception):pass
8
8
9
9
10 class DaemonLock(object):
10 class DaemonLock(object):
11 """daemon locking
11 """daemon locking
12 USAGE:
12 USAGE:
13 try:
13 try:
14 l = DaemonLock(desc='test lock')
14 l = DaemonLock(desc='test lock')
15 main()
15 main()
16 l.release()
16 l.release()
17 except LockHeld:
17 except LockHeld:
18 sys.exit(1)
18 sys.exit(1)
19 """
19 """
20
20
21 def __init__(self, file=None, callbackfn=None,
21 def __init__(self, file=None, callbackfn=None,
22 desc='daemon lock', debug=False):
22 desc='daemon lock', debug=False):
23
23
24 self.pidfile = file if file else os.path.join(os.path.dirname(__file__),
24 self.pidfile = file if file else os.path.join(os.path.dirname(__file__),
25 'running.lock')
25 'running.lock')
26 self.callbackfn = callbackfn
26 self.callbackfn = callbackfn
27 self.desc = desc
27 self.desc = desc
28 self.debug = debug
28 self.debug = debug
29 self.held = False
29 self.held = False
30 #run the lock automatically !
30 #run the lock automatically !
31 self.lock()
31 self.lock()
32 self._finalize = Finalize(self, DaemonLock._on_finalize,
32 self._finalize = Finalize(self, DaemonLock._on_finalize,
33 args=(self, debug), exitpriority=10)
33 args=(self, debug), exitpriority=10)
34
34
35 @staticmethod
35 @staticmethod
36 def _on_finalize(lock, debug):
36 def _on_finalize(lock, debug):
37 if lock.held:
37 if lock.held:
38 if debug:
38 if debug:
39 print 'leck held finilazing and running lock.release()'
39 print 'leck held finilazing and running lock.release()'
40 lock.release()
40 lock.release()
41
41
42
42
43 def lock(self):
43 def lock(self):
44 """locking function, if lock is present it will raise LockHeld exception
44 """locking function, if lock is present it will raise LockHeld exception
45 """
45 """
46 lockname = '%s' % (os.getpid())
46 lockname = '%s' % (os.getpid())
47 if self.debug:
47 if self.debug:
48 print 'running lock'
48 print 'running lock'
49 self.trylock()
49 self.trylock()
50 self.makelock(lockname, self.pidfile)
50 self.makelock(lockname, self.pidfile)
51 return True
51 return True
52
52
53 def trylock(self):
53 def trylock(self):
54 running_pid = False
54 running_pid = False
55 if self.debug:
55 if self.debug:
56 print 'checking for already running process'
56 print 'checking for already running process'
57 try:
57 try:
58 pidfile = open(self.pidfile, "r")
58 pidfile = open(self.pidfile, "r")
59 pidfile.seek(0)
59 pidfile.seek(0)
60 running_pid = int(pidfile.readline())
60 running_pid = int(pidfile.readline())
61
61
62 pidfile.close()
62 pidfile.close()
63
63
64 if self.debug:
64 if self.debug:
65 print 'lock file present running_pid: %s, checking for execution'\
65 print 'lock file present running_pid: %s, checking for execution'\
66 % running_pid
66 % running_pid
67 # Now we check the PID from lock file matches to the current
67 # Now we check the PID from lock file matches to the current
68 # process PID
68 # process PID
69 if running_pid:
69 if running_pid:
70 try:
70 try:
71 os.kill(running_pid, 0)
71 os.kill(running_pid, 0)
72 except OSError, exc:
72 except OSError, exc:
73 if exc.errno in (errno.ESRCH, errno.EPERM):
73 if exc.errno in (errno.ESRCH, errno.EPERM):
74 print "Lock File is there but the program is not running"
74 print "Lock File is there but the program is not running"
75 print "Removing lock file for the: %s" % running_pid
75 print "Removing lock file for the: %s" % running_pid
76 self.release()
76 self.release()
77 else:
77 else:
78 raise
78 raise
79 else:
79 else:
80 print "You already have an instance of the program running"
80 print "You already have an instance of the program running"
81 print "It is running as process %s" % running_pid
81 print "It is running as process %s" % running_pid
82 raise LockHeld()
82 raise LockHeld()
83
83
84 except IOError, e:
84 except IOError, e:
85 if e.errno != 2:
85 if e.errno != 2:
86 raise
86 raise
87
87
88 def release(self):
88 def release(self):
89 """releases the pid by removing the pidfile
89 """releases the pid by removing the pidfile
90 """
90 """
91 if self.debug:
91 if self.debug:
92 print 'trying to release the pidlock'
92 print 'trying to release the pidlock'
93
93
94 if self.callbackfn:
94 if self.callbackfn:
95 #execute callback function on release
95 #execute callback function on release
96 if self.debug:
96 if self.debug:
97 print 'executing callback function %s' % self.callbackfn
97 print 'executing callback function %s' % self.callbackfn
98 self.callbackfn()
98 self.callbackfn()
99 try:
99 try:
100 if self.debug:
100 if self.debug:
101 print 'removing pidfile %s' % self.pidfile
101 print 'removing pidfile %s' % self.pidfile
102 os.remove(self.pidfile)
102 os.remove(self.pidfile)
103 self.held = False
103 self.held = False
104 except OSError, e:
104 except OSError, e:
105 if self.debug:
105 if self.debug:
106 print 'removing pidfile failed %s' % e
106 print 'removing pidfile failed %s' % e
107 pass
107 pass
108
108
109 def makelock(self, lockname, pidfile):
109 def makelock(self, lockname, pidfile):
110 """
110 """
111 this function will make an actual lock
111 this function will make an actual lock
112 @param lockname: acctual pid of file
112 :param lockname: acctual pid of file
113 @param pidfile: the file to write the pid in
113 :param pidfile: the file to write the pid in
114 """
114 """
115 if self.debug:
115 if self.debug:
116 print 'creating a file %s and pid: %s' % (pidfile, lockname)
116 print 'creating a file %s and pid: %s' % (pidfile, lockname)
117 pidfile = open(self.pidfile, "wb")
117 pidfile = open(self.pidfile, "wb")
118 pidfile.write(lockname)
118 pidfile.write(lockname)
119 pidfile.close
119 pidfile.close
120 self.held = True
120 self.held = True
@@ -1,118 +1,118 b''
1 import logging
1 import logging
2 import smtplib
2 import smtplib
3 import mimetypes
3 import mimetypes
4 from email.mime.multipart import MIMEMultipart
4 from email.mime.multipart import MIMEMultipart
5 from email.mime.image import MIMEImage
5 from email.mime.image import MIMEImage
6 from email.mime.audio import MIMEAudio
6 from email.mime.audio import MIMEAudio
7 from email.mime.base import MIMEBase
7 from email.mime.base import MIMEBase
8 from email.mime.text import MIMEText
8 from email.mime.text import MIMEText
9 from email.utils import formatdate
9 from email.utils import formatdate
10 from email import encoders
10 from email import encoders
11
11
12 class SmtpMailer(object):
12 class SmtpMailer(object):
13 """simple smtp mailer class
13 """simple smtp mailer class
14
14
15 mailer = SmtpMailer(mail_from, user, passwd, mail_server, mail_port, ssl, tls)
15 mailer = SmtpMailer(mail_from, user, passwd, mail_server, mail_port, ssl, tls)
16 mailer.send(recipients, subject, body, attachment_files)
16 mailer.send(recipients, subject, body, attachment_files)
17
17
18 :param recipients might be a list of string or single string
18 :param recipients might be a list of string or single string
19 :param attachment_files is a dict of {filename:location}
19 :param attachment_files is a dict of {filename:location}
20 it tries to guess the mimetype and attach the file
20 it tries to guess the mimetype and attach the file
21 """
21 """
22
22
23 def __init__(self, mail_from, user, passwd, mail_server,
23 def __init__(self, mail_from, user, passwd, mail_server,
24 mail_port=None, ssl=False, tls=False):
24 mail_port=None, ssl=False, tls=False):
25
25
26 self.mail_from = mail_from
26 self.mail_from = mail_from
27 self.mail_server = mail_server
27 self.mail_server = mail_server
28 self.mail_port = mail_port
28 self.mail_port = mail_port
29 self.user = user
29 self.user = user
30 self.passwd = passwd
30 self.passwd = passwd
31 self.ssl = ssl
31 self.ssl = ssl
32 self.tls = tls
32 self.tls = tls
33 self.debug = False
33 self.debug = False
34
34
35 def send(self, recipients=[], subject='', body='', attachment_files={}):
35 def send(self, recipients=[], subject='', body='', attachment_files={}):
36
36
37 if isinstance(recipients, basestring):
37 if isinstance(recipients, basestring):
38 recipients = [recipients]
38 recipients = [recipients]
39 if self.ssl:
39 if self.ssl:
40 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
40 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
41 else:
41 else:
42 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
42 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
43
43
44 if self.tls:
44 if self.tls:
45 smtp_serv.starttls()
45 smtp_serv.starttls()
46
46
47 if self.debug:
47 if self.debug:
48 smtp_serv.set_debuglevel(1)
48 smtp_serv.set_debuglevel(1)
49
49
50 smtp_serv.ehlo("mailer")
50 smtp_serv.ehlo("mailer")
51
51
52 #if server requires authorization you must provide login and password
52 #if server requires authorization you must provide login and password
53 smtp_serv.login(self.user, self.passwd)
53 smtp_serv.login(self.user, self.passwd)
54
54
55 date_ = formatdate(localtime=True)
55 date_ = formatdate(localtime=True)
56 msg = MIMEMultipart()
56 msg = MIMEMultipart()
57 msg['From'] = self.mail_from
57 msg['From'] = self.mail_from
58 msg['To'] = ','.join(recipients)
58 msg['To'] = ','.join(recipients)
59 msg['Date'] = date_
59 msg['Date'] = date_
60 msg['Subject'] = subject
60 msg['Subject'] = subject
61 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
61 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
62
62
63 msg.attach(MIMEText(body))
63 msg.attach(MIMEText(body))
64
64
65 if attachment_files:
65 if attachment_files:
66 self.__atach_files(msg, attachment_files)
66 self.__atach_files(msg, attachment_files)
67
67
68 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
68 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
69 logging.info('MAIL SEND TO: %s' % recipients)
69 logging.info('MAIL SEND TO: %s' % recipients)
70 smtp_serv.quit()
70 smtp_serv.quit()
71
71
72
72
73 def __atach_files(self, msg, attachment_files):
73 def __atach_files(self, msg, attachment_files):
74 if isinstance(attachment_files, dict):
74 if isinstance(attachment_files, dict):
75 for f_name, msg_file in attachment_files.items():
75 for f_name, msg_file in attachment_files.items():
76 ctype, encoding = mimetypes.guess_type(f_name)
76 ctype, encoding = mimetypes.guess_type(f_name)
77 logging.info("guessing file %s type based on %s" , ctype, f_name)
77 logging.info("guessing file %s type based on %s" , ctype, f_name)
78 if ctype is None or encoding is not None:
78 if ctype is None or encoding is not None:
79 # No guess could be made, or the file is encoded (compressed), so
79 # No guess could be made, or the file is encoded (compressed), so
80 # use a generic bag-of-bits type.
80 # use a generic bag-of-bits type.
81 ctype = 'application/octet-stream'
81 ctype = 'application/octet-stream'
82 maintype, subtype = ctype.split('/', 1)
82 maintype, subtype = ctype.split('/', 1)
83 if maintype == 'text':
83 if maintype == 'text':
84 # Note: we should handle calculating the charset
84 # Note: we should handle calculating the charset
85 file_part = MIMEText(self.get_content(msg_file),
85 file_part = MIMEText(self.get_content(msg_file),
86 _subtype=subtype)
86 _subtype=subtype)
87 elif maintype == 'image':
87 elif maintype == 'image':
88 file_part = MIMEImage(self.get_content(msg_file),
88 file_part = MIMEImage(self.get_content(msg_file),
89 _subtype=subtype)
89 _subtype=subtype)
90 elif maintype == 'audio':
90 elif maintype == 'audio':
91 file_part = MIMEAudio(self.get_content(msg_file),
91 file_part = MIMEAudio(self.get_content(msg_file),
92 _subtype=subtype)
92 _subtype=subtype)
93 else:
93 else:
94 file_part = MIMEBase(maintype, subtype)
94 file_part = MIMEBase(maintype, subtype)
95 file_part.set_payload(self.get_content(msg_file))
95 file_part.set_payload(self.get_content(msg_file))
96 # Encode the payload using Base64
96 # Encode the payload using Base64
97 encoders.encode_base64(msg)
97 encoders.encode_base64(msg)
98 # Set the filename parameter
98 # Set the filename parameter
99 file_part.add_header('Content-Disposition', 'attachment',
99 file_part.add_header('Content-Disposition', 'attachment',
100 filename=f_name)
100 filename=f_name)
101 file_part.add_header('Content-Type', ctype, name=f_name)
101 file_part.add_header('Content-Type', ctype, name=f_name)
102 msg.attach(file_part)
102 msg.attach(file_part)
103 else:
103 else:
104 raise Exception('Attachment files should be'
104 raise Exception('Attachment files should be'
105 'a dict in format {"filename":"filepath"}')
105 'a dict in format {"filename":"filepath"}')
106
106
107 def get_content(self, msg_file):
107 def get_content(self, msg_file):
108 '''
108 '''
109 Get content based on type, if content is a string do open first
109 Get content based on type, if content is a string do open first
110 else just read because it's a probably open file object
110 else just read because it's a probably open file object
111 @param msg_file:
111 :param msg_file:
112 '''
112 '''
113 if isinstance(msg_file, str):
113 if isinstance(msg_file, str):
114 return open(msg_file, "rb").read()
114 return open(msg_file, "rb").read()
115 else:
115 else:
116 #just for safe seek to 0
116 #just for safe seek to 0
117 msg_file.seek(0)
117 msg_file.seek(0)
118 return msg_file.read()
118 return msg_file.read()
@@ -1,490 +1,502 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Utilities for RhodeCode
3 # Utilities for RhodeCode
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; version 2
7 # as published by the Free Software Foundation; version 2
8 # of the License or (at your opinion) any later version of the license.
8 # of the License or (at your opinion) any later version of the license.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # MA 02110-1301, USA.
18 # MA 02110-1301, USA.
19
19
20 """
20 """
21 Created on April 18, 2010
21 Created on April 18, 2010
22 Utilities for RhodeCode
22 Utilities for RhodeCode
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from mercurial import ui, config, hg
26 from mercurial import ui, config, hg
27 from mercurial.error import RepoError
27 from mercurial.error import RepoError
28 from rhodecode.model import meta
28 from rhodecode.model import meta
29 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, UserLog
29 from rhodecode.model.db import Repository, User, RhodeCodeUi, RhodeCodeSettings, UserLog
30 from vcs.backends.base import BaseChangeset
30 from vcs.backends.base import BaseChangeset
31 from vcs.utils.lazy import LazyProperty
31 from vcs.utils.lazy import LazyProperty
32 import logging
32 import logging
33 import datetime
33 import datetime
34 import os
34 import os
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 def get_repo_slug(request):
39 def get_repo_slug(request):
40 return request.environ['pylons.routes_dict'].get('repo_name')
40 return request.environ['pylons.routes_dict'].get('repo_name')
41
41
42 def is_mercurial(environ):
42 def is_mercurial(environ):
43 """
43 """
44 Returns True if request's target is mercurial server - header
44 Returns True if request's target is mercurial server - header
45 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
45 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
46 """
46 """
47 http_accept = environ.get('HTTP_ACCEPT')
47 http_accept = environ.get('HTTP_ACCEPT')
48 if http_accept and http_accept.startswith('application/mercurial'):
48 if http_accept and http_accept.startswith('application/mercurial'):
49 return True
49 return True
50 return False
50 return False
51
51
52 def is_git(environ):
53 """
54 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
55 then have git client version given.
56
57 :param environ:
58 """
59 http_user_agent = environ.get('HTTP_USER_AGENT')
60 if http_user_agent.startswith('git'):
61 return True
62 return False
63
52 def action_logger(user, action, repo, ipaddr, sa=None):
64 def action_logger(user, action, repo, ipaddr, sa=None):
53 """
65 """
54 Action logger for various action made by users
66 Action logger for various action made by users
55 """
67 """
56
68
57 if not sa:
69 if not sa:
58 sa = meta.Session
70 sa = meta.Session
59
71
60 try:
72 try:
61 if hasattr(user, 'user_id'):
73 if hasattr(user, 'user_id'):
62 user_id = user.user_id
74 user_id = user.user_id
63 elif isinstance(user, basestring):
75 elif isinstance(user, basestring):
64 user_id = sa.query(User).filter(User.username == user).one()
76 user_id = sa.query(User).filter(User.username == user).one()
65 else:
77 else:
66 raise Exception('You have to provide user object or username')
78 raise Exception('You have to provide user object or username')
67
79
68 repo_name = repo.lstrip('/')
80 repo_name = repo.lstrip('/')
69 user_log = UserLog()
81 user_log = UserLog()
70 user_log.user_id = user_id
82 user_log.user_id = user_id
71 user_log.action = action
83 user_log.action = action
72 user_log.repository_name = repo_name
84 user_log.repository_name = repo_name
73 user_log.repository = sa.query(Repository)\
85 user_log.repository = sa.query(Repository)\
74 .filter(Repository.repo_name == repo_name).one()
86 .filter(Repository.repo_name == repo_name).one()
75 user_log.action_date = datetime.datetime.now()
87 user_log.action_date = datetime.datetime.now()
76 user_log.user_ip = ipaddr
88 user_log.user_ip = ipaddr
77 sa.add(user_log)
89 sa.add(user_log)
78 sa.commit()
90 sa.commit()
79 log.info('Adding user %s, action %s on %s',
91 log.info('Adding user %s, action %s on %s',
80 user.username, action, repo)
92 user.username, action, repo)
81 except Exception, e:
93 except Exception, e:
82 raise
94 raise
83 sa.rollback()
95 sa.rollback()
84 log.error('could not log user action:%s', str(e))
96 log.error('could not log user action:%s', str(e))
85
97
86 def check_repo_dir(paths):
98 def check_repo_dir(paths):
87 repos_path = paths[0][1].split('/')
99 repos_path = paths[0][1].split('/')
88 if repos_path[-1] in ['*', '**']:
100 if repos_path[-1] in ['*', '**']:
89 repos_path = repos_path[:-1]
101 repos_path = repos_path[:-1]
90 if repos_path[0] != '/':
102 if repos_path[0] != '/':
91 repos_path[0] = '/'
103 repos_path[0] = '/'
92 if not os.path.isdir(os.path.join(*repos_path)):
104 if not os.path.isdir(os.path.join(*repos_path)):
93 raise Exception('Not a valid repository in %s' % paths[0][1])
105 raise Exception('Not a valid repository in %s' % paths[0][1])
94
106
95 def check_repo_fast(repo_name, base_path):
107 def check_repo_fast(repo_name, base_path):
96 if os.path.isdir(os.path.join(base_path, repo_name)):return False
108 if os.path.isdir(os.path.join(base_path, repo_name)):return False
97 return True
109 return True
98
110
99 def check_repo(repo_name, base_path, verify=True):
111 def check_repo(repo_name, base_path, verify=True):
100
112
101 repo_path = os.path.join(base_path, repo_name)
113 repo_path = os.path.join(base_path, repo_name)
102
114
103 try:
115 try:
104 if not check_repo_fast(repo_name, base_path):
116 if not check_repo_fast(repo_name, base_path):
105 return False
117 return False
106 r = hg.repository(ui.ui(), repo_path)
118 r = hg.repository(ui.ui(), repo_path)
107 if verify:
119 if verify:
108 hg.verify(r)
120 hg.verify(r)
109 #here we hnow that repo exists it was verified
121 #here we hnow that repo exists it was verified
110 log.info('%s repo is already created', repo_name)
122 log.info('%s repo is already created', repo_name)
111 return False
123 return False
112 except RepoError:
124 except RepoError:
113 #it means that there is no valid repo there...
125 #it means that there is no valid repo there...
114 log.info('%s repo is free for creation', repo_name)
126 log.info('%s repo is free for creation', repo_name)
115 return True
127 return True
116
128
117 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
129 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
118 while True:
130 while True:
119 ok = raw_input(prompt)
131 ok = raw_input(prompt)
120 if ok in ('y', 'ye', 'yes'): return True
132 if ok in ('y', 'ye', 'yes'): return True
121 if ok in ('n', 'no', 'nop', 'nope'): return False
133 if ok in ('n', 'no', 'nop', 'nope'): return False
122 retries = retries - 1
134 retries = retries - 1
123 if retries < 0: raise IOError
135 if retries < 0: raise IOError
124 print complaint
136 print complaint
125
137
126 @cache_region('super_short_term', 'cached_hg_ui')
138 @cache_region('super_short_term', 'cached_hg_ui')
127 def get_hg_ui_cached():
139 def get_hg_ui_cached():
128 try:
140 try:
129 sa = meta.Session
141 sa = meta.Session
130 ret = sa.query(RhodeCodeUi).all()
142 ret = sa.query(RhodeCodeUi).all()
131 finally:
143 finally:
132 meta.Session.remove()
144 meta.Session.remove()
133 return ret
145 return ret
134
146
135
147
136 def get_hg_settings():
148 def get_hg_settings():
137 try:
149 try:
138 sa = meta.Session
150 sa = meta.Session
139 ret = sa.query(RhodeCodeSettings).all()
151 ret = sa.query(RhodeCodeSettings).all()
140 finally:
152 finally:
141 meta.Session.remove()
153 meta.Session.remove()
142
154
143 if not ret:
155 if not ret:
144 raise Exception('Could not get application settings !')
156 raise Exception('Could not get application settings !')
145 settings = {}
157 settings = {}
146 for each in ret:
158 for each in ret:
147 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
159 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
148
160
149 return settings
161 return settings
150
162
151 def get_hg_ui_settings():
163 def get_hg_ui_settings():
152 try:
164 try:
153 sa = meta.Session
165 sa = meta.Session
154 ret = sa.query(RhodeCodeUi).all()
166 ret = sa.query(RhodeCodeUi).all()
155 finally:
167 finally:
156 meta.Session.remove()
168 meta.Session.remove()
157
169
158 if not ret:
170 if not ret:
159 raise Exception('Could not get application ui settings !')
171 raise Exception('Could not get application ui settings !')
160 settings = {}
172 settings = {}
161 for each in ret:
173 for each in ret:
162 k = each.ui_key
174 k = each.ui_key
163 v = each.ui_value
175 v = each.ui_value
164 if k == '/':
176 if k == '/':
165 k = 'root_path'
177 k = 'root_path'
166
178
167 if k.find('.') != -1:
179 if k.find('.') != -1:
168 k = k.replace('.', '_')
180 k = k.replace('.', '_')
169
181
170 if each.ui_section == 'hooks':
182 if each.ui_section == 'hooks':
171 v = each.ui_active
183 v = each.ui_active
172
184
173 settings[each.ui_section + '_' + k] = v
185 settings[each.ui_section + '_' + k] = v
174
186
175 return settings
187 return settings
176
188
177 #propagated from mercurial documentation
189 #propagated from mercurial documentation
178 ui_sections = ['alias', 'auth',
190 ui_sections = ['alias', 'auth',
179 'decode/encode', 'defaults',
191 'decode/encode', 'defaults',
180 'diff', 'email',
192 'diff', 'email',
181 'extensions', 'format',
193 'extensions', 'format',
182 'merge-patterns', 'merge-tools',
194 'merge-patterns', 'merge-tools',
183 'hooks', 'http_proxy',
195 'hooks', 'http_proxy',
184 'smtp', 'patch',
196 'smtp', 'patch',
185 'paths', 'profiling',
197 'paths', 'profiling',
186 'server', 'trusted',
198 'server', 'trusted',
187 'ui', 'web', ]
199 'ui', 'web', ]
188
200
189 def make_ui(read_from='file', path=None, checkpaths=True):
201 def make_ui(read_from='file', path=None, checkpaths=True):
190 """
202 """
191 A function that will read python rc files or database
203 A function that will read python rc files or database
192 and make an mercurial ui object from read options
204 and make an mercurial ui object from read options
193
205
194 @param path: path to mercurial config file
206 :param path: path to mercurial config file
195 @param checkpaths: check the path
207 :param checkpaths: check the path
196 @param read_from: read from 'file' or 'db'
208 :param read_from: read from 'file' or 'db'
197 """
209 """
198
210
199 baseui = ui.ui()
211 baseui = ui.ui()
200
212
201 if read_from == 'file':
213 if read_from == 'file':
202 if not os.path.isfile(path):
214 if not os.path.isfile(path):
203 log.warning('Unable to read config file %s' % path)
215 log.warning('Unable to read config file %s' % path)
204 return False
216 return False
205 log.debug('reading hgrc from %s', path)
217 log.debug('reading hgrc from %s', path)
206 cfg = config.config()
218 cfg = config.config()
207 cfg.read(path)
219 cfg.read(path)
208 for section in ui_sections:
220 for section in ui_sections:
209 for k, v in cfg.items(section):
221 for k, v in cfg.items(section):
210 baseui.setconfig(section, k, v)
222 baseui.setconfig(section, k, v)
211 log.debug('settings ui from file[%s]%s:%s', section, k, v)
223 log.debug('settings ui from file[%s]%s:%s', section, k, v)
212 if checkpaths:check_repo_dir(cfg.items('paths'))
224 if checkpaths:check_repo_dir(cfg.items('paths'))
213
225
214
226
215 elif read_from == 'db':
227 elif read_from == 'db':
216 hg_ui = get_hg_ui_cached()
228 hg_ui = get_hg_ui_cached()
217 for ui_ in hg_ui:
229 for ui_ in hg_ui:
218 if ui_.ui_active:
230 if ui_.ui_active:
219 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
231 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section, ui_.ui_key, ui_.ui_value)
220 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
232 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
221
233
222
234
223 return baseui
235 return baseui
224
236
225
237
226 def set_rhodecode_config(config):
238 def set_rhodecode_config(config):
227 hgsettings = get_hg_settings()
239 hgsettings = get_hg_settings()
228
240
229 for k, v in hgsettings.items():
241 for k, v in hgsettings.items():
230 config[k] = v
242 config[k] = v
231
243
232 def invalidate_cache(name, *args):
244 def invalidate_cache(name, *args):
233 """Invalidates given name cache"""
245 """Invalidates given name cache"""
234
246
235 from beaker.cache import region_invalidate
247 from beaker.cache import region_invalidate
236 log.info('INVALIDATING CACHE FOR %s', name)
248 log.info('INVALIDATING CACHE FOR %s', name)
237
249
238 """propagate our arguments to make sure invalidation works. First
250 """propagate our arguments to make sure invalidation works. First
239 argument has to be the name of cached func name give to cache decorator
251 argument has to be the name of cached func name give to cache decorator
240 without that the invalidation would not work"""
252 without that the invalidation would not work"""
241 tmp = [name]
253 tmp = [name]
242 tmp.extend(args)
254 tmp.extend(args)
243 args = tuple(tmp)
255 args = tuple(tmp)
244
256
245 if name == 'cached_repo_list':
257 if name == 'cached_repo_list':
246 from rhodecode.model.hg_model import _get_repos_cached
258 from rhodecode.model.hg_model import _get_repos_cached
247 region_invalidate(_get_repos_cached, None, *args)
259 region_invalidate(_get_repos_cached, None, *args)
248
260
249 if name == 'full_changelog':
261 if name == 'full_changelog':
250 from rhodecode.model.hg_model import _full_changelog_cached
262 from rhodecode.model.hg_model import _full_changelog_cached
251 region_invalidate(_full_changelog_cached, None, *args)
263 region_invalidate(_full_changelog_cached, None, *args)
252
264
253 class EmptyChangeset(BaseChangeset):
265 class EmptyChangeset(BaseChangeset):
254 """
266 """
255 An dummy empty changeset.
267 An dummy empty changeset.
256 """
268 """
257
269
258 revision = -1
270 revision = -1
259 message = ''
271 message = ''
260 author = ''
272 author = ''
261 date = ''
273 date = ''
262 @LazyProperty
274 @LazyProperty
263 def raw_id(self):
275 def raw_id(self):
264 """
276 """
265 Returns raw string identifing this changeset, useful for web
277 Returns raw string identifing this changeset, useful for web
266 representation.
278 representation.
267 """
279 """
268 return '0' * 40
280 return '0' * 40
269
281
270 @LazyProperty
282 @LazyProperty
271 def short_id(self):
283 def short_id(self):
272 return self.raw_id[:12]
284 return self.raw_id[:12]
273
285
274 def get_file_changeset(self, path):
286 def get_file_changeset(self, path):
275 return self
287 return self
276
288
277 def get_file_content(self, path):
289 def get_file_content(self, path):
278 return u''
290 return u''
279
291
280 def get_file_size(self, path):
292 def get_file_size(self, path):
281 return 0
293 return 0
282
294
283 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
295 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
284 """
296 """
285 maps all found repositories into db
297 maps all found repositories into db
286 """
298 """
287 from rhodecode.model.repo_model import RepoModel
299 from rhodecode.model.repo_model import RepoModel
288
300
289 sa = meta.Session
301 sa = meta.Session
290 user = sa.query(User).filter(User.admin == True).first()
302 user = sa.query(User).filter(User.admin == True).first()
291
303
292 rm = RepoModel()
304 rm = RepoModel()
293
305
294 for name, repo in initial_repo_list.items():
306 for name, repo in initial_repo_list.items():
295 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
307 if not sa.query(Repository).filter(Repository.repo_name == name).scalar():
296 log.info('repository %s not found creating default', name)
308 log.info('repository %s not found creating default', name)
297
309
298 form_data = {
310 form_data = {
299 'repo_name':name,
311 'repo_name':name,
300 'description':repo.description if repo.description != 'unknown' else \
312 'description':repo.description if repo.description != 'unknown' else \
301 'auto description for %s' % name,
313 'auto description for %s' % name,
302 'private':False
314 'private':False
303 }
315 }
304 rm.create(form_data, user, just_db=True)
316 rm.create(form_data, user, just_db=True)
305
317
306
318
307 if remove_obsolete:
319 if remove_obsolete:
308 #remove from database those repositories that are not in the filesystem
320 #remove from database those repositories that are not in the filesystem
309 for repo in sa.query(Repository).all():
321 for repo in sa.query(Repository).all():
310 if repo.repo_name not in initial_repo_list.keys():
322 if repo.repo_name not in initial_repo_list.keys():
311 sa.delete(repo)
323 sa.delete(repo)
312 sa.commit()
324 sa.commit()
313
325
314
326
315 meta.Session.remove()
327 meta.Session.remove()
316
328
317 from UserDict import DictMixin
329 from UserDict import DictMixin
318
330
319 class OrderedDict(dict, DictMixin):
331 class OrderedDict(dict, DictMixin):
320
332
321 def __init__(self, *args, **kwds):
333 def __init__(self, *args, **kwds):
322 if len(args) > 1:
334 if len(args) > 1:
323 raise TypeError('expected at most 1 arguments, got %d' % len(args))
335 raise TypeError('expected at most 1 arguments, got %d' % len(args))
324 try:
336 try:
325 self.__end
337 self.__end
326 except AttributeError:
338 except AttributeError:
327 self.clear()
339 self.clear()
328 self.update(*args, **kwds)
340 self.update(*args, **kwds)
329
341
330 def clear(self):
342 def clear(self):
331 self.__end = end = []
343 self.__end = end = []
332 end += [None, end, end] # sentinel node for doubly linked list
344 end += [None, end, end] # sentinel node for doubly linked list
333 self.__map = {} # key --> [key, prev, next]
345 self.__map = {} # key --> [key, prev, next]
334 dict.clear(self)
346 dict.clear(self)
335
347
336 def __setitem__(self, key, value):
348 def __setitem__(self, key, value):
337 if key not in self:
349 if key not in self:
338 end = self.__end
350 end = self.__end
339 curr = end[1]
351 curr = end[1]
340 curr[2] = end[1] = self.__map[key] = [key, curr, end]
352 curr[2] = end[1] = self.__map[key] = [key, curr, end]
341 dict.__setitem__(self, key, value)
353 dict.__setitem__(self, key, value)
342
354
343 def __delitem__(self, key):
355 def __delitem__(self, key):
344 dict.__delitem__(self, key)
356 dict.__delitem__(self, key)
345 key, prev, next = self.__map.pop(key)
357 key, prev, next = self.__map.pop(key)
346 prev[2] = next
358 prev[2] = next
347 next[1] = prev
359 next[1] = prev
348
360
349 def __iter__(self):
361 def __iter__(self):
350 end = self.__end
362 end = self.__end
351 curr = end[2]
363 curr = end[2]
352 while curr is not end:
364 while curr is not end:
353 yield curr[0]
365 yield curr[0]
354 curr = curr[2]
366 curr = curr[2]
355
367
356 def __reversed__(self):
368 def __reversed__(self):
357 end = self.__end
369 end = self.__end
358 curr = end[1]
370 curr = end[1]
359 while curr is not end:
371 while curr is not end:
360 yield curr[0]
372 yield curr[0]
361 curr = curr[1]
373 curr = curr[1]
362
374
363 def popitem(self, last=True):
375 def popitem(self, last=True):
364 if not self:
376 if not self:
365 raise KeyError('dictionary is empty')
377 raise KeyError('dictionary is empty')
366 if last:
378 if last:
367 key = reversed(self).next()
379 key = reversed(self).next()
368 else:
380 else:
369 key = iter(self).next()
381 key = iter(self).next()
370 value = self.pop(key)
382 value = self.pop(key)
371 return key, value
383 return key, value
372
384
373 def __reduce__(self):
385 def __reduce__(self):
374 items = [[k, self[k]] for k in self]
386 items = [[k, self[k]] for k in self]
375 tmp = self.__map, self.__end
387 tmp = self.__map, self.__end
376 del self.__map, self.__end
388 del self.__map, self.__end
377 inst_dict = vars(self).copy()
389 inst_dict = vars(self).copy()
378 self.__map, self.__end = tmp
390 self.__map, self.__end = tmp
379 if inst_dict:
391 if inst_dict:
380 return (self.__class__, (items,), inst_dict)
392 return (self.__class__, (items,), inst_dict)
381 return self.__class__, (items,)
393 return self.__class__, (items,)
382
394
383 def keys(self):
395 def keys(self):
384 return list(self)
396 return list(self)
385
397
386 setdefault = DictMixin.setdefault
398 setdefault = DictMixin.setdefault
387 update = DictMixin.update
399 update = DictMixin.update
388 pop = DictMixin.pop
400 pop = DictMixin.pop
389 values = DictMixin.values
401 values = DictMixin.values
390 items = DictMixin.items
402 items = DictMixin.items
391 iterkeys = DictMixin.iterkeys
403 iterkeys = DictMixin.iterkeys
392 itervalues = DictMixin.itervalues
404 itervalues = DictMixin.itervalues
393 iteritems = DictMixin.iteritems
405 iteritems = DictMixin.iteritems
394
406
395 def __repr__(self):
407 def __repr__(self):
396 if not self:
408 if not self:
397 return '%s()' % (self.__class__.__name__,)
409 return '%s()' % (self.__class__.__name__,)
398 return '%s(%r)' % (self.__class__.__name__, self.items())
410 return '%s(%r)' % (self.__class__.__name__, self.items())
399
411
400 def copy(self):
412 def copy(self):
401 return self.__class__(self)
413 return self.__class__(self)
402
414
403 @classmethod
415 @classmethod
404 def fromkeys(cls, iterable, value=None):
416 def fromkeys(cls, iterable, value=None):
405 d = cls()
417 d = cls()
406 for key in iterable:
418 for key in iterable:
407 d[key] = value
419 d[key] = value
408 return d
420 return d
409
421
410 def __eq__(self, other):
422 def __eq__(self, other):
411 if isinstance(other, OrderedDict):
423 if isinstance(other, OrderedDict):
412 return len(self) == len(other) and self.items() == other.items()
424 return len(self) == len(other) and self.items() == other.items()
413 return dict.__eq__(self, other)
425 return dict.__eq__(self, other)
414
426
415 def __ne__(self, other):
427 def __ne__(self, other):
416 return not self == other
428 return not self == other
417
429
418
430
419 #===============================================================================
431 #===============================================================================
420 # TEST FUNCTIONS
432 # TEST FUNCTIONS
421 #===============================================================================
433 #===============================================================================
422 def create_test_index(repo_location, full_index):
434 def create_test_index(repo_location, full_index):
423 """Makes default test index
435 """Makes default test index
424 @param repo_location:
436 :param repo_location:
425 @param full_index:
437 :param full_index:
426 """
438 """
427 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
439 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
428 from rhodecode.lib.pidlock import DaemonLock, LockHeld
440 from rhodecode.lib.pidlock import DaemonLock, LockHeld
429 from rhodecode.lib.indexers import IDX_LOCATION
441 from rhodecode.lib.indexers import IDX_LOCATION
430 import shutil
442 import shutil
431
443
432 if os.path.exists(IDX_LOCATION):
444 if os.path.exists(IDX_LOCATION):
433 shutil.rmtree(IDX_LOCATION)
445 shutil.rmtree(IDX_LOCATION)
434
446
435 try:
447 try:
436 l = DaemonLock()
448 l = DaemonLock()
437 WhooshIndexingDaemon(repo_location=repo_location)\
449 WhooshIndexingDaemon(repo_location=repo_location)\
438 .run(full_index=full_index)
450 .run(full_index=full_index)
439 l.release()
451 l.release()
440 except LockHeld:
452 except LockHeld:
441 pass
453 pass
442
454
443 def create_test_env(repos_test_path, config):
455 def create_test_env(repos_test_path, config):
444 """Makes a fresh database and
456 """Makes a fresh database and
445 install test repository into tmp dir
457 install test repository into tmp dir
446 """
458 """
447 from rhodecode.lib.db_manage import DbManage
459 from rhodecode.lib.db_manage import DbManage
448 import tarfile
460 import tarfile
449 import shutil
461 import shutil
450 from os.path import dirname as dn, join as jn, abspath
462 from os.path import dirname as dn, join as jn, abspath
451
463
452 log = logging.getLogger('TestEnvCreator')
464 log = logging.getLogger('TestEnvCreator')
453 # create logger
465 # create logger
454 log.setLevel(logging.DEBUG)
466 log.setLevel(logging.DEBUG)
455 log.propagate = True
467 log.propagate = True
456 # create console handler and set level to debug
468 # create console handler and set level to debug
457 ch = logging.StreamHandler()
469 ch = logging.StreamHandler()
458 ch.setLevel(logging.DEBUG)
470 ch.setLevel(logging.DEBUG)
459
471
460 # create formatter
472 # create formatter
461 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
473 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
462
474
463 # add formatter to ch
475 # add formatter to ch
464 ch.setFormatter(formatter)
476 ch.setFormatter(formatter)
465
477
466 # add ch to logger
478 # add ch to logger
467 log.addHandler(ch)
479 log.addHandler(ch)
468
480
469 #PART ONE create db
481 #PART ONE create db
470 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
482 dbname = config['sqlalchemy.db1.url'].split('/')[-1]
471 log.debug('making test db %s', dbname)
483 log.debug('making test db %s', dbname)
472
484
473 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
485 dbmanage = DbManage(log_sql=True, dbname=dbname, root=config['here'],
474 tests=True)
486 tests=True)
475 dbmanage.create_tables(override=True)
487 dbmanage.create_tables(override=True)
476 dbmanage.config_prompt(repos_test_path)
488 dbmanage.config_prompt(repos_test_path)
477 dbmanage.create_default_user()
489 dbmanage.create_default_user()
478 dbmanage.admin_prompt()
490 dbmanage.admin_prompt()
479 dbmanage.create_permissions()
491 dbmanage.create_permissions()
480 dbmanage.populate_default_permissions()
492 dbmanage.populate_default_permissions()
481
493
482 #PART TWO make test repo
494 #PART TWO make test repo
483 log.debug('making test vcs repo')
495 log.debug('making test vcs repo')
484 if os.path.isdir('/tmp/vcs_test'):
496 if os.path.isdir('/tmp/vcs_test'):
485 shutil.rmtree('/tmp/vcs_test')
497 shutil.rmtree('/tmp/vcs_test')
486
498
487 cur_dir = dn(dn(abspath(__file__)))
499 cur_dir = dn(dn(abspath(__file__)))
488 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
500 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test.tar.gz"))
489 tar.extractall('/tmp')
501 tar.extractall('/tmp')
490 tar.close()
502 tar.close()
General Comments 0
You need to be logged in to leave comments. Login now