##// END OF EJS Templates
Implemented server side forks...
marcink -
r530:a08f610e default
parent child Browse files
Show More
@@ -1,177 +1,181 b''
1 """Routes configuration
1 """Routes configuration
2
2
3 The more specific and detailed routes should be defined first so they
3 The more specific and detailed routes should be defined first so they
4 may take precedent over the more generic routes. For more information
4 may take precedent over the more generic routes. For more information
5 refer to the routes manual at http://routes.groovie.org/docs/
5 refer to the routes manual at http://routes.groovie.org/docs/
6 """
6 """
7 from __future__ import with_statement
7 from __future__ import with_statement
8 from routes import Mapper
8 from routes import Mapper
9 from pylons_app.lib.utils import check_repo_fast as cr
9 from pylons_app.lib.utils import check_repo_fast as cr
10
10
11 def make_map(config):
11 def make_map(config):
12 """Create, configure and return the routes Mapper"""
12 """Create, configure and return the routes Mapper"""
13 map = Mapper(directory=config['pylons.paths']['controllers'],
13 map = Mapper(directory=config['pylons.paths']['controllers'],
14 always_scan=config['debug'])
14 always_scan=config['debug'])
15 map.minimization = False
15 map.minimization = False
16 map.explicit = False
16 map.explicit = False
17
17
18 # The ErrorController route (handles 404/500 error pages); it should
18 # The ErrorController route (handles 404/500 error pages); it should
19 # likely stay at the top, ensuring it can always be resolved
19 # likely stay at the top, ensuring it can always be resolved
20 map.connect('/error/{action}', controller='error')
20 map.connect('/error/{action}', controller='error')
21 map.connect('/error/{action}/{id}', controller='error')
21 map.connect('/error/{action}/{id}', controller='error')
22
22
23 # CUSTOM ROUTES HERE
23 # CUSTOM ROUTES HERE
24 map.connect('hg_home', '/', controller='hg', action='index')
24 map.connect('hg_home', '/', controller='hg', action='index')
25
25
26 def check_repo(environ, match_dict):
26 def check_repo(environ, match_dict):
27 """
27 """
28 check for valid repository for proper 404 handling
28 check for valid repository for proper 404 handling
29 @param environ:
29 @param environ:
30 @param match_dict:
30 @param match_dict:
31 """
31 """
32 repo_name = match_dict.get('repo_name')
32 repo_name = match_dict.get('repo_name')
33 return not cr(repo_name, config['base_path'])
33 return not cr(repo_name, config['base_path'])
34
34
35 #REST REPO MAP
35 #REST REPO MAP
36 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
36 with map.submapper(path_prefix='/_admin', controller='admin/repos') as m:
37 m.connect("repos", "/repos",
37 m.connect("repos", "/repos",
38 action="create", conditions=dict(method=["POST"]))
38 action="create", conditions=dict(method=["POST"]))
39 m.connect("repos", "/repos",
39 m.connect("repos", "/repos",
40 action="index", conditions=dict(method=["GET"]))
40 action="index", conditions=dict(method=["GET"]))
41 m.connect("formatted_repos", "/repos.{format}",
41 m.connect("formatted_repos", "/repos.{format}",
42 action="index",
42 action="index",
43 conditions=dict(method=["GET"]))
43 conditions=dict(method=["GET"]))
44 m.connect("new_repo", "/repos/new",
44 m.connect("new_repo", "/repos/new",
45 action="new", conditions=dict(method=["GET"]))
45 action="new", conditions=dict(method=["GET"]))
46 m.connect("formatted_new_repo", "/repos/new.{format}",
46 m.connect("formatted_new_repo", "/repos/new.{format}",
47 action="new", conditions=dict(method=["GET"]))
47 action="new", conditions=dict(method=["GET"]))
48 m.connect("/repos/{repo_name:.*}",
48 m.connect("/repos/{repo_name:.*}",
49 action="update", conditions=dict(method=["PUT"],
49 action="update", conditions=dict(method=["PUT"],
50 function=check_repo))
50 function=check_repo))
51 m.connect("/repos/{repo_name:.*}",
51 m.connect("/repos/{repo_name:.*}",
52 action="delete", conditions=dict(method=["DELETE"],
52 action="delete", conditions=dict(method=["DELETE"],
53 function=check_repo))
53 function=check_repo))
54 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
54 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
55 action="edit", conditions=dict(method=["GET"],
55 action="edit", conditions=dict(method=["GET"],
56 function=check_repo))
56 function=check_repo))
57 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
57 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
58 action="edit", conditions=dict(method=["GET"],
58 action="edit", conditions=dict(method=["GET"],
59 function=check_repo))
59 function=check_repo))
60 m.connect("repo", "/repos/{repo_name:.*}",
60 m.connect("repo", "/repos/{repo_name:.*}",
61 action="show", conditions=dict(method=["GET"],
61 action="show", conditions=dict(method=["GET"],
62 function=check_repo))
62 function=check_repo))
63 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
63 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
64 action="show", conditions=dict(method=["GET"],
64 action="show", conditions=dict(method=["GET"],
65 function=check_repo))
65 function=check_repo))
66 #ajax delete repo perm user
66 #ajax delete repo perm user
67 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
67 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
68 action="delete_perm_user", conditions=dict(method=["DELETE"],
68 action="delete_perm_user", conditions=dict(method=["DELETE"],
69 function=check_repo))
69 function=check_repo))
70
70
71 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
71 map.resource('user', 'users', controller='admin/users', path_prefix='/_admin')
72 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
72 map.resource('permission', 'permissions', controller='admin/permissions', path_prefix='/_admin')
73
73
74 #REST SETTINGS MAP
74 #REST SETTINGS MAP
75 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
75 with map.submapper(path_prefix='/_admin', controller='admin/settings') as m:
76 m.connect("admin_settings", "/settings",
76 m.connect("admin_settings", "/settings",
77 action="create", conditions=dict(method=["POST"]))
77 action="create", conditions=dict(method=["POST"]))
78 m.connect("admin_settings", "/settings",
78 m.connect("admin_settings", "/settings",
79 action="index", conditions=dict(method=["GET"]))
79 action="index", conditions=dict(method=["GET"]))
80 m.connect("formatted_admin_settings", "/settings.{format}",
80 m.connect("formatted_admin_settings", "/settings.{format}",
81 action="index", conditions=dict(method=["GET"]))
81 action="index", conditions=dict(method=["GET"]))
82 m.connect("admin_new_setting", "/settings/new",
82 m.connect("admin_new_setting", "/settings/new",
83 action="new", conditions=dict(method=["GET"]))
83 action="new", conditions=dict(method=["GET"]))
84 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
84 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
85 action="new", conditions=dict(method=["GET"]))
85 action="new", conditions=dict(method=["GET"]))
86 m.connect("/settings/{setting_id}",
86 m.connect("/settings/{setting_id}",
87 action="update", conditions=dict(method=["PUT"]))
87 action="update", conditions=dict(method=["PUT"]))
88 m.connect("/settings/{setting_id}",
88 m.connect("/settings/{setting_id}",
89 action="delete", conditions=dict(method=["DELETE"]))
89 action="delete", conditions=dict(method=["DELETE"]))
90 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
90 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
91 action="edit", conditions=dict(method=["GET"]))
91 action="edit", conditions=dict(method=["GET"]))
92 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
92 m.connect("formatted_admin_edit_setting", "/settings/{setting_id}.{format}/edit",
93 action="edit", conditions=dict(method=["GET"]))
93 action="edit", conditions=dict(method=["GET"]))
94 m.connect("admin_setting", "/settings/{setting_id}",
94 m.connect("admin_setting", "/settings/{setting_id}",
95 action="show", conditions=dict(method=["GET"]))
95 action="show", conditions=dict(method=["GET"]))
96 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
96 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
97 action="show", conditions=dict(method=["GET"]))
97 action="show", conditions=dict(method=["GET"]))
98 m.connect("admin_settings_my_account", "/my_account",
98 m.connect("admin_settings_my_account", "/my_account",
99 action="my_account", conditions=dict(method=["GET"]))
99 action="my_account", conditions=dict(method=["GET"]))
100 m.connect("admin_settings_my_account_update", "/my_account_update",
100 m.connect("admin_settings_my_account_update", "/my_account_update",
101 action="my_account_update", conditions=dict(method=["PUT"]))
101 action="my_account_update", conditions=dict(method=["PUT"]))
102 m.connect("admin_settings_create_repository", "/create_repository",
102 m.connect("admin_settings_create_repository", "/create_repository",
103 action="create_repository", conditions=dict(method=["GET"]))
103 action="create_repository", conditions=dict(method=["GET"]))
104
104
105 #ADMIN
105 #ADMIN
106 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
106 with map.submapper(path_prefix='/_admin', controller='admin/admin') as m:
107 m.connect('admin_home', '', action='index')#main page
107 m.connect('admin_home', '', action='index')#main page
108 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
108 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
109 action='add_repo')
109 action='add_repo')
110 #SEARCH
110 #SEARCH
111 map.connect('search', '/_admin/search', controller='search',)
111 map.connect('search', '/_admin/search', controller='search',)
112 map.connect('search_repo', '/_admin/search/{search_repo:.*}', controller='search')
112 map.connect('search_repo', '/_admin/search/{search_repo:.*}', controller='search')
113
113
114 #LOGIN/LOGOUT/REGISTER/SIGN IN
114 #LOGIN/LOGOUT/REGISTER/SIGN IN
115 map.connect('login_home', '/_admin/login', controller='login')
115 map.connect('login_home', '/_admin/login', controller='login')
116 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
116 map.connect('logout_home', '/_admin/logout', controller='login', action='logout')
117 map.connect('register', '/_admin/register', controller='login', action='register')
117 map.connect('register', '/_admin/register', controller='login', action='register')
118 map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
118 map.connect('reset_password', '/_admin/password_reset', controller='login', action='password_reset')
119
119
120 #FEEDS
120 #FEEDS
121 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
121 map.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
122 controller='feed', action='rss',
122 controller='feed', action='rss',
123 conditions=dict(function=check_repo))
123 conditions=dict(function=check_repo))
124 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
124 map.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
125 controller='feed', action='atom',
125 controller='feed', action='atom',
126 conditions=dict(function=check_repo))
126 conditions=dict(function=check_repo))
127
127
128
128
129 #OTHERS
129 #OTHERS
130 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
130 map.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
131 controller='changeset', revision='tip',
131 controller='changeset', revision='tip',
132 conditions=dict(function=check_repo))
132 conditions=dict(function=check_repo))
133 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
133 map.connect('raw_changeset_home', '/{repo_name:.*}/raw-changeset/{revision}',
134 controller='changeset', action='raw_changeset', revision='tip',
134 controller='changeset', action='raw_changeset', revision='tip',
135 conditions=dict(function=check_repo))
135 conditions=dict(function=check_repo))
136 map.connect('summary_home', '/{repo_name:.*}/summary',
136 map.connect('summary_home', '/{repo_name:.*}/summary',
137 controller='summary', conditions=dict(function=check_repo))
137 controller='summary', conditions=dict(function=check_repo))
138 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
138 map.connect('shortlog_home', '/{repo_name:.*}/shortlog',
139 controller='shortlog', conditions=dict(function=check_repo))
139 controller='shortlog', conditions=dict(function=check_repo))
140 map.connect('branches_home', '/{repo_name:.*}/branches',
140 map.connect('branches_home', '/{repo_name:.*}/branches',
141 controller='branches', conditions=dict(function=check_repo))
141 controller='branches', conditions=dict(function=check_repo))
142 map.connect('tags_home', '/{repo_name:.*}/tags',
142 map.connect('tags_home', '/{repo_name:.*}/tags',
143 controller='tags', conditions=dict(function=check_repo))
143 controller='tags', conditions=dict(function=check_repo))
144 map.connect('changelog_home', '/{repo_name:.*}/changelog',
144 map.connect('changelog_home', '/{repo_name:.*}/changelog',
145 controller='changelog', conditions=dict(function=check_repo))
145 controller='changelog', conditions=dict(function=check_repo))
146 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
146 map.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
147 controller='files', revision='tip', f_path='',
147 controller='files', revision='tip', f_path='',
148 conditions=dict(function=check_repo))
148 conditions=dict(function=check_repo))
149 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
149 map.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
150 controller='files', action='diff', revision='tip', f_path='',
150 controller='files', action='diff', revision='tip', f_path='',
151 conditions=dict(function=check_repo))
151 conditions=dict(function=check_repo))
152 map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
152 map.connect('files_rawfile_home', '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
153 controller='files', action='rawfile', revision='tip', f_path='',
153 controller='files', action='rawfile', revision='tip', f_path='',
154 conditions=dict(function=check_repo))
154 conditions=dict(function=check_repo))
155 map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
155 map.connect('files_raw_home', '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
156 controller='files', action='raw', revision='tip', f_path='',
156 controller='files', action='raw', revision='tip', f_path='',
157 conditions=dict(function=check_repo))
157 conditions=dict(function=check_repo))
158 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
158 map.connect('files_annotate_home', '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
159 controller='files', action='annotate', revision='tip', f_path='',
159 controller='files', action='annotate', revision='tip', f_path='',
160 conditions=dict(function=check_repo))
160 conditions=dict(function=check_repo))
161 map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
161 map.connect('files_archive_home', '/{repo_name:.*}/archive/{revision}/{fileformat}',
162 controller='files', action='archivefile', revision='tip',
162 controller='files', action='archivefile', revision='tip',
163 conditions=dict(function=check_repo))
163 conditions=dict(function=check_repo))
164 map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
164 map.connect('repo_settings_delete', '/{repo_name:.*}/settings',
165 controller='settings', action="delete",
165 controller='settings', action="delete",
166 conditions=dict(method=["DELETE"], function=check_repo))
166 conditions=dict(method=["DELETE"], function=check_repo))
167 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
167 map.connect('repo_settings_update', '/{repo_name:.*}/settings',
168 controller='settings', action="update",
168 controller='settings', action="update",
169 conditions=dict(method=["PUT"], function=check_repo))
169 conditions=dict(method=["PUT"], function=check_repo))
170 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
170 map.connect('repo_settings_home', '/{repo_name:.*}/settings',
171 controller='settings', action='index',
171 controller='settings', action='index',
172 conditions=dict(function=check_repo))
172 conditions=dict(function=check_repo))
173
173
174 map.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
175 controller='settings', action='fork_create',
176 conditions=dict(function=check_repo, method=["POST"]))
174 map.connect('repo_fork_home', '/{repo_name:.*}/fork',
177 map.connect('repo_fork_home', '/{repo_name:.*}/fork',
175 controller='settings', action='fork',
178 controller='settings', action='fork',
176 conditions=dict(function=check_repo))
179 conditions=dict(function=check_repo))
180
177 return map
181 return map
@@ -1,144 +1,172 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # settings controller for pylons
3 # settings controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on June 30, 2010
21 Created on June 30, 2010
22 settings controller for pylons
22 settings controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from pylons import tmpl_context as c, request, url
26 from pylons import tmpl_context as c, request, url
27 from pylons.controllers.util import redirect
27 from pylons.controllers.util import redirect
28 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
29 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
29 from pylons_app.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 from pylons_app.lib.base import BaseController, render
30 from pylons_app.lib.base import BaseController, render
31 from pylons_app.lib.utils import invalidate_cache
31 from pylons_app.lib.utils import invalidate_cache
32 from pylons_app.model.forms import RepoSettingsForm
32 from pylons_app.model.forms import RepoSettingsForm, RepoForkForm
33 from pylons_app.model.repo_model import RepoModel
33 from pylons_app.model.repo_model import RepoModel
34 import formencode
34 import formencode
35 import logging
35 import logging
36 import pylons_app.lib.helpers as h
36 import pylons_app.lib.helpers as h
37 import traceback
37 import traceback
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 class SettingsController(BaseController):
41 class SettingsController(BaseController):
42
42
43 @LoginRequired()
43 @LoginRequired()
44 @HasRepoPermissionAllDecorator('repository.admin')
44 @HasRepoPermissionAllDecorator('repository.admin')
45 def __before__(self):
45 def __before__(self):
46 super(SettingsController, self).__before__()
46 super(SettingsController, self).__before__()
47
47
48 def index(self, repo_name):
48 def index(self, repo_name):
49 repo_model = RepoModel()
49 repo_model = RepoModel()
50 c.repo_info = repo = repo_model.get(repo_name)
50 c.repo_info = repo = repo_model.get(repo_name)
51 if not repo:
51 if not repo:
52 h.flash(_('%s repository is not mapped to db perhaps'
52 h.flash(_('%s repository is not mapped to db perhaps'
53 ' it was created or renamed from the filesystem'
53 ' it was created or renamed from the filesystem'
54 ' please run the application again'
54 ' please run the application again'
55 ' in order to rescan repositories') % repo_name,
55 ' in order to rescan repositories') % repo_name,
56 category='error')
56 category='error')
57
57
58 return redirect(url('hg_home'))
58 return redirect(url('hg_home'))
59 defaults = c.repo_info.__dict__
59 defaults = c.repo_info.__dict__
60 defaults.update({'user':c.repo_info.user.username})
60 defaults.update({'user':c.repo_info.user.username})
61 c.users_array = repo_model.get_users_js()
61 c.users_array = repo_model.get_users_js()
62
62
63 for p in c.repo_info.repo_to_perm:
63 for p in c.repo_info.repo_to_perm:
64 defaults.update({'perm_%s' % p.user.username:
64 defaults.update({'perm_%s' % p.user.username:
65 p.permission.permission_name})
65 p.permission.permission_name})
66
66
67 return htmlfill.render(
67 return htmlfill.render(
68 render('settings/repo_settings.html'),
68 render('settings/repo_settings.html'),
69 defaults=defaults,
69 defaults=defaults,
70 encoding="UTF-8",
70 encoding="UTF-8",
71 force_defaults=False
71 force_defaults=False
72 )
72 )
73
73
74 def update(self, repo_name):
74 def update(self, repo_name):
75 repo_model = RepoModel()
75 repo_model = RepoModel()
76 changed_name = repo_name
76 changed_name = repo_name
77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
78 try:
78 try:
79 form_result = _form.to_python(dict(request.POST))
79 form_result = _form.to_python(dict(request.POST))
80 repo_model.update(repo_name, form_result)
80 repo_model.update(repo_name, form_result)
81 invalidate_cache('cached_repo_list')
81 invalidate_cache('cached_repo_list')
82 h.flash(_('Repository %s updated succesfully' % repo_name),
82 h.flash(_('Repository %s updated succesfully' % repo_name),
83 category='success')
83 category='success')
84 changed_name = form_result['repo_name']
84 changed_name = form_result['repo_name']
85 except formencode.Invalid as errors:
85 except formencode.Invalid as errors:
86 c.repo_info = repo_model.get(repo_name)
86 c.repo_info = repo_model.get(repo_name)
87 c.users_array = repo_model.get_users_js()
87 c.users_array = repo_model.get_users_js()
88 errors.value.update({'user':c.repo_info.user.username})
88 errors.value.update({'user':c.repo_info.user.username})
89 return htmlfill.render(
89 return htmlfill.render(
90 render('settings/repo_settings.html'),
90 render('settings/repo_settings.html'),
91 defaults=errors.value,
91 defaults=errors.value,
92 errors=errors.error_dict or {},
92 errors=errors.error_dict or {},
93 prefix_error=False,
93 prefix_error=False,
94 encoding="UTF-8")
94 encoding="UTF-8")
95 except Exception:
95 except Exception:
96 log.error(traceback.format_exc())
96 log.error(traceback.format_exc())
97 h.flash(_('error occured during update of repository %s') \
97 h.flash(_('error occured during update of repository %s') \
98 % repo_name, category='error')
98 % repo_name, category='error')
99
99
100 return redirect(url('repo_settings_home', repo_name=changed_name))
100 return redirect(url('repo_settings_home', repo_name=changed_name))
101
101
102
102
103
103
104 def delete(self, repo_name):
104 def delete(self, repo_name):
105 """DELETE /repos/repo_name: Delete an existing item"""
105 """DELETE /repos/repo_name: Delete an existing item"""
106 # Forms posted to this method should contain a hidden field:
106 # Forms posted to this method should contain a hidden field:
107 # <input type="hidden" name="_method" value="DELETE" />
107 # <input type="hidden" name="_method" value="DELETE" />
108 # Or using helpers:
108 # Or using helpers:
109 # h.form(url('repo_settings_delete', repo_name=ID),
109 # h.form(url('repo_settings_delete', repo_name=ID),
110 # method='delete')
110 # method='delete')
111 # url('repo_settings_delete', repo_name=ID)
111 # url('repo_settings_delete', repo_name=ID)
112
112
113 repo_model = RepoModel()
113 repo_model = RepoModel()
114 repo = repo_model.get(repo_name)
114 repo = repo_model.get(repo_name)
115 if not repo:
115 if not repo:
116 h.flash(_('%s repository is not mapped to db perhaps'
116 h.flash(_('%s repository is not mapped to db perhaps'
117 ' it was moved or renamed from the filesystem'
117 ' it was moved or renamed from the filesystem'
118 ' please run the application again'
118 ' please run the application again'
119 ' in order to rescan repositories') % repo_name,
119 ' in order to rescan repositories') % repo_name,
120 category='error')
120 category='error')
121
121
122 return redirect(url('hg_home'))
122 return redirect(url('hg_home'))
123 try:
123 try:
124 repo_model.delete(repo)
124 repo_model.delete(repo)
125 invalidate_cache('cached_repo_list')
125 invalidate_cache('cached_repo_list')
126 h.flash(_('deleted repository %s') % repo_name, category='success')
126 h.flash(_('deleted repository %s') % repo_name, category='success')
127 except Exception:
127 except Exception:
128 h.flash(_('An error occured during deletion of %s') % repo_name,
128 h.flash(_('An error occured during deletion of %s') % repo_name,
129 category='error')
129 category='error')
130
130
131 return redirect(url('hg_home'))
131 return redirect(url('hg_home'))
132
132
133 def fork(self, repo_name):
133 def fork(self, repo_name):
134 repo_model = RepoModel()
134 repo_model = RepoModel()
135 c.repo_info = repo = repo_model.get(repo_name)
135 c.repo_info = repo = repo_model.get(repo_name)
136 if not repo:
136 if not repo:
137 h.flash(_('%s repository is not mapped to db perhaps'
137 h.flash(_('%s repository is not mapped to db perhaps'
138 ' it was created or renamed from the filesystem'
138 ' it was created or renamed from the filesystem'
139 ' please run the application again'
139 ' please run the application again'
140 ' in order to rescan repositories') % repo_name,
140 ' in order to rescan repositories') % repo_name,
141 category='error')
141 category='error')
142
142
143 return redirect(url('hg_home'))
143 return redirect(url('hg_home'))
144
144 return render('settings/repo_fork.html')
145 return render('settings/repo_fork.html')
146
147
148
149 def fork_create(self, repo_name):
150 repo_model = RepoModel()
151 c.repo_info = repo_model.get(repo_name)
152 _form = RepoForkForm()()
153 form_result = {}
154 try:
155 form_result = _form.to_python(dict(request.POST))
156 form_result.update({'repo_name':repo_name})
157 repo_model.create_fork(form_result, c.hg_app_user)
158 h.flash(_('fork %s repository as %s task added') \
159 % (repo_name, form_result['fork_name']),
160 category='success')
161
162 except formencode.Invalid as errors:
163 c.new_repo = errors.value['fork_name']
164 r = render('settings/repo_fork.html')
165
166 return htmlfill.render(
167 r,
168 defaults=errors.value,
169 errors=errors.error_dict or {},
170 prefix_error=False,
171 encoding="UTF-8")
172 return redirect(url('hg_home'))
@@ -1,300 +1,318 b''
1 from celery.decorators import task
1 from celery.decorators import task
2 from celery.task.sets import subtask
2 from celery.task.sets import subtask
3 from celeryconfig import PYLONS_CONFIG as config
3 from celeryconfig import PYLONS_CONFIG as config
4 from operator import itemgetter
4 from operator import itemgetter
5 from pylons.i18n.translation import _
5 from pylons.i18n.translation import _
6 from pylons_app.lib.celerylib import run_task, locked_task
6 from pylons_app.lib.celerylib import run_task, locked_task
7 from pylons_app.lib.helpers import person
7 from pylons_app.lib.helpers import person
8 from pylons_app.lib.smtp_mailer import SmtpMailer
8 from pylons_app.lib.smtp_mailer import SmtpMailer
9 from pylons_app.lib.utils import OrderedDict
9 from pylons_app.lib.utils import OrderedDict
10 from time import mktime
10 from time import mktime
11 from vcs.backends.hg import MercurialRepository
11 from vcs.backends.hg import MercurialRepository
12 import json
12 import json
13 import traceback
13 import traceback
14
14
15 __all__ = ['whoosh_index', 'get_commits_stats',
15 __all__ = ['whoosh_index', 'get_commits_stats',
16 'reset_user_password', 'send_email']
16 'reset_user_password', 'send_email']
17
17
18 def get_session():
18 def get_session():
19 from sqlalchemy import engine_from_config
19 from sqlalchemy import engine_from_config
20 from sqlalchemy.orm import sessionmaker, scoped_session
20 from sqlalchemy.orm import sessionmaker, scoped_session
21 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
21 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
22 sa = scoped_session(sessionmaker(bind=engine))
22 sa = scoped_session(sessionmaker(bind=engine))
23 return sa
23 return sa
24
24
25 def get_hg_settings():
25 def get_hg_settings():
26 from pylons_app.model.db import HgAppSettings
26 from pylons_app.model.db import HgAppSettings
27 try:
27 try:
28 sa = get_session()
28 sa = get_session()
29 ret = sa.query(HgAppSettings).all()
29 ret = sa.query(HgAppSettings).all()
30 finally:
30 finally:
31 sa.remove()
31 sa.remove()
32
32
33 if not ret:
33 if not ret:
34 raise Exception('Could not get application settings !')
34 raise Exception('Could not get application settings !')
35 settings = {}
35 settings = {}
36 for each in ret:
36 for each in ret:
37 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
37 settings['hg_app_' + each.app_settings_name] = each.app_settings_value
38
38
39 return settings
39 return settings
40
40
41 def get_hg_ui_settings():
41 def get_hg_ui_settings():
42 from pylons_app.model.db import HgAppUi
42 from pylons_app.model.db import HgAppUi
43 try:
43 try:
44 sa = get_session()
44 sa = get_session()
45 ret = sa.query(HgAppUi).all()
45 ret = sa.query(HgAppUi).all()
46 finally:
46 finally:
47 sa.remove()
47 sa.remove()
48
48
49 if not ret:
49 if not ret:
50 raise Exception('Could not get application ui settings !')
50 raise Exception('Could not get application ui settings !')
51 settings = {}
51 settings = {}
52 for each in ret:
52 for each in ret:
53 k = each.ui_key
53 k = each.ui_key
54 v = each.ui_value
54 v = each.ui_value
55 if k == '/':
55 if k == '/':
56 k = 'root_path'
56 k = 'root_path'
57
57
58 if k.find('.') != -1:
58 if k.find('.') != -1:
59 k = k.replace('.', '_')
59 k = k.replace('.', '_')
60
60
61 if each.ui_section == 'hooks':
61 if each.ui_section == 'hooks':
62 v = each.ui_active
62 v = each.ui_active
63
63
64 settings[each.ui_section + '_' + k] = v
64 settings[each.ui_section + '_' + k] = v
65
65
66 return settings
66 return settings
67
67
68 @task
68 @task
69 @locked_task
69 @locked_task
70 def whoosh_index(repo_location, full_index):
70 def whoosh_index(repo_location, full_index):
71 log = whoosh_index.get_logger()
71 log = whoosh_index.get_logger()
72 from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon
72 from pylons_app.lib.indexers.daemon import WhooshIndexingDaemon
73 WhooshIndexingDaemon(repo_location=repo_location).run(full_index=full_index)
73 WhooshIndexingDaemon(repo_location=repo_location).run(full_index=full_index)
74
74
75 @task
75 @task
76 @locked_task
76 @locked_task
77 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
77 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
78 from pylons_app.model.db import Statistics, Repository
78 from pylons_app.model.db import Statistics, Repository
79 log = get_commits_stats.get_logger()
79 log = get_commits_stats.get_logger()
80 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
80 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
81
81
82 commits_by_day_author_aggregate = {}
82 commits_by_day_author_aggregate = {}
83 commits_by_day_aggregate = {}
83 commits_by_day_aggregate = {}
84 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
84 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
85 repo = MercurialRepository(repos_path + repo_name)
85 repo = MercurialRepository(repos_path + repo_name)
86
86
87 skip_date_limit = True
87 skip_date_limit = True
88 parse_limit = 350 #limit for single task changeset parsing optimal for
88 parse_limit = 350 #limit for single task changeset parsing optimal for
89 last_rev = 0
89 last_rev = 0
90 last_cs = None
90 last_cs = None
91 timegetter = itemgetter('time')
91 timegetter = itemgetter('time')
92
92
93 sa = get_session()
93 sa = get_session()
94
94
95 dbrepo = sa.query(Repository)\
95 dbrepo = sa.query(Repository)\
96 .filter(Repository.repo_name == repo_name).scalar()
96 .filter(Repository.repo_name == repo_name).scalar()
97 cur_stats = sa.query(Statistics)\
97 cur_stats = sa.query(Statistics)\
98 .filter(Statistics.repository == dbrepo).scalar()
98 .filter(Statistics.repository == dbrepo).scalar()
99 if cur_stats:
99 if cur_stats:
100 last_rev = cur_stats.stat_on_revision
100 last_rev = cur_stats.stat_on_revision
101 if not repo.revisions:
101 if not repo.revisions:
102 return True
102 return True
103
103
104 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
104 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
105 #pass silently without any work if we're not on first revision or current
105 #pass silently without any work if we're not on first revision or current
106 #state of parsing revision(from db marker) is the last revision
106 #state of parsing revision(from db marker) is the last revision
107 return True
107 return True
108
108
109 if cur_stats:
109 if cur_stats:
110 commits_by_day_aggregate = OrderedDict(
110 commits_by_day_aggregate = OrderedDict(
111 json.loads(
111 json.loads(
112 cur_stats.commit_activity_combined))
112 cur_stats.commit_activity_combined))
113 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
113 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
114
114
115 log.debug('starting parsing %s', parse_limit)
115 log.debug('starting parsing %s', parse_limit)
116 for cnt, rev in enumerate(repo.revisions[last_rev:]):
116 for cnt, rev in enumerate(repo.revisions[last_rev:]):
117 last_cs = cs = repo.get_changeset(rev)
117 last_cs = cs = repo.get_changeset(rev)
118 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
118 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
119 cs.date.timetuple()[2])
119 cs.date.timetuple()[2])
120 timetupple = [int(x) for x in k.split('-')]
120 timetupple = [int(x) for x in k.split('-')]
121 timetupple.extend([0 for _ in xrange(6)])
121 timetupple.extend([0 for _ in xrange(6)])
122 k = mktime(timetupple)
122 k = mktime(timetupple)
123 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
123 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
124 try:
124 try:
125 l = [timegetter(x) for x in commits_by_day_author_aggregate\
125 l = [timegetter(x) for x in commits_by_day_author_aggregate\
126 [author_key_cleaner(cs.author)]['data']]
126 [author_key_cleaner(cs.author)]['data']]
127 time_pos = l.index(k)
127 time_pos = l.index(k)
128 except ValueError:
128 except ValueError:
129 time_pos = False
129 time_pos = False
130
130
131 if time_pos >= 0 and time_pos is not False:
131 if time_pos >= 0 and time_pos is not False:
132
132
133 datadict = commits_by_day_author_aggregate\
133 datadict = commits_by_day_author_aggregate\
134 [author_key_cleaner(cs.author)]['data'][time_pos]
134 [author_key_cleaner(cs.author)]['data'][time_pos]
135
135
136 datadict["commits"] += 1
136 datadict["commits"] += 1
137 datadict["added"] += len(cs.added)
137 datadict["added"] += len(cs.added)
138 datadict["changed"] += len(cs.changed)
138 datadict["changed"] += len(cs.changed)
139 datadict["removed"] += len(cs.removed)
139 datadict["removed"] += len(cs.removed)
140 #print datadict
140 #print datadict
141
141
142 else:
142 else:
143 #print 'ELSE !!!!'
143 #print 'ELSE !!!!'
144 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
144 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
145
145
146 datadict = {"time":k,
146 datadict = {"time":k,
147 "commits":1,
147 "commits":1,
148 "added":len(cs.added),
148 "added":len(cs.added),
149 "changed":len(cs.changed),
149 "changed":len(cs.changed),
150 "removed":len(cs.removed),
150 "removed":len(cs.removed),
151 }
151 }
152 commits_by_day_author_aggregate\
152 commits_by_day_author_aggregate\
153 [author_key_cleaner(cs.author)]['data'].append(datadict)
153 [author_key_cleaner(cs.author)]['data'].append(datadict)
154
154
155 else:
155 else:
156 #print k, 'nokey ADDING'
156 #print k, 'nokey ADDING'
157 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
157 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
158 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
158 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
159 "label":author_key_cleaner(cs.author),
159 "label":author_key_cleaner(cs.author),
160 "data":[{"time":k,
160 "data":[{"time":k,
161 "commits":1,
161 "commits":1,
162 "added":len(cs.added),
162 "added":len(cs.added),
163 "changed":len(cs.changed),
163 "changed":len(cs.changed),
164 "removed":len(cs.removed),
164 "removed":len(cs.removed),
165 }],
165 }],
166 "schema":["commits"],
166 "schema":["commits"],
167 }
167 }
168
168
169 # #gather all data by day
169 # #gather all data by day
170 if commits_by_day_aggregate.has_key(k):
170 if commits_by_day_aggregate.has_key(k):
171 commits_by_day_aggregate[k] += 1
171 commits_by_day_aggregate[k] += 1
172 else:
172 else:
173 commits_by_day_aggregate[k] = 1
173 commits_by_day_aggregate[k] = 1
174
174
175 if cnt >= parse_limit:
175 if cnt >= parse_limit:
176 #don't fetch to much data since we can freeze application
176 #don't fetch to much data since we can freeze application
177 break
177 break
178
178
179 overview_data = []
179 overview_data = []
180 for k, v in commits_by_day_aggregate.items():
180 for k, v in commits_by_day_aggregate.items():
181 overview_data.append([k, v])
181 overview_data.append([k, v])
182 overview_data = sorted(overview_data, key=itemgetter(0))
182 overview_data = sorted(overview_data, key=itemgetter(0))
183
183
184 if not commits_by_day_author_aggregate:
184 if not commits_by_day_author_aggregate:
185 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
185 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
186 "label":author_key_cleaner(repo.contact),
186 "label":author_key_cleaner(repo.contact),
187 "data":[0, 1],
187 "data":[0, 1],
188 "schema":["commits"],
188 "schema":["commits"],
189 }
189 }
190
190
191 stats = cur_stats if cur_stats else Statistics()
191 stats = cur_stats if cur_stats else Statistics()
192 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
192 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
193 stats.commit_activity_combined = json.dumps(overview_data)
193 stats.commit_activity_combined = json.dumps(overview_data)
194
194
195 log.debug('last revison %s', last_rev)
195 log.debug('last revison %s', last_rev)
196 leftovers = len(repo.revisions[last_rev:])
196 leftovers = len(repo.revisions[last_rev:])
197 log.debug('revisions to parse %s', leftovers)
197 log.debug('revisions to parse %s', leftovers)
198
198
199 if last_rev == 0 or leftovers < parse_limit:
199 if last_rev == 0 or leftovers < parse_limit:
200 stats.languages = json.dumps(__get_codes_stats(repo_name))
200 stats.languages = json.dumps(__get_codes_stats(repo_name))
201
201
202 stats.repository = dbrepo
202 stats.repository = dbrepo
203 stats.stat_on_revision = last_cs.revision
203 stats.stat_on_revision = last_cs.revision
204
204
205 try:
205 try:
206 sa.add(stats)
206 sa.add(stats)
207 sa.commit()
207 sa.commit()
208 except:
208 except:
209 log.error(traceback.format_exc())
209 log.error(traceback.format_exc())
210 sa.rollback()
210 sa.rollback()
211 return False
211 return False
212 if len(repo.revisions) > 1:
212 if len(repo.revisions) > 1:
213 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
213 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
214
214
215 return True
215 return True
216
216
217 @task
217 @task
218 def reset_user_password(user_email):
218 def reset_user_password(user_email):
219 log = reset_user_password.get_logger()
219 log = reset_user_password.get_logger()
220 from pylons_app.lib import auth
220 from pylons_app.lib import auth
221 from pylons_app.model.db import User
221 from pylons_app.model.db import User
222
222
223 try:
223 try:
224 try:
224 try:
225 sa = get_session()
225 sa = get_session()
226 user = sa.query(User).filter(User.email == user_email).scalar()
226 user = sa.query(User).filter(User.email == user_email).scalar()
227 new_passwd = auth.PasswordGenerator().gen_password(8,
227 new_passwd = auth.PasswordGenerator().gen_password(8,
228 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
228 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
229 if user:
229 if user:
230 user.password = auth.get_crypt_password(new_passwd)
230 user.password = auth.get_crypt_password(new_passwd)
231 sa.add(user)
231 sa.add(user)
232 sa.commit()
232 sa.commit()
233 log.info('change password for %s', user_email)
233 log.info('change password for %s', user_email)
234 if new_passwd is None:
234 if new_passwd is None:
235 raise Exception('unable to generate new password')
235 raise Exception('unable to generate new password')
236
236
237 except:
237 except:
238 log.error(traceback.format_exc())
238 log.error(traceback.format_exc())
239 sa.rollback()
239 sa.rollback()
240
240
241 run_task(send_email, user_email,
241 run_task(send_email, user_email,
242 "Your new hg-app password",
242 "Your new hg-app password",
243 'Your new hg-app password:%s' % (new_passwd))
243 'Your new hg-app password:%s' % (new_passwd))
244 log.info('send new password mail to %s', user_email)
244 log.info('send new password mail to %s', user_email)
245
245
246
246
247 except:
247 except:
248 log.error('Failed to update user password')
248 log.error('Failed to update user password')
249 log.error(traceback.format_exc())
249 log.error(traceback.format_exc())
250 return True
250 return True
251
251
252 @task
252 @task
253 def send_email(recipients, subject, body):
253 def send_email(recipients, subject, body):
254 log = send_email.get_logger()
254 log = send_email.get_logger()
255 email_config = dict(config.items('DEFAULT'))
255 email_config = dict(config.items('DEFAULT'))
256 mail_from = email_config.get('app_email_from')
256 mail_from = email_config.get('app_email_from')
257 user = email_config.get('smtp_username')
257 user = email_config.get('smtp_username')
258 passwd = email_config.get('smtp_password')
258 passwd = email_config.get('smtp_password')
259 mail_server = email_config.get('smtp_server')
259 mail_server = email_config.get('smtp_server')
260 mail_port = email_config.get('smtp_port')
260 mail_port = email_config.get('smtp_port')
261 tls = email_config.get('smtp_use_tls')
261 tls = email_config.get('smtp_use_tls')
262 ssl = False
262 ssl = False
263
263
264 try:
264 try:
265 m = SmtpMailer(mail_from, user, passwd, mail_server,
265 m = SmtpMailer(mail_from, user, passwd, mail_server,
266 mail_port, ssl, tls)
266 mail_port, ssl, tls)
267 m.send(recipients, subject, body)
267 m.send(recipients, subject, body)
268 except:
268 except:
269 log.error('Mail sending failed')
269 log.error('Mail sending failed')
270 log.error(traceback.format_exc())
270 log.error(traceback.format_exc())
271 return False
271 return False
272 return True
272 return True
273
273
274 @task
275 def create_repo_fork(form_data, cur_user):
276 import os
277 from pylons_app.lib.utils import invalidate_cache
278 from pylons_app.model.repo_model import RepoModel
279 sa = get_session()
280 rm = RepoModel(sa)
281
282 rm.create(form_data, cur_user, just_db=True, fork=True)
283
284 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
285 repo_path = os.path.join(repos_path, form_data['repo_name'])
286 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
287
288 MercurialRepository(str(repo_fork_path), True, clone_url=str(repo_path))
289 #invalidate_cache('cached_repo_list')
290
291
274 def __get_codes_stats(repo_name):
292 def __get_codes_stats(repo_name):
275 LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
293 LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
276 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el', 'erl',
294 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el', 'erl',
277 'h', 'java', 'js', 'jsp', 'jspx', 'lisp',
295 'h', 'java', 'js', 'jsp', 'jspx', 'lisp',
278 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
296 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
279 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh',
297 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh',
280 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
298 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
281 'yaws']
299 'yaws']
282 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
300 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
283 repo = MercurialRepository(repos_path + repo_name)
301 repo = MercurialRepository(repos_path + repo_name)
284
302
285 code_stats = {}
303 code_stats = {}
286 for topnode, dirs, files in repo.walk('/', 'tip'):
304 for topnode, dirs, files in repo.walk('/', 'tip'):
287 for f in files:
305 for f in files:
288 k = f.mimetype
306 k = f.mimetype
289 if f.extension in LANGUAGES_EXTENSIONS:
307 if f.extension in LANGUAGES_EXTENSIONS:
290 if code_stats.has_key(k):
308 if code_stats.has_key(k):
291 code_stats[k] += 1
309 code_stats[k] += 1
292 else:
310 else:
293 code_stats[k] = 1
311 code_stats[k] = 1
294
312
295 return code_stats or {}
313 return code_stats or {}
296
314
297
315
298
316
299
317
300
318
@@ -1,134 +1,136 b''
1 from pylons_app.model.meta import Base
1 from pylons_app.model.meta import Base
2 from sqlalchemy import *
2 from sqlalchemy import *
3 from sqlalchemy.orm import relation, backref
3 from sqlalchemy.orm import relation, backref
4 from sqlalchemy.orm.session import Session
4 from sqlalchemy.orm.session import Session
5 from vcs.utils.lazy import LazyProperty
5 from vcs.utils.lazy import LazyProperty
6 import logging
6 import logging
7
7
8 log = logging.getLogger(__name__)
8 log = logging.getLogger(__name__)
9
9
10 class HgAppSettings(Base):
10 class HgAppSettings(Base):
11 __tablename__ = 'hg_app_settings'
11 __tablename__ = 'hg_app_settings'
12 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
12 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
13 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
13 app_settings_id = Column("app_settings_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
14 app_settings_name = Column("app_settings_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
14 app_settings_name = Column("app_settings_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
15 app_settings_value = Column("app_settings_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
15 app_settings_value = Column("app_settings_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
16
16
17 class HgAppUi(Base):
17 class HgAppUi(Base):
18 __tablename__ = 'hg_app_ui'
18 __tablename__ = 'hg_app_ui'
19 __table_args__ = {'useexisting':True}
19 __table_args__ = {'useexisting':True}
20 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
20 ui_id = Column("ui_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
21 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
21 ui_section = Column("ui_section", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
22 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
22 ui_key = Column("ui_key", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
23 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
23 ui_value = Column("ui_value", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
24 ui_active = Column("ui_active", BOOLEAN(), nullable=True, unique=None, default=True)
24 ui_active = Column("ui_active", BOOLEAN(), nullable=True, unique=None, default=True)
25
25
26
26
27 class User(Base):
27 class User(Base):
28 __tablename__ = 'users'
28 __tablename__ = 'users'
29 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
29 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
30 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
30 user_id = Column("user_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
31 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
31 username = Column("username", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
32 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
32 password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
33 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
33 active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
34 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
34 admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
35 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
35 name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
36 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
36 lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
37 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
37 email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
38 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
38 last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
39
39
40 user_log = relation('UserLog')
40 user_log = relation('UserLog')
41 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id")
41 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id")
42
42
43 @LazyProperty
43 @LazyProperty
44 def full_contact(self):
44 def full_contact(self):
45 return '%s %s <%s>' % (self.name, self.lastname, self.email)
45 return '%s %s <%s>' % (self.name, self.lastname, self.email)
46
46
47 def __repr__(self):
47 def __repr__(self):
48 return "<User('id:%s:%s')>" % (self.user_id, self.username)
48 return "<User('id:%s:%s')>" % (self.user_id, self.username)
49
49
50 def update_lastlogin(self):
50 def update_lastlogin(self):
51 """Update user lastlogin"""
51 """Update user lastlogin"""
52 import datetime
52 import datetime
53
53
54 try:
54 try:
55 session = Session.object_session(self)
55 session = Session.object_session(self)
56 self.last_login = datetime.datetime.now()
56 self.last_login = datetime.datetime.now()
57 session.add(self)
57 session.add(self)
58 session.commit()
58 session.commit()
59 log.debug('updated user %s lastlogin', self.username)
59 log.debug('updated user %s lastlogin', self.username)
60 except Exception:
60 except Exception:
61 session.rollback()
61 session.rollback()
62
62
63
63
64 class UserLog(Base):
64 class UserLog(Base):
65 __tablename__ = 'user_logs'
65 __tablename__ = 'user_logs'
66 __table_args__ = {'useexisting':True}
66 __table_args__ = {'useexisting':True}
67 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
67 user_log_id = Column("user_log_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
68 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
68 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
69 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
69 user_ip = Column("user_ip", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
70 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
70 repository = Column("repository", TEXT(length=None, convert_unicode=False, assert_unicode=None), ForeignKey(u'repositories.repo_name'), nullable=False, unique=None, default=None)
71 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
71 action = Column("action", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
72 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
72 action_date = Column("action_date", DATETIME(timezone=False), nullable=True, unique=None, default=None)
73 revision = Column('revision', TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
73 revision = Column('revision', TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
74 user = relation('User')
74 user = relation('User')
75
75
76 class Repository(Base):
76 class Repository(Base):
77 __tablename__ = 'repositories'
77 __tablename__ = 'repositories'
78 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
78 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
79 repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
79 repo_id = Column("repo_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
80 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
80 repo_name = Column("repo_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
81 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
81 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=False, default=None)
82 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
82 private = Column("private", BOOLEAN(), nullable=True, unique=None, default=None)
83 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
83 description = Column("description", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
84 fork_id = Column("fork_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=True, unique=False, default=None)
84
85
85 user = relation('User')
86 user = relation('User')
87 fork = relation('Repository', remote_side=repo_id)
86 repo_to_perm = relation('RepoToPerm', cascade='all')
88 repo_to_perm = relation('RepoToPerm', cascade='all')
87
89
88 def __repr__(self):
90 def __repr__(self):
89 return "<Repository('id:%s:%s')>" % (self.repo_id, self.repo_name)
91 return "<Repository('id:%s:%s')>" % (self.repo_id, self.repo_name)
90
92
91 class Permission(Base):
93 class Permission(Base):
92 __tablename__ = 'permissions'
94 __tablename__ = 'permissions'
93 __table_args__ = {'useexisting':True}
95 __table_args__ = {'useexisting':True}
94 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
96 permission_id = Column("permission_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
95 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
97 permission_name = Column("permission_name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
96 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
98 permission_longname = Column("permission_longname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
97
99
98 def __repr__(self):
100 def __repr__(self):
99 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
101 return "<Permission('%s:%s')>" % (self.permission_id, self.permission_name)
100
102
101 class RepoToPerm(Base):
103 class RepoToPerm(Base):
102 __tablename__ = 'repo_to_perm'
104 __tablename__ = 'repo_to_perm'
103 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
105 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
104 repo_to_perm_id = Column("repo_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
106 repo_to_perm_id = Column("repo_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
105 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
107 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
106 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
108 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
107 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
109 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=None, default=None)
108
110
109 user = relation('User')
111 user = relation('User')
110 permission = relation('Permission')
112 permission = relation('Permission')
111 repository = relation('Repository')
113 repository = relation('Repository')
112
114
113 class UserToPerm(Base):
115 class UserToPerm(Base):
114 __tablename__ = 'user_to_perm'
116 __tablename__ = 'user_to_perm'
115 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
117 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
116 user_to_perm_id = Column("user_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
118 user_to_perm_id = Column("user_to_perm_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
117 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
119 user_id = Column("user_id", INTEGER(), ForeignKey(u'users.user_id'), nullable=False, unique=None, default=None)
118 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
120 permission_id = Column("permission_id", INTEGER(), ForeignKey(u'permissions.permission_id'), nullable=False, unique=None, default=None)
119
121
120 user = relation('User')
122 user = relation('User')
121 permission = relation('Permission')
123 permission = relation('Permission')
122
124
123 class Statistics(Base):
125 class Statistics(Base):
124 __tablename__ = 'statistics'
126 __tablename__ = 'statistics'
125 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
127 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
126 stat_id = Column("stat_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
128 stat_id = Column("stat_id", INTEGER(), nullable=False, unique=True, default=None, primary_key=True)
127 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
129 repository_id = Column("repository_id", INTEGER(), ForeignKey(u'repositories.repo_id'), nullable=False, unique=True, default=None)
128 stat_on_revision = Column("stat_on_revision", INTEGER(), nullable=False)
130 stat_on_revision = Column("stat_on_revision", INTEGER(), nullable=False)
129 commit_activity = Column("commit_activity", BLOB(), nullable=False)#JSON data
131 commit_activity = Column("commit_activity", BLOB(), nullable=False)#JSON data
130 commit_activity_combined = Column("commit_activity_combined", BLOB(), nullable=False)#JSON data
132 commit_activity_combined = Column("commit_activity_combined", BLOB(), nullable=False)#JSON data
131 languages = Column("languages", BLOB(), nullable=False)#JSON data
133 languages = Column("languages", BLOB(), nullable=False)#JSON data
132
134
133 repository = relation('Repository')
135 repository = relation('Repository')
134
136
@@ -1,351 +1,361 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 from formencode import All
22 from formencode import All
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
23 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
24 Email, Bool, StringBoolean
24 Email, Bool, StringBoolean
25 from pylons import session
25 from pylons import session
26 from pylons.i18n.translation import _
26 from pylons.i18n.translation import _
27 from pylons_app.lib.auth import check_password, get_crypt_password
27 from pylons_app.lib.auth import check_password, get_crypt_password
28 from pylons_app.model import meta
28 from pylons_app.model import meta
29 from pylons_app.model.user_model import UserModel
29 from pylons_app.model.user_model import UserModel
30 from pylons_app.model.db import User, Repository
30 from pylons_app.model.db import User, Repository
31 from sqlalchemy.exc import OperationalError
31 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
32 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34 import formencode
34 import formencode
35 import logging
35 import logging
36 import os
36 import os
37 import pylons_app.lib.helpers as h
37 import pylons_app.lib.helpers as h
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40
40
41 #this is needed to translate the messages using _() in validators
41 #this is needed to translate the messages using _() in validators
42 class State_obj(object):
42 class State_obj(object):
43 _ = staticmethod(_)
43 _ = staticmethod(_)
44
44
45 #===============================================================================
45 #===============================================================================
46 # VALIDATORS
46 # VALIDATORS
47 #===============================================================================
47 #===============================================================================
48 class ValidAuthToken(formencode.validators.FancyValidator):
48 class ValidAuthToken(formencode.validators.FancyValidator):
49 messages = {'invalid_token':_('Token mismatch')}
49 messages = {'invalid_token':_('Token mismatch')}
50
50
51 def validate_python(self, value, state):
51 def validate_python(self, value, state):
52
52
53 if value != authentication_token():
53 if value != authentication_token():
54 raise formencode.Invalid(self.message('invalid_token', state,
54 raise formencode.Invalid(self.message('invalid_token', state,
55 search_number=value), value, state)
55 search_number=value), value, state)
56
56
57 def ValidUsername(edit, old_data):
57 def ValidUsername(edit, old_data):
58 class _ValidUsername(formencode.validators.FancyValidator):
58 class _ValidUsername(formencode.validators.FancyValidator):
59
59
60 def validate_python(self, value, state):
60 def validate_python(self, value, state):
61 if value in ['default', 'new_user']:
61 if value in ['default', 'new_user']:
62 raise formencode.Invalid(_('Invalid username'), value, state)
62 raise formencode.Invalid(_('Invalid username'), value, state)
63 #check if user is uniq
63 #check if user is uniq
64 sa = meta.Session
64 sa = meta.Session
65 old_un = None
65 old_un = None
66 if edit:
66 if edit:
67 old_un = sa.query(User).get(old_data.get('user_id')).username
67 old_un = sa.query(User).get(old_data.get('user_id')).username
68
68
69 if old_un != value or not edit:
69 if old_un != value or not edit:
70 if sa.query(User).filter(User.username == value).scalar():
70 if sa.query(User).filter(User.username == value).scalar():
71 raise formencode.Invalid(_('This username already exists') ,
71 raise formencode.Invalid(_('This username already exists') ,
72 value, state)
72 value, state)
73 meta.Session.remove()
73 meta.Session.remove()
74
74
75 return _ValidUsername
75 return _ValidUsername
76
76
77 class ValidPassword(formencode.validators.FancyValidator):
77 class ValidPassword(formencode.validators.FancyValidator):
78
78
79 def to_python(self, value, state):
79 def to_python(self, value, state):
80 if value:
80 if value:
81 return get_crypt_password(value)
81 return get_crypt_password(value)
82
82
83 class ValidAuth(formencode.validators.FancyValidator):
83 class ValidAuth(formencode.validators.FancyValidator):
84 messages = {
84 messages = {
85 'invalid_password':_('invalid password'),
85 'invalid_password':_('invalid password'),
86 'invalid_login':_('invalid user name'),
86 'invalid_login':_('invalid user name'),
87 'disabled_account':_('Your acccount is disabled')
87 'disabled_account':_('Your acccount is disabled')
88
88
89 }
89 }
90 #error mapping
90 #error mapping
91 e_dict = {'username':messages['invalid_login'],
91 e_dict = {'username':messages['invalid_login'],
92 'password':messages['invalid_password']}
92 'password':messages['invalid_password']}
93 e_dict_disable = {'username':messages['disabled_account']}
93 e_dict_disable = {'username':messages['disabled_account']}
94
94
95 def validate_python(self, value, state):
95 def validate_python(self, value, state):
96 password = value['password']
96 password = value['password']
97 username = value['username']
97 username = value['username']
98 user = UserModel().get_user_by_name(username)
98 user = UserModel().get_user_by_name(username)
99 if user is None:
99 if user is None:
100 raise formencode.Invalid(self.message('invalid_password',
100 raise formencode.Invalid(self.message('invalid_password',
101 state=State_obj), value, state,
101 state=State_obj), value, state,
102 error_dict=self.e_dict)
102 error_dict=self.e_dict)
103 if user:
103 if user:
104 if user.active:
104 if user.active:
105 if user.username == username and check_password(password,
105 if user.username == username and check_password(password,
106 user.password):
106 user.password):
107 return value
107 return value
108 else:
108 else:
109 log.warning('user %s not authenticated', username)
109 log.warning('user %s not authenticated', username)
110 raise formencode.Invalid(self.message('invalid_password',
110 raise formencode.Invalid(self.message('invalid_password',
111 state=State_obj), value, state,
111 state=State_obj), value, state,
112 error_dict=self.e_dict)
112 error_dict=self.e_dict)
113 else:
113 else:
114 log.warning('user %s is disabled', username)
114 log.warning('user %s is disabled', username)
115 raise formencode.Invalid(self.message('disabled_account',
115 raise formencode.Invalid(self.message('disabled_account',
116 state=State_obj),
116 state=State_obj),
117 value, state,
117 value, state,
118 error_dict=self.e_dict_disable)
118 error_dict=self.e_dict_disable)
119
119
120 class ValidRepoUser(formencode.validators.FancyValidator):
120 class ValidRepoUser(formencode.validators.FancyValidator):
121
121
122 def to_python(self, value, state):
122 def to_python(self, value, state):
123 try:
123 try:
124 self.user_db = meta.Session.query(User)\
124 self.user_db = meta.Session.query(User)\
125 .filter(User.active == True)\
125 .filter(User.active == True)\
126 .filter(User.username == value).one()
126 .filter(User.username == value).one()
127 except Exception:
127 except Exception:
128 raise formencode.Invalid(_('This username is not valid'),
128 raise formencode.Invalid(_('This username is not valid'),
129 value, state)
129 value, state)
130 finally:
130 finally:
131 meta.Session.remove()
131 meta.Session.remove()
132
132
133 return self.user_db.user_id
133 return self.user_db.user_id
134
134
135 def ValidRepoName(edit, old_data):
135 def ValidRepoName(edit, old_data):
136 class _ValidRepoName(formencode.validators.FancyValidator):
136 class _ValidRepoName(formencode.validators.FancyValidator):
137
137
138 def to_python(self, value, state):
138 def to_python(self, value, state):
139 slug = h.repo_name_slug(value)
139 slug = h.repo_name_slug(value)
140 if slug in ['_admin']:
140 if slug in ['_admin']:
141 raise formencode.Invalid(_('This repository name is disallowed'),
141 raise formencode.Invalid(_('This repository name is disallowed'),
142 value, state)
142 value, state)
143 if old_data.get('repo_name') != value or not edit:
143 if old_data.get('repo_name') != value or not edit:
144 sa = meta.Session
144 sa = meta.Session
145 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
145 if sa.query(Repository).filter(Repository.repo_name == slug).scalar():
146 raise formencode.Invalid(_('This repository already exists') ,
146 raise formencode.Invalid(_('This repository already exists') ,
147 value, state)
147 value, state)
148 meta.Session.remove()
148 meta.Session.remove()
149 return slug
149 return slug
150
150
151
151
152 return _ValidRepoName
152 return _ValidRepoName
153
153
154 class ValidPerms(formencode.validators.FancyValidator):
154 class ValidPerms(formencode.validators.FancyValidator):
155 messages = {'perm_new_user_name':_('This username is not valid')}
155 messages = {'perm_new_user_name':_('This username is not valid')}
156
156
157 def to_python(self, value, state):
157 def to_python(self, value, state):
158 perms_update = []
158 perms_update = []
159 perms_new = []
159 perms_new = []
160 #build a list of permission to update and new permission to create
160 #build a list of permission to update and new permission to create
161 for k, v in value.items():
161 for k, v in value.items():
162 if k.startswith('perm_'):
162 if k.startswith('perm_'):
163 if k.startswith('perm_new_user'):
163 if k.startswith('perm_new_user'):
164 new_perm = value.get('perm_new_user', False)
164 new_perm = value.get('perm_new_user', False)
165 new_user = value.get('perm_new_user_name', False)
165 new_user = value.get('perm_new_user_name', False)
166 if new_user and new_perm:
166 if new_user and new_perm:
167 if (new_user, new_perm) not in perms_new:
167 if (new_user, new_perm) not in perms_new:
168 perms_new.append((new_user, new_perm))
168 perms_new.append((new_user, new_perm))
169 else:
169 else:
170 usr = k[5:]
170 usr = k[5:]
171 if usr == 'default':
171 if usr == 'default':
172 if value['private']:
172 if value['private']:
173 #set none for default when updating to private repo
173 #set none for default when updating to private repo
174 v = 'repository.none'
174 v = 'repository.none'
175 perms_update.append((usr, v))
175 perms_update.append((usr, v))
176 value['perms_updates'] = perms_update
176 value['perms_updates'] = perms_update
177 value['perms_new'] = perms_new
177 value['perms_new'] = perms_new
178 sa = meta.Session
178 sa = meta.Session
179 for k, v in perms_new:
179 for k, v in perms_new:
180 try:
180 try:
181 self.user_db = sa.query(User)\
181 self.user_db = sa.query(User)\
182 .filter(User.active == True)\
182 .filter(User.active == True)\
183 .filter(User.username == k).one()
183 .filter(User.username == k).one()
184 except Exception:
184 except Exception:
185 msg = self.message('perm_new_user_name',
185 msg = self.message('perm_new_user_name',
186 state=State_obj)
186 state=State_obj)
187 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
187 raise formencode.Invalid(msg, value, state, error_dict={'perm_new_user_name':msg})
188 return value
188 return value
189
189
190 class ValidSettings(formencode.validators.FancyValidator):
190 class ValidSettings(formencode.validators.FancyValidator):
191
191
192 def to_python(self, value, state):
192 def to_python(self, value, state):
193 #settings form can't edit user
193 #settings form can't edit user
194 if value.has_key('user'):
194 if value.has_key('user'):
195 del['value']['user']
195 del['value']['user']
196
196
197 return value
197 return value
198
198
199 class ValidPath(formencode.validators.FancyValidator):
199 class ValidPath(formencode.validators.FancyValidator):
200 def to_python(self, value, state):
200 def to_python(self, value, state):
201 isdir = os.path.isdir(value.replace('*', ''))
201 isdir = os.path.isdir(value.replace('*', ''))
202 if (value.endswith('/*') or value.endswith('/**')) and isdir:
202 if (value.endswith('/*') or value.endswith('/**')) and isdir:
203 return value
203 return value
204 elif not isdir:
204 elif not isdir:
205 msg = _('This is not a valid path')
205 msg = _('This is not a valid path')
206 else:
206 else:
207 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
207 msg = _('You need to specify * or ** at the end of path (ie. /tmp/*)')
208
208
209 raise formencode.Invalid(msg, value, state,
209 raise formencode.Invalid(msg, value, state,
210 error_dict={'paths_root_path':msg})
210 error_dict={'paths_root_path':msg})
211
211
212 def UniqSystemEmail(old_data):
212 def UniqSystemEmail(old_data):
213 class _UniqSystemEmail(formencode.validators.FancyValidator):
213 class _UniqSystemEmail(formencode.validators.FancyValidator):
214 def to_python(self, value, state):
214 def to_python(self, value, state):
215 if old_data.get('email') != value:
215 if old_data.get('email') != value:
216 sa = meta.Session
216 sa = meta.Session
217 try:
217 try:
218 user = sa.query(User).filter(User.email == value).scalar()
218 user = sa.query(User).filter(User.email == value).scalar()
219 if user:
219 if user:
220 raise formencode.Invalid(_("That e-mail address is already taken") ,
220 raise formencode.Invalid(_("That e-mail address is already taken") ,
221 value, state)
221 value, state)
222 finally:
222 finally:
223 meta.Session.remove()
223 meta.Session.remove()
224
224
225 return value
225 return value
226
226
227 return _UniqSystemEmail
227 return _UniqSystemEmail
228
228
229 class ValidSystemEmail(formencode.validators.FancyValidator):
229 class ValidSystemEmail(formencode.validators.FancyValidator):
230 def to_python(self, value, state):
230 def to_python(self, value, state):
231 sa = meta.Session
231 sa = meta.Session
232 try:
232 try:
233 user = sa.query(User).filter(User.email == value).scalar()
233 user = sa.query(User).filter(User.email == value).scalar()
234 if user is None:
234 if user is None:
235 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
235 raise formencode.Invalid(_("That e-mail address doesn't exist.") ,
236 value, state)
236 value, state)
237 finally:
237 finally:
238 meta.Session.remove()
238 meta.Session.remove()
239
239
240 return value
240 return value
241
241
242 #===============================================================================
242 #===============================================================================
243 # FORMS
243 # FORMS
244 #===============================================================================
244 #===============================================================================
245 class LoginForm(formencode.Schema):
245 class LoginForm(formencode.Schema):
246 allow_extra_fields = True
246 allow_extra_fields = True
247 filter_extra_fields = True
247 filter_extra_fields = True
248 username = UnicodeString(
248 username = UnicodeString(
249 strip=True,
249 strip=True,
250 min=1,
250 min=1,
251 not_empty=True,
251 not_empty=True,
252 messages={
252 messages={
253 'empty':_('Please enter a login'),
253 'empty':_('Please enter a login'),
254 'tooShort':_('Enter a value %(min)i characters long or more')}
254 'tooShort':_('Enter a value %(min)i characters long or more')}
255 )
255 )
256
256
257 password = UnicodeString(
257 password = UnicodeString(
258 strip=True,
258 strip=True,
259 min=6,
259 min=6,
260 not_empty=True,
260 not_empty=True,
261 messages={
261 messages={
262 'empty':_('Please enter a password'),
262 'empty':_('Please enter a password'),
263 'tooShort':_('Enter a value %(min)i characters long or more')}
263 'tooShort':_('Enter a value %(min)i characters long or more')}
264 )
264 )
265
265
266
266
267 #chained validators have access to all data
267 #chained validators have access to all data
268 chained_validators = [ValidAuth]
268 chained_validators = [ValidAuth]
269
269
270 def UserForm(edit=False, old_data={}):
270 def UserForm(edit=False, old_data={}):
271 class _UserForm(formencode.Schema):
271 class _UserForm(formencode.Schema):
272 allow_extra_fields = True
272 allow_extra_fields = True
273 filter_extra_fields = True
273 filter_extra_fields = True
274 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
274 username = All(UnicodeString(strip=True, min=1, not_empty=True), ValidUsername(edit, old_data))
275 if edit:
275 if edit:
276 new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword)
276 new_password = All(UnicodeString(strip=True, min=6, not_empty=False), ValidPassword)
277 admin = StringBoolean(if_missing=False)
277 admin = StringBoolean(if_missing=False)
278 else:
278 else:
279 password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword)
279 password = All(UnicodeString(strip=True, min=6, not_empty=True), ValidPassword)
280 active = StringBoolean(if_missing=False)
280 active = StringBoolean(if_missing=False)
281 name = UnicodeString(strip=True, min=1, not_empty=True)
281 name = UnicodeString(strip=True, min=1, not_empty=True)
282 lastname = UnicodeString(strip=True, min=1, not_empty=True)
282 lastname = UnicodeString(strip=True, min=1, not_empty=True)
283 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
283 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
284
284
285 return _UserForm
285 return _UserForm
286
286
287 RegisterForm = UserForm
287 RegisterForm = UserForm
288
288
289 def PasswordResetForm():
289 def PasswordResetForm():
290 class _PasswordResetForm(formencode.Schema):
290 class _PasswordResetForm(formencode.Schema):
291 allow_extra_fields = True
291 allow_extra_fields = True
292 filter_extra_fields = True
292 filter_extra_fields = True
293 email = All(ValidSystemEmail(), Email(not_empty=True))
293 email = All(ValidSystemEmail(), Email(not_empty=True))
294 return _PasswordResetForm
294 return _PasswordResetForm
295
295
296 def RepoForm(edit=False, old_data={}):
296 def RepoForm(edit=False, old_data={}):
297 class _RepoForm(formencode.Schema):
297 class _RepoForm(formencode.Schema):
298 allow_extra_fields = True
298 allow_extra_fields = True
299 filter_extra_fields = False
299 filter_extra_fields = False
300 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
300 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
301 description = UnicodeString(strip=True, min=1, not_empty=True)
301 description = UnicodeString(strip=True, min=1, not_empty=True)
302 private = StringBoolean(if_missing=False)
302 private = StringBoolean(if_missing=False)
303
303
304 if edit:
304 if edit:
305 user = All(Int(not_empty=True), ValidRepoUser)
305 user = All(Int(not_empty=True), ValidRepoUser)
306
306
307 chained_validators = [ValidPerms]
307 chained_validators = [ValidPerms]
308 return _RepoForm
308 return _RepoForm
309
309
310 def RepoForkForm(edit=False, old_data={}):
311 class _RepoForkForm(formencode.Schema):
312 allow_extra_fields = True
313 filter_extra_fields = False
314 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
315 description = UnicodeString(strip=True, min=1, not_empty=True)
316 private = StringBoolean(if_missing=False)
317
318 return _RepoForkForm
319
310 def RepoSettingsForm(edit=False, old_data={}):
320 def RepoSettingsForm(edit=False, old_data={}):
311 class _RepoForm(formencode.Schema):
321 class _RepoForm(formencode.Schema):
312 allow_extra_fields = True
322 allow_extra_fields = True
313 filter_extra_fields = False
323 filter_extra_fields = False
314 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
324 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True), ValidRepoName(edit, old_data))
315 description = UnicodeString(strip=True, min=1, not_empty=True)
325 description = UnicodeString(strip=True, min=1, not_empty=True)
316 private = StringBoolean(if_missing=False)
326 private = StringBoolean(if_missing=False)
317
327
318 chained_validators = [ValidPerms, ValidSettings]
328 chained_validators = [ValidPerms, ValidSettings]
319 return _RepoForm
329 return _RepoForm
320
330
321
331
322 def ApplicationSettingsForm():
332 def ApplicationSettingsForm():
323 class _ApplicationSettingsForm(formencode.Schema):
333 class _ApplicationSettingsForm(formencode.Schema):
324 allow_extra_fields = True
334 allow_extra_fields = True
325 filter_extra_fields = False
335 filter_extra_fields = False
326 hg_app_title = UnicodeString(strip=True, min=1, not_empty=True)
336 hg_app_title = UnicodeString(strip=True, min=1, not_empty=True)
327 hg_app_realm = UnicodeString(strip=True, min=1, not_empty=True)
337 hg_app_realm = UnicodeString(strip=True, min=1, not_empty=True)
328
338
329 return _ApplicationSettingsForm
339 return _ApplicationSettingsForm
330
340
331 def ApplicationUiSettingsForm():
341 def ApplicationUiSettingsForm():
332 class _ApplicationUiSettingsForm(formencode.Schema):
342 class _ApplicationUiSettingsForm(formencode.Schema):
333 allow_extra_fields = True
343 allow_extra_fields = True
334 filter_extra_fields = False
344 filter_extra_fields = False
335 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
345 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
336 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
346 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
337 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
347 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
338 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
348 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
339
349
340 return _ApplicationUiSettingsForm
350 return _ApplicationUiSettingsForm
341
351
342 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
352 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
343 class _DefaultPermissionsForm(formencode.Schema):
353 class _DefaultPermissionsForm(formencode.Schema):
344 allow_extra_fields = True
354 allow_extra_fields = True
345 filter_extra_fields = True
355 filter_extra_fields = True
346 overwrite_default = OneOf(['true', 'false'], if_missing='false')
356 overwrite_default = OneOf(['true', 'false'], if_missing='false')
347 default_perm = OneOf(perms_choices)
357 default_perm = OneOf(perms_choices)
348 default_register = OneOf(register_choices)
358 default_register = OneOf(register_choices)
349 default_create = OneOf(create_choices)
359 default_create = OneOf(create_choices)
350
360
351 return _DefaultPermissionsForm
361 return _DefaultPermissionsForm
@@ -1,185 +1,205 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # model for handling repositories actions
3 # model for handling repositories actions
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 Created on Jun 5, 2010
20 Created on Jun 5, 2010
21 model for handling repositories actions
21 model for handling repositories actions
22 @author: marcink
22 @author: marcink
23 """
23 """
24 from datetime import datetime
24 from datetime import datetime
25 from pylons import app_globals as g
25 from pylons import app_globals as g
26 from pylons_app.lib.utils import check_repo
26 from pylons_app.lib.utils import check_repo
27 from pylons_app.model.db import Repository, RepoToPerm, User, Permission
27 from pylons_app.model.db import Repository, RepoToPerm, User, Permission
28 from pylons_app.model.meta import Session
28 from pylons_app.model.meta import Session
29 from pylons_app.model.user_model import UserModel
29 from pylons_app.model.user_model import UserModel
30 from pylons_app.lib.celerylib.tasks import create_repo_fork, run_task
30 import logging
31 import logging
31 import os
32 import os
32 import shutil
33 import shutil
33 import traceback
34 import traceback
34 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
35
36
36 class RepoModel(object):
37 class RepoModel(object):
37
38
38 def __init__(self):
39 def __init__(self, sa=None):
39 self.sa = Session()
40 if not sa:
41 self.sa = Session()
42 else:
43 self.sa = sa
40
44
41 def get(self, id):
45 def get(self, id):
42 return self.sa.query(Repository).filter(Repository.repo_name == id).scalar()
46 return self.sa.query(Repository)\
47 .filter(Repository.repo_name == id).scalar()
43
48
44 def get_users_js(self):
49 def get_users_js(self):
45
50
46 users = self.sa.query(User).filter(User.active == True).all()
51 users = self.sa.query(User).filter(User.active == True).all()
47 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
52 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
48 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
53 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
49 u.lastname, u.username)
54 u.lastname, u.username)
50 for u in users])
55 for u in users])
51 return users_array
56 return users_array
52
57
53
58
54 def update(self, repo_name, form_data):
59 def update(self, repo_name, form_data):
55 try:
60 try:
56
61
57 #update permissions
62 #update permissions
58 for username, perm in form_data['perms_updates']:
63 for username, perm in form_data['perms_updates']:
59 r2p = self.sa.query(RepoToPerm)\
64 r2p = self.sa.query(RepoToPerm)\
60 .filter(RepoToPerm.user == self.sa.query(User)\
65 .filter(RepoToPerm.user == self.sa.query(User)\
61 .filter(User.username == username).one())\
66 .filter(User.username == username).one())\
62 .filter(RepoToPerm.repository == self.get(repo_name))\
67 .filter(RepoToPerm.repository == self.get(repo_name))\
63 .one()
68 .one()
64
69
65 r2p.permission_id = self.sa.query(Permission).filter(
70 r2p.permission_id = self.sa.query(Permission).filter(
66 Permission.permission_name ==
71 Permission.permission_name ==
67 perm).one().permission_id
72 perm).one().permission_id
68 self.sa.add(r2p)
73 self.sa.add(r2p)
69
74
70 #set new permissions
75 #set new permissions
71 for username, perm in form_data['perms_new']:
76 for username, perm in form_data['perms_new']:
72 r2p = RepoToPerm()
77 r2p = RepoToPerm()
73 r2p.repository = self.get(repo_name)
78 r2p.repository = self.get(repo_name)
74 r2p.user = self.sa.query(User)\
79 r2p.user = self.sa.query(User)\
75 .filter(User.username == username).one()
80 .filter(User.username == username).one()
76
81
77 r2p.permission_id = self.sa.query(Permission).filter(
82 r2p.permission_id = self.sa.query(Permission).filter(
78 Permission.permission_name == perm)\
83 Permission.permission_name == perm)\
79 .one().permission_id
84 .one().permission_id
80 self.sa.add(r2p)
85 self.sa.add(r2p)
81
86
82 #update current repo
87 #update current repo
83 cur_repo = self.get(repo_name)
88 cur_repo = self.get(repo_name)
84
89
85 for k, v in form_data.items():
90 for k, v in form_data.items():
86 if k == 'user':
91 if k == 'user':
87 cur_repo.user_id = v
92 cur_repo.user_id = v
88 else:
93 else:
89 setattr(cur_repo, k, v)
94 setattr(cur_repo, k, v)
90
95
91 self.sa.add(cur_repo)
96 self.sa.add(cur_repo)
92
97
93 if repo_name != form_data['repo_name']:
98 if repo_name != form_data['repo_name']:
94 #rename our data
99 #rename our data
95 self.__rename_repo(repo_name, form_data['repo_name'])
100 self.__rename_repo(repo_name, form_data['repo_name'])
96
101
97 self.sa.commit()
102 self.sa.commit()
98 except:
103 except:
99 log.error(traceback.format_exc())
104 log.error(traceback.format_exc())
100 self.sa.rollback()
105 self.sa.rollback()
101 raise
106 raise
102
107
103 def create(self, form_data, cur_user, just_db=False):
108 def create(self, form_data, cur_user, just_db=False, fork=False):
104 try:
109 try:
105 repo_name = form_data['repo_name']
110 if fork:
111 repo_name = str(form_data['fork_name'])
112 org_name = str(form_data['repo_name'])
113
114 else:
115 org_name = repo_name = str(form_data['repo_name'])
106 new_repo = Repository()
116 new_repo = Repository()
107 for k, v in form_data.items():
117 for k, v in form_data.items():
118 if k == 'repo_name':
119 v = repo_name
108 setattr(new_repo, k, v)
120 setattr(new_repo, k, v)
109
121
122 if fork:
123 parent_repo = self.sa.query(Repository)\
124 .filter(Repository.repo_name == org_name).scalar()
125 new_repo.fork = parent_repo
126
110 new_repo.user_id = cur_user.user_id
127 new_repo.user_id = cur_user.user_id
111 self.sa.add(new_repo)
128 self.sa.add(new_repo)
112
129
113 #create default permission
130 #create default permission
114 repo_to_perm = RepoToPerm()
131 repo_to_perm = RepoToPerm()
115 default = 'repository.read'
132 default = 'repository.read'
116 for p in UserModel().get_default().user_perms:
133 for p in UserModel(self.sa).get_default().user_perms:
117 if p.permission.permission_name.startswith('repository.'):
134 if p.permission.permission_name.startswith('repository.'):
118 default = p.permission.permission_name
135 default = p.permission.permission_name
119 break
136 break
120
137
121 default_perm = 'repository.none' if form_data['private'] else default
138 default_perm = 'repository.none' if form_data['private'] else default
122
139
123 repo_to_perm.permission_id = self.sa.query(Permission)\
140 repo_to_perm.permission_id = self.sa.query(Permission)\
124 .filter(Permission.permission_name == default_perm)\
141 .filter(Permission.permission_name == default_perm)\
125 .one().permission_id
142 .one().permission_id
126
143
127 repo_to_perm.repository_id = new_repo.repo_id
144 repo_to_perm.repository_id = new_repo.repo_id
128 repo_to_perm.user_id = self.sa.query(User)\
145 repo_to_perm.user_id = self.sa.query(User)\
129 .filter(User.username == 'default').one().user_id
146 .filter(User.username == 'default').one().user_id
130
147
131 self.sa.add(repo_to_perm)
148 self.sa.add(repo_to_perm)
132 self.sa.commit()
149 self.sa.commit()
133 if not just_db:
150 if not just_db:
134 self.__create_repo(repo_name)
151 self.__create_repo(repo_name)
135 except:
152 except:
136 log.error(traceback.format_exc())
153 log.error(traceback.format_exc())
137 self.sa.rollback()
154 self.sa.rollback()
138 raise
155 raise
139
156
157 def create_fork(self, form_data, cur_user):
158 run_task(create_repo_fork, form_data, cur_user)
159
140 def delete(self, repo):
160 def delete(self, repo):
141 try:
161 try:
142 self.sa.delete(repo)
162 self.sa.delete(repo)
143 self.sa.commit()
163 self.sa.commit()
144 self.__delete_repo(repo.repo_name)
164 self.__delete_repo(repo.repo_name)
145 except:
165 except:
146 log.error(traceback.format_exc())
166 log.error(traceback.format_exc())
147 self.sa.rollback()
167 self.sa.rollback()
148 raise
168 raise
149
169
150 def delete_perm_user(self, form_data, repo_name):
170 def delete_perm_user(self, form_data, repo_name):
151 try:
171 try:
152 self.sa.query(RepoToPerm)\
172 self.sa.query(RepoToPerm)\
153 .filter(RepoToPerm.repository == self.get(repo_name))\
173 .filter(RepoToPerm.repository == self.get(repo_name))\
154 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
174 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
155 self.sa.commit()
175 self.sa.commit()
156 except:
176 except:
157 log.error(traceback.format_exc())
177 log.error(traceback.format_exc())
158 self.sa.rollback()
178 self.sa.rollback()
159 raise
179 raise
160
180
161 def __create_repo(self, repo_name):
181 def __create_repo(self, repo_name):
162 repo_path = os.path.join(g.base_path, repo_name)
182 repo_path = os.path.join(g.base_path, repo_name)
163 if check_repo(repo_name, g.base_path):
183 if check_repo(repo_name, g.base_path):
164 log.info('creating repo %s in %s', repo_name, repo_path)
184 log.info('creating repo %s in %s', repo_name, repo_path)
165 from vcs.backends.hg import MercurialRepository
185 from vcs.backends.hg import MercurialRepository
166 MercurialRepository(repo_path, create=True)
186 MercurialRepository(repo_path, create=True)
167
187
168 def __rename_repo(self, old, new):
188 def __rename_repo(self, old, new):
169 log.info('renaming repo from %s to %s', old, new)
189 log.info('renaming repo from %s to %s', old, new)
170
190
171 old_path = os.path.join(g.base_path, old)
191 old_path = os.path.join(g.base_path, old)
172 new_path = os.path.join(g.base_path, new)
192 new_path = os.path.join(g.base_path, new)
173 if os.path.isdir(new_path):
193 if os.path.isdir(new_path):
174 raise Exception('Was trying to rename to already existing dir %s',
194 raise Exception('Was trying to rename to already existing dir %s',
175 new_path)
195 new_path)
176 shutil.move(old_path, new_path)
196 shutil.move(old_path, new_path)
177
197
178 def __delete_repo(self, name):
198 def __delete_repo(self, name):
179 rm_path = os.path.join(g.base_path, name)
199 rm_path = os.path.join(g.base_path, name)
180 log.info("Removing %s", rm_path)
200 log.info("Removing %s", rm_path)
181 #disable hg
201 #disable hg
182 shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
202 shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg'))
183 #disable repo
203 #disable repo
184 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
204 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
185 % (datetime.today(), name)))
205 % (datetime.today(), name)))
@@ -1,135 +1,138 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for users
3 # Model for users
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 """
21 """
22 Created on April 9, 2010
22 Created on April 9, 2010
23 Model for users
23 Model for users
24 @author: marcink
24 @author: marcink
25 """
25 """
26 from pylons_app.lib import auth
26 from pylons_app.lib import auth
27 from pylons.i18n.translation import _
27 from pylons.i18n.translation import _
28 from pylons_app.lib.celerylib import tasks, run_task
28 from pylons_app.lib.celerylib import tasks, run_task
29 from pylons_app.model.db import User
29 from pylons_app.model.db import User
30 from pylons_app.model.meta import Session
30 from pylons_app.model.meta import Session
31 import traceback
31 import traceback
32 import logging
32 import logging
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35 class DefaultUserException(Exception):pass
35 class DefaultUserException(Exception):pass
36
36
37 class UserModel(object):
37 class UserModel(object):
38
38
39 def __init__(self):
39 def __init__(self, sa=None):
40 self.sa = Session()
40 if not sa:
41 self.sa = Session()
42 else:
43 self.sa = sa
41
44
42 def get_default(self):
45 def get_default(self):
43 return self.sa.query(User).filter(User.username == 'default').scalar()
46 return self.sa.query(User).filter(User.username == 'default').scalar()
44
47
45 def get_user(self, id):
48 def get_user(self, id):
46 return self.sa.query(User).get(id)
49 return self.sa.query(User).get(id)
47
50
48 def get_user_by_name(self, name):
51 def get_user_by_name(self, name):
49 return self.sa.query(User).filter(User.username == name).scalar()
52 return self.sa.query(User).filter(User.username == name).scalar()
50
53
51 def create(self, form_data):
54 def create(self, form_data):
52 try:
55 try:
53 new_user = User()
56 new_user = User()
54 for k, v in form_data.items():
57 for k, v in form_data.items():
55 setattr(new_user, k, v)
58 setattr(new_user, k, v)
56
59
57 self.sa.add(new_user)
60 self.sa.add(new_user)
58 self.sa.commit()
61 self.sa.commit()
59 except:
62 except:
60 log.error(traceback.format_exc())
63 log.error(traceback.format_exc())
61 self.sa.rollback()
64 self.sa.rollback()
62 raise
65 raise
63
66
64 def create_registration(self, form_data):
67 def create_registration(self, form_data):
65 try:
68 try:
66 new_user = User()
69 new_user = User()
67 for k, v in form_data.items():
70 for k, v in form_data.items():
68 if k != 'admin':
71 if k != 'admin':
69 setattr(new_user, k, v)
72 setattr(new_user, k, v)
70
73
71 self.sa.add(new_user)
74 self.sa.add(new_user)
72 self.sa.commit()
75 self.sa.commit()
73 except:
76 except:
74 log.error(traceback.format_exc())
77 log.error(traceback.format_exc())
75 self.sa.rollback()
78 self.sa.rollback()
76 raise
79 raise
77
80
78 def update(self, uid, form_data):
81 def update(self, uid, form_data):
79 try:
82 try:
80 new_user = self.sa.query(User).get(uid)
83 new_user = self.sa.query(User).get(uid)
81 if new_user.username == 'default':
84 if new_user.username == 'default':
82 raise DefaultUserException(
85 raise DefaultUserException(
83 _("You can't Edit this user since it's"
86 _("You can't Edit this user since it's"
84 " crucial for entire application"))
87 " crucial for entire application"))
85 for k, v in form_data.items():
88 for k, v in form_data.items():
86 if k == 'new_password' and v != '':
89 if k == 'new_password' and v != '':
87 new_user.password = v
90 new_user.password = v
88 else:
91 else:
89 setattr(new_user, k, v)
92 setattr(new_user, k, v)
90
93
91 self.sa.add(new_user)
94 self.sa.add(new_user)
92 self.sa.commit()
95 self.sa.commit()
93 except:
96 except:
94 log.error(traceback.format_exc())
97 log.error(traceback.format_exc())
95 self.sa.rollback()
98 self.sa.rollback()
96 raise
99 raise
97
100
98 def update_my_account(self, uid, form_data):
101 def update_my_account(self, uid, form_data):
99 try:
102 try:
100 new_user = self.sa.query(User).get(uid)
103 new_user = self.sa.query(User).get(uid)
101 if new_user.username == 'default':
104 if new_user.username == 'default':
102 raise DefaultUserException(
105 raise DefaultUserException(
103 _("You can't Edit this user since it's"
106 _("You can't Edit this user since it's"
104 " crucial for entire application"))
107 " crucial for entire application"))
105 for k, v in form_data.items():
108 for k, v in form_data.items():
106 if k == 'new_password' and v != '':
109 if k == 'new_password' and v != '':
107 new_user.password = v
110 new_user.password = v
108 else:
111 else:
109 if k not in ['admin', 'active']:
112 if k not in ['admin', 'active']:
110 setattr(new_user, k, v)
113 setattr(new_user, k, v)
111
114
112 self.sa.add(new_user)
115 self.sa.add(new_user)
113 self.sa.commit()
116 self.sa.commit()
114 except:
117 except:
115 log.error(traceback.format_exc())
118 log.error(traceback.format_exc())
116 self.sa.rollback()
119 self.sa.rollback()
117 raise
120 raise
118
121
119 def delete(self, id):
122 def delete(self, id):
120 try:
123 try:
121
124
122 user = self.sa.query(User).get(id)
125 user = self.sa.query(User).get(id)
123 if user.username == 'default':
126 if user.username == 'default':
124 raise DefaultUserException(
127 raise DefaultUserException(
125 _("You can't remove this user since it's"
128 _("You can't remove this user since it's"
126 " crucial for entire application"))
129 " crucial for entire application"))
127 self.sa.delete(user)
130 self.sa.delete(user)
128 self.sa.commit()
131 self.sa.commit()
129 except:
132 except:
130 log.error(traceback.format_exc())
133 log.error(traceback.format_exc())
131 self.sa.rollback()
134 self.sa.rollback()
132 raise
135 raise
133
136
134 def reset_password(self, data):
137 def reset_password(self, data):
135 run_task(tasks.reset_user_password, data['email'])
138 run_task(tasks.reset_user_password, data['email'])
@@ -1,58 +1,58 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Fork repository')} ${c.repo_info.repo_name}
5 ${_('Fork repository')} ${c.repo_info.repo_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))}
9 ${h.link_to(c.repo_info.repo_name,h.url('summary_home',repo_name=c.repo_info.repo_name))}
10 &raquo;
10 &raquo;
11 ${_('fork')}
11 ${_('fork')}
12 </%def>
12 </%def>
13
13
14 <%def name="page_nav()">
14 <%def name="page_nav()">
15 ${self.menu('')}
15 ${self.menu('')}
16 </%def>
16 </%def>
17 <%def name="main()">
17 <%def name="main()">
18 <div class="box">
18 <div class="box">
19 <!-- box / title -->
19 <!-- box / title -->
20 <div class="title">
20 <div class="title">
21 ${self.breadcrumbs()}
21 ${self.breadcrumbs()}
22 </div>
22 </div>
23 ${h.form(url('repos'))}
23 ${h.form(url('repo_fork_create_home',repo_name=c.repo_info.repo_name))}
24 <div class="form">
24 <div class="form">
25 <!-- fields -->
25 <!-- fields -->
26 <div class="fields">
26 <div class="fields">
27 <div class="field">
27 <div class="field">
28 <div class="label">
28 <div class="label">
29 <label for="repo_name">${_('Fork name')}:</label>
29 <label for="repo_name">${_('Fork name')}:</label>
30 </div>
30 </div>
31 <div class="input">
31 <div class="input">
32 ${h.text('fork_name')}
32 ${h.text('fork_name')}
33 </div>
33 </div>
34 </div>
34 </div>
35 <div class="field">
35 <div class="field">
36 <div class="label label-textarea">
36 <div class="label label-textarea">
37 <label for="description">${_('Description')}:</label>
37 <label for="description">${_('Description')}:</label>
38 </div>
38 </div>
39 <div class="textarea text-area editor">
39 <div class="textarea text-area editor">
40 ${h.textarea('description',cols=23,rows=5)}
40 ${h.textarea('description',cols=23,rows=5)}
41 </div>
41 </div>
42 </div>
42 </div>
43 <div class="field">
43 <div class="field">
44 <div class="label label-checkbox">
44 <div class="label label-checkbox">
45 <label for="private">${_('Private')}:</label>
45 <label for="private">${_('Private')}:</label>
46 </div>
46 </div>
47 <div class="checkboxes">
47 <div class="checkboxes">
48 ${h.checkbox('private',value="True")}
48 ${h.checkbox('private',value="True")}
49 </div>
49 </div>
50 </div>
50 </div>
51 <div class="buttons">
51 <div class="buttons">
52 ${h.submit('fork','fork this repository',class_="ui-button ui-widget ui-state-default ui-corner-all")}
52 ${h.submit('','fork this repository',class_="ui-button ui-widget ui-state-default ui-corner-all")}
53 </div>
53 </div>
54 </div>
54 </div>
55 </div>
55 </div>
56 ${h.end_form()}
56 ${h.end_form()}
57 </div>
57 </div>
58 </%def>
58 </%def>
General Comments 0
You need to be logged in to leave comments. Login now