##// END OF EJS Templates
refactor codes and setup for python 2.5...
marcink -
r564:ba7e24cd default
parent child Browse files
Show More
@@ -1,94 +1,87 b''
1 --------------------------------------------------------------
1 ------------------------------------------------
2 Pylons based repository management for mercurial (and soon git)
2 Pylons based repository management for mercurial
3 --------------------------------------------------------------
3 ------------------------------------------------
4
5 Fully customizable, with authentication, permissions. Based on vcs library.
6
4
7 **Overview**
5 **Overview**
8
6
9 - has it's own middleware to handle mercurial protocol request each request can
7 - Has it's own middleware to handle mercurial protocol request. Each request can
10 be logged and authenticated + threaded performance unlikely to hgweb
8 be logged and authenticated. Runs on threads unlikely to hgweb You can make
11 - full permissions per project read/write/admin access even on mercurial request
9 multiple pulls/pushes simultaneous
12 - mako templates let's you customize look and feel of application.
10 - Full permissions and authentication per project private/read/write/admin.
13 - diffs annotations and source code all colored by pygments.
11 One account for web interface and mercurial push/pull/clone.
14 - mercurial branch graph and yui-flot powered graphs with zooming and statistics
12 - Mako templates let's you customize look and feel of application.
15 - admin interface for performing user/permission managements as well as repository
13 - Beautiful diffs, annotations and source codes all colored by pygments.
16 management.
14 - Mercurial branch graph and yui-flot powered graphs with zooming and statistics
17 - server side forks, it's possible to fork a project and hack it free without
15 - Admin interface with user/permission management. User activity journal logs
16 pulls, pushes, forks,registrations. Possible to disable built in hooks
17 - Server side forks, it's possible to fork a project and hack it free without
18 breaking the main.
18 breaking the main.
19 - full text search of source codes with indexing daemons using whoosh
19 - Full text search on source codes, search on file names. All powered by whoosh
20 and build in indexing daemons
20 (no external search servers required all in one application)
21 (no external search servers required all in one application)
21 - async tasks for speed and performance using celery (works without them too)
22 - Rss / atom feeds, gravatar support, download sources as zip/tarballs
22 - Additional settings for mercurial web, (hooks editable from admin
23 - Async tasks for speed and performance using celery (works without them too)
23 panel !) also manage paths, archive, remote messages
24 - Backup scripts can do backup of whole app and send it over scp to desired
24 - backup scripts can do backup of whole app and send it over scp to desired location
25 location
25 - setup project descriptions and info inside built in db for easy, non
26 - Setup project descriptions and info inside built in db for easy, non
26 file-system operations
27 file-system operations
27 - added cache with invalidation on push/repo management for high performance and
28 - Added cache with invalidation on push/repo management for high performance and
28 always up to date data.
29 always up to date data.
29 - rss / atom feeds, gravatar support
30 - Based on pylons 1.0 / sqlalchemy 0.6 / sqlite
30 - based on pylons 1.0 / sqlalchemy 0.6
31
31
32 **Incoming**
32 **Incoming**
33
33
34 - code review based on hg-review (when it's stable)
34 - code review based on hg-review (when it's stable)
35 - git support (when vcs can handle it - almost there !)
35 - git support (when vcs can handle it - almost there !)
36 - commit based wikis
36 - commit based wikis
37 - clonning from remote repositories into rhodecode (git/mercurial)
37 - clonning from remote repositories into rhodecode (git/mercurial)
38 - other cools stuff that i can figure out (or You can help me figure out)
38 - other cools stuff that i can figure out (or You can help me figure out)
39
40 .. note::
41 This software is still in beta mode.
42 I don't guarantee that it'll work correctly.
43
39
40 ------------
41 Installation
42 ------------
44
43
45 -------------
44 **quick setup**
46 Installation
47 -------------
48
49 quick setup
50
45
51 - pip install -E rhodecode-venv http://bitbucket.org/marcinkuzminski/rhodecode/get/tip.zip
46 - pip install -E rhodecode-venv rhodecode
52 - activate virtualenv
47 - activate virtualenv
53 - run `paster make-config RhodeCode production.ini`
48 - run `paster make-config RhodeCode production.ini`
54 - run `paster setup-app production.ini`
49 - run `paster setup-app production.ini`
55 - run `paster runserver production.ini`
50 - run `paster runserver production.ini`
56
51
57 You're ready to go.
52 You're ready to go.
58
53
59
54 **MORE DETAILED INSTRUCTIONS**
60 MORE DETAILED INSTRUCTIONS
61
55
62 - I highly recommend to install new virtualenv for rhodecode see
56 - I highly recommend to install new virtualenv for rhodecode see
63 http://pypi.python.org/pypi/virtualenv for more details.
57 http://pypi.python.org/pypi/virtualenv for more details.
64 - Create new virtualenv using `virtualenv --no-site-packages /var/www/rhodecode-venv`
58 - Create new virtualenv using `virtualenv --no-site-packages /var/www/rhodecode-venv`
65 this will install new virtual env into /var/www/rhodecode-venv.
59 this will install new virtual env into /var/www/rhodecode-venv.
66 Activate the virtualenv by running
60 Activate the virtualenv by running
67 `source activate /var/www/rhodecode-venv/bin/activate`
61 `source activate /var/www/rhodecode-venv/bin/activate`
68 - Make a folder for rhodecode somewhere on the filesystem for example /var/www/rhodecode
62 - Make a folder for rhodecode somewhere on the filesystem for example /var/www/rhodecode
69 - Run easy_install http://bitbucket.org/marcinkuzminski/rhodecode/get/tip.zip.
63 - Run easy_install rhodecode
70 - Run `paster make-config RhodeCode production.inii` in order to install
64 - Run `paster make-config RhodeCode production.inii` in order to install
71 the application config. You can play with the app settings later
65 the application config. You can play with the app settings later
72 - Run `paster setup-app production.ini` it should create all needed tables
66 - Run `paster setup-app production.ini` it should create all needed tables
73 and an admin account make sure You specify correct path to repositories.
67 and an admin account make sure You specify correct path to repositories.
74 - Remember that the given path for mercurial repositories must be write
68 - Remember that the given path for mercurial repositories must be write
75 accessible for the application
69 accessible for the application
76 - Run paster serve production.ini - or you can use sample init.d scripts.
70 - Run paster serve production.ini - or you can use sample init.d scripts.
77 the app should be available at the 127.0.0.1:5000
71 the app should be available at the 127.0.0.1:5000
78 - Use admin account you created to login.
72 - Use admin account you created to login.
79 - Default permissions on each repository is read, and owner is admin. So remember
73 - Default permissions on each repository is read, and owner is admin. So remember
80 to update these.
74 to update these.
81 - In order to use full power of async tasks, You must install message broker
75 - In order to use full power of async tasks, You must install message broker
82 preferably rabbitmq and start celeryd daemon together with rhodecode.
76 preferably rabbitmq and start celeryd daemon together with rhodecode.
83 The app should gain a lot of speed and become much more responsible.
77 The app should gain a lot of speed and become much more responsible.
84 For installation instructions You can visit:
78 For installation instructions You can visit:
85 http://ask.github.com/celery/getting-started/index.html.
79 http://ask.github.com/celery/getting-started/index.html.
86 - All needed configs are inside rhodecode ie. celeryconfig.py , production.ini
80 - All needed configs are inside rhodecode sources ie. celeryconfig.py,
87 You can configure the email, ports, loggers, workers from there.
81 development.ini, production.ini You can configure the email, ports, loggers,
82 workers from there.
88 - For full text search You can either put crontab entry for
83 - For full text search You can either put crontab entry for
89 `python /var/www/rhodecode/rhodecode/lib/indexers/daemon.py incremental <path_to_repos>`
84 `python /var/www/rhodecode/rhodecode/lib/indexers/daemon.py incremental <path_to_repos>`
90 or run indexer from admin panel. This will scann the repos given in the
85 or run indexer from admin panel. This will scann the repos given in the
91 application setup or given path for daemon.py and each scann in incremental
86 application setup or given path for daemon.py and each scann in incremental
92 mode will scann only changed files,
87 mode will scann only changed files. No newline at end of file
93 Hg Update hook must be activated to index the content it's enabled by default
94 after setup No newline at end of file
@@ -1,163 +1,163 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # permissions controller for pylons
3 # permissions controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 27, 2010
21 Created on April 27, 2010
22 permissions controller for pylons
22 permissions controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 from formencode import htmlfill
26 from formencode import htmlfill
27 from pylons import request, session, tmpl_context as c, url
27 from pylons import request, session, tmpl_context as c, url
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from rhodecode.lib import helpers as h
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.base import BaseController, render
32 from rhodecode.lib.base import BaseController, render
33 from rhodecode.model.db import User, UserLog
33 from rhodecode.model.db import User, UserLog
34 from rhodecode.model.forms import UserForm, DefaultPermissionsForm
34 from rhodecode.model.forms import UserForm, DefaultPermissionsForm
35 from rhodecode.model.permission_model import PermissionModel
35 from rhodecode.model.permission_model import PermissionModel
36 from rhodecode.model.user_model import UserModel
36 from rhodecode.model.user_model import UserModel
37 import formencode
37 import formencode
38 import logging
38 import logging
39 import traceback
39 import traceback
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43 class PermissionsController(BaseController):
43 class PermissionsController(BaseController):
44 """REST Controller styled on the Atom Publishing Protocol"""
44 """REST Controller styled on the Atom Publishing Protocol"""
45 # To properly map this controller, ensure your config/routing.py
45 # To properly map this controller, ensure your config/routing.py
46 # file has a resource setup:
46 # file has a resource setup:
47 # map.resource('permission', 'permissions')
47 # map.resource('permission', 'permissions')
48
48
49 @LoginRequired()
49 @LoginRequired()
50 @HasPermissionAllDecorator('hg.admin')
50 @HasPermissionAllDecorator('hg.admin')
51 def __before__(self):
51 def __before__(self):
52 c.admin_user = session.get('admin_user')
52 c.admin_user = session.get('admin_user')
53 c.admin_username = session.get('admin_username')
53 c.admin_username = session.get('admin_username')
54 super(PermissionsController, self).__before__()
54 super(PermissionsController, self).__before__()
55
55
56 self.perms_choices = [('repository.none', _('None'),),
56 self.perms_choices = [('repository.none', _('None'),),
57 ('repository.read', _('Read'),),
57 ('repository.read', _('Read'),),
58 ('repository.write', _('Write'),),
58 ('repository.write', _('Write'),),
59 ('repository.admin', _('Admin'),)]
59 ('repository.admin', _('Admin'),)]
60 self.register_choices = [
60 self.register_choices = [
61 ('hg.register.none', 'disabled'),
61 ('hg.register.none', 'disabled'),
62 ('hg.register.manual_activate',
62 ('hg.register.manual_activate',
63 _('allowed with manual account activation')),
63 _('allowed with manual account activation')),
64 ('hg.register.auto_activate',
64 ('hg.register.auto_activate',
65 _('allowed with automatic account activation')), ]
65 _('allowed with automatic account activation')), ]
66
66
67 self.create_choices = [('hg.create.none', _('Disabled')),
67 self.create_choices = [('hg.create.none', _('Disabled')),
68 ('hg.create.repository', _('Enabled'))]
68 ('hg.create.repository', _('Enabled'))]
69
69
70
70
71 def index(self, format='html'):
71 def index(self, format='html'):
72 """GET /permissions: All items in the collection"""
72 """GET /permissions: All items in the collection"""
73 # url('permissions')
73 # url('permissions')
74
74
75 def create(self):
75 def create(self):
76 """POST /permissions: Create a new item"""
76 """POST /permissions: Create a new item"""
77 # url('permissions')
77 # url('permissions')
78
78
79 def new(self, format='html'):
79 def new(self, format='html'):
80 """GET /permissions/new: Form to create a new item"""
80 """GET /permissions/new: Form to create a new item"""
81 # url('new_permission')
81 # url('new_permission')
82
82
83 def update(self, id):
83 def update(self, id):
84 """PUT /permissions/id: Update an existing item"""
84 """PUT /permissions/id: Update an existing item"""
85 # Forms posted to this method should contain a hidden field:
85 # Forms posted to this method should contain a hidden field:
86 # <input type="hidden" name="_method" value="PUT" />
86 # <input type="hidden" name="_method" value="PUT" />
87 # Or using helpers:
87 # Or using helpers:
88 # h.form(url('permission', id=ID),
88 # h.form(url('permission', id=ID),
89 # method='put')
89 # method='put')
90 # url('permission', id=ID)
90 # url('permission', id=ID)
91
91
92 permission_model = PermissionModel()
92 permission_model = PermissionModel()
93
93
94 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
94 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
95 [x[0] for x in self.register_choices],
95 [x[0] for x in self.register_choices],
96 [x[0] for x in self.create_choices])()
96 [x[0] for x in self.create_choices])()
97
97
98 try:
98 try:
99 form_result = _form.to_python(dict(request.POST))
99 form_result = _form.to_python(dict(request.POST))
100 form_result.update({'perm_user_name':id})
100 form_result.update({'perm_user_name':id})
101 permission_model.update(form_result)
101 permission_model.update(form_result)
102 h.flash(_('Default permissions updated succesfully'),
102 h.flash(_('Default permissions updated succesfully'),
103 category='success')
103 category='success')
104
104
105 except formencode.Invalid as errors:
105 except formencode.Invalid, errors:
106 c.perms_choices = self.perms_choices
106 c.perms_choices = self.perms_choices
107 c.register_choices = self.register_choices
107 c.register_choices = self.register_choices
108 c.create_choices = self.create_choices
108 c.create_choices = self.create_choices
109
109
110 return htmlfill.render(
110 return htmlfill.render(
111 render('admin/permissions/permissions.html'),
111 render('admin/permissions/permissions.html'),
112 defaults=errors.value,
112 defaults=errors.value,
113 errors=errors.error_dict or {},
113 errors=errors.error_dict or {},
114 prefix_error=False,
114 prefix_error=False,
115 encoding="UTF-8")
115 encoding="UTF-8")
116 except Exception:
116 except Exception:
117 log.error(traceback.format_exc())
117 log.error(traceback.format_exc())
118 h.flash(_('error occured during update of permissions'),
118 h.flash(_('error occured during update of permissions'),
119 category='error')
119 category='error')
120
120
121 return redirect(url('edit_permission', id=id))
121 return redirect(url('edit_permission', id=id))
122
122
123
123
124
124
125 def delete(self, id):
125 def delete(self, id):
126 """DELETE /permissions/id: Delete an existing item"""
126 """DELETE /permissions/id: Delete an existing item"""
127 # Forms posted to this method should contain a hidden field:
127 # Forms posted to this method should contain a hidden field:
128 # <input type="hidden" name="_method" value="DELETE" />
128 # <input type="hidden" name="_method" value="DELETE" />
129 # Or using helpers:
129 # Or using helpers:
130 # h.form(url('permission', id=ID),
130 # h.form(url('permission', id=ID),
131 # method='delete')
131 # method='delete')
132 # url('permission', id=ID)
132 # url('permission', id=ID)
133
133
134 def show(self, id, format='html'):
134 def show(self, id, format='html'):
135 """GET /permissions/id: Show a specific item"""
135 """GET /permissions/id: Show a specific item"""
136 # url('permission', id=ID)
136 # url('permission', id=ID)
137
137
138 def edit(self, id, format='html'):
138 def edit(self, id, format='html'):
139 """GET /permissions/id/edit: Form to edit an existing item"""
139 """GET /permissions/id/edit: Form to edit an existing item"""
140 #url('edit_permission', id=ID)
140 #url('edit_permission', id=ID)
141 c.perms_choices = self.perms_choices
141 c.perms_choices = self.perms_choices
142 c.register_choices = self.register_choices
142 c.register_choices = self.register_choices
143 c.create_choices = self.create_choices
143 c.create_choices = self.create_choices
144
144
145 if id == 'default':
145 if id == 'default':
146 defaults = {'_method':'put'}
146 defaults = {'_method':'put'}
147 for p in UserModel().get_default().user_perms:
147 for p in UserModel().get_default().user_perms:
148 if p.permission.permission_name.startswith('repository.'):
148 if p.permission.permission_name.startswith('repository.'):
149 defaults['default_perm'] = p.permission.permission_name
149 defaults['default_perm'] = p.permission.permission_name
150
150
151 if p.permission.permission_name.startswith('hg.register.'):
151 if p.permission.permission_name.startswith('hg.register.'):
152 defaults['default_register'] = p.permission.permission_name
152 defaults['default_register'] = p.permission.permission_name
153
153
154 if p.permission.permission_name.startswith('hg.create.'):
154 if p.permission.permission_name.startswith('hg.create.'):
155 defaults['default_create'] = p.permission.permission_name
155 defaults['default_create'] = p.permission.permission_name
156
156
157 return htmlfill.render(
157 return htmlfill.render(
158 render('admin/permissions/permissions.html'),
158 render('admin/permissions/permissions.html'),
159 defaults=defaults,
159 defaults=defaults,
160 encoding="UTF-8",
160 encoding="UTF-8",
161 force_defaults=True,)
161 force_defaults=True,)
162 else:
162 else:
163 return redirect(url('admin_home'))
163 return redirect(url('admin_home'))
@@ -1,245 +1,245 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # repos controller for pylons
3 # repos controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 7, 2010
21 Created on April 7, 2010
22 admin controller for pylons
22 admin controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from operator import itemgetter
26 from operator import itemgetter
27 from paste.httpexceptions import HTTPInternalServerError
27 from paste.httpexceptions import HTTPInternalServerError
28 from pylons import request, response, session, tmpl_context as c, url
28 from pylons import request, response, session, tmpl_context as c, url
29 from pylons.controllers.util import abort, redirect
29 from pylons.controllers.util import abort, redirect
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
33 HasPermissionAnyDecorator
33 HasPermissionAnyDecorator
34 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.utils import invalidate_cache, action_logger
35 from rhodecode.lib.utils import invalidate_cache, action_logger
36 from rhodecode.model.db import User
36 from rhodecode.model.db import User
37 from rhodecode.model.forms import RepoForm
37 from rhodecode.model.forms import RepoForm
38 from rhodecode.model.hg_model import HgModel
38 from rhodecode.model.hg_model import HgModel
39 from rhodecode.model.repo_model import RepoModel
39 from rhodecode.model.repo_model import RepoModel
40 import formencode
40 import formencode
41 import logging
41 import logging
42 import traceback
42 import traceback
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class ReposController(BaseController):
46 class ReposController(BaseController):
47 """REST Controller styled on the Atom Publishing Protocol"""
47 """REST Controller styled on the Atom Publishing Protocol"""
48 # To properly map this controller, ensure your config/routing.py
48 # To properly map this controller, ensure your config/routing.py
49 # file has a resource setup:
49 # file has a resource setup:
50 # map.resource('repo', 'repos')
50 # map.resource('repo', 'repos')
51
51
52 @LoginRequired()
52 @LoginRequired()
53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
53 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
54 def __before__(self):
54 def __before__(self):
55 c.admin_user = session.get('admin_user')
55 c.admin_user = session.get('admin_user')
56 c.admin_username = session.get('admin_username')
56 c.admin_username = session.get('admin_username')
57 super(ReposController, self).__before__()
57 super(ReposController, self).__before__()
58
58
59 @HasPermissionAllDecorator('hg.admin')
59 @HasPermissionAllDecorator('hg.admin')
60 def index(self, format='html'):
60 def index(self, format='html'):
61 """GET /repos: All items in the collection"""
61 """GET /repos: All items in the collection"""
62 # url('repos')
62 # url('repos')
63 cached_repo_list = HgModel().get_repos()
63 cached_repo_list = HgModel().get_repos()
64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
64 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
65 return render('admin/repos/repos.html')
65 return render('admin/repos/repos.html')
66
66
67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
67 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
68 def create(self):
68 def create(self):
69 """POST /repos: Create a new item"""
69 """POST /repos: Create a new item"""
70 # url('repos')
70 # url('repos')
71 repo_model = RepoModel()
71 repo_model = RepoModel()
72 _form = RepoForm()()
72 _form = RepoForm()()
73 form_result = {}
73 form_result = {}
74 try:
74 try:
75 form_result = _form.to_python(dict(request.POST))
75 form_result = _form.to_python(dict(request.POST))
76 repo_model.create(form_result, c.rhodecode_user)
76 repo_model.create(form_result, c.rhodecode_user)
77 invalidate_cache('cached_repo_list')
77 invalidate_cache('cached_repo_list')
78 h.flash(_('created repository %s') % form_result['repo_name'],
78 h.flash(_('created repository %s') % form_result['repo_name'],
79 category='success')
79 category='success')
80
80
81 if request.POST.get('user_created'):
81 if request.POST.get('user_created'):
82 action_logger(self.rhodecode_user, 'user_created_repo',
82 action_logger(self.rhodecode_user, 'user_created_repo',
83 form_result['repo_name'], '', self.sa)
83 form_result['repo_name'], '', self.sa)
84 else:
84 else:
85 action_logger(self.rhodecode_user, 'admin_created_repo',
85 action_logger(self.rhodecode_user, 'admin_created_repo',
86 form_result['repo_name'], '', self.sa)
86 form_result['repo_name'], '', self.sa)
87
87
88 except formencode.Invalid as errors:
88 except formencode.Invalid, errors:
89 c.new_repo = errors.value['repo_name']
89 c.new_repo = errors.value['repo_name']
90
90
91 if request.POST.get('user_created'):
91 if request.POST.get('user_created'):
92 r = render('admin/repos/repo_add_create_repository.html')
92 r = render('admin/repos/repo_add_create_repository.html')
93 else:
93 else:
94 r = render('admin/repos/repo_add.html')
94 r = render('admin/repos/repo_add.html')
95
95
96 return htmlfill.render(
96 return htmlfill.render(
97 r,
97 r,
98 defaults=errors.value,
98 defaults=errors.value,
99 errors=errors.error_dict or {},
99 errors=errors.error_dict or {},
100 prefix_error=False,
100 prefix_error=False,
101 encoding="UTF-8")
101 encoding="UTF-8")
102
102
103 except Exception:
103 except Exception:
104 log.error(traceback.format_exc())
104 log.error(traceback.format_exc())
105 msg = _('error occured during creation of repository %s') \
105 msg = _('error occured during creation of repository %s') \
106 % form_result.get('repo_name')
106 % form_result.get('repo_name')
107 h.flash(msg, category='error')
107 h.flash(msg, category='error')
108 if request.POST.get('user_created'):
108 if request.POST.get('user_created'):
109 return redirect(url('hg_home'))
109 return redirect(url('hg_home'))
110 return redirect(url('repos'))
110 return redirect(url('repos'))
111
111
112 @HasPermissionAllDecorator('hg.admin')
112 @HasPermissionAllDecorator('hg.admin')
113 def new(self, format='html'):
113 def new(self, format='html'):
114 """GET /repos/new: Form to create a new item"""
114 """GET /repos/new: Form to create a new item"""
115 new_repo = request.GET.get('repo', '')
115 new_repo = request.GET.get('repo', '')
116 c.new_repo = h.repo_name_slug(new_repo)
116 c.new_repo = h.repo_name_slug(new_repo)
117
117
118 return render('admin/repos/repo_add.html')
118 return render('admin/repos/repo_add.html')
119
119
120 @HasPermissionAllDecorator('hg.admin')
120 @HasPermissionAllDecorator('hg.admin')
121 def update(self, repo_name):
121 def update(self, repo_name):
122 """PUT /repos/repo_name: Update an existing item"""
122 """PUT /repos/repo_name: Update an existing item"""
123 # Forms posted to this method should contain a hidden field:
123 # Forms posted to this method should contain a hidden field:
124 # <input type="hidden" name="_method" value="PUT" />
124 # <input type="hidden" name="_method" value="PUT" />
125 # Or using helpers:
125 # Or using helpers:
126 # h.form(url('repo', repo_name=ID),
126 # h.form(url('repo', repo_name=ID),
127 # method='put')
127 # method='put')
128 # url('repo', repo_name=ID)
128 # url('repo', repo_name=ID)
129 repo_model = RepoModel()
129 repo_model = RepoModel()
130 changed_name = repo_name
130 changed_name = repo_name
131 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
131 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
132
132
133 try:
133 try:
134 form_result = _form.to_python(dict(request.POST))
134 form_result = _form.to_python(dict(request.POST))
135 repo_model.update(repo_name, form_result)
135 repo_model.update(repo_name, form_result)
136 invalidate_cache('cached_repo_list')
136 invalidate_cache('cached_repo_list')
137 h.flash(_('Repository %s updated succesfully' % repo_name),
137 h.flash(_('Repository %s updated succesfully' % repo_name),
138 category='success')
138 category='success')
139 changed_name = form_result['repo_name']
139 changed_name = form_result['repo_name']
140 except formencode.Invalid as errors:
140 except formencode.Invalid, errors:
141 c.repo_info = repo_model.get(repo_name)
141 c.repo_info = repo_model.get(repo_name)
142 c.users_array = repo_model.get_users_js()
142 c.users_array = repo_model.get_users_js()
143 errors.value.update({'user':c.repo_info.user.username})
143 errors.value.update({'user':c.repo_info.user.username})
144 return htmlfill.render(
144 return htmlfill.render(
145 render('admin/repos/repo_edit.html'),
145 render('admin/repos/repo_edit.html'),
146 defaults=errors.value,
146 defaults=errors.value,
147 errors=errors.error_dict or {},
147 errors=errors.error_dict or {},
148 prefix_error=False,
148 prefix_error=False,
149 encoding="UTF-8")
149 encoding="UTF-8")
150
150
151 except Exception:
151 except Exception:
152 log.error(traceback.format_exc())
152 log.error(traceback.format_exc())
153 h.flash(_('error occured during update of repository %s') \
153 h.flash(_('error occured during update of repository %s') \
154 % repo_name, category='error')
154 % repo_name, category='error')
155
155
156 return redirect(url('edit_repo', repo_name=changed_name))
156 return redirect(url('edit_repo', repo_name=changed_name))
157
157
158 @HasPermissionAllDecorator('hg.admin')
158 @HasPermissionAllDecorator('hg.admin')
159 def delete(self, repo_name):
159 def delete(self, repo_name):
160 """DELETE /repos/repo_name: Delete an existing item"""
160 """DELETE /repos/repo_name: Delete an existing item"""
161 # Forms posted to this method should contain a hidden field:
161 # Forms posted to this method should contain a hidden field:
162 # <input type="hidden" name="_method" value="DELETE" />
162 # <input type="hidden" name="_method" value="DELETE" />
163 # Or using helpers:
163 # Or using helpers:
164 # h.form(url('repo', repo_name=ID),
164 # h.form(url('repo', repo_name=ID),
165 # method='delete')
165 # method='delete')
166 # url('repo', repo_name=ID)
166 # url('repo', repo_name=ID)
167
167
168 repo_model = RepoModel()
168 repo_model = RepoModel()
169 repo = repo_model.get(repo_name)
169 repo = repo_model.get(repo_name)
170 if not repo:
170 if not repo:
171 h.flash(_('%s repository is not mapped to db perhaps'
171 h.flash(_('%s repository is not mapped to db perhaps'
172 ' it was moved or renamed from the filesystem'
172 ' it was moved or renamed from the filesystem'
173 ' please run the application again'
173 ' please run the application again'
174 ' in order to rescan repositories') % repo_name,
174 ' in order to rescan repositories') % repo_name,
175 category='error')
175 category='error')
176
176
177 return redirect(url('repos'))
177 return redirect(url('repos'))
178 try:
178 try:
179 action_logger(self.rhodecode_user, 'admin_deleted_repo',
179 action_logger(self.rhodecode_user, 'admin_deleted_repo',
180 repo_name, '', self.sa)
180 repo_name, '', self.sa)
181 repo_model.delete(repo)
181 repo_model.delete(repo)
182 invalidate_cache('cached_repo_list')
182 invalidate_cache('cached_repo_list')
183 h.flash(_('deleted repository %s') % repo_name, category='success')
183 h.flash(_('deleted repository %s') % repo_name, category='success')
184
184
185 except Exception, e:
185 except Exception, e:
186 log.error(traceback.format_exc())
186 log.error(traceback.format_exc())
187 h.flash(_('An error occured during deletion of %s') % repo_name,
187 h.flash(_('An error occured during deletion of %s') % repo_name,
188 category='error')
188 category='error')
189
189
190 return redirect(url('repos'))
190 return redirect(url('repos'))
191
191
192 @HasPermissionAllDecorator('hg.admin')
192 @HasPermissionAllDecorator('hg.admin')
193 def delete_perm_user(self, repo_name):
193 def delete_perm_user(self, repo_name):
194 """
194 """
195 DELETE an existing repository permission user
195 DELETE an existing repository permission user
196 @param repo_name:
196 @param repo_name:
197 """
197 """
198
198
199 try:
199 try:
200 repo_model = RepoModel()
200 repo_model = RepoModel()
201 repo_model.delete_perm_user(request.POST, repo_name)
201 repo_model.delete_perm_user(request.POST, repo_name)
202 except Exception as e:
202 except Exception, e:
203 h.flash(_('An error occured during deletion of repository user'),
203 h.flash(_('An error occured during deletion of repository user'),
204 category='error')
204 category='error')
205 raise HTTPInternalServerError()
205 raise HTTPInternalServerError()
206
206
207 @HasPermissionAllDecorator('hg.admin')
207 @HasPermissionAllDecorator('hg.admin')
208 def show(self, repo_name, format='html'):
208 def show(self, repo_name, format='html'):
209 """GET /repos/repo_name: Show a specific item"""
209 """GET /repos/repo_name: Show a specific item"""
210 # url('repo', repo_name=ID)
210 # url('repo', repo_name=ID)
211
211
212 @HasPermissionAllDecorator('hg.admin')
212 @HasPermissionAllDecorator('hg.admin')
213 def edit(self, repo_name, format='html'):
213 def edit(self, repo_name, format='html'):
214 """GET /repos/repo_name/edit: Form to edit an existing item"""
214 """GET /repos/repo_name/edit: Form to edit an existing item"""
215 # url('edit_repo', repo_name=ID)
215 # url('edit_repo', repo_name=ID)
216 repo_model = RepoModel()
216 repo_model = RepoModel()
217 c.repo_info = repo = repo_model.get(repo_name)
217 c.repo_info = repo = repo_model.get(repo_name)
218 if not repo:
218 if not repo:
219 h.flash(_('%s repository is not mapped to db perhaps'
219 h.flash(_('%s repository is not mapped to db perhaps'
220 ' it was created or renamed from the filesystem'
220 ' it was created or renamed from the filesystem'
221 ' please run the application again'
221 ' please run the application again'
222 ' in order to rescan repositories') % repo_name,
222 ' in order to rescan repositories') % repo_name,
223 category='error')
223 category='error')
224
224
225 return redirect(url('repos'))
225 return redirect(url('repos'))
226 defaults = c.repo_info.__dict__
226 defaults = c.repo_info.__dict__
227 if c.repo_info.user:
227 if c.repo_info.user:
228 defaults.update({'user':c.repo_info.user.username})
228 defaults.update({'user':c.repo_info.user.username})
229 else:
229 else:
230 replacement_user = self.sa.query(User)\
230 replacement_user = self.sa.query(User)\
231 .filter(User.admin == True).first().username
231 .filter(User.admin == True).first().username
232 defaults.update({'user':replacement_user})
232 defaults.update({'user':replacement_user})
233
233
234 c.users_array = repo_model.get_users_js()
234 c.users_array = repo_model.get_users_js()
235
235
236 for p in c.repo_info.repo_to_perm:
236 for p in c.repo_info.repo_to_perm:
237 defaults.update({'perm_%s' % p.user.username:
237 defaults.update({'perm_%s' % p.user.username:
238 p.permission.permission_name})
238 p.permission.permission_name})
239
239
240 return htmlfill.render(
240 return htmlfill.render(
241 render('admin/repos/repo_edit.html'),
241 render('admin/repos/repo_edit.html'),
242 defaults=defaults,
242 defaults=defaults,
243 encoding="UTF-8",
243 encoding="UTF-8",
244 force_defaults=False
244 force_defaults=False
245 )
245 )
@@ -1,298 +1,298 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 July 14, 2010
21 Created on July 14, 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 request, session, tmpl_context as c, url, app_globals as g, \
26 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
27 config
27 config
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30 from rhodecode.lib import helpers as h
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
32 HasPermissionAnyDecorator
32 HasPermissionAnyDecorator
33 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
34 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
35 set_rhodecode_config, get_hg_settings, get_hg_ui_settings, make_ui
35 set_rhodecode_config, get_hg_settings, get_hg_ui_settings, make_ui
36 from rhodecode.model.db import User, UserLog, RhodeCodeSettings, RhodeCodeUi
36 from rhodecode.model.db import User, UserLog, RhodeCodeSettings, RhodeCodeUi
37 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
37 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
38 ApplicationUiSettingsForm
38 ApplicationUiSettingsForm
39 from rhodecode.model.hg_model import HgModel
39 from rhodecode.model.hg_model import HgModel
40 from rhodecode.model.user_model import UserModel
40 from rhodecode.model.user_model import UserModel
41 from rhodecode.lib.celerylib import tasks, run_task
41 from rhodecode.lib.celerylib import tasks, run_task
42 import formencode
42 import formencode
43 import logging
43 import logging
44 import traceback
44 import traceback
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class SettingsController(BaseController):
49 class SettingsController(BaseController):
50 """REST Controller styled on the Atom Publishing Protocol"""
50 """REST Controller styled on the Atom Publishing Protocol"""
51 # To properly map this controller, ensure your config/routing.py
51 # To properly map this controller, ensure your config/routing.py
52 # file has a resource setup:
52 # file has a resource setup:
53 # map.resource('setting', 'settings', controller='admin/settings',
53 # map.resource('setting', 'settings', controller='admin/settings',
54 # path_prefix='/admin', name_prefix='admin_')
54 # path_prefix='/admin', name_prefix='admin_')
55
55
56
56
57 @LoginRequired()
57 @LoginRequired()
58 def __before__(self):
58 def __before__(self):
59 c.admin_user = session.get('admin_user')
59 c.admin_user = session.get('admin_user')
60 c.admin_username = session.get('admin_username')
60 c.admin_username = session.get('admin_username')
61 super(SettingsController, self).__before__()
61 super(SettingsController, self).__before__()
62
62
63
63
64 @HasPermissionAllDecorator('hg.admin')
64 @HasPermissionAllDecorator('hg.admin')
65 def index(self, format='html'):
65 def index(self, format='html'):
66 """GET /admin/settings: All items in the collection"""
66 """GET /admin/settings: All items in the collection"""
67 # url('admin_settings')
67 # url('admin_settings')
68
68
69 defaults = get_hg_settings()
69 defaults = get_hg_settings()
70 defaults.update(get_hg_ui_settings())
70 defaults.update(get_hg_ui_settings())
71 return htmlfill.render(
71 return htmlfill.render(
72 render('admin/settings/settings.html'),
72 render('admin/settings/settings.html'),
73 defaults=defaults,
73 defaults=defaults,
74 encoding="UTF-8",
74 encoding="UTF-8",
75 force_defaults=False
75 force_defaults=False
76 )
76 )
77
77
78 @HasPermissionAllDecorator('hg.admin')
78 @HasPermissionAllDecorator('hg.admin')
79 def create(self):
79 def create(self):
80 """POST /admin/settings: Create a new item"""
80 """POST /admin/settings: Create a new item"""
81 # url('admin_settings')
81 # url('admin_settings')
82
82
83 @HasPermissionAllDecorator('hg.admin')
83 @HasPermissionAllDecorator('hg.admin')
84 def new(self, format='html'):
84 def new(self, format='html'):
85 """GET /admin/settings/new: Form to create a new item"""
85 """GET /admin/settings/new: Form to create a new item"""
86 # url('admin_new_setting')
86 # url('admin_new_setting')
87
87
88 @HasPermissionAllDecorator('hg.admin')
88 @HasPermissionAllDecorator('hg.admin')
89 def update(self, setting_id):
89 def update(self, setting_id):
90 """PUT /admin/settings/setting_id: Update an existing item"""
90 """PUT /admin/settings/setting_id: Update an existing item"""
91 # Forms posted to this method should contain a hidden field:
91 # Forms posted to this method should contain a hidden field:
92 # <input type="hidden" name="_method" value="PUT" />
92 # <input type="hidden" name="_method" value="PUT" />
93 # Or using helpers:
93 # Or using helpers:
94 # h.form(url('admin_setting', setting_id=ID),
94 # h.form(url('admin_setting', setting_id=ID),
95 # method='put')
95 # method='put')
96 # url('admin_setting', setting_id=ID)
96 # url('admin_setting', setting_id=ID)
97 if setting_id == 'mapping':
97 if setting_id == 'mapping':
98 rm_obsolete = request.POST.get('destroy', False)
98 rm_obsolete = request.POST.get('destroy', False)
99 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
99 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
100
100
101 initial = HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
101 initial = HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui)
102 repo2db_mapper(initial, rm_obsolete)
102 repo2db_mapper(initial, rm_obsolete)
103 invalidate_cache('cached_repo_list')
103 invalidate_cache('cached_repo_list')
104 h.flash(_('Repositories successfully rescanned'), category='success')
104 h.flash(_('Repositories successfully rescanned'), category='success')
105
105
106 if setting_id == 'whoosh':
106 if setting_id == 'whoosh':
107 repo_location = get_hg_ui_settings()['paths_root_path']
107 repo_location = get_hg_ui_settings()['paths_root_path']
108 full_index = request.POST.get('full_index', False)
108 full_index = request.POST.get('full_index', False)
109 task = run_task(tasks.whoosh_index, repo_location, full_index)
109 task = run_task(tasks.whoosh_index, repo_location, full_index)
110
110
111 h.flash(_('Whoosh reindex task scheduled'), category='success')
111 h.flash(_('Whoosh reindex task scheduled'), category='success')
112 if setting_id == 'global':
112 if setting_id == 'global':
113
113
114 application_form = ApplicationSettingsForm()()
114 application_form = ApplicationSettingsForm()()
115 try:
115 try:
116 form_result = application_form.to_python(dict(request.POST))
116 form_result = application_form.to_python(dict(request.POST))
117
117
118 try:
118 try:
119 hgsettings1 = self.sa.query(RhodeCodeSettings)\
119 hgsettings1 = self.sa.query(RhodeCodeSettings)\
120 .filter(RhodeCodeSettings.app_settings_name == 'title').one()
120 .filter(RhodeCodeSettings.app_settings_name == 'title').one()
121 hgsettings1.app_settings_value = form_result['rhodecode_title']
121 hgsettings1.app_settings_value = form_result['rhodecode_title']
122
122
123 hgsettings2 = self.sa.query(RhodeCodeSettings)\
123 hgsettings2 = self.sa.query(RhodeCodeSettings)\
124 .filter(RhodeCodeSettings.app_settings_name == 'realm').one()
124 .filter(RhodeCodeSettings.app_settings_name == 'realm').one()
125 hgsettings2.app_settings_value = form_result['rhodecode_realm']
125 hgsettings2.app_settings_value = form_result['rhodecode_realm']
126
126
127
127
128 self.sa.add(hgsettings1)
128 self.sa.add(hgsettings1)
129 self.sa.add(hgsettings2)
129 self.sa.add(hgsettings2)
130 self.sa.commit()
130 self.sa.commit()
131 set_rhodecode_config(config)
131 set_rhodecode_config(config)
132 h.flash(_('Updated application settings'),
132 h.flash(_('Updated application settings'),
133 category='success')
133 category='success')
134
134
135 except:
135 except:
136 log.error(traceback.format_exc())
136 log.error(traceback.format_exc())
137 h.flash(_('error occurred during updating application settings'),
137 h.flash(_('error occurred during updating application settings'),
138 category='error')
138 category='error')
139
139
140 self.sa.rollback()
140 self.sa.rollback()
141
141
142
142
143 except formencode.Invalid as errors:
143 except formencode.Invalid, errors:
144 return htmlfill.render(
144 return htmlfill.render(
145 render('admin/settings/settings.html'),
145 render('admin/settings/settings.html'),
146 defaults=errors.value,
146 defaults=errors.value,
147 errors=errors.error_dict or {},
147 errors=errors.error_dict or {},
148 prefix_error=False,
148 prefix_error=False,
149 encoding="UTF-8")
149 encoding="UTF-8")
150
150
151 if setting_id == 'mercurial':
151 if setting_id == 'mercurial':
152 application_form = ApplicationUiSettingsForm()()
152 application_form = ApplicationUiSettingsForm()()
153 try:
153 try:
154 form_result = application_form.to_python(dict(request.POST))
154 form_result = application_form.to_python(dict(request.POST))
155
155
156 try:
156 try:
157
157
158 hgsettings1 = self.sa.query(RhodeCodeUi)\
158 hgsettings1 = self.sa.query(RhodeCodeUi)\
159 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
159 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
160 hgsettings1.ui_value = form_result['web_push_ssl']
160 hgsettings1.ui_value = form_result['web_push_ssl']
161
161
162 hgsettings2 = self.sa.query(RhodeCodeUi)\
162 hgsettings2 = self.sa.query(RhodeCodeUi)\
163 .filter(RhodeCodeUi.ui_key == '/').one()
163 .filter(RhodeCodeUi.ui_key == '/').one()
164 hgsettings2.ui_value = form_result['paths_root_path']
164 hgsettings2.ui_value = form_result['paths_root_path']
165
165
166
166
167 #HOOKS
167 #HOOKS
168 hgsettings3 = self.sa.query(RhodeCodeUi)\
168 hgsettings3 = self.sa.query(RhodeCodeUi)\
169 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
169 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
170 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
170 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
171
171
172 hgsettings4 = self.sa.query(RhodeCodeUi)\
172 hgsettings4 = self.sa.query(RhodeCodeUi)\
173 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
173 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
174 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
174 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
175
175
176
176
177
177
178
178
179 self.sa.add(hgsettings1)
179 self.sa.add(hgsettings1)
180 self.sa.add(hgsettings2)
180 self.sa.add(hgsettings2)
181 self.sa.add(hgsettings3)
181 self.sa.add(hgsettings3)
182 self.sa.add(hgsettings4)
182 self.sa.add(hgsettings4)
183 self.sa.commit()
183 self.sa.commit()
184
184
185 h.flash(_('Updated mercurial settings'),
185 h.flash(_('Updated mercurial settings'),
186 category='success')
186 category='success')
187
187
188 except:
188 except:
189 log.error(traceback.format_exc())
189 log.error(traceback.format_exc())
190 h.flash(_('error occurred during updating application settings'),
190 h.flash(_('error occurred during updating application settings'),
191 category='error')
191 category='error')
192
192
193 self.sa.rollback()
193 self.sa.rollback()
194
194
195
195
196 except formencode.Invalid as errors:
196 except formencode.Invalid, errors:
197 return htmlfill.render(
197 return htmlfill.render(
198 render('admin/settings/settings.html'),
198 render('admin/settings/settings.html'),
199 defaults=errors.value,
199 defaults=errors.value,
200 errors=errors.error_dict or {},
200 errors=errors.error_dict or {},
201 prefix_error=False,
201 prefix_error=False,
202 encoding="UTF-8")
202 encoding="UTF-8")
203
203
204
204
205
205
206 return redirect(url('admin_settings'))
206 return redirect(url('admin_settings'))
207
207
208 @HasPermissionAllDecorator('hg.admin')
208 @HasPermissionAllDecorator('hg.admin')
209 def delete(self, setting_id):
209 def delete(self, setting_id):
210 """DELETE /admin/settings/setting_id: Delete an existing item"""
210 """DELETE /admin/settings/setting_id: Delete an existing item"""
211 # Forms posted to this method should contain a hidden field:
211 # Forms posted to this method should contain a hidden field:
212 # <input type="hidden" name="_method" value="DELETE" />
212 # <input type="hidden" name="_method" value="DELETE" />
213 # Or using helpers:
213 # Or using helpers:
214 # h.form(url('admin_setting', setting_id=ID),
214 # h.form(url('admin_setting', setting_id=ID),
215 # method='delete')
215 # method='delete')
216 # url('admin_setting', setting_id=ID)
216 # url('admin_setting', setting_id=ID)
217
217
218 @HasPermissionAllDecorator('hg.admin')
218 @HasPermissionAllDecorator('hg.admin')
219 def show(self, setting_id, format='html'):
219 def show(self, setting_id, format='html'):
220 """GET /admin/settings/setting_id: Show a specific item"""
220 """GET /admin/settings/setting_id: Show a specific item"""
221 # url('admin_setting', setting_id=ID)
221 # url('admin_setting', setting_id=ID)
222
222
223 @HasPermissionAllDecorator('hg.admin')
223 @HasPermissionAllDecorator('hg.admin')
224 def edit(self, setting_id, format='html'):
224 def edit(self, setting_id, format='html'):
225 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
225 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
226 # url('admin_edit_setting', setting_id=ID)
226 # url('admin_edit_setting', setting_id=ID)
227
227
228
228
229 def my_account(self):
229 def my_account(self):
230 """
230 """
231 GET /_admin/my_account Displays info about my account
231 GET /_admin/my_account Displays info about my account
232 """
232 """
233 # url('admin_settings_my_account')
233 # url('admin_settings_my_account')
234 c.user = self.sa.query(User).get(c.rhodecode_user.user_id)
234 c.user = self.sa.query(User).get(c.rhodecode_user.user_id)
235 c.user_repos = []
235 c.user_repos = []
236 for repo in c.cached_repo_list.values():
236 for repo in c.cached_repo_list.values():
237 if repo.dbrepo.user.username == c.user.username:
237 if repo.dbrepo.user.username == c.user.username:
238 c.user_repos.append(repo)
238 c.user_repos.append(repo)
239
239
240 if c.user.username == 'default':
240 if c.user.username == 'default':
241 h.flash(_("You can't edit this user since it's"
241 h.flash(_("You can't edit this user since it's"
242 " crucial for entire application"), category='warning')
242 " crucial for entire application"), category='warning')
243 return redirect(url('users'))
243 return redirect(url('users'))
244
244
245 defaults = c.user.__dict__
245 defaults = c.user.__dict__
246 return htmlfill.render(
246 return htmlfill.render(
247 render('admin/users/user_edit_my_account.html'),
247 render('admin/users/user_edit_my_account.html'),
248 defaults=defaults,
248 defaults=defaults,
249 encoding="UTF-8",
249 encoding="UTF-8",
250 force_defaults=False
250 force_defaults=False
251 )
251 )
252
252
253 def my_account_update(self):
253 def my_account_update(self):
254 """PUT /_admin/my_account_update: Update an existing item"""
254 """PUT /_admin/my_account_update: Update an existing item"""
255 # Forms posted to this method should contain a hidden field:
255 # Forms posted to this method should contain a hidden field:
256 # <input type="hidden" name="_method" value="PUT" />
256 # <input type="hidden" name="_method" value="PUT" />
257 # Or using helpers:
257 # Or using helpers:
258 # h.form(url('admin_settings_my_account_update'),
258 # h.form(url('admin_settings_my_account_update'),
259 # method='put')
259 # method='put')
260 # url('admin_settings_my_account_update', id=ID)
260 # url('admin_settings_my_account_update', id=ID)
261 user_model = UserModel()
261 user_model = UserModel()
262 uid = c.rhodecode_user.user_id
262 uid = c.rhodecode_user.user_id
263 _form = UserForm(edit=True, old_data={'user_id':uid,
263 _form = UserForm(edit=True, old_data={'user_id':uid,
264 'email':c.rhodecode_user.email})()
264 'email':c.rhodecode_user.email})()
265 form_result = {}
265 form_result = {}
266 try:
266 try:
267 form_result = _form.to_python(dict(request.POST))
267 form_result = _form.to_python(dict(request.POST))
268 user_model.update_my_account(uid, form_result)
268 user_model.update_my_account(uid, form_result)
269 h.flash(_('Your account was updated succesfully'),
269 h.flash(_('Your account was updated succesfully'),
270 category='success')
270 category='success')
271
271
272 except formencode.Invalid as errors:
272 except formencode.Invalid, errors:
273 c.user = self.sa.query(User).get(c.rhodecode_user.user_id)
273 c.user = self.sa.query(User).get(c.rhodecode_user.user_id)
274 c.user_repos = []
274 c.user_repos = []
275 for repo in c.cached_repo_list.values():
275 for repo in c.cached_repo_list.values():
276 if repo.dbrepo.user.username == c.user.username:
276 if repo.dbrepo.user.username == c.user.username:
277 c.user_repos.append(repo)
277 c.user_repos.append(repo)
278 return htmlfill.render(
278 return htmlfill.render(
279 render('admin/users/user_edit_my_account.html'),
279 render('admin/users/user_edit_my_account.html'),
280 defaults=errors.value,
280 defaults=errors.value,
281 errors=errors.error_dict or {},
281 errors=errors.error_dict or {},
282 prefix_error=False,
282 prefix_error=False,
283 encoding="UTF-8")
283 encoding="UTF-8")
284 except Exception:
284 except Exception:
285 log.error(traceback.format_exc())
285 log.error(traceback.format_exc())
286 h.flash(_('error occured during update of user %s') \
286 h.flash(_('error occured during update of user %s') \
287 % form_result.get('username'), category='error')
287 % form_result.get('username'), category='error')
288
288
289 return redirect(url('my_account'))
289 return redirect(url('my_account'))
290
290
291 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
291 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
292 def create_repository(self):
292 def create_repository(self):
293 """GET /_admin/create_repository: Form to create a new item"""
293 """GET /_admin/create_repository: Form to create a new item"""
294 new_repo = request.GET.get('repo', '')
294 new_repo = request.GET.get('repo', '')
295 c.new_repo = h.repo_name_slug(new_repo)
295 c.new_repo = h.repo_name_slug(new_repo)
296
296
297 return render('admin/repos/repo_add_create_repository.html')
297 return render('admin/repos/repo_add_create_repository.html')
298
298
@@ -1,168 +1,168 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # users controller for pylons
3 # users 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 from rhodecode.lib.utils import action_logger
20 from rhodecode.lib.utils import action_logger
21 """
21 """
22 Created on April 4, 2010
22 Created on April 4, 2010
23 users controller for pylons
23 users controller for pylons
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26
27 from formencode import htmlfill
27 from formencode import htmlfill
28 from pylons import request, session, tmpl_context as c, url
28 from pylons import request, session, tmpl_context as c, url
29 from pylons.controllers.util import abort, redirect
29 from pylons.controllers.util import abort, redirect
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
33 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
34 from rhodecode.model.db import User, UserLog
34 from rhodecode.model.db import User, UserLog
35 from rhodecode.model.forms import UserForm
35 from rhodecode.model.forms import UserForm
36 from rhodecode.model.user_model import UserModel, DefaultUserException
36 from rhodecode.model.user_model import UserModel, DefaultUserException
37 import formencode
37 import formencode
38 import logging
38 import logging
39 import traceback
39 import traceback
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43 class UsersController(BaseController):
43 class UsersController(BaseController):
44 """REST Controller styled on the Atom Publishing Protocol"""
44 """REST Controller styled on the Atom Publishing Protocol"""
45 # To properly map this controller, ensure your config/routing.py
45 # To properly map this controller, ensure your config/routing.py
46 # file has a resource setup:
46 # file has a resource setup:
47 # map.resource('user', 'users')
47 # map.resource('user', 'users')
48
48
49 @LoginRequired()
49 @LoginRequired()
50 @HasPermissionAllDecorator('hg.admin')
50 @HasPermissionAllDecorator('hg.admin')
51 def __before__(self):
51 def __before__(self):
52 c.admin_user = session.get('admin_user')
52 c.admin_user = session.get('admin_user')
53 c.admin_username = session.get('admin_username')
53 c.admin_username = session.get('admin_username')
54 super(UsersController, self).__before__()
54 super(UsersController, self).__before__()
55
55
56
56
57 def index(self, format='html'):
57 def index(self, format='html'):
58 """GET /users: All items in the collection"""
58 """GET /users: All items in the collection"""
59 # url('users')
59 # url('users')
60
60
61 c.users_list = self.sa.query(User).all()
61 c.users_list = self.sa.query(User).all()
62 return render('admin/users/users.html')
62 return render('admin/users/users.html')
63
63
64 def create(self):
64 def create(self):
65 """POST /users: Create a new item"""
65 """POST /users: Create a new item"""
66 # url('users')
66 # url('users')
67
67
68 user_model = UserModel()
68 user_model = UserModel()
69 login_form = UserForm()()
69 login_form = UserForm()()
70 try:
70 try:
71 form_result = login_form.to_python(dict(request.POST))
71 form_result = login_form.to_python(dict(request.POST))
72 user_model.create(form_result)
72 user_model.create(form_result)
73 h.flash(_('created user %s') % form_result['username'],
73 h.flash(_('created user %s') % form_result['username'],
74 category='success')
74 category='success')
75 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
75 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
76 except formencode.Invalid as errors:
76 except formencode.Invalid, errors:
77 return htmlfill.render(
77 return htmlfill.render(
78 render('admin/users/user_add.html'),
78 render('admin/users/user_add.html'),
79 defaults=errors.value,
79 defaults=errors.value,
80 errors=errors.error_dict or {},
80 errors=errors.error_dict or {},
81 prefix_error=False,
81 prefix_error=False,
82 encoding="UTF-8")
82 encoding="UTF-8")
83 except Exception:
83 except Exception:
84 log.error(traceback.format_exc())
84 log.error(traceback.format_exc())
85 h.flash(_('error occured during creation of user %s') \
85 h.flash(_('error occured during creation of user %s') \
86 % request.POST.get('username'), category='error')
86 % request.POST.get('username'), category='error')
87 return redirect(url('users'))
87 return redirect(url('users'))
88
88
89 def new(self, format='html'):
89 def new(self, format='html'):
90 """GET /users/new: Form to create a new item"""
90 """GET /users/new: Form to create a new item"""
91 # url('new_user')
91 # url('new_user')
92 return render('admin/users/user_add.html')
92 return render('admin/users/user_add.html')
93
93
94 def update(self, id):
94 def update(self, id):
95 """PUT /users/id: Update an existing item"""
95 """PUT /users/id: Update an existing item"""
96 # Forms posted to this method should contain a hidden field:
96 # Forms posted to this method should contain a hidden field:
97 # <input type="hidden" name="_method" value="PUT" />
97 # <input type="hidden" name="_method" value="PUT" />
98 # Or using helpers:
98 # Or using helpers:
99 # h.form(url('user', id=ID),
99 # h.form(url('user', id=ID),
100 # method='put')
100 # method='put')
101 # url('user', id=ID)
101 # url('user', id=ID)
102 user_model = UserModel()
102 user_model = UserModel()
103 c.user = user_model.get_user(id)
103 c.user = user_model.get_user(id)
104
104
105 _form = UserForm(edit=True, old_data={'user_id':id,
105 _form = UserForm(edit=True, old_data={'user_id':id,
106 'email':c.user.email})()
106 'email':c.user.email})()
107 form_result = {}
107 form_result = {}
108 try:
108 try:
109 form_result = _form.to_python(dict(request.POST))
109 form_result = _form.to_python(dict(request.POST))
110 user_model.update(id, form_result)
110 user_model.update(id, form_result)
111 h.flash(_('User updated succesfully'), category='success')
111 h.flash(_('User updated succesfully'), category='success')
112
112
113 except formencode.Invalid as errors:
113 except formencode.Invalid, errors:
114 return htmlfill.render(
114 return htmlfill.render(
115 render('admin/users/user_edit.html'),
115 render('admin/users/user_edit.html'),
116 defaults=errors.value,
116 defaults=errors.value,
117 errors=errors.error_dict or {},
117 errors=errors.error_dict or {},
118 prefix_error=False,
118 prefix_error=False,
119 encoding="UTF-8")
119 encoding="UTF-8")
120 except Exception:
120 except Exception:
121 log.error(traceback.format_exc())
121 log.error(traceback.format_exc())
122 h.flash(_('error occured during update of user %s') \
122 h.flash(_('error occured during update of user %s') \
123 % form_result.get('username'), category='error')
123 % form_result.get('username'), category='error')
124
124
125 return redirect(url('users'))
125 return redirect(url('users'))
126
126
127 def delete(self, id):
127 def delete(self, id):
128 """DELETE /users/id: Delete an existing item"""
128 """DELETE /users/id: Delete an existing item"""
129 # Forms posted to this method should contain a hidden field:
129 # Forms posted to this method should contain a hidden field:
130 # <input type="hidden" name="_method" value="DELETE" />
130 # <input type="hidden" name="_method" value="DELETE" />
131 # Or using helpers:
131 # Or using helpers:
132 # h.form(url('user', id=ID),
132 # h.form(url('user', id=ID),
133 # method='delete')
133 # method='delete')
134 # url('user', id=ID)
134 # url('user', id=ID)
135 user_model = UserModel()
135 user_model = UserModel()
136 try:
136 try:
137 user_model.delete(id)
137 user_model.delete(id)
138 h.flash(_('sucessfully deleted user'), category='success')
138 h.flash(_('sucessfully deleted user'), category='success')
139 except DefaultUserException as e:
139 except DefaultUserException, e:
140 h.flash(str(e), category='warning')
140 h.flash(str(e), category='warning')
141 except Exception:
141 except Exception:
142 h.flash(_('An error occured during deletion of user'),
142 h.flash(_('An error occured during deletion of user'),
143 category='error')
143 category='error')
144 return redirect(url('users'))
144 return redirect(url('users'))
145
145
146 def show(self, id, format='html'):
146 def show(self, id, format='html'):
147 """GET /users/id: Show a specific item"""
147 """GET /users/id: Show a specific item"""
148 # url('user', id=ID)
148 # url('user', id=ID)
149
149
150
150
151 def edit(self, id, format='html'):
151 def edit(self, id, format='html'):
152 """GET /users/id/edit: Form to edit an existing item"""
152 """GET /users/id/edit: Form to edit an existing item"""
153 # url('edit_user', id=ID)
153 # url('edit_user', id=ID)
154 c.user = self.sa.query(User).get(id)
154 c.user = self.sa.query(User).get(id)
155 if not c.user:
155 if not c.user:
156 return redirect(url('users'))
156 return redirect(url('users'))
157 if c.user.username == 'default':
157 if c.user.username == 'default':
158 h.flash(_("You can't edit this user since it's"
158 h.flash(_("You can't edit this user since it's"
159 " crucial for entire application"), category='warning')
159 " crucial for entire application"), category='warning')
160 return redirect(url('users'))
160 return redirect(url('users'))
161
161
162 defaults = c.user.__dict__
162 defaults = c.user.__dict__
163 return htmlfill.render(
163 return htmlfill.render(
164 render('admin/users/user_edit.html'),
164 render('admin/users/user_edit.html'),
165 defaults=defaults,
165 defaults=defaults,
166 encoding="UTF-8",
166 encoding="UTF-8",
167 force_defaults=False
167 force_defaults=False
168 )
168 )
@@ -1,144 +1,144 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # login controller for pylons
3 # login 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 """
21 """
22 Created on April 22, 2010
22 Created on April 22, 2010
23 login controller for pylons
23 login controller for pylons
24 @author: marcink
24 @author: marcink
25 """
25 """
26 from formencode import htmlfill
26 from formencode import htmlfill
27 from pylons import request, response, session, tmpl_context as c, url
27 from pylons import request, response, session, tmpl_context as c, url
28 from pylons.controllers.util import abort, redirect
28 from pylons.controllers.util import abort, redirect
29 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
29 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
30 from rhodecode.lib.base import BaseController, render
30 from rhodecode.lib.base import BaseController, render
31 import rhodecode.lib.helpers as h
31 import rhodecode.lib.helpers as h
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
33 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
34 from rhodecode.model.user_model import UserModel
34 from rhodecode.model.user_model import UserModel
35 import formencode
35 import formencode
36 import logging
36 import logging
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40 class LoginController(BaseController):
40 class LoginController(BaseController):
41
41
42 def __before__(self):
42 def __before__(self):
43 super(LoginController, self).__before__()
43 super(LoginController, self).__before__()
44
44
45 def index(self):
45 def index(self):
46 #redirect if already logged in
46 #redirect if already logged in
47 c.came_from = request.GET.get('came_from', None)
47 c.came_from = request.GET.get('came_from', None)
48
48
49 if c.rhodecode_user.is_authenticated:
49 if c.rhodecode_user.is_authenticated:
50 return redirect(url('hg_home'))
50 return redirect(url('hg_home'))
51
51
52 if request.POST:
52 if request.POST:
53 #import Login Form validator class
53 #import Login Form validator class
54 login_form = LoginForm()
54 login_form = LoginForm()
55 try:
55 try:
56 c.form_result = login_form.to_python(dict(request.POST))
56 c.form_result = login_form.to_python(dict(request.POST))
57 username = c.form_result['username']
57 username = c.form_result['username']
58 user = UserModel().get_user_by_name(username)
58 user = UserModel().get_user_by_name(username)
59 auth_user = AuthUser()
59 auth_user = AuthUser()
60 auth_user.username = user.username
60 auth_user.username = user.username
61 auth_user.is_authenticated = True
61 auth_user.is_authenticated = True
62 auth_user.is_admin = user.admin
62 auth_user.is_admin = user.admin
63 auth_user.user_id = user.user_id
63 auth_user.user_id = user.user_id
64 auth_user.name = user.name
64 auth_user.name = user.name
65 auth_user.lastname = user.lastname
65 auth_user.lastname = user.lastname
66 session['rhodecode_user'] = auth_user
66 session['rhodecode_user'] = auth_user
67 session.save()
67 session.save()
68 log.info('user %s is now authenticated', username)
68 log.info('user %s is now authenticated', username)
69
69
70 user.update_lastlogin()
70 user.update_lastlogin()
71
71
72 if c.came_from:
72 if c.came_from:
73 return redirect(c.came_from)
73 return redirect(c.came_from)
74 else:
74 else:
75 return redirect(url('hg_home'))
75 return redirect(url('hg_home'))
76
76
77 except formencode.Invalid as errors:
77 except formencode.Invalid, errors:
78 return htmlfill.render(
78 return htmlfill.render(
79 render('/login.html'),
79 render('/login.html'),
80 defaults=errors.value,
80 defaults=errors.value,
81 errors=errors.error_dict or {},
81 errors=errors.error_dict or {},
82 prefix_error=False,
82 prefix_error=False,
83 encoding="UTF-8")
83 encoding="UTF-8")
84
84
85 return render('/login.html')
85 return render('/login.html')
86
86
87 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
87 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
88 'hg.register.manual_activate')
88 'hg.register.manual_activate')
89 def register(self):
89 def register(self):
90 user_model = UserModel()
90 user_model = UserModel()
91 c.auto_active = False
91 c.auto_active = False
92 for perm in user_model.get_default().user_perms:
92 for perm in user_model.get_default().user_perms:
93 if perm.permission.permission_name == 'hg.register.auto_activate':
93 if perm.permission.permission_name == 'hg.register.auto_activate':
94 c.auto_active = True
94 c.auto_active = True
95 break
95 break
96
96
97 if request.POST:
97 if request.POST:
98
98
99 register_form = RegisterForm()()
99 register_form = RegisterForm()()
100 try:
100 try:
101 form_result = register_form.to_python(dict(request.POST))
101 form_result = register_form.to_python(dict(request.POST))
102 form_result['active'] = c.auto_active
102 form_result['active'] = c.auto_active
103 user_model.create_registration(form_result)
103 user_model.create_registration(form_result)
104 h.flash(_('You have successfully registered into rhodecode'),
104 h.flash(_('You have successfully registered into rhodecode'),
105 category='success')
105 category='success')
106 return redirect(url('login_home'))
106 return redirect(url('login_home'))
107
107
108 except formencode.Invalid as errors:
108 except formencode.Invalid, errors:
109 return htmlfill.render(
109 return htmlfill.render(
110 render('/register.html'),
110 render('/register.html'),
111 defaults=errors.value,
111 defaults=errors.value,
112 errors=errors.error_dict or {},
112 errors=errors.error_dict or {},
113 prefix_error=False,
113 prefix_error=False,
114 encoding="UTF-8")
114 encoding="UTF-8")
115
115
116 return render('/register.html')
116 return render('/register.html')
117
117
118 def password_reset(self):
118 def password_reset(self):
119 user_model = UserModel()
119 user_model = UserModel()
120 if request.POST:
120 if request.POST:
121
121
122 password_reset_form = PasswordResetForm()()
122 password_reset_form = PasswordResetForm()()
123 try:
123 try:
124 form_result = password_reset_form.to_python(dict(request.POST))
124 form_result = password_reset_form.to_python(dict(request.POST))
125 user_model.reset_password(form_result)
125 user_model.reset_password(form_result)
126 h.flash(_('Your new password was sent'),
126 h.flash(_('Your new password was sent'),
127 category='success')
127 category='success')
128 return redirect(url('login_home'))
128 return redirect(url('login_home'))
129
129
130 except formencode.Invalid as errors:
130 except formencode.Invalid, errors:
131 return htmlfill.render(
131 return htmlfill.render(
132 render('/password_reset.html'),
132 render('/password_reset.html'),
133 defaults=errors.value,
133 defaults=errors.value,
134 errors=errors.error_dict or {},
134 errors=errors.error_dict or {},
135 prefix_error=False,
135 prefix_error=False,
136 encoding="UTF-8")
136 encoding="UTF-8")
137
137
138 return render('/password_reset.html')
138 return render('/password_reset.html')
139
139
140 def logout(self):
140 def logout(self):
141 session['rhodecode_user'] = AuthUser()
141 session['rhodecode_user'] = AuthUser()
142 session.save()
142 session.save()
143 log.info('Logging out and setting user as Empty')
143 log.info('Logging out and setting user as Empty')
144 redirect(url('hg_home'))
144 redirect(url('hg_home'))
@@ -1,175 +1,175 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # settings controller for pylons
3 # settings controller for pylons
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on June 30, 2010
21 Created on June 30, 2010
22 settings controller for pylons
22 settings controller for pylons
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from formencode import htmlfill
25 from formencode import htmlfill
26 from pylons import tmpl_context as c, request, url
26 from pylons import tmpl_context as c, request, url
27 from pylons.controllers.util import redirect
27 from pylons.controllers.util import redirect
28 from pylons.i18n.translation import _
28 from pylons.i18n.translation import _
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 from rhodecode.lib.base import BaseController, render
30 from rhodecode.lib.base import BaseController, render
31 from rhodecode.lib.utils import invalidate_cache, action_logger
31 from rhodecode.lib.utils import invalidate_cache, action_logger
32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
33 from rhodecode.model.repo_model import RepoModel
33 from rhodecode.model.repo_model import RepoModel
34 import formencode
34 import formencode
35 import logging
35 import logging
36 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
37 import traceback
37 import traceback
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 class SettingsController(BaseController):
41 class SettingsController(BaseController):
42
42
43 @LoginRequired()
43 @LoginRequired()
44 @HasRepoPermissionAllDecorator('repository.admin')
44 @HasRepoPermissionAllDecorator('repository.admin')
45 def __before__(self):
45 def __before__(self):
46 super(SettingsController, self).__before__()
46 super(SettingsController, self).__before__()
47
47
48 def index(self, repo_name):
48 def index(self, repo_name):
49 repo_model = RepoModel()
49 repo_model = RepoModel()
50 c.repo_info = repo = repo_model.get(repo_name)
50 c.repo_info = repo = repo_model.get(repo_name)
51 if not repo:
51 if not repo:
52 h.flash(_('%s repository is not mapped to db perhaps'
52 h.flash(_('%s repository is not mapped to db perhaps'
53 ' it was created or renamed from the filesystem'
53 ' it was created or renamed from the filesystem'
54 ' please run the application again'
54 ' please run the application again'
55 ' in order to rescan repositories') % repo_name,
55 ' in order to rescan repositories') % repo_name,
56 category='error')
56 category='error')
57
57
58 return redirect(url('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 successfully' % repo_name),
82 h.flash(_('Repository %s updated successfully' % repo_name),
83 category='success')
83 category='success')
84 changed_name = form_result['repo_name']
84 changed_name = form_result['repo_name']
85 except formencode.Invalid as errors:
85 except formencode.Invalid, 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 action_logger(self.rhodecode_user, 'user_deleted_repo',
124 action_logger(self.rhodecode_user, 'user_deleted_repo',
125 repo_name, '', self.sa)
125 repo_name, '', self.sa)
126 repo_model.delete(repo)
126 repo_model.delete(repo)
127 invalidate_cache('cached_repo_list')
127 invalidate_cache('cached_repo_list')
128 h.flash(_('deleted repository %s') % repo_name, category='success')
128 h.flash(_('deleted repository %s') % repo_name, category='success')
129 except Exception:
129 except Exception:
130 h.flash(_('An error occurred during deletion of %s') % repo_name,
130 h.flash(_('An error occurred during deletion of %s') % repo_name,
131 category='error')
131 category='error')
132
132
133 return redirect(url('hg_home'))
133 return redirect(url('hg_home'))
134
134
135 def fork(self, repo_name):
135 def fork(self, repo_name):
136 repo_model = RepoModel()
136 repo_model = RepoModel()
137 c.repo_info = repo = repo_model.get(repo_name)
137 c.repo_info = repo = repo_model.get(repo_name)
138 if not repo:
138 if not repo:
139 h.flash(_('%s repository is not mapped to db perhaps'
139 h.flash(_('%s repository is not mapped to db perhaps'
140 ' it was created or renamed from the filesystem'
140 ' it was created or renamed from the filesystem'
141 ' please run the application again'
141 ' please run the application again'
142 ' in order to rescan repositories') % repo_name,
142 ' in order to rescan repositories') % repo_name,
143 category='error')
143 category='error')
144
144
145 return redirect(url('hg_home'))
145 return redirect(url('hg_home'))
146
146
147 return render('settings/repo_fork.html')
147 return render('settings/repo_fork.html')
148
148
149
149
150
150
151 def fork_create(self, repo_name):
151 def fork_create(self, repo_name):
152 repo_model = RepoModel()
152 repo_model = RepoModel()
153 c.repo_info = repo_model.get(repo_name)
153 c.repo_info = repo_model.get(repo_name)
154 _form = RepoForkForm()()
154 _form = RepoForkForm()()
155 form_result = {}
155 form_result = {}
156 try:
156 try:
157 form_result = _form.to_python(dict(request.POST))
157 form_result = _form.to_python(dict(request.POST))
158 form_result.update({'repo_name':repo_name})
158 form_result.update({'repo_name':repo_name})
159 repo_model.create_fork(form_result, c.rhodecode_user)
159 repo_model.create_fork(form_result, c.rhodecode_user)
160 h.flash(_('fork %s repository as %s task added') \
160 h.flash(_('fork %s repository as %s task added') \
161 % (repo_name, form_result['fork_name']),
161 % (repo_name, form_result['fork_name']),
162 category='success')
162 category='success')
163 action_logger(self.rhodecode_user, 'user_forked_repo',
163 action_logger(self.rhodecode_user, 'user_forked_repo',
164 repo_name, '', self.sa)
164 repo_name, '', self.sa)
165 except formencode.Invalid as errors:
165 except formencode.Invalid, errors:
166 c.new_repo = errors.value['fork_name']
166 c.new_repo = errors.value['fork_name']
167 r = render('settings/repo_fork.html')
167 r = render('settings/repo_fork.html')
168
168
169 return htmlfill.render(
169 return htmlfill.render(
170 r,
170 r,
171 defaults=errors.value,
171 defaults=errors.value,
172 errors=errors.error_dict or {},
172 errors=errors.error_dict or {},
173 prefix_error=False,
173 prefix_error=False,
174 encoding="UTF-8")
174 encoding="UTF-8")
175 return redirect(url('hg_home'))
175 return redirect(url('hg_home'))
@@ -1,486 +1,486 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # authentication and permission libraries
3 # authentication and permission libraries
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on April 4, 2010
21 Created on April 4, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from beaker.cache import cache_region
25 from beaker.cache import cache_region
26 from pylons import config, session, url, request
26 from pylons import config, session, url, request
27 from pylons.controllers.util import abort, redirect
27 from pylons.controllers.util import abort, redirect
28 from rhodecode.lib.utils import get_repo_slug
28 from rhodecode.lib.utils import get_repo_slug
29 from rhodecode.model import meta
29 from rhodecode.model import meta
30 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
30 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
31 UserToPerm
31 UserToPerm
32 from sqlalchemy.exc import OperationalError
32 from sqlalchemy.exc import OperationalError
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
34 import bcrypt
34 import bcrypt
35 from decorator import decorator
35 from decorator import decorator
36 import logging
36 import logging
37 import random
37 import random
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41 class PasswordGenerator(object):
41 class PasswordGenerator(object):
42 """This is a simple class for generating password from
42 """This is a simple class for generating password from
43 different sets of characters
43 different sets of characters
44 usage:
44 usage:
45 passwd_gen = PasswordGenerator()
45 passwd_gen = PasswordGenerator()
46 #print 8-letter password containing only big and small letters of alphabet
46 #print 8-letter password containing only big and small letters of alphabet
47 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
47 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
48 """
48 """
49 ALPHABETS_NUM = r'''1234567890'''#[0]
49 ALPHABETS_NUM = r'''1234567890'''#[0]
50 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
50 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
51 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
51 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
52 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
52 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
53 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
53 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
54 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
54 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
55 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
55 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
56 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
56 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
57 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
57 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
58
58
59 def __init__(self, passwd=''):
59 def __init__(self, passwd=''):
60 self.passwd = passwd
60 self.passwd = passwd
61
61
62 def gen_password(self, len, type):
62 def gen_password(self, len, type):
63 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
63 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
64 return self.passwd
64 return self.passwd
65
65
66
66
67 def get_crypt_password(password):
67 def get_crypt_password(password):
68 """Cryptographic function used for password hashing based on sha1
68 """Cryptographic function used for password hashing based on sha1
69 @param password: password to hash
69 @param password: password to hash
70 """
70 """
71 return bcrypt.hashpw(password, bcrypt.gensalt(10))
71 return bcrypt.hashpw(password, bcrypt.gensalt(10))
72
72
73 def check_password(password, hashed):
73 def check_password(password, hashed):
74 return bcrypt.hashpw(password, hashed) == hashed
74 return bcrypt.hashpw(password, hashed) == hashed
75
75
76 @cache_region('super_short_term', 'cached_user')
76 @cache_region('super_short_term', 'cached_user')
77 def get_user_cached(username):
77 def get_user_cached(username):
78 sa = meta.Session
78 sa = meta.Session
79 try:
79 try:
80 user = sa.query(User).filter(User.username == username).one()
80 user = sa.query(User).filter(User.username == username).one()
81 finally:
81 finally:
82 meta.Session.remove()
82 meta.Session.remove()
83 return user
83 return user
84
84
85 def authfunc(environ, username, password):
85 def authfunc(environ, username, password):
86 try:
86 try:
87 user = get_user_cached(username)
87 user = get_user_cached(username)
88 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
88 except (NoResultFound, MultipleResultsFound, OperationalError), e:
89 log.error(e)
89 log.error(e)
90 user = None
90 user = None
91
91
92 if user:
92 if user:
93 if user.active:
93 if user.active:
94 if user.username == username and check_password(password, user.password):
94 if user.username == username and check_password(password, user.password):
95 log.info('user %s authenticated correctly', username)
95 log.info('user %s authenticated correctly', username)
96 return True
96 return True
97 else:
97 else:
98 log.error('user %s is disabled', username)
98 log.error('user %s is disabled', username)
99
99
100 return False
100 return False
101
101
102 class AuthUser(object):
102 class AuthUser(object):
103 """
103 """
104 A simple object that handles a mercurial username for authentication
104 A simple object that handles a mercurial username for authentication
105 """
105 """
106 def __init__(self):
106 def __init__(self):
107 self.username = 'None'
107 self.username = 'None'
108 self.name = ''
108 self.name = ''
109 self.lastname = ''
109 self.lastname = ''
110 self.email = ''
110 self.email = ''
111 self.user_id = None
111 self.user_id = None
112 self.is_authenticated = False
112 self.is_authenticated = False
113 self.is_admin = False
113 self.is_admin = False
114 self.permissions = {}
114 self.permissions = {}
115
115
116
116
117 def set_available_permissions(config):
117 def set_available_permissions(config):
118 """
118 """
119 This function will propagate pylons globals with all available defined
119 This function will propagate pylons globals with all available defined
120 permission given in db. We don't wannt to check each time from db for new
120 permission given in db. We don't wannt to check each time from db for new
121 permissions since adding a new permission also requires application restart
121 permissions since adding a new permission also requires application restart
122 ie. to decorate new views with the newly created permission
122 ie. to decorate new views with the newly created permission
123 @param config:
123 @param config:
124 """
124 """
125 log.info('getting information about all available permissions')
125 log.info('getting information about all available permissions')
126 try:
126 try:
127 sa = meta.Session
127 sa = meta.Session
128 all_perms = sa.query(Permission).all()
128 all_perms = sa.query(Permission).all()
129 finally:
129 finally:
130 meta.Session.remove()
130 meta.Session.remove()
131
131
132 config['available_permissions'] = [x.permission_name for x in all_perms]
132 config['available_permissions'] = [x.permission_name for x in all_perms]
133
133
134 def set_base_path(config):
134 def set_base_path(config):
135 config['base_path'] = config['pylons.app_globals'].base_path
135 config['base_path'] = config['pylons.app_globals'].base_path
136
136
137 def fill_data(user):
137 def fill_data(user):
138 """
138 """
139 Fills user data with those from database and log out user if not present
139 Fills user data with those from database and log out user if not present
140 in database
140 in database
141 @param user:
141 @param user:
142 """
142 """
143 sa = meta.Session
143 sa = meta.Session
144 dbuser = sa.query(User).get(user.user_id)
144 dbuser = sa.query(User).get(user.user_id)
145 if dbuser:
145 if dbuser:
146 user.username = dbuser.username
146 user.username = dbuser.username
147 user.is_admin = dbuser.admin
147 user.is_admin = dbuser.admin
148 user.name = dbuser.name
148 user.name = dbuser.name
149 user.lastname = dbuser.lastname
149 user.lastname = dbuser.lastname
150 user.email = dbuser.email
150 user.email = dbuser.email
151 else:
151 else:
152 user.is_authenticated = False
152 user.is_authenticated = False
153 meta.Session.remove()
153 meta.Session.remove()
154 return user
154 return user
155
155
156 def fill_perms(user):
156 def fill_perms(user):
157 """
157 """
158 Fills user permission attribute with permissions taken from database
158 Fills user permission attribute with permissions taken from database
159 @param user:
159 @param user:
160 """
160 """
161
161
162 sa = meta.Session
162 sa = meta.Session
163 user.permissions['repositories'] = {}
163 user.permissions['repositories'] = {}
164 user.permissions['global'] = set()
164 user.permissions['global'] = set()
165
165
166 #===========================================================================
166 #===========================================================================
167 # fetch default permissions
167 # fetch default permissions
168 #===========================================================================
168 #===========================================================================
169 default_perms = sa.query(RepoToPerm, Repository, Permission)\
169 default_perms = sa.query(RepoToPerm, Repository, Permission)\
170 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
170 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
171 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
171 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
172 .filter(RepoToPerm.user == sa.query(User).filter(User.username ==
172 .filter(RepoToPerm.user == sa.query(User).filter(User.username ==
173 'default').scalar()).all()
173 'default').scalar()).all()
174
174
175 if user.is_admin:
175 if user.is_admin:
176 #=======================================================================
176 #=======================================================================
177 # #admin have all default rights set to admin
177 # #admin have all default rights set to admin
178 #=======================================================================
178 #=======================================================================
179 user.permissions['global'].add('hg.admin')
179 user.permissions['global'].add('hg.admin')
180
180
181 for perm in default_perms:
181 for perm in default_perms:
182 p = 'repository.admin'
182 p = 'repository.admin'
183 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
183 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
184
184
185 else:
185 else:
186 #=======================================================================
186 #=======================================================================
187 # set default permissions
187 # set default permissions
188 #=======================================================================
188 #=======================================================================
189
189
190 #default global
190 #default global
191 default_global_perms = sa.query(UserToPerm)\
191 default_global_perms = sa.query(UserToPerm)\
192 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
192 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
193 'default').one())
193 'default').one())
194
194
195 for perm in default_global_perms:
195 for perm in default_global_perms:
196 user.permissions['global'].add(perm.permission.permission_name)
196 user.permissions['global'].add(perm.permission.permission_name)
197
197
198 #default repositories
198 #default repositories
199 for perm in default_perms:
199 for perm in default_perms:
200 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
200 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
201 #disable defaults for private repos,
201 #disable defaults for private repos,
202 p = 'repository.none'
202 p = 'repository.none'
203 elif perm.Repository.user_id == user.user_id:
203 elif perm.Repository.user_id == user.user_id:
204 #set admin if owner
204 #set admin if owner
205 p = 'repository.admin'
205 p = 'repository.admin'
206 else:
206 else:
207 p = perm.Permission.permission_name
207 p = perm.Permission.permission_name
208
208
209 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
209 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
210
210
211 #=======================================================================
211 #=======================================================================
212 # #overwrite default with user permissions if any
212 # #overwrite default with user permissions if any
213 #=======================================================================
213 #=======================================================================
214 user_perms = sa.query(RepoToPerm, Permission, Repository)\
214 user_perms = sa.query(RepoToPerm, Permission, Repository)\
215 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
215 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
216 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
216 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
217 .filter(RepoToPerm.user_id == user.user_id).all()
217 .filter(RepoToPerm.user_id == user.user_id).all()
218
218
219 for perm in user_perms:
219 for perm in user_perms:
220 if perm.Repository.user_id == user.user_id:#set admin if owner
220 if perm.Repository.user_id == user.user_id:#set admin if owner
221 p = 'repository.admin'
221 p = 'repository.admin'
222 else:
222 else:
223 p = perm.Permission.permission_name
223 p = perm.Permission.permission_name
224 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
224 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
225 meta.Session.remove()
225 meta.Session.remove()
226 return user
226 return user
227
227
228 def get_user(session):
228 def get_user(session):
229 """
229 """
230 Gets user from session, and wraps permissions into user
230 Gets user from session, and wraps permissions into user
231 @param session:
231 @param session:
232 """
232 """
233 user = session.get('rhodecode_user', AuthUser())
233 user = session.get('rhodecode_user', AuthUser())
234 if user.is_authenticated:
234 if user.is_authenticated:
235 user = fill_data(user)
235 user = fill_data(user)
236 user = fill_perms(user)
236 user = fill_perms(user)
237 session['rhodecode_user'] = user
237 session['rhodecode_user'] = user
238 session.save()
238 session.save()
239 return user
239 return user
240
240
241 #===============================================================================
241 #===============================================================================
242 # CHECK DECORATORS
242 # CHECK DECORATORS
243 #===============================================================================
243 #===============================================================================
244 class LoginRequired(object):
244 class LoginRequired(object):
245 """Must be logged in to execute this function else redirect to login page"""
245 """Must be logged in to execute this function else redirect to login page"""
246
246
247 def __call__(self, func):
247 def __call__(self, func):
248 return decorator(self.__wrapper, func)
248 return decorator(self.__wrapper, func)
249
249
250 def __wrapper(self, func, *fargs, **fkwargs):
250 def __wrapper(self, func, *fargs, **fkwargs):
251 user = session.get('rhodecode_user', AuthUser())
251 user = session.get('rhodecode_user', AuthUser())
252 log.debug('Checking login required for user:%s', user.username)
252 log.debug('Checking login required for user:%s', user.username)
253 if user.is_authenticated:
253 if user.is_authenticated:
254 log.debug('user %s is authenticated', user.username)
254 log.debug('user %s is authenticated', user.username)
255 return func(*fargs, **fkwargs)
255 return func(*fargs, **fkwargs)
256 else:
256 else:
257 log.warn('user %s not authenticated', user.username)
257 log.warn('user %s not authenticated', user.username)
258
258
259 p = ''
259 p = ''
260 if request.environ.get('SCRIPT_NAME') != '/':
260 if request.environ.get('SCRIPT_NAME') != '/':
261 p += request.environ.get('SCRIPT_NAME')
261 p += request.environ.get('SCRIPT_NAME')
262
262
263 p += request.environ.get('PATH_INFO')
263 p += request.environ.get('PATH_INFO')
264 if request.environ.get('QUERY_STRING'):
264 if request.environ.get('QUERY_STRING'):
265 p += '?' + request.environ.get('QUERY_STRING')
265 p += '?' + request.environ.get('QUERY_STRING')
266
266
267 log.debug('redirecting to login page with %s', p)
267 log.debug('redirecting to login page with %s', p)
268 return redirect(url('login_home', came_from=p))
268 return redirect(url('login_home', came_from=p))
269
269
270 class PermsDecorator(object):
270 class PermsDecorator(object):
271 """Base class for decorators"""
271 """Base class for decorators"""
272
272
273 def __init__(self, *required_perms):
273 def __init__(self, *required_perms):
274 available_perms = config['available_permissions']
274 available_perms = config['available_permissions']
275 for perm in required_perms:
275 for perm in required_perms:
276 if perm not in available_perms:
276 if perm not in available_perms:
277 raise Exception("'%s' permission is not defined" % perm)
277 raise Exception("'%s' permission is not defined" % perm)
278 self.required_perms = set(required_perms)
278 self.required_perms = set(required_perms)
279 self.user_perms = None
279 self.user_perms = None
280
280
281 def __call__(self, func):
281 def __call__(self, func):
282 return decorator(self.__wrapper, func)
282 return decorator(self.__wrapper, func)
283
283
284
284
285 def __wrapper(self, func, *fargs, **fkwargs):
285 def __wrapper(self, func, *fargs, **fkwargs):
286 # _wrapper.__name__ = func.__name__
286 # _wrapper.__name__ = func.__name__
287 # _wrapper.__dict__.update(func.__dict__)
287 # _wrapper.__dict__.update(func.__dict__)
288 # _wrapper.__doc__ = func.__doc__
288 # _wrapper.__doc__ = func.__doc__
289
289
290 self.user_perms = session.get('rhodecode_user', AuthUser()).permissions
290 self.user_perms = session.get('rhodecode_user', AuthUser()).permissions
291 log.debug('checking %s permissions %s for %s',
291 log.debug('checking %s permissions %s for %s',
292 self.__class__.__name__, self.required_perms, func.__name__)
292 self.__class__.__name__, self.required_perms, func.__name__)
293
293
294 if self.check_permissions():
294 if self.check_permissions():
295 log.debug('Permission granted for %s', func.__name__)
295 log.debug('Permission granted for %s', func.__name__)
296
296
297 return func(*fargs, **fkwargs)
297 return func(*fargs, **fkwargs)
298
298
299 else:
299 else:
300 log.warning('Permission denied for %s', func.__name__)
300 log.warning('Permission denied for %s', func.__name__)
301 #redirect with forbidden ret code
301 #redirect with forbidden ret code
302 return abort(403)
302 return abort(403)
303
303
304
304
305
305
306 def check_permissions(self):
306 def check_permissions(self):
307 """Dummy function for overriding"""
307 """Dummy function for overriding"""
308 raise Exception('You have to write this function in child class')
308 raise Exception('You have to write this function in child class')
309
309
310 class HasPermissionAllDecorator(PermsDecorator):
310 class HasPermissionAllDecorator(PermsDecorator):
311 """Checks for access permission for all given predicates. All of them
311 """Checks for access permission for all given predicates. All of them
312 have to be meet in order to fulfill the request
312 have to be meet in order to fulfill the request
313 """
313 """
314
314
315 def check_permissions(self):
315 def check_permissions(self):
316 if self.required_perms.issubset(self.user_perms.get('global')):
316 if self.required_perms.issubset(self.user_perms.get('global')):
317 return True
317 return True
318 return False
318 return False
319
319
320
320
321 class HasPermissionAnyDecorator(PermsDecorator):
321 class HasPermissionAnyDecorator(PermsDecorator):
322 """Checks for access permission for any of given predicates. In order to
322 """Checks for access permission for any of given predicates. In order to
323 fulfill the request any of predicates must be meet
323 fulfill the request any of predicates must be meet
324 """
324 """
325
325
326 def check_permissions(self):
326 def check_permissions(self):
327 if self.required_perms.intersection(self.user_perms.get('global')):
327 if self.required_perms.intersection(self.user_perms.get('global')):
328 return True
328 return True
329 return False
329 return False
330
330
331 class HasRepoPermissionAllDecorator(PermsDecorator):
331 class HasRepoPermissionAllDecorator(PermsDecorator):
332 """Checks for access permission for all given predicates for specific
332 """Checks for access permission for all given predicates for specific
333 repository. All of them have to be meet in order to fulfill the request
333 repository. All of them have to be meet in order to fulfill the request
334 """
334 """
335
335
336 def check_permissions(self):
336 def check_permissions(self):
337 repo_name = get_repo_slug(request)
337 repo_name = get_repo_slug(request)
338 try:
338 try:
339 user_perms = set([self.user_perms['repositories'][repo_name]])
339 user_perms = set([self.user_perms['repositories'][repo_name]])
340 except KeyError:
340 except KeyError:
341 return False
341 return False
342 if self.required_perms.issubset(user_perms):
342 if self.required_perms.issubset(user_perms):
343 return True
343 return True
344 return False
344 return False
345
345
346
346
347 class HasRepoPermissionAnyDecorator(PermsDecorator):
347 class HasRepoPermissionAnyDecorator(PermsDecorator):
348 """Checks for access permission for any of given predicates for specific
348 """Checks for access permission for any of given predicates for specific
349 repository. In order to fulfill the request any of predicates must be meet
349 repository. In order to fulfill the request any of predicates must be meet
350 """
350 """
351
351
352 def check_permissions(self):
352 def check_permissions(self):
353 repo_name = get_repo_slug(request)
353 repo_name = get_repo_slug(request)
354
354
355 try:
355 try:
356 user_perms = set([self.user_perms['repositories'][repo_name]])
356 user_perms = set([self.user_perms['repositories'][repo_name]])
357 except KeyError:
357 except KeyError:
358 return False
358 return False
359 if self.required_perms.intersection(user_perms):
359 if self.required_perms.intersection(user_perms):
360 return True
360 return True
361 return False
361 return False
362 #===============================================================================
362 #===============================================================================
363 # CHECK FUNCTIONS
363 # CHECK FUNCTIONS
364 #===============================================================================
364 #===============================================================================
365
365
366 class PermsFunction(object):
366 class PermsFunction(object):
367 """Base function for other check functions"""
367 """Base function for other check functions"""
368
368
369 def __init__(self, *perms):
369 def __init__(self, *perms):
370 available_perms = config['available_permissions']
370 available_perms = config['available_permissions']
371
371
372 for perm in perms:
372 for perm in perms:
373 if perm not in available_perms:
373 if perm not in available_perms:
374 raise Exception("'%s' permission in not defined" % perm)
374 raise Exception("'%s' permission in not defined" % perm)
375 self.required_perms = set(perms)
375 self.required_perms = set(perms)
376 self.user_perms = None
376 self.user_perms = None
377 self.granted_for = ''
377 self.granted_for = ''
378 self.repo_name = None
378 self.repo_name = None
379
379
380 def __call__(self, check_Location=''):
380 def __call__(self, check_Location=''):
381 user = session.get('rhodecode_user', False)
381 user = session.get('rhodecode_user', False)
382 if not user:
382 if not user:
383 return False
383 return False
384 self.user_perms = user.permissions
384 self.user_perms = user.permissions
385 self.granted_for = user.username
385 self.granted_for = user.username
386 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
386 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
387
387
388 if self.check_permissions():
388 if self.check_permissions():
389 log.debug('Permission granted for %s @%s', self.granted_for,
389 log.debug('Permission granted for %s @%s', self.granted_for,
390 check_Location)
390 check_Location)
391 return True
391 return True
392
392
393 else:
393 else:
394 log.warning('Permission denied for %s @%s', self.granted_for,
394 log.warning('Permission denied for %s @%s', self.granted_for,
395 check_Location)
395 check_Location)
396 return False
396 return False
397
397
398 def check_permissions(self):
398 def check_permissions(self):
399 """Dummy function for overriding"""
399 """Dummy function for overriding"""
400 raise Exception('You have to write this function in child class')
400 raise Exception('You have to write this function in child class')
401
401
402 class HasPermissionAll(PermsFunction):
402 class HasPermissionAll(PermsFunction):
403 def check_permissions(self):
403 def check_permissions(self):
404 if self.required_perms.issubset(self.user_perms.get('global')):
404 if self.required_perms.issubset(self.user_perms.get('global')):
405 return True
405 return True
406 return False
406 return False
407
407
408 class HasPermissionAny(PermsFunction):
408 class HasPermissionAny(PermsFunction):
409 def check_permissions(self):
409 def check_permissions(self):
410 if self.required_perms.intersection(self.user_perms.get('global')):
410 if self.required_perms.intersection(self.user_perms.get('global')):
411 return True
411 return True
412 return False
412 return False
413
413
414 class HasRepoPermissionAll(PermsFunction):
414 class HasRepoPermissionAll(PermsFunction):
415
415
416 def __call__(self, repo_name=None, check_Location=''):
416 def __call__(self, repo_name=None, check_Location=''):
417 self.repo_name = repo_name
417 self.repo_name = repo_name
418 return super(HasRepoPermissionAll, self).__call__(check_Location)
418 return super(HasRepoPermissionAll, self).__call__(check_Location)
419
419
420 def check_permissions(self):
420 def check_permissions(self):
421 if not self.repo_name:
421 if not self.repo_name:
422 self.repo_name = get_repo_slug(request)
422 self.repo_name = get_repo_slug(request)
423
423
424 try:
424 try:
425 self.user_perms = set([self.user_perms['repositories']\
425 self.user_perms = set([self.user_perms['repositories']\
426 [self.repo_name]])
426 [self.repo_name]])
427 except KeyError:
427 except KeyError:
428 return False
428 return False
429 self.granted_for = self.repo_name
429 self.granted_for = self.repo_name
430 if self.required_perms.issubset(self.user_perms):
430 if self.required_perms.issubset(self.user_perms):
431 return True
431 return True
432 return False
432 return False
433
433
434 class HasRepoPermissionAny(PermsFunction):
434 class HasRepoPermissionAny(PermsFunction):
435
435
436 def __call__(self, repo_name=None, check_Location=''):
436 def __call__(self, repo_name=None, check_Location=''):
437 self.repo_name = repo_name
437 self.repo_name = repo_name
438 return super(HasRepoPermissionAny, self).__call__(check_Location)
438 return super(HasRepoPermissionAny, self).__call__(check_Location)
439
439
440 def check_permissions(self):
440 def check_permissions(self):
441 if not self.repo_name:
441 if not self.repo_name:
442 self.repo_name = get_repo_slug(request)
442 self.repo_name = get_repo_slug(request)
443
443
444 try:
444 try:
445 self.user_perms = set([self.user_perms['repositories']\
445 self.user_perms = set([self.user_perms['repositories']\
446 [self.repo_name]])
446 [self.repo_name]])
447 except KeyError:
447 except KeyError:
448 return False
448 return False
449 self.granted_for = self.repo_name
449 self.granted_for = self.repo_name
450 if self.required_perms.intersection(self.user_perms):
450 if self.required_perms.intersection(self.user_perms):
451 return True
451 return True
452 return False
452 return False
453
453
454 #===============================================================================
454 #===============================================================================
455 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
455 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
456 #===============================================================================
456 #===============================================================================
457
457
458 class HasPermissionAnyMiddleware(object):
458 class HasPermissionAnyMiddleware(object):
459 def __init__(self, *perms):
459 def __init__(self, *perms):
460 self.required_perms = set(perms)
460 self.required_perms = set(perms)
461
461
462 def __call__(self, user, repo_name):
462 def __call__(self, user, repo_name):
463 usr = AuthUser()
463 usr = AuthUser()
464 usr.user_id = user.user_id
464 usr.user_id = user.user_id
465 usr.username = user.username
465 usr.username = user.username
466 usr.is_admin = user.admin
466 usr.is_admin = user.admin
467
467
468 try:
468 try:
469 self.user_perms = set([fill_perms(usr)\
469 self.user_perms = set([fill_perms(usr)\
470 .permissions['repositories'][repo_name]])
470 .permissions['repositories'][repo_name]])
471 except:
471 except:
472 self.user_perms = set()
472 self.user_perms = set()
473 self.granted_for = ''
473 self.granted_for = ''
474 self.username = user.username
474 self.username = user.username
475 self.repo_name = repo_name
475 self.repo_name = repo_name
476 return self.check_permissions()
476 return self.check_permissions()
477
477
478 def check_permissions(self):
478 def check_permissions(self):
479 log.debug('checking mercurial protocol '
479 log.debug('checking mercurial protocol '
480 'permissions for user:%s repository:%s',
480 'permissions for user:%s repository:%s',
481 self.username, self.repo_name)
481 self.username, self.repo_name)
482 if self.required_perms.intersection(self.user_perms):
482 if self.required_perms.intersection(self.user_perms):
483 log.debug('permission granted')
483 log.debug('permission granted')
484 return True
484 return True
485 log.debug('permission denied')
485 log.debug('permission denied')
486 return False
486 return False
@@ -1,322 +1,327 b''
1 from celery.decorators import task
1 from celery.decorators import task
2
2
3 from operator import itemgetter
3 from operator import itemgetter
4 from pylons.i18n.translation import _
4 from pylons.i18n.translation import _
5 from rhodecode.lib.celerylib import run_task, locked_task
5 from rhodecode.lib.celerylib import run_task, locked_task
6 from rhodecode.lib.helpers import person
6 from rhodecode.lib.helpers import person
7 from rhodecode.lib.smtp_mailer import SmtpMailer
7 from rhodecode.lib.smtp_mailer import SmtpMailer
8 from rhodecode.lib.utils import OrderedDict
8 from rhodecode.lib.utils import OrderedDict
9 from time import mktime
9 from time import mktime
10 from vcs.backends.hg import MercurialRepository
10 from vcs.backends.hg import MercurialRepository
11 import json
12 import traceback
11 import traceback
13
12
14 try:
13 try:
14 import json
15 except ImportError:
16 #python 2.5 compatibility
17 import simplejson as json
18
19 try:
15 from celeryconfig import PYLONS_CONFIG as config
20 from celeryconfig import PYLONS_CONFIG as config
16 celery_on = True
21 celery_on = True
17 except ImportError:
22 except ImportError:
18 #if celeryconfig is not present let's just load our pylons
23 #if celeryconfig is not present let's just load our pylons
19 #config instead
24 #config instead
20 from pylons import config
25 from pylons import config
21 celery_on = False
26 celery_on = False
22
27
23
28
24 __all__ = ['whoosh_index', 'get_commits_stats',
29 __all__ = ['whoosh_index', 'get_commits_stats',
25 'reset_user_password', 'send_email']
30 'reset_user_password', 'send_email']
26
31
27 def get_session():
32 def get_session():
28 if celery_on:
33 if celery_on:
29 from sqlalchemy import engine_from_config
34 from sqlalchemy import engine_from_config
30 from sqlalchemy.orm import sessionmaker, scoped_session
35 from sqlalchemy.orm import sessionmaker, scoped_session
31 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
36 engine = engine_from_config(dict(config.items('app:main')), 'sqlalchemy.db1.')
32 sa = scoped_session(sessionmaker(bind=engine))
37 sa = scoped_session(sessionmaker(bind=engine))
33 else:
38 else:
34 #If we don't use celery reuse our current application Session
39 #If we don't use celery reuse our current application Session
35 from rhodecode.model.meta import Session
40 from rhodecode.model.meta import Session
36 sa = Session
41 sa = Session
37
42
38 return sa
43 return sa
39
44
40 def get_hg_settings():
45 def get_hg_settings():
41 from rhodecode.model.db import RhodeCodeSettings
46 from rhodecode.model.db import RhodeCodeSettings
42 sa = get_session()
47 sa = get_session()
43 ret = sa.query(RhodeCodeSettings).all()
48 ret = sa.query(RhodeCodeSettings).all()
44
49
45 if not ret:
50 if not ret:
46 raise Exception('Could not get application settings !')
51 raise Exception('Could not get application settings !')
47 settings = {}
52 settings = {}
48 for each in ret:
53 for each in ret:
49 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
54 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
50
55
51 return settings
56 return settings
52
57
53 def get_hg_ui_settings():
58 def get_hg_ui_settings():
54 from rhodecode.model.db import RhodeCodeUi
59 from rhodecode.model.db import RhodeCodeUi
55 sa = get_session()
60 sa = get_session()
56 ret = sa.query(RhodeCodeUi).all()
61 ret = sa.query(RhodeCodeUi).all()
57
62
58 if not ret:
63 if not ret:
59 raise Exception('Could not get application ui settings !')
64 raise Exception('Could not get application ui settings !')
60 settings = {}
65 settings = {}
61 for each in ret:
66 for each in ret:
62 k = each.ui_key
67 k = each.ui_key
63 v = each.ui_value
68 v = each.ui_value
64 if k == '/':
69 if k == '/':
65 k = 'root_path'
70 k = 'root_path'
66
71
67 if k.find('.') != -1:
72 if k.find('.') != -1:
68 k = k.replace('.', '_')
73 k = k.replace('.', '_')
69
74
70 if each.ui_section == 'hooks':
75 if each.ui_section == 'hooks':
71 v = each.ui_active
76 v = each.ui_active
72
77
73 settings[each.ui_section + '_' + k] = v
78 settings[each.ui_section + '_' + k] = v
74
79
75 return settings
80 return settings
76
81
77 @task
82 @task
78 @locked_task
83 @locked_task
79 def whoosh_index(repo_location, full_index):
84 def whoosh_index(repo_location, full_index):
80 log = whoosh_index.get_logger()
85 log = whoosh_index.get_logger()
81 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
86 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
82 WhooshIndexingDaemon(repo_location=repo_location).run(full_index=full_index)
87 WhooshIndexingDaemon(repo_location=repo_location).run(full_index=full_index)
83
88
84 @task
89 @task
85 @locked_task
90 @locked_task
86 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
91 def get_commits_stats(repo_name, ts_min_y, ts_max_y):
87 from rhodecode.model.db import Statistics, Repository
92 from rhodecode.model.db import Statistics, Repository
88 log = get_commits_stats.get_logger()
93 log = get_commits_stats.get_logger()
89 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
94 author_key_cleaner = lambda k: person(k).replace('"', "") #for js data compatibilty
90
95
91 commits_by_day_author_aggregate = {}
96 commits_by_day_author_aggregate = {}
92 commits_by_day_aggregate = {}
97 commits_by_day_aggregate = {}
93 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
98 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
94 repo = MercurialRepository(repos_path + repo_name)
99 repo = MercurialRepository(repos_path + repo_name)
95
100
96 skip_date_limit = True
101 skip_date_limit = True
97 parse_limit = 350 #limit for single task changeset parsing optimal for
102 parse_limit = 350 #limit for single task changeset parsing optimal for
98 last_rev = 0
103 last_rev = 0
99 last_cs = None
104 last_cs = None
100 timegetter = itemgetter('time')
105 timegetter = itemgetter('time')
101
106
102 sa = get_session()
107 sa = get_session()
103
108
104 dbrepo = sa.query(Repository)\
109 dbrepo = sa.query(Repository)\
105 .filter(Repository.repo_name == repo_name).scalar()
110 .filter(Repository.repo_name == repo_name).scalar()
106 cur_stats = sa.query(Statistics)\
111 cur_stats = sa.query(Statistics)\
107 .filter(Statistics.repository == dbrepo).scalar()
112 .filter(Statistics.repository == dbrepo).scalar()
108 if cur_stats:
113 if cur_stats:
109 last_rev = cur_stats.stat_on_revision
114 last_rev = cur_stats.stat_on_revision
110 if not repo.revisions:
115 if not repo.revisions:
111 return True
116 return True
112
117
113 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
118 if last_rev == repo.revisions[-1] and len(repo.revisions) > 1:
114 #pass silently without any work if we're not on first revision or current
119 #pass silently without any work if we're not on first revision or current
115 #state of parsing revision(from db marker) is the last revision
120 #state of parsing revision(from db marker) is the last revision
116 return True
121 return True
117
122
118 if cur_stats:
123 if cur_stats:
119 commits_by_day_aggregate = OrderedDict(
124 commits_by_day_aggregate = OrderedDict(
120 json.loads(
125 json.loads(
121 cur_stats.commit_activity_combined))
126 cur_stats.commit_activity_combined))
122 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
127 commits_by_day_author_aggregate = json.loads(cur_stats.commit_activity)
123
128
124 log.debug('starting parsing %s', parse_limit)
129 log.debug('starting parsing %s', parse_limit)
125 for cnt, rev in enumerate(repo.revisions[last_rev:]):
130 for cnt, rev in enumerate(repo.revisions[last_rev:]):
126 last_cs = cs = repo.get_changeset(rev)
131 last_cs = cs = repo.get_changeset(rev)
127 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
132 k = '%s-%s-%s' % (cs.date.timetuple()[0], cs.date.timetuple()[1],
128 cs.date.timetuple()[2])
133 cs.date.timetuple()[2])
129 timetupple = [int(x) for x in k.split('-')]
134 timetupple = [int(x) for x in k.split('-')]
130 timetupple.extend([0 for _ in xrange(6)])
135 timetupple.extend([0 for _ in xrange(6)])
131 k = mktime(timetupple)
136 k = mktime(timetupple)
132 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
137 if commits_by_day_author_aggregate.has_key(author_key_cleaner(cs.author)):
133 try:
138 try:
134 l = [timegetter(x) for x in commits_by_day_author_aggregate\
139 l = [timegetter(x) for x in commits_by_day_author_aggregate\
135 [author_key_cleaner(cs.author)]['data']]
140 [author_key_cleaner(cs.author)]['data']]
136 time_pos = l.index(k)
141 time_pos = l.index(k)
137 except ValueError:
142 except ValueError:
138 time_pos = False
143 time_pos = False
139
144
140 if time_pos >= 0 and time_pos is not False:
145 if time_pos >= 0 and time_pos is not False:
141
146
142 datadict = commits_by_day_author_aggregate\
147 datadict = commits_by_day_author_aggregate\
143 [author_key_cleaner(cs.author)]['data'][time_pos]
148 [author_key_cleaner(cs.author)]['data'][time_pos]
144
149
145 datadict["commits"] += 1
150 datadict["commits"] += 1
146 datadict["added"] += len(cs.added)
151 datadict["added"] += len(cs.added)
147 datadict["changed"] += len(cs.changed)
152 datadict["changed"] += len(cs.changed)
148 datadict["removed"] += len(cs.removed)
153 datadict["removed"] += len(cs.removed)
149
154
150 else:
155 else:
151 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
156 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
152
157
153 datadict = {"time":k,
158 datadict = {"time":k,
154 "commits":1,
159 "commits":1,
155 "added":len(cs.added),
160 "added":len(cs.added),
156 "changed":len(cs.changed),
161 "changed":len(cs.changed),
157 "removed":len(cs.removed),
162 "removed":len(cs.removed),
158 }
163 }
159 commits_by_day_author_aggregate\
164 commits_by_day_author_aggregate\
160 [author_key_cleaner(cs.author)]['data'].append(datadict)
165 [author_key_cleaner(cs.author)]['data'].append(datadict)
161
166
162 else:
167 else:
163 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
168 if k >= ts_min_y and k <= ts_max_y or skip_date_limit:
164 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
169 commits_by_day_author_aggregate[author_key_cleaner(cs.author)] = {
165 "label":author_key_cleaner(cs.author),
170 "label":author_key_cleaner(cs.author),
166 "data":[{"time":k,
171 "data":[{"time":k,
167 "commits":1,
172 "commits":1,
168 "added":len(cs.added),
173 "added":len(cs.added),
169 "changed":len(cs.changed),
174 "changed":len(cs.changed),
170 "removed":len(cs.removed),
175 "removed":len(cs.removed),
171 }],
176 }],
172 "schema":["commits"],
177 "schema":["commits"],
173 }
178 }
174
179
175 #gather all data by day
180 #gather all data by day
176 if commits_by_day_aggregate.has_key(k):
181 if commits_by_day_aggregate.has_key(k):
177 commits_by_day_aggregate[k] += 1
182 commits_by_day_aggregate[k] += 1
178 else:
183 else:
179 commits_by_day_aggregate[k] = 1
184 commits_by_day_aggregate[k] = 1
180
185
181 if cnt >= parse_limit:
186 if cnt >= parse_limit:
182 #don't fetch to much data since we can freeze application
187 #don't fetch to much data since we can freeze application
183 break
188 break
184
189
185 overview_data = []
190 overview_data = []
186 for k, v in commits_by_day_aggregate.items():
191 for k, v in commits_by_day_aggregate.items():
187 overview_data.append([k, v])
192 overview_data.append([k, v])
188 overview_data = sorted(overview_data, key=itemgetter(0))
193 overview_data = sorted(overview_data, key=itemgetter(0))
189
194
190 if not commits_by_day_author_aggregate:
195 if not commits_by_day_author_aggregate:
191 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
196 commits_by_day_author_aggregate[author_key_cleaner(repo.contact)] = {
192 "label":author_key_cleaner(repo.contact),
197 "label":author_key_cleaner(repo.contact),
193 "data":[0, 1],
198 "data":[0, 1],
194 "schema":["commits"],
199 "schema":["commits"],
195 }
200 }
196
201
197 stats = cur_stats if cur_stats else Statistics()
202 stats = cur_stats if cur_stats else Statistics()
198 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
203 stats.commit_activity = json.dumps(commits_by_day_author_aggregate)
199 stats.commit_activity_combined = json.dumps(overview_data)
204 stats.commit_activity_combined = json.dumps(overview_data)
200
205
201 log.debug('last revison %s', last_rev)
206 log.debug('last revison %s', last_rev)
202 leftovers = len(repo.revisions[last_rev:])
207 leftovers = len(repo.revisions[last_rev:])
203 log.debug('revisions to parse %s', leftovers)
208 log.debug('revisions to parse %s', leftovers)
204
209
205 if last_rev == 0 or leftovers < parse_limit:
210 if last_rev == 0 or leftovers < parse_limit:
206 stats.languages = json.dumps(__get_codes_stats(repo_name))
211 stats.languages = json.dumps(__get_codes_stats(repo_name))
207
212
208 stats.repository = dbrepo
213 stats.repository = dbrepo
209 stats.stat_on_revision = last_cs.revision
214 stats.stat_on_revision = last_cs.revision
210
215
211 try:
216 try:
212 sa.add(stats)
217 sa.add(stats)
213 sa.commit()
218 sa.commit()
214 except:
219 except:
215 log.error(traceback.format_exc())
220 log.error(traceback.format_exc())
216 sa.rollback()
221 sa.rollback()
217 return False
222 return False
218 if len(repo.revisions) > 1:
223 if len(repo.revisions) > 1:
219 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
224 run_task(get_commits_stats, repo_name, ts_min_y, ts_max_y)
220
225
221 return True
226 return True
222
227
223 @task
228 @task
224 def reset_user_password(user_email):
229 def reset_user_password(user_email):
225 log = reset_user_password.get_logger()
230 log = reset_user_password.get_logger()
226 from rhodecode.lib import auth
231 from rhodecode.lib import auth
227 from rhodecode.model.db import User
232 from rhodecode.model.db import User
228
233
229 try:
234 try:
230 try:
235 try:
231 sa = get_session()
236 sa = get_session()
232 user = sa.query(User).filter(User.email == user_email).scalar()
237 user = sa.query(User).filter(User.email == user_email).scalar()
233 new_passwd = auth.PasswordGenerator().gen_password(8,
238 new_passwd = auth.PasswordGenerator().gen_password(8,
234 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
239 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
235 if user:
240 if user:
236 user.password = auth.get_crypt_password(new_passwd)
241 user.password = auth.get_crypt_password(new_passwd)
237 sa.add(user)
242 sa.add(user)
238 sa.commit()
243 sa.commit()
239 log.info('change password for %s', user_email)
244 log.info('change password for %s', user_email)
240 if new_passwd is None:
245 if new_passwd is None:
241 raise Exception('unable to generate new password')
246 raise Exception('unable to generate new password')
242
247
243 except:
248 except:
244 log.error(traceback.format_exc())
249 log.error(traceback.format_exc())
245 sa.rollback()
250 sa.rollback()
246
251
247 run_task(send_email, user_email,
252 run_task(send_email, user_email,
248 "Your new rhodecode password",
253 "Your new rhodecode password",
249 'Your new rhodecode password:%s' % (new_passwd))
254 'Your new rhodecode password:%s' % (new_passwd))
250 log.info('send new password mail to %s', user_email)
255 log.info('send new password mail to %s', user_email)
251
256
252
257
253 except:
258 except:
254 log.error('Failed to update user password')
259 log.error('Failed to update user password')
255 log.error(traceback.format_exc())
260 log.error(traceback.format_exc())
256 return True
261 return True
257
262
258 @task
263 @task
259 def send_email(recipients, subject, body):
264 def send_email(recipients, subject, body):
260 log = send_email.get_logger()
265 log = send_email.get_logger()
261 email_config = dict(config.items('DEFAULT'))
266 email_config = dict(config.items('DEFAULT'))
262 mail_from = email_config.get('app_email_from')
267 mail_from = email_config.get('app_email_from')
263 user = email_config.get('smtp_username')
268 user = email_config.get('smtp_username')
264 passwd = email_config.get('smtp_password')
269 passwd = email_config.get('smtp_password')
265 mail_server = email_config.get('smtp_server')
270 mail_server = email_config.get('smtp_server')
266 mail_port = email_config.get('smtp_port')
271 mail_port = email_config.get('smtp_port')
267 tls = email_config.get('smtp_use_tls')
272 tls = email_config.get('smtp_use_tls')
268 ssl = False
273 ssl = False
269
274
270 try:
275 try:
271 m = SmtpMailer(mail_from, user, passwd, mail_server,
276 m = SmtpMailer(mail_from, user, passwd, mail_server,
272 mail_port, ssl, tls)
277 mail_port, ssl, tls)
273 m.send(recipients, subject, body)
278 m.send(recipients, subject, body)
274 except:
279 except:
275 log.error('Mail sending failed')
280 log.error('Mail sending failed')
276 log.error(traceback.format_exc())
281 log.error(traceback.format_exc())
277 return False
282 return False
278 return True
283 return True
279
284
280 @task
285 @task
281 def create_repo_fork(form_data, cur_user):
286 def create_repo_fork(form_data, cur_user):
282 import os
287 import os
283 from rhodecode.model.repo_model import RepoModel
288 from rhodecode.model.repo_model import RepoModel
284 sa = get_session()
289 sa = get_session()
285 rm = RepoModel(sa)
290 rm = RepoModel(sa)
286
291
287 rm.create(form_data, cur_user, just_db=True, fork=True)
292 rm.create(form_data, cur_user, just_db=True, fork=True)
288
293
289 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
294 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
290 repo_path = os.path.join(repos_path, form_data['repo_name'])
295 repo_path = os.path.join(repos_path, form_data['repo_name'])
291 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
296 repo_fork_path = os.path.join(repos_path, form_data['fork_name'])
292
297
293 MercurialRepository(str(repo_fork_path), True, clone_url=str(repo_path))
298 MercurialRepository(str(repo_fork_path), True, clone_url=str(repo_path))
294
299
295
300
296 def __get_codes_stats(repo_name):
301 def __get_codes_stats(repo_name):
297 LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
302 LANGUAGES_EXTENSIONS = ['action', 'adp', 'ashx', 'asmx', 'aspx', 'asx', 'axd', 'c',
298 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el', 'erl',
303 'cfg', 'cfm', 'cpp', 'cs', 'diff', 'do', 'el', 'erl',
299 'h', 'java', 'js', 'jsp', 'jspx', 'lisp',
304 'h', 'java', 'js', 'jsp', 'jspx', 'lisp',
300 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
305 'lua', 'm', 'mako', 'ml', 'pas', 'patch', 'php', 'php3',
301 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh',
306 'php4', 'phtml', 'pm', 'py', 'rb', 'rst', 's', 'sh',
302 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
307 'tpl', 'txt', 'vim', 'wss', 'xhtml', 'xml', 'xsl', 'xslt',
303 'yaws']
308 'yaws']
304 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
309 repos_path = get_hg_ui_settings()['paths_root_path'].replace('*', '')
305 repo = MercurialRepository(repos_path + repo_name)
310 repo = MercurialRepository(repos_path + repo_name)
306
311
307 code_stats = {}
312 code_stats = {}
308 for topnode, dirs, files in repo.walk('/', 'tip'):
313 for topnode, dirs, files in repo.walk('/', 'tip'):
309 for f in files:
314 for f in files:
310 k = f.mimetype
315 k = f.mimetype
311 if f.extension in LANGUAGES_EXTENSIONS:
316 if f.extension in LANGUAGES_EXTENSIONS:
312 if code_stats.has_key(k):
317 if code_stats.has_key(k):
313 code_stats[k] += 1
318 code_stats[k] += 1
314 else:
319 else:
315 code_stats[k] = 1
320 code_stats[k] = 1
316
321
317 return code_stats or {}
322 return code_stats or {}
318
323
319
324
320
325
321
326
322
327
@@ -1,74 +1,78 b''
1 from rhodecode import get_version
1 from rhodecode import get_version
2 import sys
2 import sys
3 py_version = sys.version_info
3
4
4 requirements = [
5 requirements = [
5 "Pylons>=1.0.0",
6 "Pylons>=1.0.0",
6 "SQLAlchemy>=0.6",
7 "SQLAlchemy>=0.6",
7 "babel",
8 "babel",
8 "Mako>=0.3.2",
9 "Mako>=0.3.2",
9 "vcs>=0.1.7",
10 "vcs>=0.1.7",
10 "pygments>=1.3.0",
11 "pygments>=1.3.0",
11 "mercurial>=1.6",
12 "mercurial>=1.6",
12 "pysqlite",
13 "whoosh==1.0.0",
13 "whoosh==1.0.0",
14 "py-bcrypt",
14 "py-bcrypt",
15 "celery",
15 "celery",
16 ]
16 ]
17
17
18 if sys.version_info < (2, 6):
19 requirements.append("simplejson")
20 requirements.append("pysqlite")
21
18 #additional files from project that goes somewhere in the filesystem
22 #additional files from project that goes somewhere in the filesystem
19 #relative to sys.prefix
23 #relative to sys.prefix
20 data_files = []
24 data_files = []
21
25
22 #additional files that goes into package itself
26 #additional files that goes into package itself
23 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
27 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
24
28
25 #long description
29 #long description
26 try:
30 try:
27 readme_file = 'README.rst'
31 readme_file = 'README.rst'
28 long_description = open(readme_file).read()
32 long_description = open(readme_file).read()
29 except IOError, err:
33 except IOError, err:
30 sys.stderr.write("[ERROR] Cannot find file specified as "
34 sys.stderr.write("[ERROR] Cannot find file specified as "
31 "long_description (%s)\n" % readme_file)
35 "long_description (%s)\n" % readme_file)
32 sys.exit(1)
36 sys.exit(1)
33
37
34
38
35 try:
39 try:
36 from setuptools import setup, find_packages
40 from setuptools import setup, find_packages
37 except ImportError:
41 except ImportError:
38 from ez_setup import use_setuptools
42 from ez_setup import use_setuptools
39 use_setuptools()
43 use_setuptools()
40 from setuptools import setup, find_packages
44 from setuptools import setup, find_packages
41 #packages
45 #packages
42 packages = find_packages(exclude=['ez_setup'])
46 packages = find_packages(exclude=['ez_setup'])
43
47
44 setup(
48 setup(
45 name='RhodeCode',
49 name='RhodeCode',
46 version=get_version(),
50 version=get_version(),
47 description='Mercurial repository serving and browsing app',
51 description='Mercurial repository serving and browsing app',
48 long_description=long_description,
52 long_description=long_description,
49 keywords='mercurial web hgwebdir replacement serving hgweb rhodecode',
53 keywords='mercurial web hgwebdir replacement serving hgweb rhodecode',
50 license='BSD',
54 license='BSD',
51 author='Marcin Kuzminski',
55 author='Marcin Kuzminski',
52 author_email='marcin@python-works.com',
56 author_email='marcin@python-works.com',
53 url='http://hg.python-works.com',
57 url='http://hg.python-works.com',
54 install_requires=requirements,
58 install_requires=requirements,
55 setup_requires=["PasteScript>=1.6.3"],
59 setup_requires=["PasteScript>=1.6.3"],
56 data_files=data_files,
60 data_files=data_files,
57 packages=packages,
61 packages=packages,
58 include_package_data=True,
62 include_package_data=True,
59 test_suite='nose.collector',
63 test_suite='nose.collector',
60 package_data=package_data,
64 package_data=package_data,
61 message_extractors={'rhodecode': [
65 message_extractors={'rhodecode': [
62 ('**.py', 'python', None),
66 ('**.py', 'python', None),
63 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
67 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
64 ('public/**', 'ignore', None)]},
68 ('public/**', 'ignore', None)]},
65 zip_safe=False,
69 zip_safe=False,
66 paster_plugins=['PasteScript', 'Pylons'],
70 paster_plugins=['PasteScript', 'Pylons'],
67 entry_points="""
71 entry_points="""
68 [paste.app_factory]
72 [paste.app_factory]
69 main = rhodecode.config.middleware:make_app
73 main = rhodecode.config.middleware:make_app
70
74
71 [paste.app_install]
75 [paste.app_install]
72 main = pylons.util:PylonsInstaller
76 main = pylons.util:PylonsInstaller
73 """,
77 """,
74 )
78 )
General Comments 0
You need to be logged in to leave comments. Login now