##// END OF EJS Templates
fixed copyright year to 2011
marcink -
r902:07a6e8c6 beta
parent child Browse files
Show More
@@ -1,52 +1,52 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.__init__
3 rhodecode.__init__
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode, a web based repository management based on pylons
6 RhodeCode, a web based repository management based on pylons
7 versioning implementation: http://semver.org/
7 versioning implementation: http://semver.org/
8
8
9 :created_on: Apr 9, 2010
9 :created_on: Apr 9, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software; you can redistribute it and/or
14 # This program is free software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; version 2
16 # as published by the Free Software Foundation; version 2
17 # of the License or (at your opinion) any later version of the license.
17 # of the License or (at your opinion) any later version of the license.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 # MA 02110-1301, USA.
27 # MA 02110-1301, USA.
28
28
29
29
30 VERSION = (1, 2, 0, 'beta')
30 VERSION = (1, 2, 0, 'beta')
31 __version__ = '.'.join((str(each) for each in VERSION[:4]))
31 __version__ = '.'.join((str(each) for each in VERSION[:4]))
32 __dbversion__ = 3 #defines current db version for migrations
32 __dbversion__ = 3 #defines current db version for migrations
33
33
34 try:
34 try:
35 from rhodecode.lib.utils import get_current_revision
35 from rhodecode.lib.utils import get_current_revision
36 _rev = get_current_revision()
36 _rev = get_current_revision()
37 except ImportError:
37 except ImportError:
38 #this is needed when doing some setup.py operations
38 #this is needed when doing some setup.py operations
39 _rev = False
39 _rev = False
40
40
41 if len(VERSION) > 3 and _rev:
41 if len(VERSION) > 3 and _rev:
42 __version__ += ' [rev:%s]' % _rev[0]
42 __version__ += ' [rev:%s]' % _rev[0]
43
43
44 def get_version():
44 def get_version():
45 """Returns shorter version (digit parts only) as string."""
45 """Returns shorter version (digit parts only) as string."""
46
46
47 return '.'.join((str(each) for each in VERSION[:3]))
47 return '.'.join((str(each) for each in VERSION[:3]))
48
48
49 BACKENDS = {
49 BACKENDS = {
50 'hg': 'Mercurial repository',
50 'hg': 'Mercurial repository',
51 #'git': 'Git repository',
51 #'git': 'Git repository',
52 }
52 }
@@ -1,53 +1,53 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.admin
3 rhodecode.controllers.admin.admin
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Controller for Admin panel of Rhodecode
6 Controller for Admin panel of Rhodecode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 from pylons import request, tmpl_context as c
29 from pylons import request, tmpl_context as c
30 from rhodecode.lib.base import BaseController, render
30 from rhodecode.lib.base import BaseController, render
31 from rhodecode.model.db import UserLog
31 from rhodecode.model.db import UserLog
32 from webhelpers.paginate import Page
32 from webhelpers.paginate import Page
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37 class AdminController(BaseController):
37 class AdminController(BaseController):
38
38
39 @LoginRequired()
39 @LoginRequired()
40 def __before__(self):
40 def __before__(self):
41 super(AdminController, self).__before__()
41 super(AdminController, self).__before__()
42
42
43 @HasPermissionAllDecorator('hg.admin')
43 @HasPermissionAllDecorator('hg.admin')
44 def index(self):
44 def index(self):
45
45
46 users_log = self.sa.query(UserLog).order_by(UserLog.action_date.desc())
46 users_log = self.sa.query(UserLog).order_by(UserLog.action_date.desc())
47 p = int(request.params.get('page', 1))
47 p = int(request.params.get('page', 1))
48 c.users_log = Page(users_log, page=p, items_per_page=10)
48 c.users_log = Page(users_log, page=p, items_per_page=10)
49 c.log_data = render('admin/admin_log.html')
49 c.log_data = render('admin/admin_log.html')
50 if request.params.get('partial'):
50 if request.params.get('partial'):
51 return c.log_data
51 return c.log_data
52 return render('admin/admin.html')
52 return render('admin/admin.html')
53
53
@@ -1,105 +1,105 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.ldap_settings
3 rhodecode.controllers.admin.ldap_settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 ldap controller for RhodeCode
6 ldap controller for RhodeCode
7
7
8 :created_on: Nov 26, 2010
8 :created_on: Nov 26, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import logging
27 import logging
28 import formencode
28 import formencode
29 import traceback
29 import traceback
30
30
31 from formencode import htmlfill
31 from formencode import htmlfill
32
32
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36
36
37 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
40 from rhodecode.lib.auth_ldap import LdapImportError
40 from rhodecode.lib.auth_ldap import LdapImportError
41 from rhodecode.model.settings import SettingsModel
41 from rhodecode.model.settings import SettingsModel
42 from rhodecode.model.forms import LdapSettingsForm
42 from rhodecode.model.forms import LdapSettingsForm
43 from sqlalchemy.exc import DatabaseError
43 from sqlalchemy.exc import DatabaseError
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48
48
49 class LdapSettingsController(BaseController):
49 class LdapSettingsController(BaseController):
50
50
51 @LoginRequired()
51 @LoginRequired()
52 @HasPermissionAllDecorator('hg.admin')
52 @HasPermissionAllDecorator('hg.admin')
53 def __before__(self):
53 def __before__(self):
54 c.admin_user = session.get('admin_user')
54 c.admin_user = session.get('admin_user')
55 c.admin_username = session.get('admin_username')
55 c.admin_username = session.get('admin_username')
56 super(LdapSettingsController, self).__before__()
56 super(LdapSettingsController, self).__before__()
57
57
58 def index(self):
58 def index(self):
59 defaults = SettingsModel().get_ldap_settings()
59 defaults = SettingsModel().get_ldap_settings()
60
60
61 return htmlfill.render(
61 return htmlfill.render(
62 render('admin/ldap/ldap.html'),
62 render('admin/ldap/ldap.html'),
63 defaults=defaults,
63 defaults=defaults,
64 encoding="UTF-8",
64 encoding="UTF-8",
65 force_defaults=True,)
65 force_defaults=True,)
66
66
67 def ldap_settings(self):
67 def ldap_settings(self):
68 """POST ldap create and store ldap settings"""
68 """POST ldap create and store ldap settings"""
69
69
70 settings_model = SettingsModel()
70 settings_model = SettingsModel()
71 _form = LdapSettingsForm()()
71 _form = LdapSettingsForm()()
72
72
73 try:
73 try:
74 form_result = _form.to_python(dict(request.POST))
74 form_result = _form.to_python(dict(request.POST))
75 try:
75 try:
76
76
77 for k, v in form_result.items():
77 for k, v in form_result.items():
78 if k.startswith('ldap_'):
78 if k.startswith('ldap_'):
79 setting = settings_model.get(k)
79 setting = settings_model.get(k)
80 setting.app_settings_value = v
80 setting.app_settings_value = v
81 self.sa.add(setting)
81 self.sa.add(setting)
82
82
83 self.sa.commit()
83 self.sa.commit()
84 h.flash(_('Ldap settings updated successfully'),
84 h.flash(_('Ldap settings updated successfully'),
85 category='success')
85 category='success')
86 except (DatabaseError,):
86 except (DatabaseError,):
87 raise
87 raise
88 except LdapImportError:
88 except LdapImportError:
89 h.flash(_('Unable to activate ldap. The "python-ldap" library '
89 h.flash(_('Unable to activate ldap. The "python-ldap" library '
90 'is missing.'), category='warning')
90 'is missing.'), category='warning')
91
91
92 except formencode.Invalid, errors:
92 except formencode.Invalid, errors:
93
93
94 return htmlfill.render(
94 return htmlfill.render(
95 render('admin/ldap/ldap.html'),
95 render('admin/ldap/ldap.html'),
96 defaults=errors.value,
96 defaults=errors.value,
97 errors=errors.error_dict or {},
97 errors=errors.error_dict or {},
98 prefix_error=False,
98 prefix_error=False,
99 encoding="UTF-8")
99 encoding="UTF-8")
100 except Exception:
100 except Exception:
101 log.error(traceback.format_exc())
101 log.error(traceback.format_exc())
102 h.flash(_('error occurred during update of ldap settings'),
102 h.flash(_('error occurred during update of ldap settings'),
103 category='error')
103 category='error')
104
104
105 return redirect(url('ldap_home'))
105 return redirect(url('ldap_home'))
@@ -1,171 +1,171 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.permissions
3 rhodecode.controllers.admin.permissions
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 permissions controller for Rhodecode
6 permissions controller for Rhodecode
7
7
8 :created_on: Apr 27, 2010
8 :created_on: Apr 27, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 from formencode import htmlfill
28 from formencode import htmlfill
29 from pylons import request, session, tmpl_context as c, url
29 from pylons import request, session, tmpl_context as c, url
30 from pylons.controllers.util import abort, redirect
30 from pylons.controllers.util import abort, redirect
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
34 from rhodecode.lib.auth_ldap import LdapImportError
34 from rhodecode.lib.auth_ldap import LdapImportError
35 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
36 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
36 from rhodecode.model.forms import LdapSettingsForm, DefaultPermissionsForm
37 from rhodecode.model.permission import PermissionModel
37 from rhodecode.model.permission import PermissionModel
38 from rhodecode.model.settings import SettingsModel
38 from rhodecode.model.settings import SettingsModel
39 from rhodecode.model.user import UserModel
39 from rhodecode.model.user import UserModel
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 PermissionsController(BaseController):
46 class PermissionsController(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('permission', 'permissions')
50 # map.resource('permission', 'permissions')
51
51
52 @LoginRequired()
52 @LoginRequired()
53 @HasPermissionAllDecorator('hg.admin')
53 @HasPermissionAllDecorator('hg.admin')
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(PermissionsController, self).__before__()
57 super(PermissionsController, self).__before__()
58
58
59 self.perms_choices = [('repository.none', _('None'),),
59 self.perms_choices = [('repository.none', _('None'),),
60 ('repository.read', _('Read'),),
60 ('repository.read', _('Read'),),
61 ('repository.write', _('Write'),),
61 ('repository.write', _('Write'),),
62 ('repository.admin', _('Admin'),)]
62 ('repository.admin', _('Admin'),)]
63 self.register_choices = [
63 self.register_choices = [
64 ('hg.register.none',
64 ('hg.register.none',
65 _('disabled')),
65 _('disabled')),
66 ('hg.register.manual_activate',
66 ('hg.register.manual_activate',
67 _('allowed with manual account activation')),
67 _('allowed with manual account activation')),
68 ('hg.register.auto_activate',
68 ('hg.register.auto_activate',
69 _('allowed with automatic account activation')), ]
69 _('allowed with automatic account activation')), ]
70
70
71 self.create_choices = [('hg.create.none', _('Disabled')),
71 self.create_choices = [('hg.create.none', _('Disabled')),
72 ('hg.create.repository', _('Enabled'))]
72 ('hg.create.repository', _('Enabled'))]
73
73
74
74
75 def index(self, format='html'):
75 def index(self, format='html'):
76 """GET /permissions: All items in the collection"""
76 """GET /permissions: All items in the collection"""
77 # url('permissions')
77 # url('permissions')
78
78
79 def create(self):
79 def create(self):
80 """POST /permissions: Create a new item"""
80 """POST /permissions: Create a new item"""
81 # url('permissions')
81 # url('permissions')
82
82
83 def new(self, format='html'):
83 def new(self, format='html'):
84 """GET /permissions/new: Form to create a new item"""
84 """GET /permissions/new: Form to create a new item"""
85 # url('new_permission')
85 # url('new_permission')
86
86
87 def update(self, id):
87 def update(self, id):
88 """PUT /permissions/id: Update an existing item"""
88 """PUT /permissions/id: Update an existing item"""
89 # Forms posted to this method should contain a hidden field:
89 # Forms posted to this method should contain a hidden field:
90 # <input type="hidden" name="_method" value="PUT" />
90 # <input type="hidden" name="_method" value="PUT" />
91 # Or using helpers:
91 # Or using helpers:
92 # h.form(url('permission', id=ID),
92 # h.form(url('permission', id=ID),
93 # method='put')
93 # method='put')
94 # url('permission', id=ID)
94 # url('permission', id=ID)
95
95
96 permission_model = PermissionModel()
96 permission_model = PermissionModel()
97
97
98 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
98 _form = DefaultPermissionsForm([x[0] for x in self.perms_choices],
99 [x[0] for x in self.register_choices],
99 [x[0] for x in self.register_choices],
100 [x[0] for x in self.create_choices])()
100 [x[0] for x in self.create_choices])()
101
101
102 try:
102 try:
103 form_result = _form.to_python(dict(request.POST))
103 form_result = _form.to_python(dict(request.POST))
104 form_result.update({'perm_user_name':id})
104 form_result.update({'perm_user_name':id})
105 permission_model.update(form_result)
105 permission_model.update(form_result)
106 h.flash(_('Default permissions updated successfully'),
106 h.flash(_('Default permissions updated successfully'),
107 category='success')
107 category='success')
108
108
109 except formencode.Invalid, errors:
109 except formencode.Invalid, errors:
110 c.perms_choices = self.perms_choices
110 c.perms_choices = self.perms_choices
111 c.register_choices = self.register_choices
111 c.register_choices = self.register_choices
112 c.create_choices = self.create_choices
112 c.create_choices = self.create_choices
113 defaults = errors.value
113 defaults = errors.value
114
114
115 return htmlfill.render(
115 return htmlfill.render(
116 render('admin/permissions/permissions.html'),
116 render('admin/permissions/permissions.html'),
117 defaults=defaults,
117 defaults=defaults,
118 errors=errors.error_dict or {},
118 errors=errors.error_dict or {},
119 prefix_error=False,
119 prefix_error=False,
120 encoding="UTF-8")
120 encoding="UTF-8")
121 except Exception:
121 except Exception:
122 log.error(traceback.format_exc())
122 log.error(traceback.format_exc())
123 h.flash(_('error occurred during update of permissions'),
123 h.flash(_('error occurred during update of permissions'),
124 category='error')
124 category='error')
125
125
126 return redirect(url('edit_permission', id=id))
126 return redirect(url('edit_permission', id=id))
127
127
128
128
129
129
130 def delete(self, id):
130 def delete(self, id):
131 """DELETE /permissions/id: Delete an existing item"""
131 """DELETE /permissions/id: Delete an existing item"""
132 # Forms posted to this method should contain a hidden field:
132 # Forms posted to this method should contain a hidden field:
133 # <input type="hidden" name="_method" value="DELETE" />
133 # <input type="hidden" name="_method" value="DELETE" />
134 # Or using helpers:
134 # Or using helpers:
135 # h.form(url('permission', id=ID),
135 # h.form(url('permission', id=ID),
136 # method='delete')
136 # method='delete')
137 # url('permission', id=ID)
137 # url('permission', id=ID)
138
138
139 def show(self, id, format='html'):
139 def show(self, id, format='html'):
140 """GET /permissions/id: Show a specific item"""
140 """GET /permissions/id: Show a specific item"""
141 # url('permission', id=ID)
141 # url('permission', id=ID)
142
142
143 def edit(self, id, format='html'):
143 def edit(self, id, format='html'):
144 """GET /permissions/id/edit: Form to edit an existing item"""
144 """GET /permissions/id/edit: Form to edit an existing item"""
145 #url('edit_permission', id=ID)
145 #url('edit_permission', id=ID)
146 c.perms_choices = self.perms_choices
146 c.perms_choices = self.perms_choices
147 c.register_choices = self.register_choices
147 c.register_choices = self.register_choices
148 c.create_choices = self.create_choices
148 c.create_choices = self.create_choices
149
149
150 if id == 'default':
150 if id == 'default':
151 default_user = UserModel().get_by_username('default')
151 default_user = UserModel().get_by_username('default')
152 defaults = {'_method':'put',
152 defaults = {'_method':'put',
153 'anonymous':default_user.active}
153 'anonymous':default_user.active}
154
154
155 for p in default_user.user_perms:
155 for p in default_user.user_perms:
156 if p.permission.permission_name.startswith('repository.'):
156 if p.permission.permission_name.startswith('repository.'):
157 defaults['default_perm'] = p.permission.permission_name
157 defaults['default_perm'] = p.permission.permission_name
158
158
159 if p.permission.permission_name.startswith('hg.register.'):
159 if p.permission.permission_name.startswith('hg.register.'):
160 defaults['default_register'] = p.permission.permission_name
160 defaults['default_register'] = p.permission.permission_name
161
161
162 if p.permission.permission_name.startswith('hg.create.'):
162 if p.permission.permission_name.startswith('hg.create.'):
163 defaults['default_create'] = p.permission.permission_name
163 defaults['default_create'] = p.permission.permission_name
164
164
165 return htmlfill.render(
165 return htmlfill.render(
166 render('admin/permissions/permissions.html'),
166 render('admin/permissions/permissions.html'),
167 defaults=defaults,
167 defaults=defaults,
168 encoding="UTF-8",
168 encoding="UTF-8",
169 force_defaults=True,)
169 force_defaults=True,)
170 else:
170 else:
171 return redirect(url('admin_home'))
171 return redirect(url('admin_home'))
@@ -1,313 +1,313 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Admin controller for RhodeCode
6 Admin controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import formencode
30 import formencode
31 from operator import itemgetter
31 from operator import itemgetter
32 from formencode import htmlfill
32 from formencode import htmlfill
33
33
34 from paste.httpexceptions import HTTPInternalServerError
34 from paste.httpexceptions import HTTPInternalServerError
35 from pylons import request, response, session, tmpl_context as c, url
35 from pylons import request, response, session, tmpl_context as c, url
36 from pylons.controllers.util import abort, redirect
36 from pylons.controllers.util import abort, redirect
37 from pylons.i18n.translation import _
37 from pylons.i18n.translation import _
38
38
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
41 HasPermissionAnyDecorator
41 HasPermissionAnyDecorator
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.utils import invalidate_cache, action_logger
43 from rhodecode.lib.utils import invalidate_cache, action_logger
44 from rhodecode.model.db import User
44 from rhodecode.model.db import User
45 from rhodecode.model.forms import RepoForm
45 from rhodecode.model.forms import RepoForm
46 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.repo import RepoModel
48
48
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52 class ReposController(BaseController):
52 class ReposController(BaseController):
53 """REST Controller styled on the Atom Publishing Protocol"""
53 """REST Controller styled on the Atom Publishing Protocol"""
54 # To properly map this controller, ensure your config/routing.py
54 # To properly map this controller, ensure your config/routing.py
55 # file has a resource setup:
55 # file has a resource setup:
56 # map.resource('repo', 'repos')
56 # map.resource('repo', 'repos')
57
57
58 @LoginRequired()
58 @LoginRequired()
59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 def __before__(self):
60 def __before__(self):
61 c.admin_user = session.get('admin_user')
61 c.admin_user = session.get('admin_user')
62 c.admin_username = session.get('admin_username')
62 c.admin_username = session.get('admin_username')
63 super(ReposController, self).__before__()
63 super(ReposController, self).__before__()
64
64
65 @HasPermissionAllDecorator('hg.admin')
65 @HasPermissionAllDecorator('hg.admin')
66 def index(self, format='html'):
66 def index(self, format='html'):
67 """GET /repos: All items in the collection"""
67 """GET /repos: All items in the collection"""
68 # url('repos')
68 # url('repos')
69 cached_repo_list = ScmModel().get_repos()
69 cached_repo_list = ScmModel().get_repos()
70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
71 return render('admin/repos/repos.html')
71 return render('admin/repos/repos.html')
72
72
73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
74 def create(self):
74 def create(self):
75 """POST /repos: Create a new item"""
75 """POST /repos: Create a new item"""
76 # url('repos')
76 # url('repos')
77 repo_model = RepoModel()
77 repo_model = RepoModel()
78 _form = RepoForm()()
78 _form = RepoForm()()
79 form_result = {}
79 form_result = {}
80 try:
80 try:
81 form_result = _form.to_python(dict(request.POST))
81 form_result = _form.to_python(dict(request.POST))
82 repo_model.create(form_result, c.rhodecode_user)
82 repo_model.create(form_result, c.rhodecode_user)
83 h.flash(_('created repository %s') % form_result['repo_name'],
83 h.flash(_('created repository %s') % form_result['repo_name'],
84 category='success')
84 category='success')
85
85
86 if request.POST.get('user_created'):
86 if request.POST.get('user_created'):
87 action_logger(self.rhodecode_user, 'user_created_repo',
87 action_logger(self.rhodecode_user, 'user_created_repo',
88 form_result['repo_name'], '', self.sa)
88 form_result['repo_name'], '', self.sa)
89 else:
89 else:
90 action_logger(self.rhodecode_user, 'admin_created_repo',
90 action_logger(self.rhodecode_user, 'admin_created_repo',
91 form_result['repo_name'], '', self.sa)
91 form_result['repo_name'], '', self.sa)
92
92
93 except formencode.Invalid, errors:
93 except formencode.Invalid, errors:
94 c.new_repo = errors.value['repo_name']
94 c.new_repo = errors.value['repo_name']
95
95
96 if request.POST.get('user_created'):
96 if request.POST.get('user_created'):
97 r = render('admin/repos/repo_add_create_repository.html')
97 r = render('admin/repos/repo_add_create_repository.html')
98 else:
98 else:
99 r = render('admin/repos/repo_add.html')
99 r = render('admin/repos/repo_add.html')
100
100
101 return htmlfill.render(
101 return htmlfill.render(
102 r,
102 r,
103 defaults=errors.value,
103 defaults=errors.value,
104 errors=errors.error_dict or {},
104 errors=errors.error_dict or {},
105 prefix_error=False,
105 prefix_error=False,
106 encoding="UTF-8")
106 encoding="UTF-8")
107
107
108 except Exception:
108 except Exception:
109 log.error(traceback.format_exc())
109 log.error(traceback.format_exc())
110 msg = _('error occurred during creation of repository %s') \
110 msg = _('error occurred during creation of repository %s') \
111 % form_result.get('repo_name')
111 % form_result.get('repo_name')
112 h.flash(msg, category='error')
112 h.flash(msg, category='error')
113 if request.POST.get('user_created'):
113 if request.POST.get('user_created'):
114 return redirect(url('home'))
114 return redirect(url('home'))
115 return redirect(url('repos'))
115 return redirect(url('repos'))
116
116
117 @HasPermissionAllDecorator('hg.admin')
117 @HasPermissionAllDecorator('hg.admin')
118 def new(self, format='html'):
118 def new(self, format='html'):
119 """GET /repos/new: Form to create a new item"""
119 """GET /repos/new: Form to create a new item"""
120 new_repo = request.GET.get('repo', '')
120 new_repo = request.GET.get('repo', '')
121 c.new_repo = h.repo_name_slug(new_repo)
121 c.new_repo = h.repo_name_slug(new_repo)
122
122
123 return render('admin/repos/repo_add.html')
123 return render('admin/repos/repo_add.html')
124
124
125 @HasPermissionAllDecorator('hg.admin')
125 @HasPermissionAllDecorator('hg.admin')
126 def update(self, repo_name):
126 def update(self, repo_name):
127 """PUT /repos/repo_name: Update an existing item"""
127 """PUT /repos/repo_name: Update an existing item"""
128 # Forms posted to this method should contain a hidden field:
128 # Forms posted to this method should contain a hidden field:
129 # <input type="hidden" name="_method" value="PUT" />
129 # <input type="hidden" name="_method" value="PUT" />
130 # Or using helpers:
130 # Or using helpers:
131 # h.form(url('repo', repo_name=ID),
131 # h.form(url('repo', repo_name=ID),
132 # method='put')
132 # method='put')
133 # url('repo', repo_name=ID)
133 # url('repo', repo_name=ID)
134 repo_model = RepoModel()
134 repo_model = RepoModel()
135 changed_name = repo_name
135 changed_name = repo_name
136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
137
137
138 try:
138 try:
139 form_result = _form.to_python(dict(request.POST))
139 form_result = _form.to_python(dict(request.POST))
140 repo_model.update(repo_name, form_result)
140 repo_model.update(repo_name, form_result)
141 invalidate_cache('get_repo_cached_%s' % repo_name)
141 invalidate_cache('get_repo_cached_%s' % repo_name)
142 h.flash(_('Repository %s updated successfully' % repo_name),
142 h.flash(_('Repository %s updated successfully' % repo_name),
143 category='success')
143 category='success')
144 changed_name = form_result['repo_name']
144 changed_name = form_result['repo_name']
145 action_logger(self.rhodecode_user, 'admin_updated_repo',
145 action_logger(self.rhodecode_user, 'admin_updated_repo',
146 changed_name, '', self.sa)
146 changed_name, '', self.sa)
147
147
148 except formencode.Invalid, errors:
148 except formencode.Invalid, errors:
149 c.repo_info = repo_model.get_by_repo_name(repo_name)
149 c.repo_info = repo_model.get_by_repo_name(repo_name)
150 if c.repo_info.stats:
150 if c.repo_info.stats:
151 last_rev = c.repo_info.stats.stat_on_revision
151 last_rev = c.repo_info.stats.stat_on_revision
152 else:
152 else:
153 last_rev = 0
153 last_rev = 0
154 c.stats_revision = last_rev
154 c.stats_revision = last_rev
155 r = ScmModel().get(repo_name)
155 r = ScmModel().get(repo_name)
156 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
156 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
157
157
158 if last_rev == 0:
158 if last_rev == 0:
159 c.stats_percentage = 0
159 c.stats_percentage = 0
160 else:
160 else:
161 c.stats_percentage = '%.2f' % ((float((last_rev)) /
161 c.stats_percentage = '%.2f' % ((float((last_rev)) /
162 c.repo_last_rev) * 100)
162 c.repo_last_rev) * 100)
163
163
164 c.users_array = repo_model.get_users_js()
164 c.users_array = repo_model.get_users_js()
165 errors.value.update({'user':c.repo_info.user.username})
165 errors.value.update({'user':c.repo_info.user.username})
166 return htmlfill.render(
166 return htmlfill.render(
167 render('admin/repos/repo_edit.html'),
167 render('admin/repos/repo_edit.html'),
168 defaults=errors.value,
168 defaults=errors.value,
169 errors=errors.error_dict or {},
169 errors=errors.error_dict or {},
170 prefix_error=False,
170 prefix_error=False,
171 encoding="UTF-8")
171 encoding="UTF-8")
172
172
173 except Exception:
173 except Exception:
174 log.error(traceback.format_exc())
174 log.error(traceback.format_exc())
175 h.flash(_('error occurred during update of repository %s') \
175 h.flash(_('error occurred during update of repository %s') \
176 % repo_name, category='error')
176 % repo_name, category='error')
177
177
178 return redirect(url('edit_repo', repo_name=changed_name))
178 return redirect(url('edit_repo', repo_name=changed_name))
179
179
180 @HasPermissionAllDecorator('hg.admin')
180 @HasPermissionAllDecorator('hg.admin')
181 def delete(self, repo_name):
181 def delete(self, repo_name):
182 """DELETE /repos/repo_name: Delete an existing item"""
182 """DELETE /repos/repo_name: Delete an existing item"""
183 # Forms posted to this method should contain a hidden field:
183 # Forms posted to this method should contain a hidden field:
184 # <input type="hidden" name="_method" value="DELETE" />
184 # <input type="hidden" name="_method" value="DELETE" />
185 # Or using helpers:
185 # Or using helpers:
186 # h.form(url('repo', repo_name=ID),
186 # h.form(url('repo', repo_name=ID),
187 # method='delete')
187 # method='delete')
188 # url('repo', repo_name=ID)
188 # url('repo', repo_name=ID)
189
189
190 repo_model = RepoModel()
190 repo_model = RepoModel()
191 repo = repo_model.get_by_repo_name(repo_name)
191 repo = repo_model.get_by_repo_name(repo_name)
192 if not repo:
192 if not repo:
193 h.flash(_('%s repository is not mapped to db perhaps'
193 h.flash(_('%s repository is not mapped to db perhaps'
194 ' it was moved or renamed from the filesystem'
194 ' it was moved or renamed from the filesystem'
195 ' please run the application again'
195 ' please run the application again'
196 ' in order to rescan repositories') % repo_name,
196 ' in order to rescan repositories') % repo_name,
197 category='error')
197 category='error')
198
198
199 return redirect(url('repos'))
199 return redirect(url('repos'))
200 try:
200 try:
201 action_logger(self.rhodecode_user, 'admin_deleted_repo',
201 action_logger(self.rhodecode_user, 'admin_deleted_repo',
202 repo_name, '', self.sa)
202 repo_name, '', self.sa)
203 repo_model.delete(repo)
203 repo_model.delete(repo)
204 invalidate_cache('get_repo_cached_%s' % repo_name)
204 invalidate_cache('get_repo_cached_%s' % repo_name)
205 h.flash(_('deleted repository %s') % repo_name, category='success')
205 h.flash(_('deleted repository %s') % repo_name, category='success')
206
206
207 except Exception, e:
207 except Exception, e:
208 log.error(traceback.format_exc())
208 log.error(traceback.format_exc())
209 h.flash(_('An error occurred during deletion of %s') % repo_name,
209 h.flash(_('An error occurred during deletion of %s') % repo_name,
210 category='error')
210 category='error')
211
211
212 return redirect(url('repos'))
212 return redirect(url('repos'))
213
213
214 @HasPermissionAllDecorator('hg.admin')
214 @HasPermissionAllDecorator('hg.admin')
215 def delete_perm_user(self, repo_name):
215 def delete_perm_user(self, repo_name):
216 """
216 """
217 DELETE an existing repository permission user
217 DELETE an existing repository permission user
218 :param repo_name:
218 :param repo_name:
219 """
219 """
220
220
221 try:
221 try:
222 repo_model = RepoModel()
222 repo_model = RepoModel()
223 repo_model.delete_perm_user(request.POST, repo_name)
223 repo_model.delete_perm_user(request.POST, repo_name)
224 except Exception, e:
224 except Exception, e:
225 h.flash(_('An error occurred during deletion of repository user'),
225 h.flash(_('An error occurred during deletion of repository user'),
226 category='error')
226 category='error')
227 raise HTTPInternalServerError()
227 raise HTTPInternalServerError()
228
228
229 @HasPermissionAllDecorator('hg.admin')
229 @HasPermissionAllDecorator('hg.admin')
230 def repo_stats(self, repo_name):
230 def repo_stats(self, repo_name):
231 """
231 """
232 DELETE an existing repository statistics
232 DELETE an existing repository statistics
233 :param repo_name:
233 :param repo_name:
234 """
234 """
235
235
236 try:
236 try:
237 repo_model = RepoModel()
237 repo_model = RepoModel()
238 repo_model.delete_stats(repo_name)
238 repo_model.delete_stats(repo_name)
239 except Exception, e:
239 except Exception, e:
240 h.flash(_('An error occurred during deletion of repository stats'),
240 h.flash(_('An error occurred during deletion of repository stats'),
241 category='error')
241 category='error')
242 return redirect(url('edit_repo', repo_name=repo_name))
242 return redirect(url('edit_repo', repo_name=repo_name))
243
243
244 @HasPermissionAllDecorator('hg.admin')
244 @HasPermissionAllDecorator('hg.admin')
245 def repo_cache(self, repo_name):
245 def repo_cache(self, repo_name):
246 """
246 """
247 INVALIDATE existing repository cache
247 INVALIDATE existing repository cache
248 :param repo_name:
248 :param repo_name:
249 """
249 """
250
250
251 try:
251 try:
252 ScmModel().mark_for_invalidation(repo_name)
252 ScmModel().mark_for_invalidation(repo_name)
253 except Exception, e:
253 except Exception, e:
254 h.flash(_('An error occurred during cache invalidation'),
254 h.flash(_('An error occurred during cache invalidation'),
255 category='error')
255 category='error')
256 return redirect(url('edit_repo', repo_name=repo_name))
256 return redirect(url('edit_repo', repo_name=repo_name))
257
257
258 @HasPermissionAllDecorator('hg.admin')
258 @HasPermissionAllDecorator('hg.admin')
259 def show(self, repo_name, format='html'):
259 def show(self, repo_name, format='html'):
260 """GET /repos/repo_name: Show a specific item"""
260 """GET /repos/repo_name: Show a specific item"""
261 # url('repo', repo_name=ID)
261 # url('repo', repo_name=ID)
262
262
263 @HasPermissionAllDecorator('hg.admin')
263 @HasPermissionAllDecorator('hg.admin')
264 def edit(self, repo_name, format='html'):
264 def edit(self, repo_name, format='html'):
265 """GET /repos/repo_name/edit: Form to edit an existing item"""
265 """GET /repos/repo_name/edit: Form to edit an existing item"""
266 # url('edit_repo', repo_name=ID)
266 # url('edit_repo', repo_name=ID)
267 repo_model = RepoModel()
267 repo_model = RepoModel()
268 r = ScmModel().get(repo_name)
268 r = ScmModel().get(repo_name)
269 c.repo_info = repo_model.get_by_repo_name(repo_name)
269 c.repo_info = repo_model.get_by_repo_name(repo_name)
270
270
271 if c.repo_info is None:
271 if c.repo_info is None:
272 h.flash(_('%s repository is not mapped to db perhaps'
272 h.flash(_('%s repository is not mapped to db perhaps'
273 ' it was created or renamed from the filesystem'
273 ' it was created or renamed from the filesystem'
274 ' please run the application again'
274 ' please run the application again'
275 ' in order to rescan repositories') % repo_name,
275 ' in order to rescan repositories') % repo_name,
276 category='error')
276 category='error')
277
277
278 return redirect(url('repos'))
278 return redirect(url('repos'))
279
279
280 if c.repo_info.stats:
280 if c.repo_info.stats:
281 last_rev = c.repo_info.stats.stat_on_revision
281 last_rev = c.repo_info.stats.stat_on_revision
282 else:
282 else:
283 last_rev = 0
283 last_rev = 0
284 c.stats_revision = last_rev
284 c.stats_revision = last_rev
285
285
286 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
286 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
287
287
288 if last_rev == 0:
288 if last_rev == 0:
289 c.stats_percentage = 0
289 c.stats_percentage = 0
290 else:
290 else:
291 c.stats_percentage = '%.2f' % ((float((last_rev)) /
291 c.stats_percentage = '%.2f' % ((float((last_rev)) /
292 c.repo_last_rev) * 100)
292 c.repo_last_rev) * 100)
293
293
294 defaults = c.repo_info.get_dict()
294 defaults = c.repo_info.get_dict()
295 if c.repo_info.user:
295 if c.repo_info.user:
296 defaults.update({'user':c.repo_info.user.username})
296 defaults.update({'user':c.repo_info.user.username})
297 else:
297 else:
298 replacement_user = self.sa.query(User)\
298 replacement_user = self.sa.query(User)\
299 .filter(User.admin == True).first().username
299 .filter(User.admin == True).first().username
300 defaults.update({'user':replacement_user})
300 defaults.update({'user':replacement_user})
301
301
302 c.users_array = repo_model.get_users_js()
302 c.users_array = repo_model.get_users_js()
303
303
304 for p in c.repo_info.repo_to_perm:
304 for p in c.repo_info.repo_to_perm:
305 defaults.update({'perm_%s' % p.user.username:
305 defaults.update({'perm_%s' % p.user.username:
306 p.permission.permission_name})
306 p.permission.permission_name})
307
307
308 return htmlfill.render(
308 return htmlfill.render(
309 render('admin/repos/repo_edit.html'),
309 render('admin/repos/repo_edit.html'),
310 defaults=defaults,
310 defaults=defaults,
311 encoding="UTF-8",
311 encoding="UTF-8",
312 force_defaults=False
312 force_defaults=False
313 )
313 )
@@ -1,351 +1,351 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 settings controller for rhodecode admin
6 settings controller for rhodecode admin
7
7
8 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import formencode
30 import formencode
31
31
32 from operator import itemgetter
32 from operator import itemgetter
33 from formencode import htmlfill
33 from formencode import htmlfill
34 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
34 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
35 config
35 config
36 from pylons.controllers.util import abort, redirect
36 from pylons.controllers.util import abort, redirect
37 from pylons.i18n.translation import _
37 from pylons.i18n.translation import _
38
38
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
41 HasPermissionAnyDecorator, NotAnonymous
41 HasPermissionAnyDecorator, NotAnonymous
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.celerylib import tasks, run_task
43 from rhodecode.lib.celerylib import tasks, run_task
44 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
44 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
45 set_rhodecode_config
45 set_rhodecode_config
46 from rhodecode.model.db import RhodeCodeUi, Repository
46 from rhodecode.model.db import RhodeCodeUi, Repository
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
47 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
48 ApplicationUiSettingsForm
48 ApplicationUiSettingsForm
49 from rhodecode.model.scm import ScmModel
49 from rhodecode.model.scm import ScmModel
50 from rhodecode.model.settings import SettingsModel
50 from rhodecode.model.settings import SettingsModel
51 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
52
52
53 from sqlalchemy import func
53 from sqlalchemy import func
54
54
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class SettingsController(BaseController):
59 class SettingsController(BaseController):
60 """REST Controller styled on the Atom Publishing Protocol"""
60 """REST Controller styled on the Atom Publishing Protocol"""
61 # To properly map this controller, ensure your config/routing.py
61 # To properly map this controller, ensure your config/routing.py
62 # file has a resource setup:
62 # file has a resource setup:
63 # map.resource('setting', 'settings', controller='admin/settings',
63 # map.resource('setting', 'settings', controller='admin/settings',
64 # path_prefix='/admin', name_prefix='admin_')
64 # path_prefix='/admin', name_prefix='admin_')
65
65
66
66
67 @LoginRequired()
67 @LoginRequired()
68 def __before__(self):
68 def __before__(self):
69 c.admin_user = session.get('admin_user')
69 c.admin_user = session.get('admin_user')
70 c.admin_username = session.get('admin_username')
70 c.admin_username = session.get('admin_username')
71 super(SettingsController, self).__before__()
71 super(SettingsController, self).__before__()
72
72
73
73
74 @HasPermissionAllDecorator('hg.admin')
74 @HasPermissionAllDecorator('hg.admin')
75 def index(self, format='html'):
75 def index(self, format='html'):
76 """GET /admin/settings: All items in the collection"""
76 """GET /admin/settings: All items in the collection"""
77 # url('admin_settings')
77 # url('admin_settings')
78
78
79 defaults = SettingsModel().get_app_settings()
79 defaults = SettingsModel().get_app_settings()
80 defaults.update(self.get_hg_ui_settings())
80 defaults.update(self.get_hg_ui_settings())
81 return htmlfill.render(
81 return htmlfill.render(
82 render('admin/settings/settings.html'),
82 render('admin/settings/settings.html'),
83 defaults=defaults,
83 defaults=defaults,
84 encoding="UTF-8",
84 encoding="UTF-8",
85 force_defaults=False
85 force_defaults=False
86 )
86 )
87
87
88 @HasPermissionAllDecorator('hg.admin')
88 @HasPermissionAllDecorator('hg.admin')
89 def create(self):
89 def create(self):
90 """POST /admin/settings: Create a new item"""
90 """POST /admin/settings: Create a new item"""
91 # url('admin_settings')
91 # url('admin_settings')
92
92
93 @HasPermissionAllDecorator('hg.admin')
93 @HasPermissionAllDecorator('hg.admin')
94 def new(self, format='html'):
94 def new(self, format='html'):
95 """GET /admin/settings/new: Form to create a new item"""
95 """GET /admin/settings/new: Form to create a new item"""
96 # url('admin_new_setting')
96 # url('admin_new_setting')
97
97
98 @HasPermissionAllDecorator('hg.admin')
98 @HasPermissionAllDecorator('hg.admin')
99 def update(self, setting_id):
99 def update(self, setting_id):
100 """PUT /admin/settings/setting_id: Update an existing item"""
100 """PUT /admin/settings/setting_id: Update an existing item"""
101 # Forms posted to this method should contain a hidden field:
101 # Forms posted to this method should contain a hidden field:
102 # <input type="hidden" name="_method" value="PUT" />
102 # <input type="hidden" name="_method" value="PUT" />
103 # Or using helpers:
103 # Or using helpers:
104 # h.form(url('admin_setting', setting_id=ID),
104 # h.form(url('admin_setting', setting_id=ID),
105 # method='put')
105 # method='put')
106 # url('admin_setting', setting_id=ID)
106 # url('admin_setting', setting_id=ID)
107 if setting_id == 'mapping':
107 if setting_id == 'mapping':
108 rm_obsolete = request.POST.get('destroy', False)
108 rm_obsolete = request.POST.get('destroy', False)
109 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
109 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
110
110
111 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
111 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
112 for repo_name in initial.keys():
112 for repo_name in initial.keys():
113 invalidate_cache('get_repo_cached_%s' % repo_name)
113 invalidate_cache('get_repo_cached_%s' % repo_name)
114
114
115 repo2db_mapper(initial, rm_obsolete)
115 repo2db_mapper(initial, rm_obsolete)
116
116
117 h.flash(_('Repositories successfully rescanned'), category='success')
117 h.flash(_('Repositories successfully rescanned'), category='success')
118
118
119 if setting_id == 'whoosh':
119 if setting_id == 'whoosh':
120 repo_location = self.get_hg_ui_settings()['paths_root_path']
120 repo_location = self.get_hg_ui_settings()['paths_root_path']
121 full_index = request.POST.get('full_index', False)
121 full_index = request.POST.get('full_index', False)
122 task = run_task(tasks.whoosh_index, repo_location, full_index)
122 task = run_task(tasks.whoosh_index, repo_location, full_index)
123
123
124 h.flash(_('Whoosh reindex task scheduled'), category='success')
124 h.flash(_('Whoosh reindex task scheduled'), category='success')
125 if setting_id == 'global':
125 if setting_id == 'global':
126
126
127 application_form = ApplicationSettingsForm()()
127 application_form = ApplicationSettingsForm()()
128 try:
128 try:
129 form_result = application_form.to_python(dict(request.POST))
129 form_result = application_form.to_python(dict(request.POST))
130 settings_model = SettingsModel()
130 settings_model = SettingsModel()
131
131
132 try:
132 try:
133 hgsettings1 = settings_model.get('title')
133 hgsettings1 = settings_model.get('title')
134 hgsettings1.app_settings_value = form_result['rhodecode_title']
134 hgsettings1.app_settings_value = form_result['rhodecode_title']
135
135
136 hgsettings2 = settings_model.get('realm')
136 hgsettings2 = settings_model.get('realm')
137 hgsettings2.app_settings_value = form_result['rhodecode_realm']
137 hgsettings2.app_settings_value = form_result['rhodecode_realm']
138
138
139 hgsettings3 = settings_model.get('ga_code')
139 hgsettings3 = settings_model.get('ga_code')
140 hgsettings3.app_settings_value = form_result['rhodecode_ga_code']
140 hgsettings3.app_settings_value = form_result['rhodecode_ga_code']
141
141
142
142
143
143
144 self.sa.add(hgsettings1)
144 self.sa.add(hgsettings1)
145 self.sa.add(hgsettings2)
145 self.sa.add(hgsettings2)
146 self.sa.add(hgsettings3)
146 self.sa.add(hgsettings3)
147 self.sa.commit()
147 self.sa.commit()
148 set_rhodecode_config(config)
148 set_rhodecode_config(config)
149 h.flash(_('Updated application settings'),
149 h.flash(_('Updated application settings'),
150 category='success')
150 category='success')
151
151
152 except:
152 except:
153 log.error(traceback.format_exc())
153 log.error(traceback.format_exc())
154 h.flash(_('error occurred during updating application settings'),
154 h.flash(_('error occurred during updating application settings'),
155 category='error')
155 category='error')
156
156
157 self.sa.rollback()
157 self.sa.rollback()
158
158
159
159
160 except formencode.Invalid, errors:
160 except formencode.Invalid, errors:
161 return htmlfill.render(
161 return htmlfill.render(
162 render('admin/settings/settings.html'),
162 render('admin/settings/settings.html'),
163 defaults=errors.value,
163 defaults=errors.value,
164 errors=errors.error_dict or {},
164 errors=errors.error_dict or {},
165 prefix_error=False,
165 prefix_error=False,
166 encoding="UTF-8")
166 encoding="UTF-8")
167
167
168 if setting_id == 'mercurial':
168 if setting_id == 'mercurial':
169 application_form = ApplicationUiSettingsForm()()
169 application_form = ApplicationUiSettingsForm()()
170 try:
170 try:
171 form_result = application_form.to_python(dict(request.POST))
171 form_result = application_form.to_python(dict(request.POST))
172
172
173 try:
173 try:
174
174
175 hgsettings1 = self.sa.query(RhodeCodeUi)\
175 hgsettings1 = self.sa.query(RhodeCodeUi)\
176 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
176 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
177 hgsettings1.ui_value = form_result['web_push_ssl']
177 hgsettings1.ui_value = form_result['web_push_ssl']
178
178
179 hgsettings2 = self.sa.query(RhodeCodeUi)\
179 hgsettings2 = self.sa.query(RhodeCodeUi)\
180 .filter(RhodeCodeUi.ui_key == '/').one()
180 .filter(RhodeCodeUi.ui_key == '/').one()
181 hgsettings2.ui_value = form_result['paths_root_path']
181 hgsettings2.ui_value = form_result['paths_root_path']
182
182
183
183
184 #HOOKS
184 #HOOKS
185 hgsettings3 = self.sa.query(RhodeCodeUi)\
185 hgsettings3 = self.sa.query(RhodeCodeUi)\
186 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
186 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
187 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
187 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
188
188
189 hgsettings4 = self.sa.query(RhodeCodeUi)\
189 hgsettings4 = self.sa.query(RhodeCodeUi)\
190 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
190 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
191 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
191 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
192
192
193 hgsettings5 = self.sa.query(RhodeCodeUi)\
193 hgsettings5 = self.sa.query(RhodeCodeUi)\
194 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
194 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
195 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
195 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
196
196
197 hgsettings6 = self.sa.query(RhodeCodeUi)\
197 hgsettings6 = self.sa.query(RhodeCodeUi)\
198 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
198 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
199 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
199 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
200
200
201
201
202 self.sa.add(hgsettings1)
202 self.sa.add(hgsettings1)
203 self.sa.add(hgsettings2)
203 self.sa.add(hgsettings2)
204 self.sa.add(hgsettings3)
204 self.sa.add(hgsettings3)
205 self.sa.add(hgsettings4)
205 self.sa.add(hgsettings4)
206 self.sa.add(hgsettings5)
206 self.sa.add(hgsettings5)
207 self.sa.add(hgsettings6)
207 self.sa.add(hgsettings6)
208 self.sa.commit()
208 self.sa.commit()
209
209
210 h.flash(_('Updated mercurial settings'),
210 h.flash(_('Updated mercurial settings'),
211 category='success')
211 category='success')
212
212
213 except:
213 except:
214 log.error(traceback.format_exc())
214 log.error(traceback.format_exc())
215 h.flash(_('error occurred during updating application settings'),
215 h.flash(_('error occurred during updating application settings'),
216 category='error')
216 category='error')
217
217
218 self.sa.rollback()
218 self.sa.rollback()
219
219
220
220
221 except formencode.Invalid, errors:
221 except formencode.Invalid, errors:
222 return htmlfill.render(
222 return htmlfill.render(
223 render('admin/settings/settings.html'),
223 render('admin/settings/settings.html'),
224 defaults=errors.value,
224 defaults=errors.value,
225 errors=errors.error_dict or {},
225 errors=errors.error_dict or {},
226 prefix_error=False,
226 prefix_error=False,
227 encoding="UTF-8")
227 encoding="UTF-8")
228
228
229
229
230
230
231 return redirect(url('admin_settings'))
231 return redirect(url('admin_settings'))
232
232
233 @HasPermissionAllDecorator('hg.admin')
233 @HasPermissionAllDecorator('hg.admin')
234 def delete(self, setting_id):
234 def delete(self, setting_id):
235 """DELETE /admin/settings/setting_id: Delete an existing item"""
235 """DELETE /admin/settings/setting_id: Delete an existing item"""
236 # Forms posted to this method should contain a hidden field:
236 # Forms posted to this method should contain a hidden field:
237 # <input type="hidden" name="_method" value="DELETE" />
237 # <input type="hidden" name="_method" value="DELETE" />
238 # Or using helpers:
238 # Or using helpers:
239 # h.form(url('admin_setting', setting_id=ID),
239 # h.form(url('admin_setting', setting_id=ID),
240 # method='delete')
240 # method='delete')
241 # url('admin_setting', setting_id=ID)
241 # url('admin_setting', setting_id=ID)
242
242
243 @HasPermissionAllDecorator('hg.admin')
243 @HasPermissionAllDecorator('hg.admin')
244 def show(self, setting_id, format='html'):
244 def show(self, setting_id, format='html'):
245 """GET /admin/settings/setting_id: Show a specific item"""
245 """GET /admin/settings/setting_id: Show a specific item"""
246 # url('admin_setting', setting_id=ID)
246 # url('admin_setting', setting_id=ID)
247
247
248 @HasPermissionAllDecorator('hg.admin')
248 @HasPermissionAllDecorator('hg.admin')
249 def edit(self, setting_id, format='html'):
249 def edit(self, setting_id, format='html'):
250 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
250 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
251 # url('admin_edit_setting', setting_id=ID)
251 # url('admin_edit_setting', setting_id=ID)
252
252
253 @NotAnonymous()
253 @NotAnonymous()
254 def my_account(self):
254 def my_account(self):
255 """
255 """
256 GET /_admin/my_account Displays info about my account
256 GET /_admin/my_account Displays info about my account
257 """
257 """
258 # url('admin_settings_my_account')
258 # url('admin_settings_my_account')
259
259
260 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
260 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
261 all_repos = self.sa.query(Repository)\
261 all_repos = self.sa.query(Repository)\
262 .filter(Repository.user_id == c.user.user_id)\
262 .filter(Repository.user_id == c.user.user_id)\
263 .order_by(func.lower(Repository.repo_name))\
263 .order_by(func.lower(Repository.repo_name))\
264 .all()
264 .all()
265
265
266 c.user_repos = ScmModel().get_repos(all_repos)
266 c.user_repos = ScmModel().get_repos(all_repos)
267
267
268 if c.user.username == 'default':
268 if c.user.username == 'default':
269 h.flash(_("You can't edit this user since it's"
269 h.flash(_("You can't edit this user since it's"
270 " crucial for entire application"), category='warning')
270 " crucial for entire application"), category='warning')
271 return redirect(url('users'))
271 return redirect(url('users'))
272
272
273 defaults = c.user.get_dict()
273 defaults = c.user.get_dict()
274 return htmlfill.render(
274 return htmlfill.render(
275 render('admin/users/user_edit_my_account.html'),
275 render('admin/users/user_edit_my_account.html'),
276 defaults=defaults,
276 defaults=defaults,
277 encoding="UTF-8",
277 encoding="UTF-8",
278 force_defaults=False
278 force_defaults=False
279 )
279 )
280
280
281 def my_account_update(self):
281 def my_account_update(self):
282 """PUT /_admin/my_account_update: Update an existing item"""
282 """PUT /_admin/my_account_update: Update an existing item"""
283 # Forms posted to this method should contain a hidden field:
283 # Forms posted to this method should contain a hidden field:
284 # <input type="hidden" name="_method" value="PUT" />
284 # <input type="hidden" name="_method" value="PUT" />
285 # Or using helpers:
285 # Or using helpers:
286 # h.form(url('admin_settings_my_account_update'),
286 # h.form(url('admin_settings_my_account_update'),
287 # method='put')
287 # method='put')
288 # url('admin_settings_my_account_update', id=ID)
288 # url('admin_settings_my_account_update', id=ID)
289 user_model = UserModel()
289 user_model = UserModel()
290 uid = c.rhodecode_user.user_id
290 uid = c.rhodecode_user.user_id
291 _form = UserForm(edit=True, old_data={'user_id':uid,
291 _form = UserForm(edit=True, old_data={'user_id':uid,
292 'email':c.rhodecode_user.email})()
292 'email':c.rhodecode_user.email})()
293 form_result = {}
293 form_result = {}
294 try:
294 try:
295 form_result = _form.to_python(dict(request.POST))
295 form_result = _form.to_python(dict(request.POST))
296 user_model.update_my_account(uid, form_result)
296 user_model.update_my_account(uid, form_result)
297 h.flash(_('Your account was updated successfully'),
297 h.flash(_('Your account was updated successfully'),
298 category='success')
298 category='success')
299
299
300 except formencode.Invalid, errors:
300 except formencode.Invalid, errors:
301 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
301 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
302 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
302 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
303 all_repos = self.sa.query(Repository)\
303 all_repos = self.sa.query(Repository)\
304 .filter(Repository.user_id == c.user.user_id)\
304 .filter(Repository.user_id == c.user.user_id)\
305 .order_by(func.lower(Repository.repo_name))\
305 .order_by(func.lower(Repository.repo_name))\
306 .all()
306 .all()
307 c.user_repos = ScmModel().get_repos(all_repos)
307 c.user_repos = ScmModel().get_repos(all_repos)
308
308
309 return htmlfill.render(
309 return htmlfill.render(
310 render('admin/users/user_edit_my_account.html'),
310 render('admin/users/user_edit_my_account.html'),
311 defaults=errors.value,
311 defaults=errors.value,
312 errors=errors.error_dict or {},
312 errors=errors.error_dict or {},
313 prefix_error=False,
313 prefix_error=False,
314 encoding="UTF-8")
314 encoding="UTF-8")
315 except Exception:
315 except Exception:
316 log.error(traceback.format_exc())
316 log.error(traceback.format_exc())
317 h.flash(_('error occurred during update of user %s') \
317 h.flash(_('error occurred during update of user %s') \
318 % form_result.get('username'), category='error')
318 % form_result.get('username'), category='error')
319
319
320 return redirect(url('my_account'))
320 return redirect(url('my_account'))
321
321
322 @NotAnonymous()
322 @NotAnonymous()
323 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
323 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
324 def create_repository(self):
324 def create_repository(self):
325 """GET /_admin/create_repository: Form to create a new item"""
325 """GET /_admin/create_repository: Form to create a new item"""
326 new_repo = request.GET.get('repo', '')
326 new_repo = request.GET.get('repo', '')
327 c.new_repo = h.repo_name_slug(new_repo)
327 c.new_repo = h.repo_name_slug(new_repo)
328
328
329 return render('admin/repos/repo_add_create_repository.html')
329 return render('admin/repos/repo_add_create_repository.html')
330
330
331 def get_hg_ui_settings(self):
331 def get_hg_ui_settings(self):
332 ret = self.sa.query(RhodeCodeUi).all()
332 ret = self.sa.query(RhodeCodeUi).all()
333
333
334 if not ret:
334 if not ret:
335 raise Exception('Could not get application ui settings !')
335 raise Exception('Could not get application ui settings !')
336 settings = {}
336 settings = {}
337 for each in ret:
337 for each in ret:
338 k = each.ui_key
338 k = each.ui_key
339 v = each.ui_value
339 v = each.ui_value
340 if k == '/':
340 if k == '/':
341 k = 'root_path'
341 k = 'root_path'
342
342
343 if k.find('.') != -1:
343 if k.find('.') != -1:
344 k = k.replace('.', '_')
344 k = k.replace('.', '_')
345
345
346 if each.ui_section == 'hooks':
346 if each.ui_section == 'hooks':
347 v = each.ui_active
347 v = each.ui_active
348
348
349 settings[each.ui_section + '_' + k] = v
349 settings[each.ui_section + '_' + k] = v
350
350
351 return settings
351 return settings
@@ -1,176 +1,176 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users
3 rhodecode.controllers.admin.users
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Users crud controller for pylons
6 Users crud controller for pylons
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import formencode
30 import formencode
31
31
32 from formencode import htmlfill
32 from formencode import htmlfill
33 from pylons import request, session, tmpl_context as c, url, config
33 from pylons import request, session, tmpl_context as c, url, config
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36
36
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 fill_perms
40 fill_perms
41 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.base import BaseController, render
42
42
43 from rhodecode.model.db import User
43 from rhodecode.model.db import User
44 from rhodecode.model.forms import UserForm
44 from rhodecode.model.forms import UserForm
45 from rhodecode.model.user import UserModel
45 from rhodecode.model.user import UserModel
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49 class UsersController(BaseController):
49 class UsersController(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('user', 'users')
53 # map.resource('user', 'users')
54
54
55 @LoginRequired()
55 @LoginRequired()
56 @HasPermissionAllDecorator('hg.admin')
56 @HasPermissionAllDecorator('hg.admin')
57 def __before__(self):
57 def __before__(self):
58 c.admin_user = session.get('admin_user')
58 c.admin_user = session.get('admin_user')
59 c.admin_username = session.get('admin_username')
59 c.admin_username = session.get('admin_username')
60 super(UsersController, self).__before__()
60 super(UsersController, self).__before__()
61 c.available_permissions = config['available_permissions']
61 c.available_permissions = config['available_permissions']
62
62
63 def index(self, format='html'):
63 def index(self, format='html'):
64 """GET /users: All items in the collection"""
64 """GET /users: All items in the collection"""
65 # url('users')
65 # url('users')
66
66
67 c.users_list = self.sa.query(User).all()
67 c.users_list = self.sa.query(User).all()
68 return render('admin/users/users.html')
68 return render('admin/users/users.html')
69
69
70 def create(self):
70 def create(self):
71 """POST /users: Create a new item"""
71 """POST /users: Create a new item"""
72 # url('users')
72 # url('users')
73
73
74 user_model = UserModel()
74 user_model = UserModel()
75 login_form = UserForm()()
75 login_form = UserForm()()
76 try:
76 try:
77 form_result = login_form.to_python(dict(request.POST))
77 form_result = login_form.to_python(dict(request.POST))
78 user_model.create(form_result)
78 user_model.create(form_result)
79 h.flash(_('created user %s') % form_result['username'],
79 h.flash(_('created user %s') % form_result['username'],
80 category='success')
80 category='success')
81 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
81 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
82 except formencode.Invalid, errors:
82 except formencode.Invalid, errors:
83 return htmlfill.render(
83 return htmlfill.render(
84 render('admin/users/user_add.html'),
84 render('admin/users/user_add.html'),
85 defaults=errors.value,
85 defaults=errors.value,
86 errors=errors.error_dict or {},
86 errors=errors.error_dict or {},
87 prefix_error=False,
87 prefix_error=False,
88 encoding="UTF-8")
88 encoding="UTF-8")
89 except Exception:
89 except Exception:
90 log.error(traceback.format_exc())
90 log.error(traceback.format_exc())
91 h.flash(_('error occurred during creation of user %s') \
91 h.flash(_('error occurred during creation of user %s') \
92 % request.POST.get('username'), category='error')
92 % request.POST.get('username'), category='error')
93 return redirect(url('users'))
93 return redirect(url('users'))
94
94
95 def new(self, format='html'):
95 def new(self, format='html'):
96 """GET /users/new: Form to create a new item"""
96 """GET /users/new: Form to create a new item"""
97 # url('new_user')
97 # url('new_user')
98 return render('admin/users/user_add.html')
98 return render('admin/users/user_add.html')
99
99
100 def update(self, id):
100 def update(self, id):
101 """PUT /users/id: Update an existing item"""
101 """PUT /users/id: Update an existing item"""
102 # Forms posted to this method should contain a hidden field:
102 # Forms posted to this method should contain a hidden field:
103 # <input type="hidden" name="_method" value="PUT" />
103 # <input type="hidden" name="_method" value="PUT" />
104 # Or using helpers:
104 # Or using helpers:
105 # h.form(url('user', id=ID),
105 # h.form(url('user', id=ID),
106 # method='put')
106 # method='put')
107 # url('user', id=ID)
107 # url('user', id=ID)
108 user_model = UserModel()
108 user_model = UserModel()
109 c.user = user_model.get(id)
109 c.user = user_model.get(id)
110
110
111 _form = UserForm(edit=True, old_data={'user_id':id,
111 _form = UserForm(edit=True, old_data={'user_id':id,
112 'email':c.user.email})()
112 'email':c.user.email})()
113 form_result = {}
113 form_result = {}
114 try:
114 try:
115 form_result = _form.to_python(dict(request.POST))
115 form_result = _form.to_python(dict(request.POST))
116 user_model.update(id, form_result)
116 user_model.update(id, form_result)
117 h.flash(_('User updated succesfully'), category='success')
117 h.flash(_('User updated succesfully'), category='success')
118
118
119 except formencode.Invalid, errors:
119 except formencode.Invalid, errors:
120 return htmlfill.render(
120 return htmlfill.render(
121 render('admin/users/user_edit.html'),
121 render('admin/users/user_edit.html'),
122 defaults=errors.value,
122 defaults=errors.value,
123 errors=errors.error_dict or {},
123 errors=errors.error_dict or {},
124 prefix_error=False,
124 prefix_error=False,
125 encoding="UTF-8")
125 encoding="UTF-8")
126 except Exception:
126 except Exception:
127 log.error(traceback.format_exc())
127 log.error(traceback.format_exc())
128 h.flash(_('error occurred during update of user %s') \
128 h.flash(_('error occurred during update of user %s') \
129 % form_result.get('username'), category='error')
129 % form_result.get('username'), category='error')
130
130
131 return redirect(url('users'))
131 return redirect(url('users'))
132
132
133 def delete(self, id):
133 def delete(self, id):
134 """DELETE /users/id: Delete an existing item"""
134 """DELETE /users/id: Delete an existing item"""
135 # Forms posted to this method should contain a hidden field:
135 # Forms posted to this method should contain a hidden field:
136 # <input type="hidden" name="_method" value="DELETE" />
136 # <input type="hidden" name="_method" value="DELETE" />
137 # Or using helpers:
137 # Or using helpers:
138 # h.form(url('user', id=ID),
138 # h.form(url('user', id=ID),
139 # method='delete')
139 # method='delete')
140 # url('user', id=ID)
140 # url('user', id=ID)
141 user_model = UserModel()
141 user_model = UserModel()
142 try:
142 try:
143 user_model.delete(id)
143 user_model.delete(id)
144 h.flash(_('successfully deleted user'), category='success')
144 h.flash(_('successfully deleted user'), category='success')
145 except (UserOwnsReposException, DefaultUserException), e:
145 except (UserOwnsReposException, DefaultUserException), e:
146 h.flash(str(e), category='warning')
146 h.flash(str(e), category='warning')
147 except Exception:
147 except Exception:
148 h.flash(_('An error occurred during deletion of user'),
148 h.flash(_('An error occurred during deletion of user'),
149 category='error')
149 category='error')
150 return redirect(url('users'))
150 return redirect(url('users'))
151
151
152 def show(self, id, format='html'):
152 def show(self, id, format='html'):
153 """GET /users/id: Show a specific item"""
153 """GET /users/id: Show a specific item"""
154 # url('user', id=ID)
154 # url('user', id=ID)
155
155
156
156
157 def edit(self, id, format='html'):
157 def edit(self, id, format='html'):
158 """GET /users/id/edit: Form to edit an existing item"""
158 """GET /users/id/edit: Form to edit an existing item"""
159 # url('edit_user', id=ID)
159 # url('edit_user', id=ID)
160 c.user = self.sa.query(User).get(id)
160 c.user = self.sa.query(User).get(id)
161 if not c.user:
161 if not c.user:
162 return redirect(url('users'))
162 return redirect(url('users'))
163 if c.user.username == 'default':
163 if c.user.username == 'default':
164 h.flash(_("You can't edit this user"), category='warning')
164 h.flash(_("You can't edit this user"), category='warning')
165 return redirect(url('users'))
165 return redirect(url('users'))
166 c.user.permissions = {}
166 c.user.permissions = {}
167 c.granted_permissions = fill_perms(c.user).permissions['global']
167 c.granted_permissions = fill_perms(c.user).permissions['global']
168
168
169 defaults = c.user.get_dict()
169 defaults = c.user.get_dict()
170
170
171 return htmlfill.render(
171 return htmlfill.render(
172 render('admin/users/user_edit.html'),
172 render('admin/users/user_edit.html'),
173 defaults=defaults,
173 defaults=defaults,
174 encoding="UTF-8",
174 encoding="UTF-8",
175 force_defaults=False
175 force_defaults=False
176 )
176 )
@@ -1,54 +1,54 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.branches
3 rhodecode.controllers.branches
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 branches controller for rhodecode
6 branches controller for rhodecode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29
29
30 from pylons import tmpl_context as c
30 from pylons import tmpl_context as c
31
31
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.utils import OrderedDict
34 from rhodecode.lib.utils import OrderedDict
35 from rhodecode.model.scm import ScmModel
35 from rhodecode.model.scm import ScmModel
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 class BranchesController(BaseController):
39 class BranchesController(BaseController):
40
40
41 @LoginRequired()
41 @LoginRequired()
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 'repository.admin')
43 'repository.admin')
44 def __before__(self):
44 def __before__(self):
45 super(BranchesController, self).__before__()
45 super(BranchesController, self).__before__()
46
46
47 def index(self):
47 def index(self):
48 hg_model = ScmModel()
48 hg_model = ScmModel()
49 c.repo_info = hg_model.get_repo(c.repo_name)
49 c.repo_info = hg_model.get_repo(c.repo_name)
50 c.repo_branches = OrderedDict()
50 c.repo_branches = OrderedDict()
51 for name, hash_ in c.repo_info.branches.items():
51 for name, hash_ in c.repo_info.branches.items():
52 c.repo_branches[name] = c.repo_info.get_changeset(hash_)
52 c.repo_branches[name] = c.repo_info.get_changeset(hash_)
53
53
54 return render('branches/branches.html')
54 return render('branches/branches.html')
@@ -1,106 +1,106 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changelog
3 rhodecode.controllers.changelog
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 changelog controller for rhodecode
6 changelog controller for rhodecode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29
29
30 try:
30 try:
31 import json
31 import json
32 except ImportError:
32 except ImportError:
33 #python 2.5 compatibility
33 #python 2.5 compatibility
34 import simplejson as json
34 import simplejson as json
35
35
36 from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev
36 from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev
37 from pylons import request, session, tmpl_context as c
37 from pylons import request, session, tmpl_context as c
38
38
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.model.scm import ScmModel
41 from rhodecode.model.scm import ScmModel
42
42
43 from webhelpers.paginate import Page
43 from webhelpers.paginate import Page
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47 class ChangelogController(BaseController):
47 class ChangelogController(BaseController):
48
48
49 @LoginRequired()
49 @LoginRequired()
50 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
50 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
51 'repository.admin')
51 'repository.admin')
52 def __before__(self):
52 def __before__(self):
53 super(ChangelogController, self).__before__()
53 super(ChangelogController, self).__before__()
54
54
55 def index(self):
55 def index(self):
56 limit = 100
56 limit = 100
57 default = 20
57 default = 20
58 if request.params.get('size'):
58 if request.params.get('size'):
59 try:
59 try:
60 int_size = int(request.params.get('size'))
60 int_size = int(request.params.get('size'))
61 except ValueError:
61 except ValueError:
62 int_size = default
62 int_size = default
63 int_size = int_size if int_size <= limit else limit
63 int_size = int_size if int_size <= limit else limit
64 c.size = int_size
64 c.size = int_size
65 session['changelog_size'] = c.size
65 session['changelog_size'] = c.size
66 session.save()
66 session.save()
67 else:
67 else:
68 c.size = int(session.get('changelog_size', default))
68 c.size = int(session.get('changelog_size', default))
69
69
70 changesets = ScmModel().get_repo(c.repo_name)
70 changesets = ScmModel().get_repo(c.repo_name)
71
71
72 p = int(request.params.get('page', 1))
72 p = int(request.params.get('page', 1))
73 c.total_cs = len(changesets)
73 c.total_cs = len(changesets)
74 c.pagination = Page(changesets, page=p, item_count=c.total_cs,
74 c.pagination = Page(changesets, page=p, item_count=c.total_cs,
75 items_per_page=c.size)
75 items_per_page=c.size)
76
76
77 self._graph(changesets, c.size, p)
77 self._graph(changesets, c.size, p)
78
78
79 return render('changelog/changelog.html')
79 return render('changelog/changelog.html')
80
80
81
81
82 def _graph(self, repo, size, p):
82 def _graph(self, repo, size, p):
83 revcount = size
83 revcount = size
84 if not repo.revisions or repo.alias == 'git':
84 if not repo.revisions or repo.alias == 'git':
85 c.jsdata = json.dumps([])
85 c.jsdata = json.dumps([])
86 return
86 return
87
87
88 max_rev = repo.revisions[-1]
88 max_rev = repo.revisions[-1]
89
89
90 offset = 1 if p == 1 else ((p - 1) * revcount + 1)
90 offset = 1 if p == 1 else ((p - 1) * revcount + 1)
91
91
92 rev_start = repo.revisions[(-1 * offset)]
92 rev_start = repo.revisions[(-1 * offset)]
93
93
94 revcount = min(max_rev, revcount)
94 revcount = min(max_rev, revcount)
95 rev_end = max(0, rev_start - revcount)
95 rev_end = max(0, rev_start - revcount)
96 dag = graph_rev(repo.repo, rev_start, rev_end)
96 dag = graph_rev(repo.repo, rev_start, rev_end)
97
97
98 c.dag = tree = list(colored(dag))
98 c.dag = tree = list(colored(dag))
99 data = []
99 data = []
100 for (id, type, ctx, vtx, edges) in tree:
100 for (id, type, ctx, vtx, edges) in tree:
101 if type != CHANGESET:
101 if type != CHANGESET:
102 continue
102 continue
103 data.append(('', vtx, edges))
103 data.append(('', vtx, edges))
104
104
105 c.jsdata = json.dumps(data)
105 c.jsdata = json.dumps(data)
106
106
@@ -1,192 +1,192 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.changeset
3 rhodecode.controllers.changeset
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 changeset controller for pylons
6 changeset controller for pylons
7
7
8 :created_on: Apr 25, 2010
8 :created_on: Apr 25, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from pylons import tmpl_context as c, url, request, response
30 from pylons import tmpl_context as c, url, request, response
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32 from pylons.controllers.util import redirect
32 from pylons.controllers.util import redirect
33
33
34 import rhodecode.lib.helpers as h
34 import rhodecode.lib.helpers as h
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.utils import EmptyChangeset
37 from rhodecode.lib.utils import EmptyChangeset
38 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.scm import ScmModel
39
39
40 from vcs.exceptions import RepositoryError, ChangesetError
40 from vcs.exceptions import RepositoryError, ChangesetError
41 from vcs.nodes import FileNode
41 from vcs.nodes import FileNode
42 from vcs.utils import diffs as differ
42 from vcs.utils import diffs as differ
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class ChangesetController(BaseController):
46 class ChangesetController(BaseController):
47
47
48 @LoginRequired()
48 @LoginRequired()
49 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
49 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
50 'repository.admin')
50 'repository.admin')
51 def __before__(self):
51 def __before__(self):
52 super(ChangesetController, self).__before__()
52 super(ChangesetController, self).__before__()
53
53
54 def index(self, revision):
54 def index(self, revision):
55 hg_model = ScmModel()
55 hg_model = ScmModel()
56
56
57 def wrap_to_table(str):
57 def wrap_to_table(str):
58
58
59 return '''<table class="code-difftable">
59 return '''<table class="code-difftable">
60 <tr class="line">
60 <tr class="line">
61 <td class="lineno new"></td>
61 <td class="lineno new"></td>
62 <td class="code"><pre>%s</pre></td>
62 <td class="code"><pre>%s</pre></td>
63 </tr>
63 </tr>
64 </table>''' % str
64 </table>''' % str
65
65
66 try:
66 try:
67 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
67 c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision)
68 except RepositoryError, e:
68 except RepositoryError, e:
69 log.error(traceback.format_exc())
69 log.error(traceback.format_exc())
70 h.flash(str(e), category='warning')
70 h.flash(str(e), category='warning')
71 return redirect(url('home'))
71 return redirect(url('home'))
72 else:
72 else:
73 try:
73 try:
74 c.changeset_old = c.changeset.parents[0]
74 c.changeset_old = c.changeset.parents[0]
75 except IndexError:
75 except IndexError:
76 c.changeset_old = None
76 c.changeset_old = None
77 c.changes = []
77 c.changes = []
78
78
79 #===================================================================
79 #===================================================================
80 # ADDED FILES
80 # ADDED FILES
81 #===================================================================
81 #===================================================================
82 c.sum_added = 0
82 c.sum_added = 0
83 for node in c.changeset.added:
83 for node in c.changeset.added:
84
84
85 filenode_old = FileNode(node.path, '', EmptyChangeset())
85 filenode_old = FileNode(node.path, '', EmptyChangeset())
86 if filenode_old.is_binary or node.is_binary:
86 if filenode_old.is_binary or node.is_binary:
87 diff = wrap_to_table(_('binary file'))
87 diff = wrap_to_table(_('binary file'))
88 else:
88 else:
89 c.sum_added += node.size
89 c.sum_added += node.size
90 if c.sum_added < self.cut_off_limit:
90 if c.sum_added < self.cut_off_limit:
91 f_udiff = differ.get_udiff(filenode_old, node)
91 f_udiff = differ.get_udiff(filenode_old, node)
92 diff = differ.DiffProcessor(f_udiff).as_html()
92 diff = differ.DiffProcessor(f_udiff).as_html()
93
93
94 else:
94 else:
95 diff = wrap_to_table(_('Changeset is to big and was cut'
95 diff = wrap_to_table(_('Changeset is to big and was cut'
96 ' off, see raw changeset instead'))
96 ' off, see raw changeset instead'))
97
97
98 cs1 = None
98 cs1 = None
99 cs2 = node.last_changeset.raw_id
99 cs2 = node.last_changeset.raw_id
100 c.changes.append(('added', node, diff, cs1, cs2))
100 c.changes.append(('added', node, diff, cs1, cs2))
101
101
102 #===================================================================
102 #===================================================================
103 # CHANGED FILES
103 # CHANGED FILES
104 #===================================================================
104 #===================================================================
105 c.sum_removed = 0
105 c.sum_removed = 0
106 for node in c.changeset.changed:
106 for node in c.changeset.changed:
107 try:
107 try:
108 filenode_old = c.changeset_old.get_node(node.path)
108 filenode_old = c.changeset_old.get_node(node.path)
109 except ChangesetError:
109 except ChangesetError:
110 filenode_old = FileNode(node.path, '', EmptyChangeset())
110 filenode_old = FileNode(node.path, '', EmptyChangeset())
111
111
112 if filenode_old.is_binary or node.is_binary:
112 if filenode_old.is_binary or node.is_binary:
113 diff = wrap_to_table(_('binary file'))
113 diff = wrap_to_table(_('binary file'))
114 else:
114 else:
115
115
116 if c.sum_removed < self.cut_off_limit:
116 if c.sum_removed < self.cut_off_limit:
117 f_udiff = differ.get_udiff(filenode_old, node)
117 f_udiff = differ.get_udiff(filenode_old, node)
118 diff = differ.DiffProcessor(f_udiff).as_html()
118 diff = differ.DiffProcessor(f_udiff).as_html()
119 if diff:
119 if diff:
120 c.sum_removed += len(diff)
120 c.sum_removed += len(diff)
121 else:
121 else:
122 diff = wrap_to_table(_('Changeset is to big and was cut'
122 diff = wrap_to_table(_('Changeset is to big and was cut'
123 ' off, see raw changeset instead'))
123 ' off, see raw changeset instead'))
124
124
125
125
126 cs1 = filenode_old.last_changeset.raw_id
126 cs1 = filenode_old.last_changeset.raw_id
127 cs2 = node.last_changeset.raw_id
127 cs2 = node.last_changeset.raw_id
128 c.changes.append(('changed', node, diff, cs1, cs2))
128 c.changes.append(('changed', node, diff, cs1, cs2))
129
129
130 #===================================================================
130 #===================================================================
131 # REMOVED FILES
131 # REMOVED FILES
132 #===================================================================
132 #===================================================================
133 for node in c.changeset.removed:
133 for node in c.changeset.removed:
134 c.changes.append(('removed', node, None, None, None))
134 c.changes.append(('removed', node, None, None, None))
135
135
136 return render('changeset/changeset.html')
136 return render('changeset/changeset.html')
137
137
138 def raw_changeset(self, revision):
138 def raw_changeset(self, revision):
139
139
140 hg_model = ScmModel()
140 hg_model = ScmModel()
141 method = request.GET.get('diff', 'show')
141 method = request.GET.get('diff', 'show')
142 try:
142 try:
143 r = hg_model.get_repo(c.repo_name)
143 r = hg_model.get_repo(c.repo_name)
144 c.scm_type = r.alias
144 c.scm_type = r.alias
145 c.changeset = r.get_changeset(revision)
145 c.changeset = r.get_changeset(revision)
146 except RepositoryError:
146 except RepositoryError:
147 log.error(traceback.format_exc())
147 log.error(traceback.format_exc())
148 return redirect(url('home'))
148 return redirect(url('home'))
149 else:
149 else:
150 try:
150 try:
151 c.changeset_old = c.changeset.parents[0]
151 c.changeset_old = c.changeset.parents[0]
152 except IndexError:
152 except IndexError:
153 c.changeset_old = None
153 c.changeset_old = None
154 c.changes = []
154 c.changes = []
155
155
156 for node in c.changeset.added:
156 for node in c.changeset.added:
157 filenode_old = FileNode(node.path, '')
157 filenode_old = FileNode(node.path, '')
158 if filenode_old.is_binary or node.is_binary:
158 if filenode_old.is_binary or node.is_binary:
159 diff = _('binary file') + '\n'
159 diff = _('binary file') + '\n'
160 else:
160 else:
161 f_udiff = differ.get_udiff(filenode_old, node)
161 f_udiff = differ.get_udiff(filenode_old, node)
162 diff = differ.DiffProcessor(f_udiff).raw_diff()
162 diff = differ.DiffProcessor(f_udiff).raw_diff()
163
163
164 cs1 = None
164 cs1 = None
165 cs2 = node.last_changeset.raw_id
165 cs2 = node.last_changeset.raw_id
166 c.changes.append(('added', node, diff, cs1, cs2))
166 c.changes.append(('added', node, diff, cs1, cs2))
167
167
168 for node in c.changeset.changed:
168 for node in c.changeset.changed:
169 filenode_old = c.changeset_old.get_node(node.path)
169 filenode_old = c.changeset_old.get_node(node.path)
170 if filenode_old.is_binary or node.is_binary:
170 if filenode_old.is_binary or node.is_binary:
171 diff = _('binary file')
171 diff = _('binary file')
172 else:
172 else:
173 f_udiff = differ.get_udiff(filenode_old, node)
173 f_udiff = differ.get_udiff(filenode_old, node)
174 diff = differ.DiffProcessor(f_udiff).raw_diff()
174 diff = differ.DiffProcessor(f_udiff).raw_diff()
175
175
176 cs1 = filenode_old.last_changeset.raw_id
176 cs1 = filenode_old.last_changeset.raw_id
177 cs2 = node.last_changeset.raw_id
177 cs2 = node.last_changeset.raw_id
178 c.changes.append(('changed', node, diff, cs1, cs2))
178 c.changes.append(('changed', node, diff, cs1, cs2))
179
179
180 response.content_type = 'text/plain'
180 response.content_type = 'text/plain'
181
181
182 if method == 'download':
182 if method == 'download':
183 response.content_disposition = 'attachment; filename=%s.patch' % revision
183 response.content_disposition = 'attachment; filename=%s.patch' % revision
184
184
185 parent = True if len(c.changeset.parents) > 0 else False
185 parent = True if len(c.changeset.parents) > 0 else False
186 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0].raw_id if parent else ''
186 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0].raw_id if parent else ''
187
187
188 c.diffs = ''
188 c.diffs = ''
189 for x in c.changes:
189 for x in c.changes:
190 c.diffs += x[2]
190 c.diffs += x[2]
191
191
192 return render('changeset/raw_changeset.html')
192 return render('changeset/raw_changeset.html')
@@ -1,110 +1,110 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.error
3 rhodecode.controllers.error
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode error controller
6 RhodeCode error controller
7
7
8 :created_on: Dec 8, 2010
8 :created_on: Dec 8, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import os
27 import os
28 import cgi
28 import cgi
29 import logging
29 import logging
30 import paste.fileapp
30 import paste.fileapp
31
31
32 from pylons import tmpl_context as c, request
32 from pylons import tmpl_context as c, request
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from pylons.middleware import media_path
34 from pylons.middleware import media_path
35
35
36 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib.base import BaseController, render
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40 class ErrorController(BaseController):
40 class ErrorController(BaseController):
41 """Generates error documents as and when they are required.
41 """Generates error documents as and when they are required.
42
42
43 The ErrorDocuments middleware forwards to ErrorController when error
43 The ErrorDocuments middleware forwards to ErrorController when error
44 related status codes are returned from the application.
44 related status codes are returned from the application.
45
45
46 This behavior can be altered by changing the parameters to the
46 This behavior can be altered by changing the parameters to the
47 ErrorDocuments middleware in your config/middleware.py file.
47 ErrorDocuments middleware in your config/middleware.py file.
48 """
48 """
49
49
50 def __before__(self):
50 def __before__(self):
51 pass#disable all base actions since we don't need them here
51 pass#disable all base actions since we don't need them here
52
52
53 def document(self):
53 def document(self):
54 resp = request.environ.get('pylons.original_response')
54 resp = request.environ.get('pylons.original_response')
55
55
56 log.debug('### %s ###', resp.status)
56 log.debug('### %s ###', resp.status)
57
57
58 e = request.environ
58 e = request.environ
59 c.serv_p = r'%(protocol)s://%(host)s/' % {
59 c.serv_p = r'%(protocol)s://%(host)s/' % {
60 'protocol': e.get('wsgi.url_scheme'),
60 'protocol': e.get('wsgi.url_scheme'),
61 'host':e.get('HTTP_HOST'),
61 'host':e.get('HTTP_HOST'),
62 }
62 }
63
63
64
64
65 c.error_message = cgi.escape(request.GET.get('code', str(resp.status)))
65 c.error_message = cgi.escape(request.GET.get('code', str(resp.status)))
66 c.error_explanation = self.get_error_explanation(resp.status_int)
66 c.error_explanation = self.get_error_explanation(resp.status_int)
67
67
68 #redirect to when error with given seconds
68 #redirect to when error with given seconds
69 c.redirect_time = 0
69 c.redirect_time = 0
70 c.redirect_module = _('Home page')# name to what your going to be redirected
70 c.redirect_module = _('Home page')# name to what your going to be redirected
71 c.url_redirect = "/"
71 c.url_redirect = "/"
72
72
73 return render('/errors/error_document.html')
73 return render('/errors/error_document.html')
74
74
75
75
76 def img(self, id):
76 def img(self, id):
77 """Serve Pylons' stock images"""
77 """Serve Pylons' stock images"""
78 return self._serve_file(os.path.join(media_path, 'img', id))
78 return self._serve_file(os.path.join(media_path, 'img', id))
79
79
80 def style(self, id):
80 def style(self, id):
81 """Serve Pylons' stock stylesheets"""
81 """Serve Pylons' stock stylesheets"""
82 return self._serve_file(os.path.join(media_path, 'style', id))
82 return self._serve_file(os.path.join(media_path, 'style', id))
83
83
84 def _serve_file(self, path):
84 def _serve_file(self, path):
85 """Call Paste's FileApp (a WSGI application) to serve the file
85 """Call Paste's FileApp (a WSGI application) to serve the file
86 at the specified path
86 at the specified path
87 """
87 """
88 fapp = paste.fileapp.FileApp(path)
88 fapp = paste.fileapp.FileApp(path)
89 return fapp(request.environ, self.start_response)
89 return fapp(request.environ, self.start_response)
90
90
91 def get_error_explanation(self, code):
91 def get_error_explanation(self, code):
92 ''' get the error explanations of int codes
92 ''' get the error explanations of int codes
93 [400, 401, 403, 404, 500]'''
93 [400, 401, 403, 404, 500]'''
94 try:
94 try:
95 code = int(code)
95 code = int(code)
96 except:
96 except:
97 code = 500
97 code = 500
98
98
99 if code == 400:
99 if code == 400:
100 return _('The request could not be understood by the server due to malformed syntax.')
100 return _('The request could not be understood by the server due to malformed syntax.')
101 if code == 401:
101 if code == 401:
102 return _('Unauthorized access to resource')
102 return _('Unauthorized access to resource')
103 if code == 403:
103 if code == 403:
104 return _("You don't have permission to view this page")
104 return _("You don't have permission to view this page")
105 if code == 404:
105 if code == 404:
106 return _('The resource could not be found')
106 return _('The resource could not be found')
107 if code == 500:
107 if code == 500:
108 return _('The server encountered an unexpected condition which prevented it from fulfilling the request.')
108 return _('The server encountered an unexpected condition which prevented it from fulfilling the request.')
109
109
110
110
@@ -1,90 +1,90 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.feed
3 rhodecode.controllers.feed
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Feed controller for rhodecode
6 Feed controller for rhodecode
7
7
8 :created_on: Apr 23, 2010
8 :created_on: Apr 23, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29
29
30 from pylons import url, response
30 from pylons import url, response
31
31
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
33 from rhodecode.lib.base import BaseController
33 from rhodecode.lib.base import BaseController
34 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.scm import ScmModel
35
35
36 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
36 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40 class FeedController(BaseController):
40 class FeedController(BaseController):
41
41
42 @LoginRequired()
42 @LoginRequired()
43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 'repository.admin')
44 'repository.admin')
45 def __before__(self):
45 def __before__(self):
46 super(FeedController, self).__before__()
46 super(FeedController, self).__before__()
47 #common values for feeds
47 #common values for feeds
48 self.description = 'Changes on %s repository'
48 self.description = 'Changes on %s repository'
49 self.title = "%s feed"
49 self.title = "%s feed"
50 self.language = 'en-us'
50 self.language = 'en-us'
51 self.ttl = "5"
51 self.ttl = "5"
52 self.feed_nr = 10
52 self.feed_nr = 10
53
53
54 def atom(self, repo_name):
54 def atom(self, repo_name):
55 """Produce an atom-1.0 feed via feedgenerator module"""
55 """Produce an atom-1.0 feed via feedgenerator module"""
56 feed = Atom1Feed(title=self.title % repo_name,
56 feed = Atom1Feed(title=self.title % repo_name,
57 link=url('summary_home', repo_name=repo_name, qualified=True),
57 link=url('summary_home', repo_name=repo_name, qualified=True),
58 description=self.description % repo_name,
58 description=self.description % repo_name,
59 language=self.language,
59 language=self.language,
60 ttl=self.ttl)
60 ttl=self.ttl)
61
61
62 changesets = ScmModel().get_repo(repo_name)
62 changesets = ScmModel().get_repo(repo_name)
63
63
64 for cs in changesets[:self.feed_nr]:
64 for cs in changesets[:self.feed_nr]:
65 feed.add_item(title=cs.message,
65 feed.add_item(title=cs.message,
66 link=url('changeset_home', repo_name=repo_name,
66 link=url('changeset_home', repo_name=repo_name,
67 revision=cs.raw_id, qualified=True),
67 revision=cs.raw_id, qualified=True),
68 description=str(cs.date))
68 description=str(cs.date))
69
69
70 response.content_type = feed.mime_type
70 response.content_type = feed.mime_type
71 return feed.writeString('utf-8')
71 return feed.writeString('utf-8')
72
72
73
73
74 def rss(self, repo_name):
74 def rss(self, repo_name):
75 """Produce an rss2 feed via feedgenerator module"""
75 """Produce an rss2 feed via feedgenerator module"""
76 feed = Rss201rev2Feed(title=self.title % repo_name,
76 feed = Rss201rev2Feed(title=self.title % repo_name,
77 link=url('summary_home', repo_name=repo_name, qualified=True),
77 link=url('summary_home', repo_name=repo_name, qualified=True),
78 description=self.description % repo_name,
78 description=self.description % repo_name,
79 language=self.language,
79 language=self.language,
80 ttl=self.ttl)
80 ttl=self.ttl)
81
81
82 changesets = ScmModel().get_repo(repo_name)
82 changesets = ScmModel().get_repo(repo_name)
83 for cs in changesets[:self.feed_nr]:
83 for cs in changesets[:self.feed_nr]:
84 feed.add_item(title=cs.message,
84 feed.add_item(title=cs.message,
85 link=url('changeset_home', repo_name=repo_name,
85 link=url('changeset_home', repo_name=repo_name,
86 revision=cs.raw_id, qualified=True),
86 revision=cs.raw_id, qualified=True),
87 description=str(cs.date))
87 description=str(cs.date))
88
88
89 response.content_type = feed.mime_type
89 response.content_type = feed.mime_type
90 return feed.writeString('utf-8')
90 return feed.writeString('utf-8')
@@ -1,268 +1,268 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.files
3 rhodecode.controllers.files
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Files controller for RhodeCode
6 Files controller for RhodeCode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import tempfile
27 import tempfile
28 import logging
28 import logging
29 import rhodecode.lib.helpers as h
29 import rhodecode.lib.helpers as h
30
30
31 from mercurial import archival
31 from mercurial import archival
32
32
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36
36
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
38 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.utils import EmptyChangeset
39 from rhodecode.lib.utils import EmptyChangeset
40 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.scm import ScmModel
41
41
42 from vcs.exceptions import RepositoryError, ChangesetError, ChangesetDoesNotExistError
42 from vcs.exceptions import RepositoryError, ChangesetError, ChangesetDoesNotExistError
43 from vcs.nodes import FileNode
43 from vcs.nodes import FileNode
44 from vcs.utils import diffs as differ
44 from vcs.utils import diffs as differ
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48 class FilesController(BaseController):
48 class FilesController(BaseController):
49
49
50 @LoginRequired()
50 @LoginRequired()
51 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
51 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
52 'repository.admin')
52 'repository.admin')
53 def __before__(self):
53 def __before__(self):
54 super(FilesController, self).__before__()
54 super(FilesController, self).__before__()
55 c.cut_off_limit = self.cut_off_limit
55 c.cut_off_limit = self.cut_off_limit
56
56
57 def index(self, repo_name, revision, f_path):
57 def index(self, repo_name, revision, f_path):
58 hg_model = ScmModel()
58 hg_model = ScmModel()
59 c.repo = hg_model.get_repo(c.repo_name)
59 c.repo = hg_model.get_repo(c.repo_name)
60
60
61 try:
61 try:
62 #reditect to given revision from form
62 #reditect to given revision from form
63 post_revision = request.POST.get('at_rev', None)
63 post_revision = request.POST.get('at_rev', None)
64 if post_revision:
64 if post_revision:
65 post_revision = c.repo.get_changeset(post_revision).raw_id
65 post_revision = c.repo.get_changeset(post_revision).raw_id
66 redirect(url('files_home', repo_name=c.repo_name,
66 redirect(url('files_home', repo_name=c.repo_name,
67 revision=post_revision, f_path=f_path))
67 revision=post_revision, f_path=f_path))
68
68
69 c.branch = request.GET.get('branch', None)
69 c.branch = request.GET.get('branch', None)
70
70
71 c.f_path = f_path
71 c.f_path = f_path
72
72
73 c.changeset = c.repo.get_changeset(revision)
73 c.changeset = c.repo.get_changeset(revision)
74 cur_rev = c.changeset.revision
74 cur_rev = c.changeset.revision
75
75
76 #prev link
76 #prev link
77 try:
77 try:
78 prev_rev = c.repo.get_changeset(cur_rev).prev(c.branch).raw_id
78 prev_rev = c.repo.get_changeset(cur_rev).prev(c.branch).raw_id
79 c.url_prev = url('files_home', repo_name=c.repo_name,
79 c.url_prev = url('files_home', repo_name=c.repo_name,
80 revision=prev_rev, f_path=f_path)
80 revision=prev_rev, f_path=f_path)
81 if c.branch:
81 if c.branch:
82 c.url_prev += '?branch=%s' % c.branch
82 c.url_prev += '?branch=%s' % c.branch
83 except ChangesetDoesNotExistError:
83 except ChangesetDoesNotExistError:
84 c.url_prev = '#'
84 c.url_prev = '#'
85
85
86 #next link
86 #next link
87 try:
87 try:
88 next_rev = c.repo.get_changeset(cur_rev).next(c.branch).raw_id
88 next_rev = c.repo.get_changeset(cur_rev).next(c.branch).raw_id
89 c.url_next = url('files_home', repo_name=c.repo_name,
89 c.url_next = url('files_home', repo_name=c.repo_name,
90 revision=next_rev, f_path=f_path)
90 revision=next_rev, f_path=f_path)
91 if c.branch:
91 if c.branch:
92 c.url_next += '?branch=%s' % c.branch
92 c.url_next += '?branch=%s' % c.branch
93 except ChangesetDoesNotExistError:
93 except ChangesetDoesNotExistError:
94 c.url_next = '#'
94 c.url_next = '#'
95
95
96 #files
96 #files
97 try:
97 try:
98 c.files_list = c.changeset.get_node(f_path)
98 c.files_list = c.changeset.get_node(f_path)
99 c.file_history = self._get_history(c.repo, c.files_list, f_path)
99 c.file_history = self._get_history(c.repo, c.files_list, f_path)
100 except RepositoryError, e:
100 except RepositoryError, e:
101 h.flash(str(e), category='warning')
101 h.flash(str(e), category='warning')
102 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
102 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
103
103
104 except RepositoryError, e:
104 except RepositoryError, e:
105 h.flash(str(e), category='warning')
105 h.flash(str(e), category='warning')
106 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
106 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
107
107
108
108
109
109
110 return render('files/files.html')
110 return render('files/files.html')
111
111
112 def rawfile(self, repo_name, revision, f_path):
112 def rawfile(self, repo_name, revision, f_path):
113 hg_model = ScmModel()
113 hg_model = ScmModel()
114 c.repo = hg_model.get_repo(c.repo_name)
114 c.repo = hg_model.get_repo(c.repo_name)
115 file_node = c.repo.get_changeset(revision).get_node(f_path)
115 file_node = c.repo.get_changeset(revision).get_node(f_path)
116 response.content_type = file_node.mimetype
116 response.content_type = file_node.mimetype
117 response.content_disposition = 'attachment; filename=%s' \
117 response.content_disposition = 'attachment; filename=%s' \
118 % f_path.split('/')[-1]
118 % f_path.split('/')[-1]
119 return file_node.content
119 return file_node.content
120
120
121 def raw(self, repo_name, revision, f_path):
121 def raw(self, repo_name, revision, f_path):
122 hg_model = ScmModel()
122 hg_model = ScmModel()
123 c.repo = hg_model.get_repo(c.repo_name)
123 c.repo = hg_model.get_repo(c.repo_name)
124 file_node = c.repo.get_changeset(revision).get_node(f_path)
124 file_node = c.repo.get_changeset(revision).get_node(f_path)
125 response.content_type = 'text/plain'
125 response.content_type = 'text/plain'
126
126
127 return file_node.content
127 return file_node.content
128
128
129 def annotate(self, repo_name, revision, f_path):
129 def annotate(self, repo_name, revision, f_path):
130 hg_model = ScmModel()
130 hg_model = ScmModel()
131 c.repo = hg_model.get_repo(c.repo_name)
131 c.repo = hg_model.get_repo(c.repo_name)
132
132
133 try:
133 try:
134 c.cs = c.repo.get_changeset(revision)
134 c.cs = c.repo.get_changeset(revision)
135 c.file = c.cs.get_node(f_path)
135 c.file = c.cs.get_node(f_path)
136 except RepositoryError, e:
136 except RepositoryError, e:
137 h.flash(str(e), category='warning')
137 h.flash(str(e), category='warning')
138 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
138 redirect(h.url('files_home', repo_name=repo_name, revision=revision))
139
139
140 c.file_history = self._get_history(c.repo, c.file, f_path)
140 c.file_history = self._get_history(c.repo, c.file, f_path)
141
141
142 c.f_path = f_path
142 c.f_path = f_path
143
143
144 return render('files/files_annotate.html')
144 return render('files/files_annotate.html')
145
145
146 def archivefile(self, repo_name, fname):
146 def archivefile(self, repo_name, fname):
147 info = fname.split('.')
147 info = fname.split('.')
148 revision, fileformat = info[0], '.' + '.'.join(info[1:])
148 revision, fileformat = info[0], '.' + '.'.join(info[1:])
149 archive_specs = {
149 archive_specs = {
150 '.tar.bz2': ('application/x-tar', 'tbz2'),
150 '.tar.bz2': ('application/x-tar', 'tbz2'),
151 '.tar.gz': ('application/x-tar', 'tgz'),
151 '.tar.gz': ('application/x-tar', 'tgz'),
152 '.zip': ('application/zip', 'zip'),
152 '.zip': ('application/zip', 'zip'),
153 }
153 }
154 if not archive_specs.has_key(fileformat):
154 if not archive_specs.has_key(fileformat):
155 return _('Unknown archive type %s') % fileformat
155 return _('Unknown archive type %s') % fileformat
156
156
157 repo = ScmModel().get_repo(repo_name)
157 repo = ScmModel().get_repo(repo_name)
158
158
159 try:
159 try:
160 repo.get_changeset(revision)
160 repo.get_changeset(revision)
161 except ChangesetDoesNotExistError:
161 except ChangesetDoesNotExistError:
162 return _('Unknown revision %s') % revision
162 return _('Unknown revision %s') % revision
163
163
164 archive = tempfile.TemporaryFile()
164 archive = tempfile.TemporaryFile()
165 localrepo = repo.repo
165 localrepo = repo.repo
166 fname = '%s-%s%s' % (repo_name, revision, fileformat)
166 fname = '%s-%s%s' % (repo_name, revision, fileformat)
167 archival.archive(localrepo, archive, revision, archive_specs[fileformat][1],
167 archival.archive(localrepo, archive, revision, archive_specs[fileformat][1],
168 prefix='%s-%s' % (repo_name, revision))
168 prefix='%s-%s' % (repo_name, revision))
169 response.content_type = archive_specs[fileformat][0]
169 response.content_type = archive_specs[fileformat][0]
170 response.content_disposition = 'attachment; filename=%s' % fname
170 response.content_disposition = 'attachment; filename=%s' % fname
171 archive.seek(0)
171 archive.seek(0)
172
172
173 def read_in_chunks(file_object, chunk_size=1024 * 40):
173 def read_in_chunks(file_object, chunk_size=1024 * 40):
174 """Lazy function (generator) to read a file piece by piece.
174 """Lazy function (generator) to read a file piece by piece.
175 Default chunk size: 40k."""
175 Default chunk size: 40k."""
176 while True:
176 while True:
177 data = file_object.read(chunk_size)
177 data = file_object.read(chunk_size)
178 if not data:
178 if not data:
179 break
179 break
180 yield data
180 yield data
181
181
182 return read_in_chunks(archive)
182 return read_in_chunks(archive)
183
183
184 def diff(self, repo_name, f_path):
184 def diff(self, repo_name, f_path):
185 hg_model = ScmModel()
185 hg_model = ScmModel()
186 diff1 = request.GET.get('diff1')
186 diff1 = request.GET.get('diff1')
187 diff2 = request.GET.get('diff2')
187 diff2 = request.GET.get('diff2')
188 c.action = request.GET.get('diff')
188 c.action = request.GET.get('diff')
189 c.no_changes = diff1 == diff2
189 c.no_changes = diff1 == diff2
190 c.f_path = f_path
190 c.f_path = f_path
191 c.repo = hg_model.get_repo(c.repo_name)
191 c.repo = hg_model.get_repo(c.repo_name)
192
192
193 try:
193 try:
194 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
194 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
195 c.changeset_1 = c.repo.get_changeset(diff1)
195 c.changeset_1 = c.repo.get_changeset(diff1)
196 node1 = c.changeset_1.get_node(f_path)
196 node1 = c.changeset_1.get_node(f_path)
197 else:
197 else:
198 c.changeset_1 = EmptyChangeset()
198 c.changeset_1 = EmptyChangeset()
199 node1 = FileNode('.', '', changeset=c.changeset_1)
199 node1 = FileNode('.', '', changeset=c.changeset_1)
200
200
201 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
201 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
202 c.changeset_2 = c.repo.get_changeset(diff2)
202 c.changeset_2 = c.repo.get_changeset(diff2)
203 node2 = c.changeset_2.get_node(f_path)
203 node2 = c.changeset_2.get_node(f_path)
204 else:
204 else:
205 c.changeset_2 = EmptyChangeset()
205 c.changeset_2 = EmptyChangeset()
206 node2 = FileNode('.', '', changeset=c.changeset_2)
206 node2 = FileNode('.', '', changeset=c.changeset_2)
207 except RepositoryError:
207 except RepositoryError:
208 return redirect(url('files_home',
208 return redirect(url('files_home',
209 repo_name=c.repo_name, f_path=f_path))
209 repo_name=c.repo_name, f_path=f_path))
210
210
211 f_udiff = differ.get_udiff(node1, node2)
211 f_udiff = differ.get_udiff(node1, node2)
212 diff = differ.DiffProcessor(f_udiff)
212 diff = differ.DiffProcessor(f_udiff)
213
213
214 if c.action == 'download':
214 if c.action == 'download':
215 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
215 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
216 response.content_type = 'text/plain'
216 response.content_type = 'text/plain'
217 response.content_disposition = 'attachment; filename=%s' \
217 response.content_disposition = 'attachment; filename=%s' \
218 % diff_name
218 % diff_name
219 return diff.raw_diff()
219 return diff.raw_diff()
220
220
221 elif c.action == 'raw':
221 elif c.action == 'raw':
222 response.content_type = 'text/plain'
222 response.content_type = 'text/plain'
223 return diff.raw_diff()
223 return diff.raw_diff()
224
224
225 elif c.action == 'diff':
225 elif c.action == 'diff':
226 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
226 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
227 c.cur_diff = _('Diff is to big to display')
227 c.cur_diff = _('Diff is to big to display')
228 else:
228 else:
229 c.cur_diff = diff.as_html()
229 c.cur_diff = diff.as_html()
230 else:
230 else:
231 #default option
231 #default option
232 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
232 if node1.size > self.cut_off_limit or node2.size > self.cut_off_limit:
233 c.cur_diff = _('Diff is to big to display')
233 c.cur_diff = _('Diff is to big to display')
234 else:
234 else:
235 c.cur_diff = diff.as_html()
235 c.cur_diff = diff.as_html()
236
236
237 if not c.cur_diff: c.no_changes = True
237 if not c.cur_diff: c.no_changes = True
238 return render('files/file_diff.html')
238 return render('files/file_diff.html')
239
239
240 def _get_history(self, repo, node, f_path):
240 def _get_history(self, repo, node, f_path):
241 from vcs.nodes import NodeKind
241 from vcs.nodes import NodeKind
242 if not node.kind is NodeKind.FILE:
242 if not node.kind is NodeKind.FILE:
243 return []
243 return []
244 changesets = node.history
244 changesets = node.history
245 hist_l = []
245 hist_l = []
246
246
247 changesets_group = ([], _("Changesets"))
247 changesets_group = ([], _("Changesets"))
248 branches_group = ([], _("Branches"))
248 branches_group = ([], _("Branches"))
249 tags_group = ([], _("Tags"))
249 tags_group = ([], _("Tags"))
250
250
251 for chs in changesets:
251 for chs in changesets:
252 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
252 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
253 changesets_group[0].append((chs.raw_id, n_desc,))
253 changesets_group[0].append((chs.raw_id, n_desc,))
254
254
255 hist_l.append(changesets_group)
255 hist_l.append(changesets_group)
256
256
257 for name, chs in c.repository_branches.items():
257 for name, chs in c.repository_branches.items():
258 #chs = chs.split(':')[-1]
258 #chs = chs.split(':')[-1]
259 branches_group[0].append((chs, name),)
259 branches_group[0].append((chs, name),)
260 hist_l.append(branches_group)
260 hist_l.append(branches_group)
261
261
262 for name, chs in c.repository_tags.items():
262 for name, chs in c.repository_tags.items():
263 #chs = chs.split(':')[-1]
263 #chs = chs.split(':')[-1]
264 tags_group[0].append((chs, name),)
264 tags_group[0].append((chs, name),)
265 hist_l.append(tags_group)
265 hist_l.append(tags_group)
266
266
267 return hist_l
267 return hist_l
268
268
@@ -1,66 +1,66 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.home
3 rhodecode.controllers.home
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Home controller for Rhodecode
6 Home controller for Rhodecode
7
7
8 :created_on: Feb 18, 2010
8 :created_on: Feb 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 from operator import itemgetter
29 from operator import itemgetter
30
30
31 from pylons import tmpl_context as c, request
31 from pylons import tmpl_context as c, request
32
32
33 from rhodecode.lib.auth import LoginRequired
33 from rhodecode.lib.auth import LoginRequired
34 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
35 from rhodecode.model.scm import ScmModel
35 from rhodecode.model.scm import ScmModel
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 class HomeController(BaseController):
39 class HomeController(BaseController):
40
40
41 @LoginRequired()
41 @LoginRequired()
42 def __before__(self):
42 def __before__(self):
43 super(HomeController, self).__before__()
43 super(HomeController, self).__before__()
44
44
45 def index(self):
45 def index(self):
46 sortables = ['name', 'description', 'last_change', 'tip', 'contact']
46 sortables = ['name', 'description', 'last_change', 'tip', 'contact']
47 current_sort = request.GET.get('sort', 'name')
47 current_sort = request.GET.get('sort', 'name')
48 current_sort_slug = current_sort.replace('-', '')
48 current_sort_slug = current_sort.replace('-', '')
49
49
50 if current_sort_slug not in sortables:
50 if current_sort_slug not in sortables:
51 c.sort_by = 'name'
51 c.sort_by = 'name'
52 current_sort_slug = c.sort_by
52 current_sort_slug = c.sort_by
53 else:
53 else:
54 c.sort_by = current_sort
54 c.sort_by = current_sort
55 c.sort_slug = current_sort_slug
55 c.sort_slug = current_sort_slug
56 cached_repo_list = ScmModel().get_repos()
56 cached_repo_list = ScmModel().get_repos()
57
57
58 sort_key = current_sort_slug + '_sort'
58 sort_key = current_sort_slug + '_sort'
59 if c.sort_by.startswith('-'):
59 if c.sort_by.startswith('-'):
60 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
60 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
61 reverse=True)
61 reverse=True)
62 else:
62 else:
63 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
63 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
64 reverse=False)
64 reverse=False)
65
65
66 return render('/index.html')
66 return render('/index.html')
@@ -1,98 +1,98 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.journal
3 rhodecode.controllers.journal
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Journal controller for pylons
6 Journal controller for pylons
7
7
8 :created_on: Nov 21, 2010
8 :created_on: Nov 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 from sqlalchemy import or_
29 from sqlalchemy import or_
30
30
31 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
32
32
33 from rhodecode.lib.auth import LoginRequired, NotAnonymous
33 from rhodecode.lib.auth import LoginRequired, NotAnonymous
34 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.helpers import get_token
35 from rhodecode.lib.helpers import get_token
36 from rhodecode.model.db import UserLog, UserFollowing
36 from rhodecode.model.db import UserLog, UserFollowing
37 from rhodecode.model.scm import ScmModel
37 from rhodecode.model.scm import ScmModel
38
38
39 from paste.httpexceptions import HTTPInternalServerError
39 from paste.httpexceptions import HTTPInternalServerError
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43 class JournalController(BaseController):
43 class JournalController(BaseController):
44
44
45
45
46 @LoginRequired()
46 @LoginRequired()
47 @NotAnonymous()
47 @NotAnonymous()
48 def __before__(self):
48 def __before__(self):
49 super(JournalController, self).__before__()
49 super(JournalController, self).__before__()
50
50
51 def index(self):
51 def index(self):
52 # Return a rendered template
52 # Return a rendered template
53
53
54 c.following = self.sa.query(UserFollowing)\
54 c.following = self.sa.query(UserFollowing)\
55 .filter(UserFollowing.user_id == c.rhodecode_user.user_id).all()
55 .filter(UserFollowing.user_id == c.rhodecode_user.user_id).all()
56
56
57 repo_ids = [x.follows_repository.repo_id for x in c.following
57 repo_ids = [x.follows_repository.repo_id for x in c.following
58 if x.follows_repository is not None]
58 if x.follows_repository is not None]
59 user_ids = [x.follows_user.user_id for x in c.following
59 user_ids = [x.follows_user.user_id for x in c.following
60 if x.follows_user is not None]
60 if x.follows_user is not None]
61
61
62 c.journal = self.sa.query(UserLog)\
62 c.journal = self.sa.query(UserLog)\
63 .filter(or_(
63 .filter(or_(
64 UserLog.repository_id.in_(repo_ids),
64 UserLog.repository_id.in_(repo_ids),
65 UserLog.user_id.in_(user_ids),
65 UserLog.user_id.in_(user_ids),
66 ))\
66 ))\
67 .order_by(UserLog.action_date.desc())\
67 .order_by(UserLog.action_date.desc())\
68 .limit(20)\
68 .limit(20)\
69 .all()
69 .all()
70 return render('/journal.html')
70 return render('/journal.html')
71
71
72 def toggle_following(self):
72 def toggle_following(self):
73 cur_token = request.POST.get('auth_token')
73 cur_token = request.POST.get('auth_token')
74 token = get_token()
74 token = get_token()
75 if cur_token == token:
75 if cur_token == token:
76 scm_model = ScmModel()
76 scm_model = ScmModel()
77
77
78 user_id = request.POST.get('follows_user_id')
78 user_id = request.POST.get('follows_user_id')
79 if user_id:
79 if user_id:
80 try:
80 try:
81 scm_model.toggle_following_user(user_id,
81 scm_model.toggle_following_user(user_id,
82 c.rhodecode_user.user_id)
82 c.rhodecode_user.user_id)
83 return 'ok'
83 return 'ok'
84 except:
84 except:
85 raise HTTPInternalServerError()
85 raise HTTPInternalServerError()
86
86
87 repo_id = request.POST.get('follows_repo_id')
87 repo_id = request.POST.get('follows_repo_id')
88 if repo_id:
88 if repo_id:
89 try:
89 try:
90 scm_model.toggle_following_repo(repo_id,
90 scm_model.toggle_following_repo(repo_id,
91 c.rhodecode_user.user_id)
91 c.rhodecode_user.user_id)
92 return 'ok'
92 return 'ok'
93 except:
93 except:
94 raise HTTPInternalServerError()
94 raise HTTPInternalServerError()
95
95
96
96
97 log.debug('token mismatch %s vs %s', cur_token, token)
97 log.debug('token mismatch %s vs %s', cur_token, token)
98 raise HTTPInternalServerError()
98 raise HTTPInternalServerError()
@@ -1,152 +1,152 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.login
3 rhodecode.controllers.login
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Login controller for rhodeocode
6 Login controller for rhodeocode
7
7
8 :created_on: Apr 22, 2010
8 :created_on: Apr 22, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import formencode
29 import formencode
30
30
31 from formencode import htmlfill
31 from formencode import htmlfill
32
32
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons import request, response, session, tmpl_context as c, url
35 from pylons import request, response, session, tmpl_context as c, url
36
36
37 import rhodecode.lib.helpers as h
37 import rhodecode.lib.helpers as h
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
41 from rhodecode.model.user import UserModel
41 from rhodecode.model.user import UserModel
42
42
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 class LoginController(BaseController):
46 class LoginController(BaseController):
47
47
48 def __before__(self):
48 def __before__(self):
49 super(LoginController, self).__before__()
49 super(LoginController, self).__before__()
50
50
51 def index(self):
51 def index(self):
52 #redirect if already logged in
52 #redirect if already logged in
53 c.came_from = request.GET.get('came_from', None)
53 c.came_from = request.GET.get('came_from', None)
54
54
55 if c.rhodecode_user.is_authenticated \
55 if c.rhodecode_user.is_authenticated \
56 and c.rhodecode_user.username != 'default':
56 and c.rhodecode_user.username != 'default':
57
57
58 return redirect(url('home'))
58 return redirect(url('home'))
59
59
60 if request.POST:
60 if request.POST:
61 #import Login Form validator class
61 #import Login Form validator class
62 login_form = LoginForm()
62 login_form = LoginForm()
63 try:
63 try:
64 c.form_result = login_form.to_python(dict(request.POST))
64 c.form_result = login_form.to_python(dict(request.POST))
65 username = c.form_result['username']
65 username = c.form_result['username']
66 user = UserModel().get_by_username(username, case_insensitive=True)
66 user = UserModel().get_by_username(username, case_insensitive=True)
67 auth_user = AuthUser()
67 auth_user = AuthUser()
68 auth_user.username = user.username
68 auth_user.username = user.username
69 auth_user.is_authenticated = True
69 auth_user.is_authenticated = True
70 auth_user.is_admin = user.admin
70 auth_user.is_admin = user.admin
71 auth_user.user_id = user.user_id
71 auth_user.user_id = user.user_id
72 auth_user.name = user.name
72 auth_user.name = user.name
73 auth_user.lastname = user.lastname
73 auth_user.lastname = user.lastname
74 session['rhodecode_user'] = auth_user
74 session['rhodecode_user'] = auth_user
75 session.save()
75 session.save()
76 log.info('user %s is now authenticated', username)
76 log.info('user %s is now authenticated', username)
77
77
78 user.update_lastlogin()
78 user.update_lastlogin()
79
79
80 if c.came_from:
80 if c.came_from:
81 return redirect(c.came_from)
81 return redirect(c.came_from)
82 else:
82 else:
83 return redirect(url('home'))
83 return redirect(url('home'))
84
84
85 except formencode.Invalid, errors:
85 except formencode.Invalid, errors:
86 return htmlfill.render(
86 return htmlfill.render(
87 render('/login.html'),
87 render('/login.html'),
88 defaults=errors.value,
88 defaults=errors.value,
89 errors=errors.error_dict or {},
89 errors=errors.error_dict or {},
90 prefix_error=False,
90 prefix_error=False,
91 encoding="UTF-8")
91 encoding="UTF-8")
92
92
93 return render('/login.html')
93 return render('/login.html')
94
94
95 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
95 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
96 'hg.register.manual_activate')
96 'hg.register.manual_activate')
97 def register(self):
97 def register(self):
98 user_model = UserModel()
98 user_model = UserModel()
99 c.auto_active = False
99 c.auto_active = False
100 for perm in user_model.get_by_username('default', cache=False).user_perms:
100 for perm in user_model.get_by_username('default', cache=False).user_perms:
101 if perm.permission.permission_name == 'hg.register.auto_activate':
101 if perm.permission.permission_name == 'hg.register.auto_activate':
102 c.auto_active = True
102 c.auto_active = True
103 break
103 break
104
104
105 if request.POST:
105 if request.POST:
106
106
107 register_form = RegisterForm()()
107 register_form = RegisterForm()()
108 try:
108 try:
109 form_result = register_form.to_python(dict(request.POST))
109 form_result = register_form.to_python(dict(request.POST))
110 form_result['active'] = c.auto_active
110 form_result['active'] = c.auto_active
111 user_model.create_registration(form_result)
111 user_model.create_registration(form_result)
112 h.flash(_('You have successfully registered into rhodecode'),
112 h.flash(_('You have successfully registered into rhodecode'),
113 category='success')
113 category='success')
114 return redirect(url('login_home'))
114 return redirect(url('login_home'))
115
115
116 except formencode.Invalid, errors:
116 except formencode.Invalid, errors:
117 return htmlfill.render(
117 return htmlfill.render(
118 render('/register.html'),
118 render('/register.html'),
119 defaults=errors.value,
119 defaults=errors.value,
120 errors=errors.error_dict or {},
120 errors=errors.error_dict or {},
121 prefix_error=False,
121 prefix_error=False,
122 encoding="UTF-8")
122 encoding="UTF-8")
123
123
124 return render('/register.html')
124 return render('/register.html')
125
125
126 def password_reset(self):
126 def password_reset(self):
127 user_model = UserModel()
127 user_model = UserModel()
128 if request.POST:
128 if request.POST:
129
129
130 password_reset_form = PasswordResetForm()()
130 password_reset_form = PasswordResetForm()()
131 try:
131 try:
132 form_result = password_reset_form.to_python(dict(request.POST))
132 form_result = password_reset_form.to_python(dict(request.POST))
133 user_model.reset_password(form_result)
133 user_model.reset_password(form_result)
134 h.flash(_('Your new password was sent'),
134 h.flash(_('Your new password was sent'),
135 category='success')
135 category='success')
136 return redirect(url('login_home'))
136 return redirect(url('login_home'))
137
137
138 except formencode.Invalid, errors:
138 except formencode.Invalid, errors:
139 return htmlfill.render(
139 return htmlfill.render(
140 render('/password_reset.html'),
140 render('/password_reset.html'),
141 defaults=errors.value,
141 defaults=errors.value,
142 errors=errors.error_dict or {},
142 errors=errors.error_dict or {},
143 prefix_error=False,
143 prefix_error=False,
144 encoding="UTF-8")
144 encoding="UTF-8")
145
145
146 return render('/password_reset.html')
146 return render('/password_reset.html')
147
147
148 def logout(self):
148 def logout(self):
149 session['rhodecode_user'] = AuthUser()
149 session['rhodecode_user'] = AuthUser()
150 session.save()
150 session.save()
151 log.info('Logging out and setting user as Empty')
151 log.info('Logging out and setting user as Empty')
152 redirect(url('home'))
152 redirect(url('home'))
@@ -1,120 +1,120 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.search
3 rhodecode.controllers.search
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Search controller for rhodecode
6 Search controller for rhodecode
7
7
8 :created_on: Aug 7, 2010
8 :created_on: Aug 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import logging
27 import logging
28 import traceback
28 import traceback
29
29
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from pylons import request, response, config, session, tmpl_context as c, url
31 from pylons import request, response, config, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33
33
34 from rhodecode.lib.auth import LoginRequired
34 from rhodecode.lib.auth import LoginRequired
35 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
36 from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
37
37
38 from webhelpers.paginate import Page
38 from webhelpers.paginate import Page
39 from webhelpers.util import update_params
39 from webhelpers.util import update_params
40
40
41 from whoosh.index import open_dir, EmptyIndexError
41 from whoosh.index import open_dir, EmptyIndexError
42 from whoosh.qparser import QueryParser, QueryParserError
42 from whoosh.qparser import QueryParser, QueryParserError
43 from whoosh.query import Phrase
43 from whoosh.query import Phrase
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47 class SearchController(BaseController):
47 class SearchController(BaseController):
48
48
49 @LoginRequired()
49 @LoginRequired()
50 def __before__(self):
50 def __before__(self):
51 super(SearchController, self).__before__()
51 super(SearchController, self).__before__()
52
52
53 def index(self, search_repo=None):
53 def index(self, search_repo=None):
54 c.repo_name = search_repo
54 c.repo_name = search_repo
55 c.formated_results = []
55 c.formated_results = []
56 c.runtime = ''
56 c.runtime = ''
57 c.cur_query = request.GET.get('q', None)
57 c.cur_query = request.GET.get('q', None)
58 c.cur_type = request.GET.get('type', 'source')
58 c.cur_type = request.GET.get('type', 'source')
59 c.cur_search = search_type = {'content':'content',
59 c.cur_search = search_type = {'content':'content',
60 'commit':'content',
60 'commit':'content',
61 'path':'path',
61 'path':'path',
62 'repository':'repository'}\
62 'repository':'repository'}\
63 .get(c.cur_type, 'content')
63 .get(c.cur_type, 'content')
64
64
65
65
66 if c.cur_query:
66 if c.cur_query:
67 cur_query = c.cur_query.lower()
67 cur_query = c.cur_query.lower()
68
68
69 if c.cur_query:
69 if c.cur_query:
70 p = int(request.params.get('page', 1))
70 p = int(request.params.get('page', 1))
71 highlight_items = set()
71 highlight_items = set()
72 try:
72 try:
73 idx = open_dir(config['app_conf']['index_dir']
73 idx = open_dir(config['app_conf']['index_dir']
74 , indexname=IDX_NAME)
74 , indexname=IDX_NAME)
75 searcher = idx.searcher()
75 searcher = idx.searcher()
76
76
77 qp = QueryParser(search_type, schema=SCHEMA)
77 qp = QueryParser(search_type, schema=SCHEMA)
78 if c.repo_name:
78 if c.repo_name:
79 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
79 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
80 try:
80 try:
81 query = qp.parse(unicode(cur_query))
81 query = qp.parse(unicode(cur_query))
82
82
83 if isinstance(query, Phrase):
83 if isinstance(query, Phrase):
84 highlight_items.update(query.words)
84 highlight_items.update(query.words)
85 else:
85 else:
86 for i in query.all_terms():
86 for i in query.all_terms():
87 if i[0] == 'content':
87 if i[0] == 'content':
88 highlight_items.add(i[1])
88 highlight_items.add(i[1])
89
89
90 matcher = query.matcher(searcher)
90 matcher = query.matcher(searcher)
91
91
92 log.debug(query)
92 log.debug(query)
93 log.debug(highlight_items)
93 log.debug(highlight_items)
94 results = searcher.search(query)
94 results = searcher.search(query)
95 res_ln = len(results)
95 res_ln = len(results)
96 c.runtime = '%s results (%.3f seconds)' \
96 c.runtime = '%s results (%.3f seconds)' \
97 % (res_ln, results.runtime)
97 % (res_ln, results.runtime)
98
98
99 def url_generator(**kw):
99 def url_generator(**kw):
100 return update_params("?q=%s&type=%s" \
100 return update_params("?q=%s&type=%s" \
101 % (c.cur_query, c.cur_search), **kw)
101 % (c.cur_query, c.cur_search), **kw)
102
102
103 c.formated_results = Page(
103 c.formated_results = Page(
104 ResultWrapper(search_type, searcher, matcher,
104 ResultWrapper(search_type, searcher, matcher,
105 highlight_items),
105 highlight_items),
106 page=p, item_count=res_ln,
106 page=p, item_count=res_ln,
107 items_per_page=10, url=url_generator)
107 items_per_page=10, url=url_generator)
108
108
109
109
110 except QueryParserError:
110 except QueryParserError:
111 c.runtime = _('Invalid search query. Try quoting it.')
111 c.runtime = _('Invalid search query. Try quoting it.')
112 searcher.close()
112 searcher.close()
113 except (EmptyIndexError, IOError):
113 except (EmptyIndexError, IOError):
114 log.error(traceback.format_exc())
114 log.error(traceback.format_exc())
115 log.error('Empty Index data')
115 log.error('Empty Index data')
116 c.runtime = _('There is no index to search in. '
116 c.runtime = _('There is no index to search in. '
117 'Please run whoosh indexer')
117 'Please run whoosh indexer')
118
118
119 # Return a rendered template
119 # Return a rendered template
120 return render('/search/search.html')
120 return render('/search/search.html')
@@ -1,184 +1,184 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.settings
3 rhodecode.controllers.settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Settings controller for rhodecode
6 Settings controller for rhodecode
7
7
8 :created_on: Jun 30, 2010
8 :created_on: Jun 30, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 import formencode
31 import formencode
32 from formencode import htmlfill
32 from formencode import htmlfill
33
33
34 from pylons import tmpl_context as c, request, url
34 from pylons import tmpl_context as c, request, url
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 import rhodecode.lib.helpers as h
38 import rhodecode.lib.helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41 from rhodecode.lib.utils import invalidate_cache, action_logger
41 from rhodecode.lib.utils import invalidate_cache, action_logger
42 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
42 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
43 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.repo import RepoModel
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47 class SettingsController(BaseController):
47 class SettingsController(BaseController):
48
48
49 @LoginRequired()
49 @LoginRequired()
50 @HasRepoPermissionAllDecorator('repository.admin')
50 @HasRepoPermissionAllDecorator('repository.admin')
51 def __before__(self):
51 def __before__(self):
52 super(SettingsController, self).__before__()
52 super(SettingsController, self).__before__()
53
53
54 def index(self, repo_name):
54 def index(self, repo_name):
55 repo_model = RepoModel()
55 repo_model = RepoModel()
56 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
56 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
57 if not repo:
57 if not repo:
58 h.flash(_('%s repository is not mapped to db perhaps'
58 h.flash(_('%s repository is not mapped to db perhaps'
59 ' it was created or renamed from the file system'
59 ' it was created or renamed from the file system'
60 ' please run the application again'
60 ' please run the application again'
61 ' in order to rescan repositories') % repo_name,
61 ' in order to rescan repositories') % repo_name,
62 category='error')
62 category='error')
63
63
64 return redirect(url('home'))
64 return redirect(url('home'))
65 defaults = c.repo_info.get_dict()
65 defaults = c.repo_info.get_dict()
66 defaults.update({'user':c.repo_info.user.username})
66 defaults.update({'user':c.repo_info.user.username})
67 c.users_array = repo_model.get_users_js()
67 c.users_array = repo_model.get_users_js()
68
68
69 for p in c.repo_info.repo_to_perm:
69 for p in c.repo_info.repo_to_perm:
70 defaults.update({'perm_%s' % p.user.username:
70 defaults.update({'perm_%s' % p.user.username:
71 p.permission.permission_name})
71 p.permission.permission_name})
72
72
73 return htmlfill.render(
73 return htmlfill.render(
74 render('settings/repo_settings.html'),
74 render('settings/repo_settings.html'),
75 defaults=defaults,
75 defaults=defaults,
76 encoding="UTF-8",
76 encoding="UTF-8",
77 force_defaults=False
77 force_defaults=False
78 )
78 )
79
79
80 def update(self, repo_name):
80 def update(self, repo_name):
81 repo_model = RepoModel()
81 repo_model = RepoModel()
82 changed_name = repo_name
82 changed_name = repo_name
83 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
83 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
84 try:
84 try:
85 form_result = _form.to_python(dict(request.POST))
85 form_result = _form.to_python(dict(request.POST))
86 repo_model.update(repo_name, form_result)
86 repo_model.update(repo_name, form_result)
87 invalidate_cache('get_repo_cached_%s' % repo_name)
87 invalidate_cache('get_repo_cached_%s' % repo_name)
88 h.flash(_('Repository %s updated successfully' % repo_name),
88 h.flash(_('Repository %s updated successfully' % repo_name),
89 category='success')
89 category='success')
90 changed_name = form_result['repo_name']
90 changed_name = form_result['repo_name']
91 action_logger(self.rhodecode_user, 'user_updated_repo',
91 action_logger(self.rhodecode_user, 'user_updated_repo',
92 changed_name, '', self.sa)
92 changed_name, '', self.sa)
93 except formencode.Invalid, errors:
93 except formencode.Invalid, errors:
94 c.repo_info = repo_model.get_by_repo_name(repo_name)
94 c.repo_info = repo_model.get_by_repo_name(repo_name)
95 c.users_array = repo_model.get_users_js()
95 c.users_array = repo_model.get_users_js()
96 errors.value.update({'user':c.repo_info.user.username})
96 errors.value.update({'user':c.repo_info.user.username})
97 return htmlfill.render(
97 return htmlfill.render(
98 render('settings/repo_settings.html'),
98 render('settings/repo_settings.html'),
99 defaults=errors.value,
99 defaults=errors.value,
100 errors=errors.error_dict or {},
100 errors=errors.error_dict or {},
101 prefix_error=False,
101 prefix_error=False,
102 encoding="UTF-8")
102 encoding="UTF-8")
103 except Exception:
103 except Exception:
104 log.error(traceback.format_exc())
104 log.error(traceback.format_exc())
105 h.flash(_('error occurred during update of repository %s') \
105 h.flash(_('error occurred during update of repository %s') \
106 % repo_name, category='error')
106 % repo_name, category='error')
107
107
108 return redirect(url('repo_settings_home', repo_name=changed_name))
108 return redirect(url('repo_settings_home', repo_name=changed_name))
109
109
110
110
111
111
112 def delete(self, repo_name):
112 def delete(self, repo_name):
113 """DELETE /repos/repo_name: Delete an existing item"""
113 """DELETE /repos/repo_name: Delete an existing item"""
114 # Forms posted to this method should contain a hidden field:
114 # Forms posted to this method should contain a hidden field:
115 # <input type="hidden" name="_method" value="DELETE" />
115 # <input type="hidden" name="_method" value="DELETE" />
116 # Or using helpers:
116 # Or using helpers:
117 # h.form(url('repo_settings_delete', repo_name=ID),
117 # h.form(url('repo_settings_delete', repo_name=ID),
118 # method='delete')
118 # method='delete')
119 # url('repo_settings_delete', repo_name=ID)
119 # url('repo_settings_delete', repo_name=ID)
120
120
121 repo_model = RepoModel()
121 repo_model = RepoModel()
122 repo = repo_model.get_by_repo_name(repo_name)
122 repo = repo_model.get_by_repo_name(repo_name)
123 if not repo:
123 if not repo:
124 h.flash(_('%s repository is not mapped to db perhaps'
124 h.flash(_('%s repository is not mapped to db perhaps'
125 ' it was moved or renamed from the filesystem'
125 ' it was moved or renamed from the filesystem'
126 ' please run the application again'
126 ' please run the application again'
127 ' in order to rescan repositories') % repo_name,
127 ' in order to rescan repositories') % repo_name,
128 category='error')
128 category='error')
129
129
130 return redirect(url('home'))
130 return redirect(url('home'))
131 try:
131 try:
132 action_logger(self.rhodecode_user, 'user_deleted_repo',
132 action_logger(self.rhodecode_user, 'user_deleted_repo',
133 repo_name, '', self.sa)
133 repo_name, '', self.sa)
134 repo_model.delete(repo)
134 repo_model.delete(repo)
135 invalidate_cache('get_repo_cached_%s' % repo_name)
135 invalidate_cache('get_repo_cached_%s' % repo_name)
136 h.flash(_('deleted repository %s') % repo_name, category='success')
136 h.flash(_('deleted repository %s') % repo_name, category='success')
137 except Exception:
137 except Exception:
138 h.flash(_('An error occurred during deletion of %s') % repo_name,
138 h.flash(_('An error occurred during deletion of %s') % repo_name,
139 category='error')
139 category='error')
140
140
141 return redirect(url('home'))
141 return redirect(url('home'))
142
142
143 def fork(self, repo_name):
143 def fork(self, repo_name):
144 repo_model = RepoModel()
144 repo_model = RepoModel()
145 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
145 c.repo_info = repo = repo_model.get_by_repo_name(repo_name)
146 if not repo:
146 if not repo:
147 h.flash(_('%s repository is not mapped to db perhaps'
147 h.flash(_('%s repository is not mapped to db perhaps'
148 ' it was created or renamed from the file system'
148 ' it was created or renamed from the file system'
149 ' please run the application again'
149 ' please run the application again'
150 ' in order to rescan repositories') % repo_name,
150 ' in order to rescan repositories') % repo_name,
151 category='error')
151 category='error')
152
152
153 return redirect(url('home'))
153 return redirect(url('home'))
154
154
155 return render('settings/repo_fork.html')
155 return render('settings/repo_fork.html')
156
156
157
157
158
158
159 def fork_create(self, repo_name):
159 def fork_create(self, repo_name):
160 repo_model = RepoModel()
160 repo_model = RepoModel()
161 c.repo_info = repo_model.get_by_repo_name(repo_name)
161 c.repo_info = repo_model.get_by_repo_name(repo_name)
162 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
162 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
163 form_result = {}
163 form_result = {}
164 try:
164 try:
165 form_result = _form.to_python(dict(request.POST))
165 form_result = _form.to_python(dict(request.POST))
166 form_result.update({'repo_name':repo_name})
166 form_result.update({'repo_name':repo_name})
167 repo_model.create_fork(form_result, c.rhodecode_user)
167 repo_model.create_fork(form_result, c.rhodecode_user)
168 h.flash(_('forked %s repository as %s') \
168 h.flash(_('forked %s repository as %s') \
169 % (repo_name, form_result['fork_name']),
169 % (repo_name, form_result['fork_name']),
170 category='success')
170 category='success')
171 action_logger(self.rhodecode_user,
171 action_logger(self.rhodecode_user,
172 'user_forked_repo:%s' % form_result['fork_name'],
172 'user_forked_repo:%s' % form_result['fork_name'],
173 repo_name, '', self.sa)
173 repo_name, '', self.sa)
174 except formencode.Invalid, errors:
174 except formencode.Invalid, errors:
175 c.new_repo = errors.value['fork_name']
175 c.new_repo = errors.value['fork_name']
176 r = render('settings/repo_fork.html')
176 r = render('settings/repo_fork.html')
177
177
178 return htmlfill.render(
178 return htmlfill.render(
179 r,
179 r,
180 defaults=errors.value,
180 defaults=errors.value,
181 errors=errors.error_dict or {},
181 errors=errors.error_dict or {},
182 prefix_error=False,
182 prefix_error=False,
183 encoding="UTF-8")
183 encoding="UTF-8")
184 return redirect(url('home'))
184 return redirect(url('home'))
@@ -1,56 +1,56 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.shortlog
3 rhodecode.controllers.shortlog
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Shortlog controller for rhodecode
6 Shortlog controller for rhodecode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29
29
30 from pylons import tmpl_context as c, request
30 from pylons import tmpl_context as c, request
31
31
32 from webhelpers.paginate import Page
32 from webhelpers.paginate import Page
33
33
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
36 from rhodecode.model.scm import ScmModel
36 from rhodecode.model.scm import ScmModel
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40 class ShortlogController(BaseController):
40 class ShortlogController(BaseController):
41
41
42 @LoginRequired()
42 @LoginRequired()
43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 'repository.admin')
44 'repository.admin')
45 def __before__(self):
45 def __before__(self):
46 super(ShortlogController, self).__before__()
46 super(ShortlogController, self).__before__()
47
47
48 def index(self):
48 def index(self):
49 p = int(request.params.get('page', 1))
49 p = int(request.params.get('page', 1))
50 repo = ScmModel().get_repo(c.repo_name)
50 repo = ScmModel().get_repo(c.repo_name)
51 c.repo_changesets = Page(repo, page=p, items_per_page=20)
51 c.repo_changesets = Page(repo, page=p, items_per_page=20)
52 c.shortlog_data = render('shortlog/shortlog_data.html')
52 c.shortlog_data = render('shortlog/shortlog_data.html')
53 if request.params.get('partial'):
53 if request.params.get('partial'):
54 return c.shortlog_data
54 return c.shortlog_data
55 r = render('shortlog/shortlog.html')
55 r = render('shortlog/shortlog.html')
56 return r
56 return r
@@ -1,144 +1,144 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.summary
3 rhodecode.controllers.summary
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Summary controller for Rhodecode
6 Summary controller for Rhodecode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import calendar
28 import calendar
29 import logging
29 import logging
30 from time import mktime
30 from time import mktime
31 from datetime import datetime, timedelta
31 from datetime import datetime, timedelta
32
32
33 from vcs.exceptions import ChangesetError
33 from vcs.exceptions import ChangesetError
34
34
35 from pylons import tmpl_context as c, request, url
35 from pylons import tmpl_context as c, request, url
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.db import Statistics
39 from rhodecode.model.db import Statistics
40
40
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
41 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
43 from rhodecode.lib.utils import OrderedDict, EmptyChangeset
44
44
45 from rhodecode.lib.celerylib import run_task
45 from rhodecode.lib.celerylib import run_task
46 from rhodecode.lib.celerylib.tasks import get_commits_stats
46 from rhodecode.lib.celerylib.tasks import get_commits_stats
47
47
48 from webhelpers.paginate import Page
48 from webhelpers.paginate import Page
49
49
50 try:
50 try:
51 import json
51 import json
52 except ImportError:
52 except ImportError:
53 #python 2.5 compatibility
53 #python 2.5 compatibility
54 import simplejson as json
54 import simplejson as json
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57 class SummaryController(BaseController):
57 class SummaryController(BaseController):
58
58
59 @LoginRequired()
59 @LoginRequired()
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 'repository.admin')
61 'repository.admin')
62 def __before__(self):
62 def __before__(self):
63 super(SummaryController, self).__before__()
63 super(SummaryController, self).__before__()
64
64
65 def index(self):
65 def index(self):
66 scm_model = ScmModel()
66 scm_model = ScmModel()
67 c.repo_info = scm_model.get_repo(c.repo_name)
67 c.repo_info = scm_model.get_repo(c.repo_name)
68 c.following = scm_model.is_following_repo(c.repo_name,
68 c.following = scm_model.is_following_repo(c.repo_name,
69 c.rhodecode_user.user_id)
69 c.rhodecode_user.user_id)
70 def url_generator(**kw):
70 def url_generator(**kw):
71 return url('shortlog_home', repo_name=c.repo_name, **kw)
71 return url('shortlog_home', repo_name=c.repo_name, **kw)
72
72
73 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
73 c.repo_changesets = Page(c.repo_info, page=1, items_per_page=10,
74 url=url_generator)
74 url=url_generator)
75
75
76 e = request.environ
76 e = request.environ
77
77
78 if self.rhodecode_user.username == 'default':
78 if self.rhodecode_user.username == 'default':
79 password = ':default'
79 password = ':default'
80 else:
80 else:
81 password = ''
81 password = ''
82
82
83 uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
83 uri = u'%(protocol)s://%(user)s%(password)s@%(host)s%(prefix)s/%(repo_name)s' % {
84 'protocol': e.get('wsgi.url_scheme'),
84 'protocol': e.get('wsgi.url_scheme'),
85 'user':str(c.rhodecode_user.username),
85 'user':str(c.rhodecode_user.username),
86 'password':password,
86 'password':password,
87 'host':e.get('HTTP_HOST'),
87 'host':e.get('HTTP_HOST'),
88 'prefix':e.get('SCRIPT_NAME'),
88 'prefix':e.get('SCRIPT_NAME'),
89 'repo_name':c.repo_name, }
89 'repo_name':c.repo_name, }
90 c.clone_repo_url = uri
90 c.clone_repo_url = uri
91 c.repo_tags = OrderedDict()
91 c.repo_tags = OrderedDict()
92 for name, hash in c.repo_info.tags.items()[:10]:
92 for name, hash in c.repo_info.tags.items()[:10]:
93 try:
93 try:
94 c.repo_tags[name] = c.repo_info.get_changeset(hash)
94 c.repo_tags[name] = c.repo_info.get_changeset(hash)
95 except ChangesetError:
95 except ChangesetError:
96 c.repo_tags[name] = EmptyChangeset(hash)
96 c.repo_tags[name] = EmptyChangeset(hash)
97
97
98 c.repo_branches = OrderedDict()
98 c.repo_branches = OrderedDict()
99 for name, hash in c.repo_info.branches.items()[:10]:
99 for name, hash in c.repo_info.branches.items()[:10]:
100 try:
100 try:
101 c.repo_branches[name] = c.repo_info.get_changeset(hash)
101 c.repo_branches[name] = c.repo_info.get_changeset(hash)
102 except ChangesetError:
102 except ChangesetError:
103 c.repo_branches[name] = EmptyChangeset(hash)
103 c.repo_branches[name] = EmptyChangeset(hash)
104
104
105 td = datetime.today() + timedelta(days=1)
105 td = datetime.today() + timedelta(days=1)
106 y, m, d = td.year, td.month, td.day
106 y, m, d = td.year, td.month, td.day
107
107
108 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
108 ts_min_y = mktime((y - 1, (td - timedelta(days=calendar.mdays[m])).month,
109 d, 0, 0, 0, 0, 0, 0,))
109 d, 0, 0, 0, 0, 0, 0,))
110 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
110 ts_min_m = mktime((y, (td - timedelta(days=calendar.mdays[m])).month,
111 d, 0, 0, 0, 0, 0, 0,))
111 d, 0, 0, 0, 0, 0, 0,))
112
112
113 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
113 ts_max_y = mktime((y, m, d, 0, 0, 0, 0, 0, 0,))
114 if c.repo_info.dbrepo.enable_statistics:
114 if c.repo_info.dbrepo.enable_statistics:
115 c.no_data_msg = _('No data loaded yet')
115 c.no_data_msg = _('No data loaded yet')
116 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
116 run_task(get_commits_stats, c.repo_info.name, ts_min_y, ts_max_y)
117 else:
117 else:
118 c.no_data_msg = _('Statistics update are disabled for this repository')
118 c.no_data_msg = _('Statistics update are disabled for this repository')
119 c.ts_min = ts_min_m
119 c.ts_min = ts_min_m
120 c.ts_max = ts_max_y
120 c.ts_max = ts_max_y
121
121
122 stats = self.sa.query(Statistics)\
122 stats = self.sa.query(Statistics)\
123 .filter(Statistics.repository == c.repo_info.dbrepo)\
123 .filter(Statistics.repository == c.repo_info.dbrepo)\
124 .scalar()
124 .scalar()
125
125
126
126
127 if stats and stats.languages:
127 if stats and stats.languages:
128 c.no_data = False is c.repo_info.dbrepo.enable_statistics
128 c.no_data = False is c.repo_info.dbrepo.enable_statistics
129 lang_stats = json.loads(stats.languages)
129 lang_stats = json.loads(stats.languages)
130 c.commit_data = stats.commit_activity
130 c.commit_data = stats.commit_activity
131 c.overview_data = stats.commit_activity_combined
131 c.overview_data = stats.commit_activity_combined
132 c.trending_languages = json.dumps(OrderedDict(
132 c.trending_languages = json.dumps(OrderedDict(
133 sorted(lang_stats.items(), reverse=True,
133 sorted(lang_stats.items(), reverse=True,
134 key=lambda k: k[1])[:10]
134 key=lambda k: k[1])[:10]
135 )
135 )
136 )
136 )
137 else:
137 else:
138 c.commit_data = json.dumps({})
138 c.commit_data = json.dumps({})
139 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
139 c.overview_data = json.dumps([[ts_min_y, 0], [ts_max_y, 10] ])
140 c.trending_languages = json.dumps({})
140 c.trending_languages = json.dumps({})
141 c.no_data = True
141 c.no_data = True
142
142
143 return render('summary/summary.html')
143 return render('summary/summary.html')
144
144
@@ -1,53 +1,53 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.tags
3 rhodecode.controllers.tags
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Tags controller for rhodecode
6 Tags controller for rhodecode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import logging
27 import logging
28
28
29 from pylons import tmpl_context as c
29 from pylons import tmpl_context as c
30
30
31 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
31 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
32 from rhodecode.lib.base import BaseController, render
32 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.utils import OrderedDict
33 from rhodecode.lib.utils import OrderedDict
34 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.scm import ScmModel
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38 class TagsController(BaseController):
38 class TagsController(BaseController):
39
39
40 @LoginRequired()
40 @LoginRequired()
41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
42 'repository.admin')
42 'repository.admin')
43 def __before__(self):
43 def __before__(self):
44 super(TagsController, self).__before__()
44 super(TagsController, self).__before__()
45
45
46 def index(self):
46 def index(self):
47 hg_model = ScmModel()
47 hg_model = ScmModel()
48 c.repo_info = hg_model.get_repo(c.repo_name)
48 c.repo_info = hg_model.get_repo(c.repo_name)
49 c.repo_tags = OrderedDict()
49 c.repo_tags = OrderedDict()
50 for name, hash_ in c.repo_info.tags.items():
50 for name, hash_ in c.repo_info.tags.items():
51 c.repo_tags[name] = c.repo_info.get_changeset(hash_)
51 c.repo_tags[name] = c.repo_info.get_changeset(hash_)
52
52
53 return render('tags/tags.html')
53 return render('tags/tags.html')
@@ -1,104 +1,104 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # ldap authentication lib
3 # ldap authentication lib
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 Nov 17, 2010
21 Created on Nov 17, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 from rhodecode.lib.exceptions import *
26 from rhodecode.lib.exceptions import *
27 import logging
27 import logging
28
28
29 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
30
30
31 try:
31 try:
32 import ldap
32 import ldap
33 except ImportError:
33 except ImportError:
34 pass
34 pass
35
35
36 class AuthLdap(object):
36 class AuthLdap(object):
37
37
38 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
38 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
39 use_ldaps=False, ldap_version=3):
39 use_ldaps=False, ldap_version=3):
40 self.ldap_version = ldap_version
40 self.ldap_version = ldap_version
41 if use_ldaps:
41 if use_ldaps:
42 port = port or 689
42 port = port or 689
43 self.LDAP_USE_LDAPS = use_ldaps
43 self.LDAP_USE_LDAPS = use_ldaps
44 self.LDAP_SERVER_ADDRESS = server
44 self.LDAP_SERVER_ADDRESS = server
45 self.LDAP_SERVER_PORT = port
45 self.LDAP_SERVER_PORT = port
46
46
47 #USE FOR READ ONLY BIND TO LDAP SERVER
47 #USE FOR READ ONLY BIND TO LDAP SERVER
48 self.LDAP_BIND_DN = bind_dn
48 self.LDAP_BIND_DN = bind_dn
49 self.LDAP_BIND_PASS = bind_pass
49 self.LDAP_BIND_PASS = bind_pass
50
50
51 ldap_server_type = 'ldap'
51 ldap_server_type = 'ldap'
52 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
52 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
53 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
53 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
54 self.LDAP_SERVER_ADDRESS,
54 self.LDAP_SERVER_ADDRESS,
55 self.LDAP_SERVER_PORT)
55 self.LDAP_SERVER_PORT)
56
56
57 self.BASE_DN = base_dn
57 self.BASE_DN = base_dn
58
58
59 def authenticate_ldap(self, username, password):
59 def authenticate_ldap(self, username, password):
60 """Authenticate a user via LDAP and return his/her LDAP properties.
60 """Authenticate a user via LDAP and return his/her LDAP properties.
61
61
62 Raises AuthenticationError if the credentials are rejected, or
62 Raises AuthenticationError if the credentials are rejected, or
63 EnvironmentError if the LDAP server can't be reached.
63 EnvironmentError if the LDAP server can't be reached.
64
64
65 :param username: username
65 :param username: username
66 :param password: password
66 :param password: password
67 """
67 """
68
68
69 from rhodecode.lib.helpers import chop_at
69 from rhodecode.lib.helpers import chop_at
70
70
71 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
71 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
72
72
73 if "," in username:
73 if "," in username:
74 raise LdapUsernameError("invalid character in username: ,")
74 raise LdapUsernameError("invalid character in username: ,")
75 try:
75 try:
76 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
76 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
77 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
77 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
78 server = ldap.initialize(self.LDAP_SERVER)
78 server = ldap.initialize(self.LDAP_SERVER)
79 if self.ldap_version == 2:
79 if self.ldap_version == 2:
80 server.protocol = ldap.VERSION2
80 server.protocol = ldap.VERSION2
81 else:
81 else:
82 server.protocol = ldap.VERSION3
82 server.protocol = ldap.VERSION3
83
83
84 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
84 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
85 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
85 server.simple_bind_s(self.LDAP_BIND_DN, self.LDAP_BIND_PASS)
86
86
87 dn = self.BASE_DN % {'user':uid}
87 dn = self.BASE_DN % {'user':uid}
88 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
88 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
89 server.simple_bind_s(dn, password)
89 server.simple_bind_s(dn, password)
90
90
91 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
91 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
92 if not properties:
92 if not properties:
93 raise ldap.NO_SUCH_OBJECT()
93 raise ldap.NO_SUCH_OBJECT()
94 except ldap.NO_SUCH_OBJECT, e:
94 except ldap.NO_SUCH_OBJECT, e:
95 log.debug("LDAP says no such user '%s' (%s)", uid, username)
95 log.debug("LDAP says no such user '%s' (%s)", uid, username)
96 raise LdapUsernameError()
96 raise LdapUsernameError()
97 except ldap.INVALID_CREDENTIALS, e:
97 except ldap.INVALID_CREDENTIALS, e:
98 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
98 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
99 raise LdapPasswordError()
99 raise LdapPasswordError()
100 except ldap.SERVER_DOWN, e:
100 except ldap.SERVER_DOWN, e:
101 raise LdapConnectionError("LDAP can't access authentication server")
101 raise LdapConnectionError("LDAP can't access authentication server")
102
102
103 return properties[0]
103 return properties[0]
104
104
@@ -1,108 +1,108 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # mercurial repository backup manager
3 # mercurial repository backup manager
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 Feb 28, 2010
22 Created on Feb 28, 2010
23 Mercurial repositories backup manager
23 Mercurial repositories backup manager
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26
27
27
28 import logging
28 import logging
29 import tarfile
29 import tarfile
30 import os
30 import os
31 import datetime
31 import datetime
32 import sys
32 import sys
33 import subprocess
33 import subprocess
34 logging.basicConfig(level=logging.DEBUG,
34 logging.basicConfig(level=logging.DEBUG,
35 format="%(asctime)s %(levelname)-5.5s %(message)s")
35 format="%(asctime)s %(levelname)-5.5s %(message)s")
36
36
37 class BackupManager(object):
37 class BackupManager(object):
38 def __init__(self, repos_location, rsa_key, backup_server):
38 def __init__(self, repos_location, rsa_key, backup_server):
39 today = datetime.datetime.now().weekday() + 1
39 today = datetime.datetime.now().weekday() + 1
40 self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
40 self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
41
41
42 self.id_rsa_path = self.get_id_rsa(rsa_key)
42 self.id_rsa_path = self.get_id_rsa(rsa_key)
43 self.repos_path = self.get_repos_path(repos_location)
43 self.repos_path = self.get_repos_path(repos_location)
44 self.backup_server = backup_server
44 self.backup_server = backup_server
45
45
46 self.backup_file_path = '/tmp'
46 self.backup_file_path = '/tmp'
47
47
48 logging.info('starting backup for %s', self.repos_path)
48 logging.info('starting backup for %s', self.repos_path)
49 logging.info('backup target %s', self.backup_file_path)
49 logging.info('backup target %s', self.backup_file_path)
50
50
51
51
52 def get_id_rsa(self, rsa_key):
52 def get_id_rsa(self, rsa_key):
53 if not os.path.isfile(rsa_key):
53 if not os.path.isfile(rsa_key):
54 logging.error('Could not load id_rsa key file in %s', rsa_key)
54 logging.error('Could not load id_rsa key file in %s', rsa_key)
55 sys.exit()
55 sys.exit()
56 return rsa_key
56 return rsa_key
57
57
58 def get_repos_path(self, path):
58 def get_repos_path(self, path):
59 if not os.path.isdir(path):
59 if not os.path.isdir(path):
60 logging.error('Wrong location for repositories in %s', path)
60 logging.error('Wrong location for repositories in %s', path)
61 sys.exit()
61 sys.exit()
62 return path
62 return path
63
63
64 def backup_repos(self):
64 def backup_repos(self):
65 bckp_file = os.path.join(self.backup_file_path, self.backup_file_name)
65 bckp_file = os.path.join(self.backup_file_path, self.backup_file_name)
66 tar = tarfile.open(bckp_file, "w:gz")
66 tar = tarfile.open(bckp_file, "w:gz")
67
67
68 for dir_name in os.listdir(self.repos_path):
68 for dir_name in os.listdir(self.repos_path):
69 logging.info('backing up %s', dir_name)
69 logging.info('backing up %s', dir_name)
70 tar.add(os.path.join(self.repos_path, dir_name), dir_name)
70 tar.add(os.path.join(self.repos_path, dir_name), dir_name)
71 tar.close()
71 tar.close()
72 logging.info('finished backup of mercurial repositories')
72 logging.info('finished backup of mercurial repositories')
73
73
74
74
75
75
76 def transfer_files(self):
76 def transfer_files(self):
77 params = {
77 params = {
78 'id_rsa_key': self.id_rsa_path,
78 'id_rsa_key': self.id_rsa_path,
79 'backup_file':os.path.join(self.backup_file_path,
79 'backup_file':os.path.join(self.backup_file_path,
80 self.backup_file_name),
80 self.backup_file_name),
81 'backup_server':self.backup_server
81 'backup_server':self.backup_server
82 }
82 }
83 cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params,
83 cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params,
84 '%(backup_file)s' % params,
84 '%(backup_file)s' % params,
85 '%(backup_server)s' % params]
85 '%(backup_server)s' % params]
86
86
87 subprocess.call(cmd)
87 subprocess.call(cmd)
88 logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4])
88 logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4])
89
89
90
90
91 def rm_file(self):
91 def rm_file(self):
92 logging.info('Removing file %s', self.backup_file_name)
92 logging.info('Removing file %s', self.backup_file_name)
93 os.remove(os.path.join(self.backup_file_path, self.backup_file_name))
93 os.remove(os.path.join(self.backup_file_path, self.backup_file_name))
94
94
95
95
96
96
97 if __name__ == "__main__":
97 if __name__ == "__main__":
98
98
99 repo_location = '/home/repo_path'
99 repo_location = '/home/repo_path'
100 backup_server = 'root@192.168.1.100:/backups/mercurial'
100 backup_server = 'root@192.168.1.100:/backups/mercurial'
101 rsa_key = '/home/id_rsa'
101 rsa_key = '/home/id_rsa'
102
102
103 B_MANAGER = BackupManager(repo_location, rsa_key, backup_server)
103 B_MANAGER = BackupManager(repo_location, rsa_key, backup_server)
104 B_MANAGER.backup_repos()
104 B_MANAGER.backup_repos()
105 B_MANAGER.transfer_files()
105 B_MANAGER.transfer_files()
106 B_MANAGER.rm_file()
106 B_MANAGER.rm_file()
107
107
108
108
@@ -1,106 +1,106 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.rhodecode.lib.celerylib.__init__
3 package.rhodecode.lib.celerylib.__init__
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~
5
5
6 celery libs for RhodeCode
6 celery libs for RhodeCode
7
7
8 :created_on: Nov 27, 2010
8 :created_on: Nov 27, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import os
28 import os
29 import sys
29 import sys
30 import socket
30 import socket
31 import traceback
31 import traceback
32 import logging
32 import logging
33
33
34 from hashlib import md5
34 from hashlib import md5
35 from decorator import decorator
35 from decorator import decorator
36 from vcs.utils.lazy import LazyProperty
36 from vcs.utils.lazy import LazyProperty
37
37
38 from rhodecode.lib.pidlock import DaemonLock, LockHeld
38 from rhodecode.lib.pidlock import DaemonLock, LockHeld
39
39
40 from pylons import config
40 from pylons import config
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44 def str2bool(v):
44 def str2bool(v):
45 return v.lower() in ["yes", "true", "t", "1"] if v else None
45 return v.lower() in ["yes", "true", "t", "1"] if v else None
46
46
47 try:
47 try:
48 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
48 CELERY_ON = str2bool(config['app_conf'].get('use_celery'))
49 except KeyError:
49 except KeyError:
50 CELERY_ON = False
50 CELERY_ON = False
51
51
52 class ResultWrapper(object):
52 class ResultWrapper(object):
53 def __init__(self, task):
53 def __init__(self, task):
54 self.task = task
54 self.task = task
55
55
56 @LazyProperty
56 @LazyProperty
57 def result(self):
57 def result(self):
58 return self.task
58 return self.task
59
59
60 def run_task(task, *args, **kwargs):
60 def run_task(task, *args, **kwargs):
61 if CELERY_ON:
61 if CELERY_ON:
62 try:
62 try:
63 t = task.delay(*args, **kwargs)
63 t = task.delay(*args, **kwargs)
64 log.info('running task %s:%s', t.task_id, task)
64 log.info('running task %s:%s', t.task_id, task)
65 return t
65 return t
66 except socket.error, e:
66 except socket.error, e:
67 if e.errno == 111:
67 if e.errno == 111:
68 log.debug('Unable to connect to celeryd. Sync execution')
68 log.debug('Unable to connect to celeryd. Sync execution')
69 else:
69 else:
70 log.error(traceback.format_exc())
70 log.error(traceback.format_exc())
71 except KeyError, e:
71 except KeyError, e:
72 log.debug('Unable to connect to celeryd. Sync execution')
72 log.debug('Unable to connect to celeryd. Sync execution')
73 except Exception, e:
73 except Exception, e:
74 log.error(traceback.format_exc())
74 log.error(traceback.format_exc())
75
75
76 log.debug('executing task %s in sync mode', task)
76 log.debug('executing task %s in sync mode', task)
77 return ResultWrapper(task(*args, **kwargs))
77 return ResultWrapper(task(*args, **kwargs))
78
78
79
79
80 def locked_task(func):
80 def locked_task(func):
81 def __wrapper(func, *fargs, **fkwargs):
81 def __wrapper(func, *fargs, **fkwargs):
82 params = list(fargs)
82 params = list(fargs)
83 params.extend(['%s-%s' % ar for ar in fkwargs.items()])
83 params.extend(['%s-%s' % ar for ar in fkwargs.items()])
84
84
85 lockkey = 'task_%s' % \
85 lockkey = 'task_%s' % \
86 md5(str(func.__name__) + '-' + \
86 md5(str(func.__name__) + '-' + \
87 '-'.join(map(str, params))).hexdigest()
87 '-'.join(map(str, params))).hexdigest()
88 log.info('running task with lockkey %s', lockkey)
88 log.info('running task with lockkey %s', lockkey)
89 try:
89 try:
90 l = DaemonLock(lockkey)
90 l = DaemonLock(lockkey)
91 ret = func(*fargs, **fkwargs)
91 ret = func(*fargs, **fkwargs)
92 l.release()
92 l.release()
93 return ret
93 return ret
94 except LockHeld:
94 except LockHeld:
95 log.info('LockHeld')
95 log.info('LockHeld')
96 return 'Task with key %s already running' % lockkey
96 return 'Task with key %s already running' % lockkey
97
97
98 return decorator(__wrapper, func)
98 return decorator(__wrapper, func)
99
99
100
100
101
101
102
102
103
103
104
104
105
105
106
106
@@ -1,494 +1,494 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.db_manage
3 rhodecode.lib.db_manage
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database creation, and setup module for RhodeCode. Used for creation
6 Database creation, and setup module for RhodeCode. Used for creation
7 of database as well as for migration operations
7 of database as well as for migration operations
8
8
9 :created_on: Apr 10, 2010
9 :created_on: Apr 10, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software; you can redistribute it and/or
14 # This program is free software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; version 2
16 # as published by the Free Software Foundation; version 2
17 # of the License or (at your opinion) any later version of the license.
17 # of the License or (at your opinion) any later version of the license.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 # MA 02110-1301, USA.
27 # MA 02110-1301, USA.
28
28
29 import os
29 import os
30 import sys
30 import sys
31 import uuid
31 import uuid
32 import logging
32 import logging
33 from os.path import dirname as dn, join as jn
33 from os.path import dirname as dn, join as jn
34
34
35 from rhodecode import __dbversion__
35 from rhodecode import __dbversion__
36 from rhodecode.model import meta
36 from rhodecode.model import meta
37
37
38 from rhodecode.lib.auth import get_crypt_password
38 from rhodecode.lib.auth import get_crypt_password
39 from rhodecode.lib.utils import ask_ok
39 from rhodecode.lib.utils import ask_ok
40 from rhodecode.model import init_model
40 from rhodecode.model import init_model
41 from rhodecode.model.db import User, Permission, RhodeCodeUi, RhodeCodeSettings, \
41 from rhodecode.model.db import User, Permission, RhodeCodeUi, RhodeCodeSettings, \
42 UserToPerm, DbMigrateVersion
42 UserToPerm, DbMigrateVersion
43
43
44 from sqlalchemy.engine import create_engine
44 from sqlalchemy.engine import create_engine
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48 class DbManage(object):
48 class DbManage(object):
49 def __init__(self, log_sql, dbconf, root, tests=False):
49 def __init__(self, log_sql, dbconf, root, tests=False):
50 self.dbname = dbconf.split('/')[-1]
50 self.dbname = dbconf.split('/')[-1]
51 self.tests = tests
51 self.tests = tests
52 self.root = root
52 self.root = root
53 self.dburi = dbconf
53 self.dburi = dbconf
54 engine = create_engine(self.dburi, echo=log_sql)
54 engine = create_engine(self.dburi, echo=log_sql)
55 init_model(engine)
55 init_model(engine)
56 self.sa = meta.Session()
56 self.sa = meta.Session()
57 self.db_exists = False
57 self.db_exists = False
58
58
59 def check_for_db(self, override):
59 def check_for_db(self, override):
60 db_path = jn(self.root, self.dbname)
60 db_path = jn(self.root, self.dbname)
61 if self.dburi.startswith('sqlite'):
61 if self.dburi.startswith('sqlite'):
62 log.info('checking for existing db in %s', db_path)
62 log.info('checking for existing db in %s', db_path)
63 if os.path.isfile(db_path):
63 if os.path.isfile(db_path):
64
64
65 self.db_exists = True
65 self.db_exists = True
66 if not override:
66 if not override:
67 raise Exception('database already exists')
67 raise Exception('database already exists')
68
68
69 def create_tables(self, override=False):
69 def create_tables(self, override=False):
70 """Create a auth database
70 """Create a auth database
71 """
71 """
72
72
73 self.check_for_db(override)
73 self.check_for_db(override)
74 if self.db_exists:
74 if self.db_exists:
75 log.info("database exist and it's going to be destroyed")
75 log.info("database exist and it's going to be destroyed")
76 if self.tests:
76 if self.tests:
77 destroy = True
77 destroy = True
78 else:
78 else:
79 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
79 destroy = ask_ok('Are you sure to destroy old database ? [y/n]')
80 if not destroy:
80 if not destroy:
81 sys.exit()
81 sys.exit()
82 if self.db_exists and destroy:
82 if self.db_exists and destroy:
83 os.remove(jn(self.root, self.dbname))
83 os.remove(jn(self.root, self.dbname))
84 checkfirst = not override
84 checkfirst = not override
85 meta.Base.metadata.create_all(checkfirst=checkfirst)
85 meta.Base.metadata.create_all(checkfirst=checkfirst)
86 log.info('Created tables for %s', self.dbname)
86 log.info('Created tables for %s', self.dbname)
87
87
88
88
89
89
90 def set_db_version(self):
90 def set_db_version(self):
91 try:
91 try:
92 ver = DbMigrateVersion()
92 ver = DbMigrateVersion()
93 ver.version = __dbversion__
93 ver.version = __dbversion__
94 ver.repository_id = 'rhodecode_db_migrations'
94 ver.repository_id = 'rhodecode_db_migrations'
95 ver.repository_path = 'versions'
95 ver.repository_path = 'versions'
96 self.sa.add(ver)
96 self.sa.add(ver)
97 self.sa.commit()
97 self.sa.commit()
98 except:
98 except:
99 self.sa.rollback()
99 self.sa.rollback()
100 raise
100 raise
101 log.info('db version set to: %s', __dbversion__)
101 log.info('db version set to: %s', __dbversion__)
102
102
103
103
104 def upgrade(self):
104 def upgrade(self):
105 """Upgrades given database schema to given revision following
105 """Upgrades given database schema to given revision following
106 all needed steps, to perform the upgrade
106 all needed steps, to perform the upgrade
107
107
108 """
108 """
109
109
110 from rhodecode.lib.dbmigrate.migrate.versioning import api
110 from rhodecode.lib.dbmigrate.migrate.versioning import api
111 from rhodecode.lib.dbmigrate.migrate.exceptions import \
111 from rhodecode.lib.dbmigrate.migrate.exceptions import \
112 DatabaseNotControlledError
112 DatabaseNotControlledError
113
113
114 upgrade = ask_ok('You are about to perform database upgrade, make '
114 upgrade = ask_ok('You are about to perform database upgrade, make '
115 'sure You backed up your database before. '
115 'sure You backed up your database before. '
116 'Continue ? [y/n]')
116 'Continue ? [y/n]')
117 if not upgrade:
117 if not upgrade:
118 sys.exit('Nothing done')
118 sys.exit('Nothing done')
119
119
120 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
120 repository_path = jn(dn(dn(dn(os.path.realpath(__file__)))),
121 'rhodecode/lib/dbmigrate')
121 'rhodecode/lib/dbmigrate')
122 db_uri = self.dburi
122 db_uri = self.dburi
123
123
124 try:
124 try:
125 curr_version = api.db_version(db_uri, repository_path)
125 curr_version = api.db_version(db_uri, repository_path)
126 msg = ('Found current database under version'
126 msg = ('Found current database under version'
127 ' control with version %s' % curr_version)
127 ' control with version %s' % curr_version)
128
128
129 except (RuntimeError, DatabaseNotControlledError), e:
129 except (RuntimeError, DatabaseNotControlledError), e:
130 curr_version = 1
130 curr_version = 1
131 msg = ('Current database is not under version control. Setting'
131 msg = ('Current database is not under version control. Setting'
132 ' as version %s' % curr_version)
132 ' as version %s' % curr_version)
133 api.version_control(db_uri, repository_path, curr_version)
133 api.version_control(db_uri, repository_path, curr_version)
134
134
135 print (msg)
135 print (msg)
136
136
137 if curr_version == __dbversion__:
137 if curr_version == __dbversion__:
138 sys.exit('This database is already at the newest version')
138 sys.exit('This database is already at the newest version')
139
139
140 #======================================================================
140 #======================================================================
141 # UPGRADE STEPS
141 # UPGRADE STEPS
142 #======================================================================
142 #======================================================================
143 class UpgradeSteps(object):
143 class UpgradeSteps(object):
144 """Those steps follow schema versions so for example schema
144 """Those steps follow schema versions so for example schema
145 for example schema with seq 002 == step_2 and so on.
145 for example schema with seq 002 == step_2 and so on.
146 """
146 """
147
147
148 def __init__(self, klass):
148 def __init__(self, klass):
149 self.klass = klass
149 self.klass = klass
150
150
151 def step_0(self):
151 def step_0(self):
152 #step 0 is the schema upgrade, and than follow proper upgrades
152 #step 0 is the schema upgrade, and than follow proper upgrades
153 print ('attempting to do database upgrade to version %s' \
153 print ('attempting to do database upgrade to version %s' \
154 % __dbversion__)
154 % __dbversion__)
155 api.upgrade(db_uri, repository_path, __dbversion__)
155 api.upgrade(db_uri, repository_path, __dbversion__)
156 print ('Schema upgrade completed')
156 print ('Schema upgrade completed')
157
157
158 def step_1(self):
158 def step_1(self):
159 pass
159 pass
160
160
161 def step_2(self):
161 def step_2(self):
162 print ('Patching repo paths for newer version of RhodeCode')
162 print ('Patching repo paths for newer version of RhodeCode')
163 self.klass.fix_repo_paths()
163 self.klass.fix_repo_paths()
164
164
165 print ('Patching default user of RhodeCode')
165 print ('Patching default user of RhodeCode')
166 self.klass.fix_default_user()
166 self.klass.fix_default_user()
167
167
168 log.info('Changing ui settings')
168 log.info('Changing ui settings')
169 self.klass.create_ui_settings()
169 self.klass.create_ui_settings()
170
170
171 def step_3(self):
171 def step_3(self):
172 print ('Adding additional settings into RhodeCode db')
172 print ('Adding additional settings into RhodeCode db')
173 self.klass.fix_settings()
173 self.klass.fix_settings()
174
174
175 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
175 upgrade_steps = [0] + range(curr_version + 1, __dbversion__ + 1)
176
176
177 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
177 #CALL THE PROPER ORDER OF STEPS TO PERFORM FULL UPGRADE
178 for step in upgrade_steps:
178 for step in upgrade_steps:
179 print ('performing upgrade step %s' % step)
179 print ('performing upgrade step %s' % step)
180 callable = getattr(UpgradeSteps(self), 'step_%s' % step)()
180 callable = getattr(UpgradeSteps(self), 'step_%s' % step)()
181
181
182
182
183
183
184 def fix_repo_paths(self):
184 def fix_repo_paths(self):
185 """Fixes a old rhodecode version path into new one without a '*'
185 """Fixes a old rhodecode version path into new one without a '*'
186 """
186 """
187
187
188 paths = self.sa.query(RhodeCodeUi)\
188 paths = self.sa.query(RhodeCodeUi)\
189 .filter(RhodeCodeUi.ui_key == '/')\
189 .filter(RhodeCodeUi.ui_key == '/')\
190 .scalar()
190 .scalar()
191
191
192 paths.ui_value = paths.ui_value.replace('*', '')
192 paths.ui_value = paths.ui_value.replace('*', '')
193
193
194 try:
194 try:
195 self.sa.add(paths)
195 self.sa.add(paths)
196 self.sa.commit()
196 self.sa.commit()
197 except:
197 except:
198 self.sa.rollback()
198 self.sa.rollback()
199 raise
199 raise
200
200
201 def fix_default_user(self):
201 def fix_default_user(self):
202 """Fixes a old default user with some 'nicer' default values,
202 """Fixes a old default user with some 'nicer' default values,
203 used mostly for anonymous access
203 used mostly for anonymous access
204 """
204 """
205 def_user = self.sa.query(User)\
205 def_user = self.sa.query(User)\
206 .filter(User.username == 'default')\
206 .filter(User.username == 'default')\
207 .one()
207 .one()
208
208
209 def_user.name = 'Anonymous'
209 def_user.name = 'Anonymous'
210 def_user.lastname = 'User'
210 def_user.lastname = 'User'
211 def_user.email = 'anonymous@rhodecode.org'
211 def_user.email = 'anonymous@rhodecode.org'
212
212
213 try:
213 try:
214 self.sa.add(def_user)
214 self.sa.add(def_user)
215 self.sa.commit()
215 self.sa.commit()
216 except:
216 except:
217 self.sa.rollback()
217 self.sa.rollback()
218 raise
218 raise
219
219
220 def fix_settings(self):
220 def fix_settings(self):
221 """Fixes rhodecode settings adds ga_code key for google analytics
221 """Fixes rhodecode settings adds ga_code key for google analytics
222 """
222 """
223
223
224 hgsettings3 = RhodeCodeSettings('ga_code', '')
224 hgsettings3 = RhodeCodeSettings('ga_code', '')
225 try:
225 try:
226 self.sa.add(hgsettings3)
226 self.sa.add(hgsettings3)
227 self.sa.commit()
227 self.sa.commit()
228 except:
228 except:
229 self.sa.rollback()
229 self.sa.rollback()
230 raise
230 raise
231
231
232 def admin_prompt(self, second=False):
232 def admin_prompt(self, second=False):
233 if not self.tests:
233 if not self.tests:
234 import getpass
234 import getpass
235
235
236
236
237 def get_password():
237 def get_password():
238 password = getpass.getpass('Specify admin password (min 6 chars):')
238 password = getpass.getpass('Specify admin password (min 6 chars):')
239 confirm = getpass.getpass('Confirm password:')
239 confirm = getpass.getpass('Confirm password:')
240
240
241 if password != confirm:
241 if password != confirm:
242 log.error('passwords mismatch')
242 log.error('passwords mismatch')
243 return False
243 return False
244 if len(password) < 6:
244 if len(password) < 6:
245 log.error('password is to short use at least 6 characters')
245 log.error('password is to short use at least 6 characters')
246 return False
246 return False
247
247
248 return password
248 return password
249
249
250 username = raw_input('Specify admin username:')
250 username = raw_input('Specify admin username:')
251
251
252 password = get_password()
252 password = get_password()
253 if not password:
253 if not password:
254 #second try
254 #second try
255 password = get_password()
255 password = get_password()
256 if not password:
256 if not password:
257 sys.exit()
257 sys.exit()
258
258
259 email = raw_input('Specify admin email:')
259 email = raw_input('Specify admin email:')
260 self.create_user(username, password, email, True)
260 self.create_user(username, password, email, True)
261 else:
261 else:
262 log.info('creating admin and regular test users')
262 log.info('creating admin and regular test users')
263 self.create_user('test_admin', 'test12', 'test_admin@mail.com', True)
263 self.create_user('test_admin', 'test12', 'test_admin@mail.com', True)
264 self.create_user('test_regular', 'test12', 'test_regular@mail.com', False)
264 self.create_user('test_regular', 'test12', 'test_regular@mail.com', False)
265 self.create_user('test_regular2', 'test12', 'test_regular2@mail.com', False)
265 self.create_user('test_regular2', 'test12', 'test_regular2@mail.com', False)
266
266
267 def create_ui_settings(self):
267 def create_ui_settings(self):
268 """Creates ui settings, fills out hooks
268 """Creates ui settings, fills out hooks
269 and disables dotencode
269 and disables dotencode
270
270
271 """
271 """
272 #HOOKS
272 #HOOKS
273 hooks1_key = 'changegroup.update'
273 hooks1_key = 'changegroup.update'
274 hooks1_ = self.sa.query(RhodeCodeUi)\
274 hooks1_ = self.sa.query(RhodeCodeUi)\
275 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
275 .filter(RhodeCodeUi.ui_key == hooks1_key).scalar()
276
276
277 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
277 hooks1 = RhodeCodeUi() if hooks1_ is None else hooks1_
278 hooks1.ui_section = 'hooks'
278 hooks1.ui_section = 'hooks'
279 hooks1.ui_key = hooks1_key
279 hooks1.ui_key = hooks1_key
280 hooks1.ui_value = 'hg update >&2'
280 hooks1.ui_value = 'hg update >&2'
281 hooks1.ui_active = False
281 hooks1.ui_active = False
282
282
283 hooks2_key = 'changegroup.repo_size'
283 hooks2_key = 'changegroup.repo_size'
284 hooks2_ = self.sa.query(RhodeCodeUi)\
284 hooks2_ = self.sa.query(RhodeCodeUi)\
285 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
285 .filter(RhodeCodeUi.ui_key == hooks2_key).scalar()
286
286
287 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
287 hooks2 = RhodeCodeUi() if hooks2_ is None else hooks2_
288 hooks2.ui_section = 'hooks'
288 hooks2.ui_section = 'hooks'
289 hooks2.ui_key = hooks2_key
289 hooks2.ui_key = hooks2_key
290 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
290 hooks2.ui_value = 'python:rhodecode.lib.hooks.repo_size'
291
291
292 hooks3 = RhodeCodeUi()
292 hooks3 = RhodeCodeUi()
293 hooks3.ui_section = 'hooks'
293 hooks3.ui_section = 'hooks'
294 hooks3.ui_key = 'pretxnchangegroup.push_logger'
294 hooks3.ui_key = 'pretxnchangegroup.push_logger'
295 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
295 hooks3.ui_value = 'python:rhodecode.lib.hooks.log_push_action'
296
296
297 hooks4 = RhodeCodeUi()
297 hooks4 = RhodeCodeUi()
298 hooks4.ui_section = 'hooks'
298 hooks4.ui_section = 'hooks'
299 hooks4.ui_key = 'preoutgoing.pull_logger'
299 hooks4.ui_key = 'preoutgoing.pull_logger'
300 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
300 hooks4.ui_value = 'python:rhodecode.lib.hooks.log_pull_action'
301
301
302 #For mercurial 1.7 set backward comapatibility with format
302 #For mercurial 1.7 set backward comapatibility with format
303 dotencode_disable = RhodeCodeUi()
303 dotencode_disable = RhodeCodeUi()
304 dotencode_disable.ui_section = 'format'
304 dotencode_disable.ui_section = 'format'
305 dotencode_disable.ui_key = 'dotencode'
305 dotencode_disable.ui_key = 'dotencode'
306 dotencode_disable.ui_value = 'false'
306 dotencode_disable.ui_value = 'false'
307
307
308 try:
308 try:
309 self.sa.add(hooks1)
309 self.sa.add(hooks1)
310 self.sa.add(hooks2)
310 self.sa.add(hooks2)
311 self.sa.add(hooks3)
311 self.sa.add(hooks3)
312 self.sa.add(hooks4)
312 self.sa.add(hooks4)
313 self.sa.add(dotencode_disable)
313 self.sa.add(dotencode_disable)
314 self.sa.commit()
314 self.sa.commit()
315 except:
315 except:
316 self.sa.rollback()
316 self.sa.rollback()
317 raise
317 raise
318
318
319
319
320 def create_ldap_options(self):
320 def create_ldap_options(self):
321 """Creates ldap settings"""
321 """Creates ldap settings"""
322
322
323 try:
323 try:
324 for k in ['ldap_active', 'ldap_host', 'ldap_port', 'ldap_ldaps',
324 for k in ['ldap_active', 'ldap_host', 'ldap_port', 'ldap_ldaps',
325 'ldap_dn_user', 'ldap_dn_pass', 'ldap_base_dn']:
325 'ldap_dn_user', 'ldap_dn_pass', 'ldap_base_dn']:
326
326
327 setting = RhodeCodeSettings(k, '')
327 setting = RhodeCodeSettings(k, '')
328 self.sa.add(setting)
328 self.sa.add(setting)
329 self.sa.commit()
329 self.sa.commit()
330 except:
330 except:
331 self.sa.rollback()
331 self.sa.rollback()
332 raise
332 raise
333
333
334 def config_prompt(self, test_repo_path=''):
334 def config_prompt(self, test_repo_path=''):
335 log.info('Setting up repositories config')
335 log.info('Setting up repositories config')
336
336
337 if not self.tests and not test_repo_path:
337 if not self.tests and not test_repo_path:
338 path = raw_input('Specify valid full path to your repositories'
338 path = raw_input('Specify valid full path to your repositories'
339 ' you can change this later in application settings:')
339 ' you can change this later in application settings:')
340 else:
340 else:
341 path = test_repo_path
341 path = test_repo_path
342
342
343 if not os.path.isdir(path):
343 if not os.path.isdir(path):
344 log.error('You entered wrong path: %s', path)
344 log.error('You entered wrong path: %s', path)
345 sys.exit()
345 sys.exit()
346
346
347 self.create_ui_settings()
347 self.create_ui_settings()
348
348
349 #HG UI OPTIONS
349 #HG UI OPTIONS
350 web1 = RhodeCodeUi()
350 web1 = RhodeCodeUi()
351 web1.ui_section = 'web'
351 web1.ui_section = 'web'
352 web1.ui_key = 'push_ssl'
352 web1.ui_key = 'push_ssl'
353 web1.ui_value = 'false'
353 web1.ui_value = 'false'
354
354
355 web2 = RhodeCodeUi()
355 web2 = RhodeCodeUi()
356 web2.ui_section = 'web'
356 web2.ui_section = 'web'
357 web2.ui_key = 'allow_archive'
357 web2.ui_key = 'allow_archive'
358 web2.ui_value = 'gz zip bz2'
358 web2.ui_value = 'gz zip bz2'
359
359
360 web3 = RhodeCodeUi()
360 web3 = RhodeCodeUi()
361 web3.ui_section = 'web'
361 web3.ui_section = 'web'
362 web3.ui_key = 'allow_push'
362 web3.ui_key = 'allow_push'
363 web3.ui_value = '*'
363 web3.ui_value = '*'
364
364
365 web4 = RhodeCodeUi()
365 web4 = RhodeCodeUi()
366 web4.ui_section = 'web'
366 web4.ui_section = 'web'
367 web4.ui_key = 'baseurl'
367 web4.ui_key = 'baseurl'
368 web4.ui_value = '/'
368 web4.ui_value = '/'
369
369
370 paths = RhodeCodeUi()
370 paths = RhodeCodeUi()
371 paths.ui_section = 'paths'
371 paths.ui_section = 'paths'
372 paths.ui_key = '/'
372 paths.ui_key = '/'
373 paths.ui_value = path
373 paths.ui_value = path
374
374
375
375
376 hgsettings1 = RhodeCodeSettings('realm', 'RhodeCode authentication')
376 hgsettings1 = RhodeCodeSettings('realm', 'RhodeCode authentication')
377 hgsettings2 = RhodeCodeSettings('title', 'RhodeCode')
377 hgsettings2 = RhodeCodeSettings('title', 'RhodeCode')
378 hgsettings3 = RhodeCodeSettings('ga_code', '')
378 hgsettings3 = RhodeCodeSettings('ga_code', '')
379
379
380
380
381 try:
381 try:
382 self.sa.add(web1)
382 self.sa.add(web1)
383 self.sa.add(web2)
383 self.sa.add(web2)
384 self.sa.add(web3)
384 self.sa.add(web3)
385 self.sa.add(web4)
385 self.sa.add(web4)
386 self.sa.add(paths)
386 self.sa.add(paths)
387 self.sa.add(hgsettings1)
387 self.sa.add(hgsettings1)
388 self.sa.add(hgsettings2)
388 self.sa.add(hgsettings2)
389 self.sa.add(hgsettings3)
389 self.sa.add(hgsettings3)
390
390
391 self.sa.commit()
391 self.sa.commit()
392 except:
392 except:
393 self.sa.rollback()
393 self.sa.rollback()
394 raise
394 raise
395
395
396 self.create_ldap_options()
396 self.create_ldap_options()
397
397
398 log.info('created ui config')
398 log.info('created ui config')
399
399
400 def create_user(self, username, password, email='', admin=False):
400 def create_user(self, username, password, email='', admin=False):
401 log.info('creating administrator user %s', username)
401 log.info('creating administrator user %s', username)
402 new_user = User()
402 new_user = User()
403 new_user.username = username
403 new_user.username = username
404 new_user.password = get_crypt_password(password)
404 new_user.password = get_crypt_password(password)
405 new_user.name = 'RhodeCode'
405 new_user.name = 'RhodeCode'
406 new_user.lastname = 'Admin'
406 new_user.lastname = 'Admin'
407 new_user.email = email
407 new_user.email = email
408 new_user.admin = admin
408 new_user.admin = admin
409 new_user.active = True
409 new_user.active = True
410
410
411 try:
411 try:
412 self.sa.add(new_user)
412 self.sa.add(new_user)
413 self.sa.commit()
413 self.sa.commit()
414 except:
414 except:
415 self.sa.rollback()
415 self.sa.rollback()
416 raise
416 raise
417
417
418 def create_default_user(self):
418 def create_default_user(self):
419 log.info('creating default user')
419 log.info('creating default user')
420 #create default user for handling default permissions.
420 #create default user for handling default permissions.
421 def_user = User()
421 def_user = User()
422 def_user.username = 'default'
422 def_user.username = 'default'
423 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
423 def_user.password = get_crypt_password(str(uuid.uuid1())[:8])
424 def_user.name = 'Anonymous'
424 def_user.name = 'Anonymous'
425 def_user.lastname = 'User'
425 def_user.lastname = 'User'
426 def_user.email = 'anonymous@rhodecode.org'
426 def_user.email = 'anonymous@rhodecode.org'
427 def_user.admin = False
427 def_user.admin = False
428 def_user.active = False
428 def_user.active = False
429 try:
429 try:
430 self.sa.add(def_user)
430 self.sa.add(def_user)
431 self.sa.commit()
431 self.sa.commit()
432 except:
432 except:
433 self.sa.rollback()
433 self.sa.rollback()
434 raise
434 raise
435
435
436 def create_permissions(self):
436 def create_permissions(self):
437 #module.(access|create|change|delete)_[name]
437 #module.(access|create|change|delete)_[name]
438 #module.(read|write|owner)
438 #module.(read|write|owner)
439 perms = [('repository.none', 'Repository no access'),
439 perms = [('repository.none', 'Repository no access'),
440 ('repository.read', 'Repository read access'),
440 ('repository.read', 'Repository read access'),
441 ('repository.write', 'Repository write access'),
441 ('repository.write', 'Repository write access'),
442 ('repository.admin', 'Repository admin access'),
442 ('repository.admin', 'Repository admin access'),
443 ('hg.admin', 'Hg Administrator'),
443 ('hg.admin', 'Hg Administrator'),
444 ('hg.create.repository', 'Repository create'),
444 ('hg.create.repository', 'Repository create'),
445 ('hg.create.none', 'Repository creation disabled'),
445 ('hg.create.none', 'Repository creation disabled'),
446 ('hg.register.none', 'Register disabled'),
446 ('hg.register.none', 'Register disabled'),
447 ('hg.register.manual_activate', 'Register new user with rhodecode without manual activation'),
447 ('hg.register.manual_activate', 'Register new user with rhodecode without manual activation'),
448 ('hg.register.auto_activate', 'Register new user with rhodecode without auto activation'),
448 ('hg.register.auto_activate', 'Register new user with rhodecode without auto activation'),
449 ]
449 ]
450
450
451 for p in perms:
451 for p in perms:
452 new_perm = Permission()
452 new_perm = Permission()
453 new_perm.permission_name = p[0]
453 new_perm.permission_name = p[0]
454 new_perm.permission_longname = p[1]
454 new_perm.permission_longname = p[1]
455 try:
455 try:
456 self.sa.add(new_perm)
456 self.sa.add(new_perm)
457 self.sa.commit()
457 self.sa.commit()
458 except:
458 except:
459 self.sa.rollback()
459 self.sa.rollback()
460 raise
460 raise
461
461
462 def populate_default_permissions(self):
462 def populate_default_permissions(self):
463 log.info('creating default user permissions')
463 log.info('creating default user permissions')
464
464
465 default_user = self.sa.query(User)\
465 default_user = self.sa.query(User)\
466 .filter(User.username == 'default').scalar()
466 .filter(User.username == 'default').scalar()
467
467
468 reg_perm = UserToPerm()
468 reg_perm = UserToPerm()
469 reg_perm.user = default_user
469 reg_perm.user = default_user
470 reg_perm.permission = self.sa.query(Permission)\
470 reg_perm.permission = self.sa.query(Permission)\
471 .filter(Permission.permission_name == 'hg.register.manual_activate')\
471 .filter(Permission.permission_name == 'hg.register.manual_activate')\
472 .scalar()
472 .scalar()
473
473
474 create_repo_perm = UserToPerm()
474 create_repo_perm = UserToPerm()
475 create_repo_perm.user = default_user
475 create_repo_perm.user = default_user
476 create_repo_perm.permission = self.sa.query(Permission)\
476 create_repo_perm.permission = self.sa.query(Permission)\
477 .filter(Permission.permission_name == 'hg.create.repository')\
477 .filter(Permission.permission_name == 'hg.create.repository')\
478 .scalar()
478 .scalar()
479
479
480 default_repo_perm = UserToPerm()
480 default_repo_perm = UserToPerm()
481 default_repo_perm.user = default_user
481 default_repo_perm.user = default_user
482 default_repo_perm.permission = self.sa.query(Permission)\
482 default_repo_perm.permission = self.sa.query(Permission)\
483 .filter(Permission.permission_name == 'repository.read')\
483 .filter(Permission.permission_name == 'repository.read')\
484 .scalar()
484 .scalar()
485
485
486 try:
486 try:
487 self.sa.add(reg_perm)
487 self.sa.add(reg_perm)
488 self.sa.add(create_repo_perm)
488 self.sa.add(create_repo_perm)
489 self.sa.add(default_repo_perm)
489 self.sa.add(default_repo_perm)
490 self.sa.commit()
490 self.sa.commit()
491 except:
491 except:
492 self.sa.rollback()
492 self.sa.rollback()
493 raise
493 raise
494
494
@@ -1,69 +1,69 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.dbmigrate.__init__
3 rhodecode.lib.dbmigrate.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Database migration modules
6 Database migration modules
7
7
8 :created_on: Dec 11, 2010
8 :created_on: Dec 11, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 from sqlalchemy import engine_from_config
29 from sqlalchemy import engine_from_config
30
30
31
31
32 from rhodecode.lib.utils import BasePasterCommand, Command, add_cache
32 from rhodecode.lib.utils import BasePasterCommand, Command, add_cache
33 from rhodecode.lib.db_manage import DbManage
33 from rhodecode.lib.db_manage import DbManage
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37 class UpgradeDb(BasePasterCommand):
37 class UpgradeDb(BasePasterCommand):
38 """Command used for paster to upgrade our database to newer version
38 """Command used for paster to upgrade our database to newer version
39 """
39 """
40
40
41 max_args = 1
41 max_args = 1
42 min_args = 1
42 min_args = 1
43
43
44 usage = "CONFIG_FILE"
44 usage = "CONFIG_FILE"
45 summary = "Upgrades current db to newer version given configuration file"
45 summary = "Upgrades current db to newer version given configuration file"
46 group_name = "RhodeCode"
46 group_name = "RhodeCode"
47
47
48 parser = Command.standard_parser(verbose=True)
48 parser = Command.standard_parser(verbose=True)
49
49
50 def command(self):
50 def command(self):
51 from pylons import config
51 from pylons import config
52
52
53 add_cache(config)
53 add_cache(config)
54
54
55 db_uri = config['sqlalchemy.db1.url']
55 db_uri = config['sqlalchemy.db1.url']
56
56
57 dbmanage = DbManage(log_sql=True, dbconf=db_uri,
57 dbmanage = DbManage(log_sql=True, dbconf=db_uri,
58 root=config['here'], tests=False)
58 root=config['here'], tests=False)
59
59
60 dbmanage.upgrade()
60 dbmanage.upgrade()
61
61
62
62
63
63
64 def update_parser(self):
64 def update_parser(self):
65 self.parser.add_option('--sql',
65 self.parser.add_option('--sql',
66 action='store_true',
66 action='store_true',
67 dest='just_sql',
67 dest='just_sql',
68 help="Prints upgrade sql for further investigation",
68 help="Prints upgrade sql for further investigation",
69 default=False)
69 default=False)
@@ -1,26 +1,26 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.dbmigrate.versions.__init__
3 rhodecode.lib.dbmigrate.versions.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Package containing new versions of database models
6 Package containing new versions of database models
7
7
8 :created_on: Dec 11, 2010
8 :created_on: Dec 11, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
@@ -1,32 +1,32 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Custom Exceptions modules
3 # Custom Exceptions modules
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 Nov 17, 2010
21 Created on Nov 17, 2010
22 Custom Exceptions modules
22 Custom Exceptions modules
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 class LdapUsernameError(Exception):pass
26 class LdapUsernameError(Exception):pass
27 class LdapPasswordError(Exception):pass
27 class LdapPasswordError(Exception):pass
28 class LdapConnectionError(Exception):pass
28 class LdapConnectionError(Exception):pass
29 class LdapImportError(Exception):pass
29 class LdapImportError(Exception):pass
30
30
31 class DefaultUserException(Exception):pass
31 class DefaultUserException(Exception):pass
32 class UserOwnsReposException(Exception):pass
32 class UserOwnsReposException(Exception):pass
@@ -1,105 +1,105 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # custom hooks for application
3 # custom hooks for application
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on Aug 6, 2010
21 Created on Aug 6, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25 from mercurial.cmdutil import revrange
25 from mercurial.cmdutil import revrange
26 from mercurial.node import nullrev
26 from mercurial.node import nullrev
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.utils import action_logger
28 from rhodecode.lib.utils import action_logger
29 import os
29 import os
30 import sys
30 import sys
31
31
32 def repo_size(ui, repo, hooktype=None, **kwargs):
32 def repo_size(ui, repo, hooktype=None, **kwargs):
33
33
34 if hooktype != 'changegroup':
34 if hooktype != 'changegroup':
35 return False
35 return False
36 size_hg, size_root = 0, 0
36 size_hg, size_root = 0, 0
37 for path, dirs, files in os.walk(repo.root):
37 for path, dirs, files in os.walk(repo.root):
38 if path.find('.hg') != -1:
38 if path.find('.hg') != -1:
39 for f in files:
39 for f in files:
40 try:
40 try:
41 size_hg += os.path.getsize(os.path.join(path, f))
41 size_hg += os.path.getsize(os.path.join(path, f))
42 except OSError:
42 except OSError:
43 pass
43 pass
44 else:
44 else:
45 for f in files:
45 for f in files:
46 try:
46 try:
47 size_root += os.path.getsize(os.path.join(path, f))
47 size_root += os.path.getsize(os.path.join(path, f))
48 except OSError:
48 except OSError:
49 pass
49 pass
50
50
51 size_hg_f = h.format_byte_size(size_hg)
51 size_hg_f = h.format_byte_size(size_hg)
52 size_root_f = h.format_byte_size(size_root)
52 size_root_f = h.format_byte_size(size_root)
53 size_total_f = h.format_byte_size(size_root + size_hg)
53 size_total_f = h.format_byte_size(size_root + size_hg)
54 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
54 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
55 % (size_hg_f, size_root_f, size_total_f))
55 % (size_hg_f, size_root_f, size_total_f))
56
56
57 def log_pull_action(ui, repo, **kwargs):
57 def log_pull_action(ui, repo, **kwargs):
58 """
58 """
59 Logs user last pull action
59 Logs user last pull action
60 :param ui:
60 :param ui:
61 :param repo:
61 :param repo:
62 """
62 """
63
63
64 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
64 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
65 username = extra_params['username']
65 username = extra_params['username']
66 repository = extra_params['repository']
66 repository = extra_params['repository']
67 action = 'pull'
67 action = 'pull'
68
68
69 action_logger(username, action, repository, extra_params['ip'])
69 action_logger(username, action, repository, extra_params['ip'])
70
70
71 return 0
71 return 0
72
72
73 def log_push_action(ui, repo, **kwargs):
73 def log_push_action(ui, repo, **kwargs):
74 """
74 """
75 Maps user last push action to new changeset id, from mercurial
75 Maps user last push action to new changeset id, from mercurial
76 :param ui:
76 :param ui:
77 :param repo:
77 :param repo:
78 """
78 """
79
79
80 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
80 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
81 username = extra_params['username']
81 username = extra_params['username']
82 repository = extra_params['repository']
82 repository = extra_params['repository']
83 action = 'push:%s'
83 action = 'push:%s'
84 node = kwargs['node']
84 node = kwargs['node']
85
85
86 def get_revs(repo, rev_opt):
86 def get_revs(repo, rev_opt):
87 if rev_opt:
87 if rev_opt:
88 revs = revrange(repo, rev_opt)
88 revs = revrange(repo, rev_opt)
89
89
90 if len(revs) == 0:
90 if len(revs) == 0:
91 return (nullrev, nullrev)
91 return (nullrev, nullrev)
92 return (max(revs), min(revs))
92 return (max(revs), min(revs))
93 else:
93 else:
94 return (len(repo) - 1, 0)
94 return (len(repo) - 1, 0)
95
95
96 stop, start = get_revs(repo, [node + ':'])
96 stop, start = get_revs(repo, [node + ':'])
97
97
98 revs = (str(repo[r]) for r in xrange(start, stop + 1))
98 revs = (str(repo[r]) for r in xrange(start, stop + 1))
99
99
100 action = action % ','.join(revs)
100 action = action % ','.join(revs)
101
101
102 action_logger(username, action, repository, extra_params['ip'])
102 action_logger(username, action, repository, extra_params['ip'])
103
103
104 return 0
104 return 0
105
105
@@ -1,236 +1,236 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.indexers.daemon
3 rhodecode.lib.indexers.daemon
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 A deamon will read from task table and run tasks
6 A deamon will read from task table and run tasks
7
7
8 :created_on: Jan 26, 2010
8 :created_on: Jan 26, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import sys
28 import sys
29 import os
29 import os
30 import traceback
30 import traceback
31 from os.path import dirname as dn
31 from os.path import dirname as dn
32 from os.path import join as jn
32 from os.path import join as jn
33
33
34 #to get the rhodecode import
34 #to get the rhodecode import
35 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
35 project_path = dn(dn(dn(dn(os.path.realpath(__file__)))))
36 sys.path.append(project_path)
36 sys.path.append(project_path)
37
37
38
38
39 from rhodecode.model.scm import ScmModel
39 from rhodecode.model.scm import ScmModel
40 from rhodecode.lib.helpers import safe_unicode
40 from rhodecode.lib.helpers import safe_unicode
41 from whoosh.index import create_in, open_dir
41 from whoosh.index import create_in, open_dir
42 from shutil import rmtree
42 from shutil import rmtree
43 from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
43 from rhodecode.lib.indexers import INDEX_EXTENSIONS, SCHEMA, IDX_NAME
44
44
45 from time import mktime
45 from time import mktime
46 from vcs.exceptions import ChangesetError, RepositoryError
46 from vcs.exceptions import ChangesetError, RepositoryError
47
47
48 import logging
48 import logging
49
49
50 log = logging.getLogger('whooshIndexer')
50 log = logging.getLogger('whooshIndexer')
51 # create logger
51 # create logger
52 log.setLevel(logging.DEBUG)
52 log.setLevel(logging.DEBUG)
53 log.propagate = False
53 log.propagate = False
54 # create console handler and set level to debug
54 # create console handler and set level to debug
55 ch = logging.StreamHandler()
55 ch = logging.StreamHandler()
56 ch.setLevel(logging.DEBUG)
56 ch.setLevel(logging.DEBUG)
57
57
58 # create formatter
58 # create formatter
59 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
59 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
60
60
61 # add formatter to ch
61 # add formatter to ch
62 ch.setFormatter(formatter)
62 ch.setFormatter(formatter)
63
63
64 # add ch to logger
64 # add ch to logger
65 log.addHandler(ch)
65 log.addHandler(ch)
66
66
67 class WhooshIndexingDaemon(object):
67 class WhooshIndexingDaemon(object):
68 """
68 """
69 Deamon for atomic jobs
69 Deamon for atomic jobs
70 """
70 """
71
71
72 def __init__(self, indexname='HG_INDEX', index_location=None,
72 def __init__(self, indexname='HG_INDEX', index_location=None,
73 repo_location=None, sa=None, repo_list=None):
73 repo_location=None, sa=None, repo_list=None):
74 self.indexname = indexname
74 self.indexname = indexname
75
75
76 self.index_location = index_location
76 self.index_location = index_location
77 if not index_location:
77 if not index_location:
78 raise Exception('You have to provide index location')
78 raise Exception('You have to provide index location')
79
79
80 self.repo_location = repo_location
80 self.repo_location = repo_location
81 if not repo_location:
81 if not repo_location:
82 raise Exception('You have to provide repositories location')
82 raise Exception('You have to provide repositories location')
83
83
84 self.repo_paths = ScmModel(sa).repo_scan(self.repo_location, None)
84 self.repo_paths = ScmModel(sa).repo_scan(self.repo_location, None)
85
85
86 if repo_list:
86 if repo_list:
87 filtered_repo_paths = {}
87 filtered_repo_paths = {}
88 for repo_name, repo in self.repo_paths.items():
88 for repo_name, repo in self.repo_paths.items():
89 if repo_name in repo_list:
89 if repo_name in repo_list:
90 filtered_repo_paths[repo.name] = repo
90 filtered_repo_paths[repo.name] = repo
91
91
92 self.repo_paths = filtered_repo_paths
92 self.repo_paths = filtered_repo_paths
93
93
94
94
95 self.initial = False
95 self.initial = False
96 if not os.path.isdir(self.index_location):
96 if not os.path.isdir(self.index_location):
97 os.makedirs(self.index_location)
97 os.makedirs(self.index_location)
98 log.info('Cannot run incremental index since it does not'
98 log.info('Cannot run incremental index since it does not'
99 ' yet exist running full build')
99 ' yet exist running full build')
100 self.initial = True
100 self.initial = True
101
101
102 def get_paths(self, repo):
102 def get_paths(self, repo):
103 """recursive walk in root dir and return a set of all path in that dir
103 """recursive walk in root dir and return a set of all path in that dir
104 based on repository walk function
104 based on repository walk function
105 """
105 """
106 index_paths_ = set()
106 index_paths_ = set()
107 try:
107 try:
108 for topnode, dirs, files in repo.walk('/', 'tip'):
108 for topnode, dirs, files in repo.walk('/', 'tip'):
109 for f in files:
109 for f in files:
110 index_paths_.add(jn(repo.path, f.path))
110 index_paths_.add(jn(repo.path, f.path))
111 for dir in dirs:
111 for dir in dirs:
112 for f in files:
112 for f in files:
113 index_paths_.add(jn(repo.path, f.path))
113 index_paths_.add(jn(repo.path, f.path))
114
114
115 except RepositoryError, e:
115 except RepositoryError, e:
116 log.debug(traceback.format_exc())
116 log.debug(traceback.format_exc())
117 pass
117 pass
118 return index_paths_
118 return index_paths_
119
119
120 def get_node(self, repo, path):
120 def get_node(self, repo, path):
121 n_path = path[len(repo.path) + 1:]
121 n_path = path[len(repo.path) + 1:]
122 node = repo.get_changeset().get_node(n_path)
122 node = repo.get_changeset().get_node(n_path)
123 return node
123 return node
124
124
125 def get_node_mtime(self, node):
125 def get_node_mtime(self, node):
126 return mktime(node.last_changeset.date.timetuple())
126 return mktime(node.last_changeset.date.timetuple())
127
127
128 def add_doc(self, writer, path, repo):
128 def add_doc(self, writer, path, repo):
129 """Adding doc to writer this function itself fetches data from
129 """Adding doc to writer this function itself fetches data from
130 the instance of vcs backend"""
130 the instance of vcs backend"""
131 node = self.get_node(repo, path)
131 node = self.get_node(repo, path)
132
132
133 #we just index the content of chosen files, and skip binary files
133 #we just index the content of chosen files, and skip binary files
134 if node.extension in INDEX_EXTENSIONS and not node.is_binary:
134 if node.extension in INDEX_EXTENSIONS and not node.is_binary:
135
135
136 u_content = node.content
136 u_content = node.content
137 if not isinstance(u_content, unicode):
137 if not isinstance(u_content, unicode):
138 log.warning(' >> %s Could not get this content as unicode '
138 log.warning(' >> %s Could not get this content as unicode '
139 'replacing with empty content', path)
139 'replacing with empty content', path)
140 u_content = u''
140 u_content = u''
141 else:
141 else:
142 log.debug(' >> %s [WITH CONTENT]' % path)
142 log.debug(' >> %s [WITH CONTENT]' % path)
143
143
144 else:
144 else:
145 log.debug(' >> %s' % path)
145 log.debug(' >> %s' % path)
146 #just index file name without it's content
146 #just index file name without it's content
147 u_content = u''
147 u_content = u''
148
148
149 writer.add_document(owner=unicode(repo.contact),
149 writer.add_document(owner=unicode(repo.contact),
150 repository=safe_unicode(repo.name),
150 repository=safe_unicode(repo.name),
151 path=safe_unicode(path),
151 path=safe_unicode(path),
152 content=u_content,
152 content=u_content,
153 modtime=self.get_node_mtime(node),
153 modtime=self.get_node_mtime(node),
154 extension=node.extension)
154 extension=node.extension)
155
155
156
156
157 def build_index(self):
157 def build_index(self):
158 if os.path.exists(self.index_location):
158 if os.path.exists(self.index_location):
159 log.debug('removing previous index')
159 log.debug('removing previous index')
160 rmtree(self.index_location)
160 rmtree(self.index_location)
161
161
162 if not os.path.exists(self.index_location):
162 if not os.path.exists(self.index_location):
163 os.mkdir(self.index_location)
163 os.mkdir(self.index_location)
164
164
165 idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
165 idx = create_in(self.index_location, SCHEMA, indexname=IDX_NAME)
166 writer = idx.writer()
166 writer = idx.writer()
167
167
168 for repo in self.repo_paths.values():
168 for repo in self.repo_paths.values():
169 log.debug('building index @ %s' % repo.path)
169 log.debug('building index @ %s' % repo.path)
170
170
171 for idx_path in self.get_paths(repo):
171 for idx_path in self.get_paths(repo):
172 self.add_doc(writer, idx_path, repo)
172 self.add_doc(writer, idx_path, repo)
173
173
174 log.debug('>> COMMITING CHANGES <<')
174 log.debug('>> COMMITING CHANGES <<')
175 writer.commit(merge=True)
175 writer.commit(merge=True)
176 log.debug('>>> FINISHED BUILDING INDEX <<<')
176 log.debug('>>> FINISHED BUILDING INDEX <<<')
177
177
178
178
179 def update_index(self):
179 def update_index(self):
180 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
180 log.debug('STARTING INCREMENTAL INDEXING UPDATE')
181
181
182 idx = open_dir(self.index_location, indexname=self.indexname)
182 idx = open_dir(self.index_location, indexname=self.indexname)
183 # The set of all paths in the index
183 # The set of all paths in the index
184 indexed_paths = set()
184 indexed_paths = set()
185 # The set of all paths we need to re-index
185 # The set of all paths we need to re-index
186 to_index = set()
186 to_index = set()
187
187
188 reader = idx.reader()
188 reader = idx.reader()
189 writer = idx.writer()
189 writer = idx.writer()
190
190
191 # Loop over the stored fields in the index
191 # Loop over the stored fields in the index
192 for fields in reader.all_stored_fields():
192 for fields in reader.all_stored_fields():
193 indexed_path = fields['path']
193 indexed_path = fields['path']
194 indexed_paths.add(indexed_path)
194 indexed_paths.add(indexed_path)
195
195
196 repo = self.repo_paths[fields['repository']]
196 repo = self.repo_paths[fields['repository']]
197
197
198 try:
198 try:
199 node = self.get_node(repo, indexed_path)
199 node = self.get_node(repo, indexed_path)
200 except ChangesetError:
200 except ChangesetError:
201 # This file was deleted since it was indexed
201 # This file was deleted since it was indexed
202 log.debug('removing from index %s' % indexed_path)
202 log.debug('removing from index %s' % indexed_path)
203 writer.delete_by_term('path', indexed_path)
203 writer.delete_by_term('path', indexed_path)
204
204
205 else:
205 else:
206 # Check if this file was changed since it was indexed
206 # Check if this file was changed since it was indexed
207 indexed_time = fields['modtime']
207 indexed_time = fields['modtime']
208 mtime = self.get_node_mtime(node)
208 mtime = self.get_node_mtime(node)
209 if mtime > indexed_time:
209 if mtime > indexed_time:
210 # The file has changed, delete it and add it to the list of
210 # The file has changed, delete it and add it to the list of
211 # files to reindex
211 # files to reindex
212 log.debug('adding to reindex list %s' % indexed_path)
212 log.debug('adding to reindex list %s' % indexed_path)
213 writer.delete_by_term('path', indexed_path)
213 writer.delete_by_term('path', indexed_path)
214 to_index.add(indexed_path)
214 to_index.add(indexed_path)
215
215
216 # Loop over the files in the filesystem
216 # Loop over the files in the filesystem
217 # Assume we have a function that gathers the filenames of the
217 # Assume we have a function that gathers the filenames of the
218 # documents to be indexed
218 # documents to be indexed
219 for repo in self.repo_paths.values():
219 for repo in self.repo_paths.values():
220 for path in self.get_paths(repo):
220 for path in self.get_paths(repo):
221 if path in to_index or path not in indexed_paths:
221 if path in to_index or path not in indexed_paths:
222 # This is either a file that's changed, or a new file
222 # This is either a file that's changed, or a new file
223 # that wasn't indexed before. So index it!
223 # that wasn't indexed before. So index it!
224 self.add_doc(writer, path, repo)
224 self.add_doc(writer, path, repo)
225 log.debug('re indexing %s' % path)
225 log.debug('re indexing %s' % path)
226
226
227 log.debug('>> COMMITING CHANGES <<')
227 log.debug('>> COMMITING CHANGES <<')
228 writer.commit(merge=True)
228 writer.commit(merge=True)
229 log.debug('>>> FINISHED REBUILDING INDEX <<<')
229 log.debug('>>> FINISHED REBUILDING INDEX <<<')
230
230
231 def run(self, full_index=False):
231 def run(self, full_index=False):
232 """Run daemon"""
232 """Run daemon"""
233 if full_index or self.initial:
233 if full_index or self.initial:
234 self.build_index()
234 self.build_index()
235 else:
235 else:
236 self.update_index()
236 self.update_index()
@@ -1,47 +1,47 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # middleware to handle https correctly
3 # middleware to handle https correctly
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 May 23, 2010
22 Created on May 23, 2010
23
23
24 @author: marcink
24 @author: marcink
25 """
25 """
26
26
27 class HttpsFixup(object):
27 class HttpsFixup(object):
28 def __init__(self, app):
28 def __init__(self, app):
29 self.application = app
29 self.application = app
30
30
31 def __call__(self, environ, start_response):
31 def __call__(self, environ, start_response):
32 self.__fixup(environ)
32 self.__fixup(environ)
33 return self.application(environ, start_response)
33 return self.application(environ, start_response)
34
34
35
35
36 def __fixup(self, environ):
36 def __fixup(self, environ):
37 """Function to fixup the environ as needed. In order to use this
37 """Function to fixup the environ as needed. In order to use this
38 middleware you should set this header inside your
38 middleware you should set this header inside your
39 proxy ie. nginx, apache etc.
39 proxy ie. nginx, apache etc.
40 """
40 """
41 proto = environ.get('HTTP_X_URL_SCHEME')
41 proto = environ.get('HTTP_X_URL_SCHEME')
42
42
43 if proto == 'https':
43 if proto == 'https':
44 environ['wsgi.url_scheme'] = proto
44 environ['wsgi.url_scheme'] = proto
45 else:
45 else:
46 environ['wsgi.url_scheme'] = 'http'
46 environ['wsgi.url_scheme'] = 'http'
47 return None
47 return None
@@ -1,222 +1,222 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # middleware to handle git api calls
3 # middleware to handle git api calls
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 2010-04-28
21 Created on 2010-04-28
22
22
23 @author: marcink
23 @author: marcink
24 SimpleGit middleware for handling git protocol request (push/clone etc.)
24 SimpleGit middleware for handling git protocol request (push/clone etc.)
25 It's implemented with basic auth function
25 It's implemented with basic auth function
26 """
26 """
27
27
28 from dulwich import server as dulserver
28 from dulwich import server as dulserver
29
29
30 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
30 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
31
31
32 def handle(self):
32 def handle(self):
33 write = lambda x: self.proto.write_sideband(1, x)
33 write = lambda x: self.proto.write_sideband(1, x)
34
34
35 graph_walker = dulserver.ProtocolGraphWalker(self, self.repo.object_store,
35 graph_walker = dulserver.ProtocolGraphWalker(self, self.repo.object_store,
36 self.repo.get_peeled)
36 self.repo.get_peeled)
37 objects_iter = self.repo.fetch_objects(
37 objects_iter = self.repo.fetch_objects(
38 graph_walker.determine_wants, graph_walker, self.progress,
38 graph_walker.determine_wants, graph_walker, self.progress,
39 get_tagged=self.get_tagged)
39 get_tagged=self.get_tagged)
40
40
41 # Do they want any objects?
41 # Do they want any objects?
42 if len(objects_iter) == 0:
42 if len(objects_iter) == 0:
43 return
43 return
44
44
45 self.progress("counting objects: %d, done.\n" % len(objects_iter))
45 self.progress("counting objects: %d, done.\n" % len(objects_iter))
46 dulserver.write_pack_data(dulserver.ProtocolFile(None, write), objects_iter,
46 dulserver.write_pack_data(dulserver.ProtocolFile(None, write), objects_iter,
47 len(objects_iter))
47 len(objects_iter))
48 messages = []
48 messages = []
49 messages.append('thank you for using rhodecode')
49 messages.append('thank you for using rhodecode')
50
50
51 for msg in messages:
51 for msg in messages:
52 self.progress(msg + "\n")
52 self.progress(msg + "\n")
53 # we are done
53 # we are done
54 self.proto.write("0000")
54 self.proto.write("0000")
55
55
56 dulserver.DEFAULT_HANDLERS = {
56 dulserver.DEFAULT_HANDLERS = {
57 'git-upload-pack': SimpleGitUploadPackHandler,
57 'git-upload-pack': SimpleGitUploadPackHandler,
58 'git-receive-pack': dulserver.ReceivePackHandler,
58 'git-receive-pack': dulserver.ReceivePackHandler,
59 }
59 }
60
60
61 from dulwich.repo import Repo
61 from dulwich.repo import Repo
62 from dulwich.web import HTTPGitApplication
62 from dulwich.web import HTTPGitApplication
63 from paste.auth.basic import AuthBasicAuthenticator
63 from paste.auth.basic import AuthBasicAuthenticator
64 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
64 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
65 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
65 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
66 from rhodecode.lib.utils import invalidate_cache, check_repo_fast
66 from rhodecode.lib.utils import invalidate_cache, check_repo_fast
67 from rhodecode.model.user import UserModel
67 from rhodecode.model.user import UserModel
68 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
68 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
69 import logging
69 import logging
70 import os
70 import os
71 import traceback
71 import traceback
72
72
73 log = logging.getLogger(__name__)
73 log = logging.getLogger(__name__)
74
74
75 def is_git(environ):
75 def is_git(environ):
76 """
76 """
77 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
77 Returns True if request's target is git server. ``HTTP_USER_AGENT`` would
78 then have git client version given.
78 then have git client version given.
79
79
80 :param environ:
80 :param environ:
81 """
81 """
82 http_user_agent = environ.get('HTTP_USER_AGENT')
82 http_user_agent = environ.get('HTTP_USER_AGENT')
83 if http_user_agent and http_user_agent.startswith('git'):
83 if http_user_agent and http_user_agent.startswith('git'):
84 return True
84 return True
85 return False
85 return False
86
86
87 class SimpleGit(object):
87 class SimpleGit(object):
88
88
89 def __init__(self, application, config):
89 def __init__(self, application, config):
90 self.application = application
90 self.application = application
91 self.config = config
91 self.config = config
92 #authenticate this git request using
92 #authenticate this git request using
93 self.authenticate = AuthBasicAuthenticator('', authfunc)
93 self.authenticate = AuthBasicAuthenticator('', authfunc)
94 self.ipaddr = '0.0.0.0'
94 self.ipaddr = '0.0.0.0'
95 self.repository = None
95 self.repository = None
96 self.username = None
96 self.username = None
97 self.action = None
97 self.action = None
98
98
99 def __call__(self, environ, start_response):
99 def __call__(self, environ, start_response):
100 if not is_git(environ):
100 if not is_git(environ):
101 return self.application(environ, start_response)
101 return self.application(environ, start_response)
102
102
103 proxy_key = 'HTTP_X_REAL_IP'
103 proxy_key = 'HTTP_X_REAL_IP'
104 def_key = 'REMOTE_ADDR'
104 def_key = 'REMOTE_ADDR'
105 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
105 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
106 # skip passing error to error controller
106 # skip passing error to error controller
107 environ['pylons.status_code_redirect'] = True
107 environ['pylons.status_code_redirect'] = True
108 #===================================================================
108 #===================================================================
109 # AUTHENTICATE THIS GIT REQUEST
109 # AUTHENTICATE THIS GIT REQUEST
110 #===================================================================
110 #===================================================================
111 username = REMOTE_USER(environ)
111 username = REMOTE_USER(environ)
112 if not username:
112 if not username:
113 self.authenticate.realm = self.config['rhodecode_realm']
113 self.authenticate.realm = self.config['rhodecode_realm']
114 result = self.authenticate(environ)
114 result = self.authenticate(environ)
115 if isinstance(result, str):
115 if isinstance(result, str):
116 AUTH_TYPE.update(environ, 'basic')
116 AUTH_TYPE.update(environ, 'basic')
117 REMOTE_USER.update(environ, result)
117 REMOTE_USER.update(environ, result)
118 else:
118 else:
119 return result.wsgi_application(environ, start_response)
119 return result.wsgi_application(environ, start_response)
120
120
121 #=======================================================================
121 #=======================================================================
122 # GET REPOSITORY
122 # GET REPOSITORY
123 #=======================================================================
123 #=======================================================================
124 try:
124 try:
125 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
125 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
126 if repo_name.endswith('/'):
126 if repo_name.endswith('/'):
127 repo_name = repo_name.rstrip('/')
127 repo_name = repo_name.rstrip('/')
128 self.repository = repo_name
128 self.repository = repo_name
129 except:
129 except:
130 log.error(traceback.format_exc())
130 log.error(traceback.format_exc())
131 return HTTPInternalServerError()(environ, start_response)
131 return HTTPInternalServerError()(environ, start_response)
132
132
133 #===================================================================
133 #===================================================================
134 # CHECK PERMISSIONS FOR THIS REQUEST
134 # CHECK PERMISSIONS FOR THIS REQUEST
135 #===================================================================
135 #===================================================================
136 self.action = self.__get_action(environ)
136 self.action = self.__get_action(environ)
137 if self.action:
137 if self.action:
138 username = self.__get_environ_user(environ)
138 username = self.__get_environ_user(environ)
139 try:
139 try:
140 user = self.__get_user(username)
140 user = self.__get_user(username)
141 self.username = user.username
141 self.username = user.username
142 except:
142 except:
143 log.error(traceback.format_exc())
143 log.error(traceback.format_exc())
144 return HTTPInternalServerError()(environ, start_response)
144 return HTTPInternalServerError()(environ, start_response)
145
145
146 #check permissions for this repository
146 #check permissions for this repository
147 if self.action == 'push':
147 if self.action == 'push':
148 if not HasPermissionAnyMiddleware('repository.write',
148 if not HasPermissionAnyMiddleware('repository.write',
149 'repository.admin')\
149 'repository.admin')\
150 (user, repo_name):
150 (user, repo_name):
151 return HTTPForbidden()(environ, start_response)
151 return HTTPForbidden()(environ, start_response)
152
152
153 else:
153 else:
154 #any other action need at least read permission
154 #any other action need at least read permission
155 if not HasPermissionAnyMiddleware('repository.read',
155 if not HasPermissionAnyMiddleware('repository.read',
156 'repository.write',
156 'repository.write',
157 'repository.admin')\
157 'repository.admin')\
158 (user, repo_name):
158 (user, repo_name):
159 return HTTPForbidden()(environ, start_response)
159 return HTTPForbidden()(environ, start_response)
160
160
161 self.extras = {'ip':self.ipaddr,
161 self.extras = {'ip':self.ipaddr,
162 'username':self.username,
162 'username':self.username,
163 'action':self.action,
163 'action':self.action,
164 'repository':self.repository}
164 'repository':self.repository}
165
165
166 #===================================================================
166 #===================================================================
167 # GIT REQUEST HANDLING
167 # GIT REQUEST HANDLING
168 #===================================================================
168 #===================================================================
169 self.basepath = self.config['base_path']
169 self.basepath = self.config['base_path']
170 self.repo_path = os.path.join(self.basepath, self.repo_name)
170 self.repo_path = os.path.join(self.basepath, self.repo_name)
171 #quick check if that dir exists...
171 #quick check if that dir exists...
172 if check_repo_fast(self.repo_name, self.basepath):
172 if check_repo_fast(self.repo_name, self.basepath):
173 return HTTPNotFound()(environ, start_response)
173 return HTTPNotFound()(environ, start_response)
174 try:
174 try:
175 app = self.__make_app()
175 app = self.__make_app()
176 except:
176 except:
177 log.error(traceback.format_exc())
177 log.error(traceback.format_exc())
178 return HTTPInternalServerError()(environ, start_response)
178 return HTTPInternalServerError()(environ, start_response)
179
179
180 #invalidate cache on push
180 #invalidate cache on push
181 if self.action == 'push':
181 if self.action == 'push':
182 self.__invalidate_cache(self.repo_name)
182 self.__invalidate_cache(self.repo_name)
183 messages = []
183 messages = []
184 messages.append('thank you for using rhodecode')
184 messages.append('thank you for using rhodecode')
185 return app(environ, start_response)
185 return app(environ, start_response)
186 else:
186 else:
187 return app(environ, start_response)
187 return app(environ, start_response)
188
188
189
189
190 def __make_app(self):
190 def __make_app(self):
191 backend = dulserver.DictBackend({'/' + self.repo_name: Repo(self.repo_path)})
191 backend = dulserver.DictBackend({'/' + self.repo_name: Repo(self.repo_path)})
192 gitserve = HTTPGitApplication(backend)
192 gitserve = HTTPGitApplication(backend)
193
193
194 return gitserve
194 return gitserve
195
195
196 def __get_environ_user(self, environ):
196 def __get_environ_user(self, environ):
197 return environ.get('REMOTE_USER')
197 return environ.get('REMOTE_USER')
198
198
199 def __get_user(self, username):
199 def __get_user(self, username):
200 return UserModel().get_by_username(username, cache=True)
200 return UserModel().get_by_username(username, cache=True)
201
201
202 def __get_action(self, environ):
202 def __get_action(self, environ):
203 """
203 """
204 Maps git request commands into a pull or push command.
204 Maps git request commands into a pull or push command.
205 :param environ:
205 :param environ:
206 """
206 """
207 service = environ['QUERY_STRING'].split('=')
207 service = environ['QUERY_STRING'].split('=')
208 if len(service) > 1:
208 if len(service) > 1:
209 service_cmd = service[1]
209 service_cmd = service[1]
210 mapping = {'git-receive-pack': 'push',
210 mapping = {'git-receive-pack': 'push',
211 'git-upload-pack': 'pull',
211 'git-upload-pack': 'pull',
212 }
212 }
213
213
214 return mapping.get(service_cmd, service_cmd if service_cmd else 'other')
214 return mapping.get(service_cmd, service_cmd if service_cmd else 'other')
215 else:
215 else:
216 return 'other'
216 return 'other'
217
217
218 def __invalidate_cache(self, repo_name):
218 def __invalidate_cache(self, repo_name):
219 """we know that some change was made to repositories and we should
219 """we know that some change was made to repositories and we should
220 invalidate the cache to see the changes right away but only for
220 invalidate the cache to see the changes right away but only for
221 push requests"""
221 push requests"""
222 invalidate_cache('get_repo_cached_%s' % repo_name)
222 invalidate_cache('get_repo_cached_%s' % repo_name)
@@ -1,230 +1,230 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # middleware to handle mercurial api calls
3 # middleware to handle mercurial api calls
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 2010-04-28
21 Created on 2010-04-28
22
22
23 @author: marcink
23 @author: marcink
24 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
24 SimpleHG middleware for handling mercurial protocol request (push/clone etc.)
25 It's implemented with basic auth function
25 It's implemented with basic auth function
26 """
26 """
27 from mercurial.error import RepoError
27 from mercurial.error import RepoError
28 from mercurial.hgweb import hgweb
28 from mercurial.hgweb import hgweb
29 from mercurial.hgweb.request import wsgiapplication
29 from mercurial.hgweb.request import wsgiapplication
30 from paste.auth.basic import AuthBasicAuthenticator
30 from paste.auth.basic import AuthBasicAuthenticator
31 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
31 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
32 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
32 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
33 from rhodecode.lib.utils import make_ui, invalidate_cache, \
33 from rhodecode.lib.utils import make_ui, invalidate_cache, \
34 check_repo_fast, ui_sections
34 check_repo_fast, ui_sections
35 from rhodecode.model.user import UserModel
35 from rhodecode.model.user import UserModel
36 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
36 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
37 import logging
37 import logging
38 import os
38 import os
39 import traceback
39 import traceback
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43 def is_mercurial(environ):
43 def is_mercurial(environ):
44 """
44 """
45 Returns True if request's target is mercurial server - header
45 Returns True if request's target is mercurial server - header
46 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
46 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
47 """
47 """
48 http_accept = environ.get('HTTP_ACCEPT')
48 http_accept = environ.get('HTTP_ACCEPT')
49 if http_accept and http_accept.startswith('application/mercurial'):
49 if http_accept and http_accept.startswith('application/mercurial'):
50 return True
50 return True
51 return False
51 return False
52
52
53 class SimpleHg(object):
53 class SimpleHg(object):
54
54
55 def __init__(self, application, config):
55 def __init__(self, application, config):
56 self.application = application
56 self.application = application
57 self.config = config
57 self.config = config
58 #authenticate this mercurial request using authfunc
58 #authenticate this mercurial request using authfunc
59 self.authenticate = AuthBasicAuthenticator('', authfunc)
59 self.authenticate = AuthBasicAuthenticator('', authfunc)
60 self.ipaddr = '0.0.0.0'
60 self.ipaddr = '0.0.0.0'
61 self.repository = None
61 self.repository = None
62 self.username = None
62 self.username = None
63 self.action = None
63 self.action = None
64
64
65 def __call__(self, environ, start_response):
65 def __call__(self, environ, start_response):
66 if not is_mercurial(environ):
66 if not is_mercurial(environ):
67 return self.application(environ, start_response)
67 return self.application(environ, start_response)
68
68
69 proxy_key = 'HTTP_X_REAL_IP'
69 proxy_key = 'HTTP_X_REAL_IP'
70 def_key = 'REMOTE_ADDR'
70 def_key = 'REMOTE_ADDR'
71 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
71 self.ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
72 # skip passing error to error controller
72 # skip passing error to error controller
73 environ['pylons.status_code_redirect'] = True
73 environ['pylons.status_code_redirect'] = True
74 #===================================================================
74 #===================================================================
75 # AUTHENTICATE THIS MERCURIAL REQUEST
75 # AUTHENTICATE THIS MERCURIAL REQUEST
76 #===================================================================
76 #===================================================================
77 username = REMOTE_USER(environ)
77 username = REMOTE_USER(environ)
78
78
79 if not username:
79 if not username:
80 self.authenticate.realm = self.config['rhodecode_realm']
80 self.authenticate.realm = self.config['rhodecode_realm']
81 result = self.authenticate(environ)
81 result = self.authenticate(environ)
82 if isinstance(result, str):
82 if isinstance(result, str):
83 AUTH_TYPE.update(environ, 'basic')
83 AUTH_TYPE.update(environ, 'basic')
84 REMOTE_USER.update(environ, result)
84 REMOTE_USER.update(environ, result)
85 else:
85 else:
86 return result.wsgi_application(environ, start_response)
86 return result.wsgi_application(environ, start_response)
87
87
88 #=======================================================================
88 #=======================================================================
89 # GET REPOSITORY
89 # GET REPOSITORY
90 #=======================================================================
90 #=======================================================================
91 try:
91 try:
92 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
92 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
93 if repo_name.endswith('/'):
93 if repo_name.endswith('/'):
94 repo_name = repo_name.rstrip('/')
94 repo_name = repo_name.rstrip('/')
95 self.repository = repo_name
95 self.repository = repo_name
96 except:
96 except:
97 log.error(traceback.format_exc())
97 log.error(traceback.format_exc())
98 return HTTPInternalServerError()(environ, start_response)
98 return HTTPInternalServerError()(environ, start_response)
99
99
100 #===================================================================
100 #===================================================================
101 # CHECK PERMISSIONS FOR THIS REQUEST
101 # CHECK PERMISSIONS FOR THIS REQUEST
102 #===================================================================
102 #===================================================================
103 self.action = self.__get_action(environ)
103 self.action = self.__get_action(environ)
104 if self.action:
104 if self.action:
105 username = self.__get_environ_user(environ)
105 username = self.__get_environ_user(environ)
106 try:
106 try:
107 user = self.__get_user(username)
107 user = self.__get_user(username)
108 self.username = user.username
108 self.username = user.username
109 except:
109 except:
110 log.error(traceback.format_exc())
110 log.error(traceback.format_exc())
111 return HTTPInternalServerError()(environ, start_response)
111 return HTTPInternalServerError()(environ, start_response)
112
112
113 #check permissions for this repository
113 #check permissions for this repository
114 if self.action == 'push':
114 if self.action == 'push':
115 if not HasPermissionAnyMiddleware('repository.write',
115 if not HasPermissionAnyMiddleware('repository.write',
116 'repository.admin')\
116 'repository.admin')\
117 (user, repo_name):
117 (user, repo_name):
118 return HTTPForbidden()(environ, start_response)
118 return HTTPForbidden()(environ, start_response)
119
119
120 else:
120 else:
121 #any other action need at least read permission
121 #any other action need at least read permission
122 if not HasPermissionAnyMiddleware('repository.read',
122 if not HasPermissionAnyMiddleware('repository.read',
123 'repository.write',
123 'repository.write',
124 'repository.admin')\
124 'repository.admin')\
125 (user, repo_name):
125 (user, repo_name):
126 return HTTPForbidden()(environ, start_response)
126 return HTTPForbidden()(environ, start_response)
127
127
128 self.extras = {'ip':self.ipaddr,
128 self.extras = {'ip':self.ipaddr,
129 'username':self.username,
129 'username':self.username,
130 'action':self.action,
130 'action':self.action,
131 'repository':self.repository}
131 'repository':self.repository}
132
132
133 #===================================================================
133 #===================================================================
134 # MERCURIAL REQUEST HANDLING
134 # MERCURIAL REQUEST HANDLING
135 #===================================================================
135 #===================================================================
136 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
136 environ['PATH_INFO'] = '/'#since we wrap into hgweb, reset the path
137 self.baseui = make_ui('db')
137 self.baseui = make_ui('db')
138 self.basepath = self.config['base_path']
138 self.basepath = self.config['base_path']
139 self.repo_path = os.path.join(self.basepath, repo_name)
139 self.repo_path = os.path.join(self.basepath, repo_name)
140
140
141 #quick check if that dir exists...
141 #quick check if that dir exists...
142 if check_repo_fast(repo_name, self.basepath):
142 if check_repo_fast(repo_name, self.basepath):
143 return HTTPNotFound()(environ, start_response)
143 return HTTPNotFound()(environ, start_response)
144 try:
144 try:
145 app = wsgiapplication(self.__make_app)
145 app = wsgiapplication(self.__make_app)
146 except RepoError, e:
146 except RepoError, e:
147 if str(e).find('not found') != -1:
147 if str(e).find('not found') != -1:
148 return HTTPNotFound()(environ, start_response)
148 return HTTPNotFound()(environ, start_response)
149 except Exception:
149 except Exception:
150 log.error(traceback.format_exc())
150 log.error(traceback.format_exc())
151 return HTTPInternalServerError()(environ, start_response)
151 return HTTPInternalServerError()(environ, start_response)
152
152
153 #invalidate cache on push
153 #invalidate cache on push
154 if self.action == 'push':
154 if self.action == 'push':
155 self.__invalidate_cache(repo_name)
155 self.__invalidate_cache(repo_name)
156
156
157 return app(environ, start_response)
157 return app(environ, start_response)
158
158
159
159
160 def __make_app(self):
160 def __make_app(self):
161 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
161 hgserve = hgweb(str(self.repo_path), baseui=self.baseui)
162 return self.__load_web_settings(hgserve, self.extras)
162 return self.__load_web_settings(hgserve, self.extras)
163
163
164 def __get_environ_user(self, environ):
164 def __get_environ_user(self, environ):
165 return environ.get('REMOTE_USER')
165 return environ.get('REMOTE_USER')
166
166
167 def __get_user(self, username):
167 def __get_user(self, username):
168 return UserModel().get_by_username(username, cache=True)
168 return UserModel().get_by_username(username, cache=True)
169
169
170 def __get_action(self, environ):
170 def __get_action(self, environ):
171 """
171 """
172 Maps mercurial request commands into a clone,pull or push command.
172 Maps mercurial request commands into a clone,pull or push command.
173 This should always return a valid command string
173 This should always return a valid command string
174 :param environ:
174 :param environ:
175 """
175 """
176 mapping = {'changegroup': 'pull',
176 mapping = {'changegroup': 'pull',
177 'changegroupsubset': 'pull',
177 'changegroupsubset': 'pull',
178 'stream_out': 'pull',
178 'stream_out': 'pull',
179 'listkeys': 'pull',
179 'listkeys': 'pull',
180 'unbundle': 'push',
180 'unbundle': 'push',
181 'pushkey': 'push', }
181 'pushkey': 'push', }
182 for qry in environ['QUERY_STRING'].split('&'):
182 for qry in environ['QUERY_STRING'].split('&'):
183 if qry.startswith('cmd'):
183 if qry.startswith('cmd'):
184 cmd = qry.split('=')[-1]
184 cmd = qry.split('=')[-1]
185 if mapping.has_key(cmd):
185 if mapping.has_key(cmd):
186 return mapping[cmd]
186 return mapping[cmd]
187 else:
187 else:
188 return cmd
188 return cmd
189
189
190 def __invalidate_cache(self, repo_name):
190 def __invalidate_cache(self, repo_name):
191 """we know that some change was made to repositories and we should
191 """we know that some change was made to repositories and we should
192 invalidate the cache to see the changes right away but only for
192 invalidate the cache to see the changes right away but only for
193 push requests"""
193 push requests"""
194 invalidate_cache('get_repo_cached_%s' % repo_name)
194 invalidate_cache('get_repo_cached_%s' % repo_name)
195
195
196
196
197 def __load_web_settings(self, hgserve, extras={}):
197 def __load_web_settings(self, hgserve, extras={}):
198 #set the global ui for hgserve instance passed
198 #set the global ui for hgserve instance passed
199 hgserve.repo.ui = self.baseui
199 hgserve.repo.ui = self.baseui
200
200
201 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
201 hgrc = os.path.join(self.repo_path, '.hg', 'hgrc')
202
202
203 #inject some additional parameters that will be available in ui
203 #inject some additional parameters that will be available in ui
204 #for hooks
204 #for hooks
205 for k, v in extras.items():
205 for k, v in extras.items():
206 hgserve.repo.ui.setconfig('rhodecode_extras', k, v)
206 hgserve.repo.ui.setconfig('rhodecode_extras', k, v)
207
207
208 repoui = make_ui('file', hgrc, False)
208 repoui = make_ui('file', hgrc, False)
209
209
210 if repoui:
210 if repoui:
211 #overwrite our ui instance with the section from hgrc file
211 #overwrite our ui instance with the section from hgrc file
212 for section in ui_sections:
212 for section in ui_sections:
213 for k, v in repoui.configitems(section):
213 for k, v in repoui.configitems(section):
214 hgserve.repo.ui.setconfig(section, k, v)
214 hgserve.repo.ui.setconfig(section, k, v)
215
215
216 return hgserve
216 return hgserve
217
217
218
218
219
219
220
220
221
221
222
222
223
223
224
224
225
225
226
226
227
227
228
228
229
229
230
230
@@ -1,650 +1,650 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import os
28 import os
29 import logging
29 import logging
30 import datetime
30 import datetime
31 import traceback
31 import traceback
32
32
33 from UserDict import DictMixin
33 from UserDict import DictMixin
34
34
35 from mercurial import ui, config, hg
35 from mercurial import ui, config, hg
36 from mercurial.error import RepoError
36 from mercurial.error import RepoError
37
37
38 import paste
38 import paste
39 import beaker
39 import beaker
40 from paste.script.command import Command, BadCommand
40 from paste.script.command import Command, BadCommand
41
41
42 from vcs.backends.base import BaseChangeset
42 from vcs.backends.base import BaseChangeset
43 from vcs.utils.lazy import LazyProperty
43 from vcs.utils.lazy import LazyProperty
44
44
45 from rhodecode.model import meta
45 from rhodecode.model import meta
46 from rhodecode.model.caching_query import FromCache
46 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group
47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group
48 from rhodecode.model.repo import RepoModel
48 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.user import UserModel
49 from rhodecode.model.user import UserModel
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 def get_repo_slug(request):
54 def get_repo_slug(request):
55 return request.environ['pylons.routes_dict'].get('repo_name')
55 return request.environ['pylons.routes_dict'].get('repo_name')
56
56
57 def action_logger(user, action, repo, ipaddr='', sa=None):
57 def action_logger(user, action, repo, ipaddr='', sa=None):
58 """
58 """
59 Action logger for various actions made by users
59 Action logger for various actions made by users
60
60
61 :param user: user that made this action, can be a unique username string or
61 :param user: user that made this action, can be a unique username string or
62 object containing user_id attribute
62 object containing user_id attribute
63 :param action: action to log, should be on of predefined unique actions for
63 :param action: action to log, should be on of predefined unique actions for
64 easy translations
64 easy translations
65 :param repo: string name of repository or object containing repo_id,
65 :param repo: string name of repository or object containing repo_id,
66 that action was made on
66 that action was made on
67 :param ipaddr: optional ip address from what the action was made
67 :param ipaddr: optional ip address from what the action was made
68 :param sa: optional sqlalchemy session
68 :param sa: optional sqlalchemy session
69
69
70 """
70 """
71
71
72 if not sa:
72 if not sa:
73 sa = meta.Session()
73 sa = meta.Session()
74
74
75 try:
75 try:
76 um = UserModel()
76 um = UserModel()
77 if hasattr(user, 'user_id'):
77 if hasattr(user, 'user_id'):
78 user_obj = user
78 user_obj = user
79 elif isinstance(user, basestring):
79 elif isinstance(user, basestring):
80 user_obj = um.get_by_username(user, cache=False)
80 user_obj = um.get_by_username(user, cache=False)
81 else:
81 else:
82 raise Exception('You have to provide user object or username')
82 raise Exception('You have to provide user object or username')
83
83
84
84
85 rm = RepoModel()
85 rm = RepoModel()
86 if hasattr(repo, 'repo_id'):
86 if hasattr(repo, 'repo_id'):
87 repo_obj = rm.get(repo.repo_id, cache=False)
87 repo_obj = rm.get(repo.repo_id, cache=False)
88 repo_name = repo_obj.repo_name
88 repo_name = repo_obj.repo_name
89 elif isinstance(repo, basestring):
89 elif isinstance(repo, basestring):
90 repo_name = repo.lstrip('/')
90 repo_name = repo.lstrip('/')
91 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
91 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
92 else:
92 else:
93 raise Exception('You have to provide repository to action logger')
93 raise Exception('You have to provide repository to action logger')
94
94
95
95
96 user_log = UserLog()
96 user_log = UserLog()
97 user_log.user_id = user_obj.user_id
97 user_log.user_id = user_obj.user_id
98 user_log.action = action
98 user_log.action = action
99
99
100 user_log.repository_id = repo_obj.repo_id
100 user_log.repository_id = repo_obj.repo_id
101 user_log.repository_name = repo_name
101 user_log.repository_name = repo_name
102
102
103 user_log.action_date = datetime.datetime.now()
103 user_log.action_date = datetime.datetime.now()
104 user_log.user_ip = ipaddr
104 user_log.user_ip = ipaddr
105 sa.add(user_log)
105 sa.add(user_log)
106 sa.commit()
106 sa.commit()
107
107
108 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
108 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
109 except:
109 except:
110 log.error(traceback.format_exc())
110 log.error(traceback.format_exc())
111 sa.rollback()
111 sa.rollback()
112
112
113 def get_repos(path, recursive=False):
113 def get_repos(path, recursive=False):
114 """
114 """
115 Scans given path for repos and return (name,(type,path)) tuple
115 Scans given path for repos and return (name,(type,path)) tuple
116
116
117 :param path: path to scann for repositories
117 :param path: path to scann for repositories
118 :param recursive: recursive search and return names with subdirs in front
118 :param recursive: recursive search and return names with subdirs in front
119 """
119 """
120 from vcs.utils.helpers import get_scm
120 from vcs.utils.helpers import get_scm
121 from vcs.exceptions import VCSError
121 from vcs.exceptions import VCSError
122
122
123 if path.endswith('/'):
123 if path.endswith('/'):
124 #add ending slash for better results
124 #add ending slash for better results
125 path = path[:-1]
125 path = path[:-1]
126
126
127 def _get_repos(p):
127 def _get_repos(p):
128 for dirpath in os.listdir(p):
128 for dirpath in os.listdir(p):
129 if os.path.isfile(os.path.join(p, dirpath)):
129 if os.path.isfile(os.path.join(p, dirpath)):
130 continue
130 continue
131 cur_path = os.path.join(p, dirpath)
131 cur_path = os.path.join(p, dirpath)
132 try:
132 try:
133 scm_info = get_scm(cur_path)
133 scm_info = get_scm(cur_path)
134 yield scm_info[1].split(path)[-1].lstrip('/'), scm_info
134 yield scm_info[1].split(path)[-1].lstrip('/'), scm_info
135 except VCSError:
135 except VCSError:
136 if not recursive:
136 if not recursive:
137 continue
137 continue
138 #check if this dir containts other repos for recursive scan
138 #check if this dir containts other repos for recursive scan
139 rec_path = os.path.join(p, dirpath)
139 rec_path = os.path.join(p, dirpath)
140 if os.path.isdir(rec_path):
140 if os.path.isdir(rec_path):
141 for inner_scm in _get_repos(rec_path):
141 for inner_scm in _get_repos(rec_path):
142 yield inner_scm
142 yield inner_scm
143
143
144 return _get_repos(path)
144 return _get_repos(path)
145
145
146 def check_repo_fast(repo_name, base_path):
146 def check_repo_fast(repo_name, base_path):
147 """
147 """
148 Check given path for existence of directory
148 Check given path for existence of directory
149 :param repo_name:
149 :param repo_name:
150 :param base_path:
150 :param base_path:
151
151
152 :return False: if this directory is present
152 :return False: if this directory is present
153 """
153 """
154 if os.path.isdir(os.path.join(base_path, repo_name)):return False
154 if os.path.isdir(os.path.join(base_path, repo_name)):return False
155 return True
155 return True
156
156
157 def check_repo(repo_name, base_path, verify=True):
157 def check_repo(repo_name, base_path, verify=True):
158
158
159 repo_path = os.path.join(base_path, repo_name)
159 repo_path = os.path.join(base_path, repo_name)
160
160
161 try:
161 try:
162 if not check_repo_fast(repo_name, base_path):
162 if not check_repo_fast(repo_name, base_path):
163 return False
163 return False
164 r = hg.repository(ui.ui(), repo_path)
164 r = hg.repository(ui.ui(), repo_path)
165 if verify:
165 if verify:
166 hg.verify(r)
166 hg.verify(r)
167 #here we hnow that repo exists it was verified
167 #here we hnow that repo exists it was verified
168 log.info('%s repo is already created', repo_name)
168 log.info('%s repo is already created', repo_name)
169 return False
169 return False
170 except RepoError:
170 except RepoError:
171 #it means that there is no valid repo there...
171 #it means that there is no valid repo there...
172 log.info('%s repo is free for creation', repo_name)
172 log.info('%s repo is free for creation', repo_name)
173 return True
173 return True
174
174
175 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
175 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
176 while True:
176 while True:
177 ok = raw_input(prompt)
177 ok = raw_input(prompt)
178 if ok in ('y', 'ye', 'yes'): return True
178 if ok in ('y', 'ye', 'yes'): return True
179 if ok in ('n', 'no', 'nop', 'nope'): return False
179 if ok in ('n', 'no', 'nop', 'nope'): return False
180 retries = retries - 1
180 retries = retries - 1
181 if retries < 0: raise IOError
181 if retries < 0: raise IOError
182 print complaint
182 print complaint
183
183
184 #propagated from mercurial documentation
184 #propagated from mercurial documentation
185 ui_sections = ['alias', 'auth',
185 ui_sections = ['alias', 'auth',
186 'decode/encode', 'defaults',
186 'decode/encode', 'defaults',
187 'diff', 'email',
187 'diff', 'email',
188 'extensions', 'format',
188 'extensions', 'format',
189 'merge-patterns', 'merge-tools',
189 'merge-patterns', 'merge-tools',
190 'hooks', 'http_proxy',
190 'hooks', 'http_proxy',
191 'smtp', 'patch',
191 'smtp', 'patch',
192 'paths', 'profiling',
192 'paths', 'profiling',
193 'server', 'trusted',
193 'server', 'trusted',
194 'ui', 'web', ]
194 'ui', 'web', ]
195
195
196 def make_ui(read_from='file', path=None, checkpaths=True):
196 def make_ui(read_from='file', path=None, checkpaths=True):
197 """
197 """
198 A function that will read python rc files or database
198 A function that will read python rc files or database
199 and make an mercurial ui object from read options
199 and make an mercurial ui object from read options
200
200
201 :param path: path to mercurial config file
201 :param path: path to mercurial config file
202 :param checkpaths: check the path
202 :param checkpaths: check the path
203 :param read_from: read from 'file' or 'db'
203 :param read_from: read from 'file' or 'db'
204 """
204 """
205
205
206 baseui = ui.ui()
206 baseui = ui.ui()
207
207
208 #clean the baseui object
208 #clean the baseui object
209 baseui._ocfg = config.config()
209 baseui._ocfg = config.config()
210 baseui._ucfg = config.config()
210 baseui._ucfg = config.config()
211 baseui._tcfg = config.config()
211 baseui._tcfg = config.config()
212
212
213 if read_from == 'file':
213 if read_from == 'file':
214 if not os.path.isfile(path):
214 if not os.path.isfile(path):
215 log.warning('Unable to read config file %s' % path)
215 log.warning('Unable to read config file %s' % path)
216 return False
216 return False
217 log.debug('reading hgrc from %s', path)
217 log.debug('reading hgrc from %s', path)
218 cfg = config.config()
218 cfg = config.config()
219 cfg.read(path)
219 cfg.read(path)
220 for section in ui_sections:
220 for section in ui_sections:
221 for k, v in cfg.items(section):
221 for k, v in cfg.items(section):
222 log.debug('settings ui from file[%s]%s:%s', section, k, v)
222 log.debug('settings ui from file[%s]%s:%s', section, k, v)
223 baseui.setconfig(section, k, v)
223 baseui.setconfig(section, k, v)
224
224
225
225
226 elif read_from == 'db':
226 elif read_from == 'db':
227 sa = meta.Session()
227 sa = meta.Session()
228 ret = sa.query(RhodeCodeUi)\
228 ret = sa.query(RhodeCodeUi)\
229 .options(FromCache("sql_cache_short",
229 .options(FromCache("sql_cache_short",
230 "get_hg_ui_settings")).all()
230 "get_hg_ui_settings")).all()
231
231
232 hg_ui = ret
232 hg_ui = ret
233 for ui_ in hg_ui:
233 for ui_ in hg_ui:
234 if ui_.ui_active:
234 if ui_.ui_active:
235 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
235 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
236 ui_.ui_key, ui_.ui_value)
236 ui_.ui_key, ui_.ui_value)
237 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
237 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
238
238
239 meta.Session.remove()
239 meta.Session.remove()
240 return baseui
240 return baseui
241
241
242
242
243 def set_rhodecode_config(config):
243 def set_rhodecode_config(config):
244 """Updates pylons config with new settings from database
244 """Updates pylons config with new settings from database
245
245
246 :param config:
246 :param config:
247 """
247 """
248 from rhodecode.model.settings import SettingsModel
248 from rhodecode.model.settings import SettingsModel
249 hgsettings = SettingsModel().get_app_settings()
249 hgsettings = SettingsModel().get_app_settings()
250
250
251 for k, v in hgsettings.items():
251 for k, v in hgsettings.items():
252 config[k] = v
252 config[k] = v
253
253
254 def invalidate_cache(cache_key, *args):
254 def invalidate_cache(cache_key, *args):
255 """Puts cache invalidation task into db for
255 """Puts cache invalidation task into db for
256 further global cache invalidation
256 further global cache invalidation
257 """
257 """
258
258
259 from rhodecode.model.scm import ScmModel
259 from rhodecode.model.scm import ScmModel
260
260
261 if cache_key.startswith('get_repo_cached_'):
261 if cache_key.startswith('get_repo_cached_'):
262 name = cache_key.split('get_repo_cached_')[-1]
262 name = cache_key.split('get_repo_cached_')[-1]
263 ScmModel().mark_for_invalidation(name)
263 ScmModel().mark_for_invalidation(name)
264
264
265 class EmptyChangeset(BaseChangeset):
265 class EmptyChangeset(BaseChangeset):
266 """
266 """
267 An dummy empty changeset. It's possible to pass hash when creating
267 An dummy empty changeset. It's possible to pass hash when creating
268 an EmptyChangeset
268 an EmptyChangeset
269 """
269 """
270
270
271 def __init__(self, cs='0' * 40):
271 def __init__(self, cs='0' * 40):
272 self._empty_cs = cs
272 self._empty_cs = cs
273 self.revision = -1
273 self.revision = -1
274 self.message = ''
274 self.message = ''
275 self.author = ''
275 self.author = ''
276 self.date = ''
276 self.date = ''
277
277
278 @LazyProperty
278 @LazyProperty
279 def raw_id(self):
279 def raw_id(self):
280 """Returns raw string identifying this changeset, useful for web
280 """Returns raw string identifying this changeset, useful for web
281 representation.
281 representation.
282 """
282 """
283
283
284 return self._empty_cs
284 return self._empty_cs
285
285
286 @LazyProperty
286 @LazyProperty
287 def short_id(self):
287 def short_id(self):
288 return self.raw_id[:12]
288 return self.raw_id[:12]
289
289
290 def get_file_changeset(self, path):
290 def get_file_changeset(self, path):
291 return self
291 return self
292
292
293 def get_file_content(self, path):
293 def get_file_content(self, path):
294 return u''
294 return u''
295
295
296 def get_file_size(self, path):
296 def get_file_size(self, path):
297 return 0
297 return 0
298
298
299 def map_groups(groups):
299 def map_groups(groups):
300 """Checks for groups existence, and creates groups structures.
300 """Checks for groups existence, and creates groups structures.
301 It returns last group in structure
301 It returns last group in structure
302
302
303 :param groups: list of groups structure
303 :param groups: list of groups structure
304 """
304 """
305 sa = meta.Session()
305 sa = meta.Session()
306
306
307 parent = None
307 parent = None
308 group = None
308 group = None
309 for lvl, group_name in enumerate(groups[:-1]):
309 for lvl, group_name in enumerate(groups[:-1]):
310 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
310 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
311
311
312 if group is None:
312 if group is None:
313 group = Group(group_name, parent)
313 group = Group(group_name, parent)
314 sa.add(group)
314 sa.add(group)
315 sa.commit()
315 sa.commit()
316
316
317 parent = group
317 parent = group
318
318
319 return group
319 return group
320
320
321 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
321 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
322 """maps all repos given in initial_repo_list, non existing repositories
322 """maps all repos given in initial_repo_list, non existing repositories
323 are created, if remove_obsolete is True it also check for db entries
323 are created, if remove_obsolete is True it also check for db entries
324 that are not in initial_repo_list and removes them.
324 that are not in initial_repo_list and removes them.
325
325
326 :param initial_repo_list: list of repositories found by scanning methods
326 :param initial_repo_list: list of repositories found by scanning methods
327 :param remove_obsolete: check for obsolete entries in database
327 :param remove_obsolete: check for obsolete entries in database
328 """
328 """
329
329
330 sa = meta.Session()
330 sa = meta.Session()
331 rm = RepoModel()
331 rm = RepoModel()
332 user = sa.query(User).filter(User.admin == True).first()
332 user = sa.query(User).filter(User.admin == True).first()
333
333
334 for name, repo in initial_repo_list.items():
334 for name, repo in initial_repo_list.items():
335 group = map_groups(name.split('/'))
335 group = map_groups(name.split('/'))
336 if not rm.get_by_repo_name(name, cache=False):
336 if not rm.get_by_repo_name(name, cache=False):
337 log.info('repository %s not found creating default', name)
337 log.info('repository %s not found creating default', name)
338
338
339 form_data = {
339 form_data = {
340 'repo_name':name,
340 'repo_name':name,
341 'repo_type':repo.alias,
341 'repo_type':repo.alias,
342 'description':repo.description \
342 'description':repo.description \
343 if repo.description != 'unknown' else \
343 if repo.description != 'unknown' else \
344 '%s repository' % name,
344 '%s repository' % name,
345 'private':False,
345 'private':False,
346 'group_id':getattr(group, 'group_id', None)
346 'group_id':getattr(group, 'group_id', None)
347 }
347 }
348 rm.create(form_data, user, just_db=True)
348 rm.create(form_data, user, just_db=True)
349
349
350 if remove_obsolete:
350 if remove_obsolete:
351 #remove from database those repositories that are not in the filesystem
351 #remove from database those repositories that are not in the filesystem
352 for repo in sa.query(Repository).all():
352 for repo in sa.query(Repository).all():
353 if repo.repo_name not in initial_repo_list.keys():
353 if repo.repo_name not in initial_repo_list.keys():
354 sa.delete(repo)
354 sa.delete(repo)
355 sa.commit()
355 sa.commit()
356
356
357 class OrderedDict(dict, DictMixin):
357 class OrderedDict(dict, DictMixin):
358
358
359 def __init__(self, *args, **kwds):
359 def __init__(self, *args, **kwds):
360 if len(args) > 1:
360 if len(args) > 1:
361 raise TypeError('expected at most 1 arguments, got %d' % len(args))
361 raise TypeError('expected at most 1 arguments, got %d' % len(args))
362 try:
362 try:
363 self.__end
363 self.__end
364 except AttributeError:
364 except AttributeError:
365 self.clear()
365 self.clear()
366 self.update(*args, **kwds)
366 self.update(*args, **kwds)
367
367
368 def clear(self):
368 def clear(self):
369 self.__end = end = []
369 self.__end = end = []
370 end += [None, end, end] # sentinel node for doubly linked list
370 end += [None, end, end] # sentinel node for doubly linked list
371 self.__map = {} # key --> [key, prev, next]
371 self.__map = {} # key --> [key, prev, next]
372 dict.clear(self)
372 dict.clear(self)
373
373
374 def __setitem__(self, key, value):
374 def __setitem__(self, key, value):
375 if key not in self:
375 if key not in self:
376 end = self.__end
376 end = self.__end
377 curr = end[1]
377 curr = end[1]
378 curr[2] = end[1] = self.__map[key] = [key, curr, end]
378 curr[2] = end[1] = self.__map[key] = [key, curr, end]
379 dict.__setitem__(self, key, value)
379 dict.__setitem__(self, key, value)
380
380
381 def __delitem__(self, key):
381 def __delitem__(self, key):
382 dict.__delitem__(self, key)
382 dict.__delitem__(self, key)
383 key, prev, next = self.__map.pop(key)
383 key, prev, next = self.__map.pop(key)
384 prev[2] = next
384 prev[2] = next
385 next[1] = prev
385 next[1] = prev
386
386
387 def __iter__(self):
387 def __iter__(self):
388 end = self.__end
388 end = self.__end
389 curr = end[2]
389 curr = end[2]
390 while curr is not end:
390 while curr is not end:
391 yield curr[0]
391 yield curr[0]
392 curr = curr[2]
392 curr = curr[2]
393
393
394 def __reversed__(self):
394 def __reversed__(self):
395 end = self.__end
395 end = self.__end
396 curr = end[1]
396 curr = end[1]
397 while curr is not end:
397 while curr is not end:
398 yield curr[0]
398 yield curr[0]
399 curr = curr[1]
399 curr = curr[1]
400
400
401 def popitem(self, last=True):
401 def popitem(self, last=True):
402 if not self:
402 if not self:
403 raise KeyError('dictionary is empty')
403 raise KeyError('dictionary is empty')
404 if last:
404 if last:
405 key = reversed(self).next()
405 key = reversed(self).next()
406 else:
406 else:
407 key = iter(self).next()
407 key = iter(self).next()
408 value = self.pop(key)
408 value = self.pop(key)
409 return key, value
409 return key, value
410
410
411 def __reduce__(self):
411 def __reduce__(self):
412 items = [[k, self[k]] for k in self]
412 items = [[k, self[k]] for k in self]
413 tmp = self.__map, self.__end
413 tmp = self.__map, self.__end
414 del self.__map, self.__end
414 del self.__map, self.__end
415 inst_dict = vars(self).copy()
415 inst_dict = vars(self).copy()
416 self.__map, self.__end = tmp
416 self.__map, self.__end = tmp
417 if inst_dict:
417 if inst_dict:
418 return (self.__class__, (items,), inst_dict)
418 return (self.__class__, (items,), inst_dict)
419 return self.__class__, (items,)
419 return self.__class__, (items,)
420
420
421 def keys(self):
421 def keys(self):
422 return list(self)
422 return list(self)
423
423
424 setdefault = DictMixin.setdefault
424 setdefault = DictMixin.setdefault
425 update = DictMixin.update
425 update = DictMixin.update
426 pop = DictMixin.pop
426 pop = DictMixin.pop
427 values = DictMixin.values
427 values = DictMixin.values
428 items = DictMixin.items
428 items = DictMixin.items
429 iterkeys = DictMixin.iterkeys
429 iterkeys = DictMixin.iterkeys
430 itervalues = DictMixin.itervalues
430 itervalues = DictMixin.itervalues
431 iteritems = DictMixin.iteritems
431 iteritems = DictMixin.iteritems
432
432
433 def __repr__(self):
433 def __repr__(self):
434 if not self:
434 if not self:
435 return '%s()' % (self.__class__.__name__,)
435 return '%s()' % (self.__class__.__name__,)
436 return '%s(%r)' % (self.__class__.__name__, self.items())
436 return '%s(%r)' % (self.__class__.__name__, self.items())
437
437
438 def copy(self):
438 def copy(self):
439 return self.__class__(self)
439 return self.__class__(self)
440
440
441 @classmethod
441 @classmethod
442 def fromkeys(cls, iterable, value=None):
442 def fromkeys(cls, iterable, value=None):
443 d = cls()
443 d = cls()
444 for key in iterable:
444 for key in iterable:
445 d[key] = value
445 d[key] = value
446 return d
446 return d
447
447
448 def __eq__(self, other):
448 def __eq__(self, other):
449 if isinstance(other, OrderedDict):
449 if isinstance(other, OrderedDict):
450 return len(self) == len(other) and self.items() == other.items()
450 return len(self) == len(other) and self.items() == other.items()
451 return dict.__eq__(self, other)
451 return dict.__eq__(self, other)
452
452
453 def __ne__(self, other):
453 def __ne__(self, other):
454 return not self == other
454 return not self == other
455
455
456
456
457 #set cache regions for beaker so celery can utilise it
457 #set cache regions for beaker so celery can utilise it
458 def add_cache(settings):
458 def add_cache(settings):
459 cache_settings = {'regions':None}
459 cache_settings = {'regions':None}
460 for key in settings.keys():
460 for key in settings.keys():
461 for prefix in ['beaker.cache.', 'cache.']:
461 for prefix in ['beaker.cache.', 'cache.']:
462 if key.startswith(prefix):
462 if key.startswith(prefix):
463 name = key.split(prefix)[1].strip()
463 name = key.split(prefix)[1].strip()
464 cache_settings[name] = settings[key].strip()
464 cache_settings[name] = settings[key].strip()
465 if cache_settings['regions']:
465 if cache_settings['regions']:
466 for region in cache_settings['regions'].split(','):
466 for region in cache_settings['regions'].split(','):
467 region = region.strip()
467 region = region.strip()
468 region_settings = {}
468 region_settings = {}
469 for key, value in cache_settings.items():
469 for key, value in cache_settings.items():
470 if key.startswith(region):
470 if key.startswith(region):
471 region_settings[key.split('.')[1]] = value
471 region_settings[key.split('.')[1]] = value
472 region_settings['expire'] = int(region_settings.get('expire',
472 region_settings['expire'] = int(region_settings.get('expire',
473 60))
473 60))
474 region_settings.setdefault('lock_dir',
474 region_settings.setdefault('lock_dir',
475 cache_settings.get('lock_dir'))
475 cache_settings.get('lock_dir'))
476 if 'type' not in region_settings:
476 if 'type' not in region_settings:
477 region_settings['type'] = cache_settings.get('type',
477 region_settings['type'] = cache_settings.get('type',
478 'memory')
478 'memory')
479 beaker.cache.cache_regions[region] = region_settings
479 beaker.cache.cache_regions[region] = region_settings
480
480
481 def get_current_revision():
481 def get_current_revision():
482 """Returns tuple of (number, id) from repository containing this package
482 """Returns tuple of (number, id) from repository containing this package
483 or None if repository could not be found.
483 or None if repository could not be found.
484 """
484 """
485
485
486 try:
486 try:
487 from vcs import get_repo
487 from vcs import get_repo
488 from vcs.utils.helpers import get_scm
488 from vcs.utils.helpers import get_scm
489 from vcs.exceptions import RepositoryError, VCSError
489 from vcs.exceptions import RepositoryError, VCSError
490 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
490 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
491 scm = get_scm(repopath)[0]
491 scm = get_scm(repopath)[0]
492 repo = get_repo(path=repopath, alias=scm)
492 repo = get_repo(path=repopath, alias=scm)
493 tip = repo.get_changeset()
493 tip = repo.get_changeset()
494 return (tip.revision, tip.short_id)
494 return (tip.revision, tip.short_id)
495 except (ImportError, RepositoryError, VCSError), err:
495 except (ImportError, RepositoryError, VCSError), err:
496 logging.debug("Cannot retrieve rhodecode's revision. Original error "
496 logging.debug("Cannot retrieve rhodecode's revision. Original error "
497 "was: %s" % err)
497 "was: %s" % err)
498 return None
498 return None
499
499
500 #===============================================================================
500 #===============================================================================
501 # TEST FUNCTIONS AND CREATORS
501 # TEST FUNCTIONS AND CREATORS
502 #===============================================================================
502 #===============================================================================
503 def create_test_index(repo_location, full_index):
503 def create_test_index(repo_location, full_index):
504 """Makes default test index
504 """Makes default test index
505 :param repo_location:
505 :param repo_location:
506 :param full_index:
506 :param full_index:
507 """
507 """
508 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
508 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
509 from rhodecode.lib.pidlock import DaemonLock, LockHeld
509 from rhodecode.lib.pidlock import DaemonLock, LockHeld
510 import shutil
510 import shutil
511
511
512 index_location = os.path.join(repo_location, 'index')
512 index_location = os.path.join(repo_location, 'index')
513 if os.path.exists(index_location):
513 if os.path.exists(index_location):
514 shutil.rmtree(index_location)
514 shutil.rmtree(index_location)
515
515
516 try:
516 try:
517 l = DaemonLock()
517 l = DaemonLock()
518 WhooshIndexingDaemon(index_location=index_location,
518 WhooshIndexingDaemon(index_location=index_location,
519 repo_location=repo_location)\
519 repo_location=repo_location)\
520 .run(full_index=full_index)
520 .run(full_index=full_index)
521 l.release()
521 l.release()
522 except LockHeld:
522 except LockHeld:
523 pass
523 pass
524
524
525 def create_test_env(repos_test_path, config):
525 def create_test_env(repos_test_path, config):
526 """Makes a fresh database and
526 """Makes a fresh database and
527 install test repository into tmp dir
527 install test repository into tmp dir
528 """
528 """
529 from rhodecode.lib.db_manage import DbManage
529 from rhodecode.lib.db_manage import DbManage
530 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
530 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
531 HG_FORK, GIT_FORK, TESTS_TMP_PATH
531 HG_FORK, GIT_FORK, TESTS_TMP_PATH
532 import tarfile
532 import tarfile
533 import shutil
533 import shutil
534 from os.path import dirname as dn, join as jn, abspath
534 from os.path import dirname as dn, join as jn, abspath
535
535
536 log = logging.getLogger('TestEnvCreator')
536 log = logging.getLogger('TestEnvCreator')
537 # create logger
537 # create logger
538 log.setLevel(logging.DEBUG)
538 log.setLevel(logging.DEBUG)
539 log.propagate = True
539 log.propagate = True
540 # create console handler and set level to debug
540 # create console handler and set level to debug
541 ch = logging.StreamHandler()
541 ch = logging.StreamHandler()
542 ch.setLevel(logging.DEBUG)
542 ch.setLevel(logging.DEBUG)
543
543
544 # create formatter
544 # create formatter
545 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
545 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
546
546
547 # add formatter to ch
547 # add formatter to ch
548 ch.setFormatter(formatter)
548 ch.setFormatter(formatter)
549
549
550 # add ch to logger
550 # add ch to logger
551 log.addHandler(ch)
551 log.addHandler(ch)
552
552
553 #PART ONE create db
553 #PART ONE create db
554 dbconf = config['sqlalchemy.db1.url']
554 dbconf = config['sqlalchemy.db1.url']
555 log.debug('making test db %s', dbconf)
555 log.debug('making test db %s', dbconf)
556
556
557 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
557 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
558 tests=True)
558 tests=True)
559 dbmanage.create_tables(override=True)
559 dbmanage.create_tables(override=True)
560 dbmanage.config_prompt(repos_test_path)
560 dbmanage.config_prompt(repos_test_path)
561 dbmanage.create_default_user()
561 dbmanage.create_default_user()
562 dbmanage.admin_prompt()
562 dbmanage.admin_prompt()
563 dbmanage.create_permissions()
563 dbmanage.create_permissions()
564 dbmanage.populate_default_permissions()
564 dbmanage.populate_default_permissions()
565
565
566 #PART TWO make test repo
566 #PART TWO make test repo
567 log.debug('making test vcs repositories')
567 log.debug('making test vcs repositories')
568
568
569 #remove old one from previos tests
569 #remove old one from previos tests
570 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
570 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
571
571
572 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
572 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
573 log.debug('removing %s', r)
573 log.debug('removing %s', r)
574 shutil.rmtree(jn(TESTS_TMP_PATH, r))
574 shutil.rmtree(jn(TESTS_TMP_PATH, r))
575
575
576 #CREATE DEFAULT HG REPOSITORY
576 #CREATE DEFAULT HG REPOSITORY
577 cur_dir = dn(dn(abspath(__file__)))
577 cur_dir = dn(dn(abspath(__file__)))
578 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
578 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
579 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
579 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
580 tar.close()
580 tar.close()
581
581
582
582
583 #==============================================================================
583 #==============================================================================
584 # PASTER COMMANDS
584 # PASTER COMMANDS
585 #==============================================================================
585 #==============================================================================
586
586
587 class BasePasterCommand(Command):
587 class BasePasterCommand(Command):
588 """
588 """
589 Abstract Base Class for paster commands.
589 Abstract Base Class for paster commands.
590
590
591 The celery commands are somewhat aggressive about loading
591 The celery commands are somewhat aggressive about loading
592 celery.conf, and since our module sets the `CELERY_LOADER`
592 celery.conf, and since our module sets the `CELERY_LOADER`
593 environment variable to our loader, we have to bootstrap a bit and
593 environment variable to our loader, we have to bootstrap a bit and
594 make sure we've had a chance to load the pylons config off of the
594 make sure we've had a chance to load the pylons config off of the
595 command line, otherwise everything fails.
595 command line, otherwise everything fails.
596 """
596 """
597 min_args = 1
597 min_args = 1
598 min_args_error = "Please provide a paster config file as an argument."
598 min_args_error = "Please provide a paster config file as an argument."
599 takes_config_file = 1
599 takes_config_file = 1
600 requires_config_file = True
600 requires_config_file = True
601
601
602 def notify_msg(self, msg, log=False):
602 def notify_msg(self, msg, log=False):
603 """Make a notification to user, additionally if logger is passed
603 """Make a notification to user, additionally if logger is passed
604 it logs this action using given logger
604 it logs this action using given logger
605
605
606 :param msg: message that will be printed to user
606 :param msg: message that will be printed to user
607 :param log: logging instance, to use to additionally log this message
607 :param log: logging instance, to use to additionally log this message
608
608
609 """
609 """
610 print msg
610 print msg
611 if log and isinstance(log, logging):
611 if log and isinstance(log, logging):
612 log(msg)
612 log(msg)
613
613
614
614
615 def run(self, args):
615 def run(self, args):
616 """
616 """
617 Overrides Command.run
617 Overrides Command.run
618
618
619 Checks for a config file argument and loads it.
619 Checks for a config file argument and loads it.
620 """
620 """
621 if len(args) < self.min_args:
621 if len(args) < self.min_args:
622 raise BadCommand(
622 raise BadCommand(
623 self.min_args_error % {'min_args': self.min_args,
623 self.min_args_error % {'min_args': self.min_args,
624 'actual_args': len(args)})
624 'actual_args': len(args)})
625
625
626 # Decrement because we're going to lob off the first argument.
626 # Decrement because we're going to lob off the first argument.
627 # @@ This is hacky
627 # @@ This is hacky
628 self.min_args -= 1
628 self.min_args -= 1
629 self.bootstrap_config(args[0])
629 self.bootstrap_config(args[0])
630 self.update_parser()
630 self.update_parser()
631 return super(BasePasterCommand, self).run(args[1:])
631 return super(BasePasterCommand, self).run(args[1:])
632
632
633 def update_parser(self):
633 def update_parser(self):
634 """
634 """
635 Abstract method. Allows for the class's parser to be updated
635 Abstract method. Allows for the class's parser to be updated
636 before the superclass's `run` method is called. Necessary to
636 before the superclass's `run` method is called. Necessary to
637 allow options/arguments to be passed through to the underlying
637 allow options/arguments to be passed through to the underlying
638 celery command.
638 celery command.
639 """
639 """
640 raise NotImplementedError("Abstract Method.")
640 raise NotImplementedError("Abstract Method.")
641
641
642 def bootstrap_config(self, conf):
642 def bootstrap_config(self, conf):
643 """
643 """
644 Loads the pylons configuration.
644 Loads the pylons configuration.
645 """
645 """
646 from pylons import config as pylonsconfig
646 from pylons import config as pylonsconfig
647
647
648 path_to_ini_file = os.path.realpath(conf)
648 path_to_ini_file = os.path.realpath(conf)
649 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
649 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
650 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
650 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
@@ -1,71 +1,71 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.__init__
3 rhodecode.model.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 The application's model objects
6 The application's model objects
7
7
8 :created_on: Nov 25, 2010
8 :created_on: Nov 25, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12
12
13
13
14 :example:
14 :example:
15
15
16 .. code-block:: python
16 .. code-block:: python
17
17
18 from paste.deploy import appconfig
18 from paste.deploy import appconfig
19 from pylons import config
19 from pylons import config
20 from sqlalchemy import engine_from_config
20 from sqlalchemy import engine_from_config
21 from rhodecode.config.environment import load_environment
21 from rhodecode.config.environment import load_environment
22
22
23 conf = appconfig('config:development.ini', relative_to = './../../')
23 conf = appconfig('config:development.ini', relative_to = './../../')
24 load_environment(conf.global_conf, conf.local_conf)
24 load_environment(conf.global_conf, conf.local_conf)
25
25
26 engine = engine_from_config(config, 'sqlalchemy.')
26 engine = engine_from_config(config, 'sqlalchemy.')
27 init_model(engine)
27 init_model(engine)
28 # RUN YOUR CODE HERE
28 # RUN YOUR CODE HERE
29
29
30 """
30 """
31 # This program is free software; you can redistribute it and/or
31 # This program is free software; you can redistribute it and/or
32 # modify it under the terms of the GNU General Public License
32 # modify it under the terms of the GNU General Public License
33 # as published by the Free Software Foundation; version 2
33 # as published by the Free Software Foundation; version 2
34 # of the License or (at your opinion) any later version of the license.
34 # of the License or (at your opinion) any later version of the license.
35 #
35 #
36 # This program is distributed in the hope that it will be useful,
36 # This program is distributed in the hope that it will be useful,
37 # but WITHOUT ANY WARRANTY; without even the implied warranty of
37 # but WITHOUT ANY WARRANTY; without even the implied warranty of
38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 # GNU General Public License for more details.
39 # GNU General Public License for more details.
40 #
40 #
41 # You should have received a copy of the GNU General Public License
41 # You should have received a copy of the GNU General Public License
42 # along with this program; if not, write to the Free Software
42 # along with this program; if not, write to the Free Software
43 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
43 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
44 # MA 02110-1301, USA.
44 # MA 02110-1301, USA.
45
45
46 import logging
46 import logging
47 from rhodecode.model import meta
47 from rhodecode.model import meta
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50 def init_model(engine):
50 def init_model(engine):
51 """Initializes db session, bind the engine with the metadata,
51 """Initializes db session, bind the engine with the metadata,
52 Call this before using any of the tables or classes in the model, preferably
52 Call this before using any of the tables or classes in the model, preferably
53 once in application start
53 once in application start
54
54
55 :param engine: engine to bind to
55 :param engine: engine to bind to
56 """
56 """
57 log.info("initializing db models for %s", engine)
57 log.info("initializing db models for %s", engine)
58 meta.Base.metadata.bind = engine
58 meta.Base.metadata.bind = engine
59
59
60 class BaseModel(object):
60 class BaseModel(object):
61 """Base Model for all RhodeCode models, it adds sql alchemy session
61 """Base Model for all RhodeCode models, it adds sql alchemy session
62 into instance of model
62 into instance of model
63
63
64 :param sa: If passed it reuses this session instead of creating a new one
64 :param sa: If passed it reuses this session instead of creating a new one
65 """
65 """
66
66
67 def __init__(self, sa=None):
67 def __init__(self, sa=None):
68 if sa is not None:
68 if sa is not None:
69 self.sa = sa
69 self.sa = sa
70 else:
70 else:
71 self.sa = meta.Session()
71 self.sa = meta.Session()
@@ -1,298 +1,298 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import logging
27 import logging
28 import datetime
28 import datetime
29
29
30 from sqlalchemy import *
30 from sqlalchemy import *
31 from sqlalchemy.exc import DatabaseError
31 from sqlalchemy.exc import DatabaseError
32 from sqlalchemy.orm import relation, backref, class_mapper
32 from sqlalchemy.orm import relation, backref, class_mapper
33 from sqlalchemy.orm.session import Session
33 from sqlalchemy.orm.session import Session
34
34
35 from rhodecode.model.meta import Base
35 from rhodecode.model.meta import Base
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39 class BaseModel(object):
39 class BaseModel(object):
40
40
41 @classmethod
41 @classmethod
42 def _get_keys(cls):
42 def _get_keys(cls):
43 """return column names for this model """
43 """return column names for this model """
44 return class_mapper(cls).c.keys()
44 return class_mapper(cls).c.keys()
45
45
46 def get_dict(self):
46 def get_dict(self):
47 """return dict with keys and values corresponding
47 """return dict with keys and values corresponding
48 to this model data """
48 to this model data """
49
49
50 d = {}
50 d = {}
51 for k in self._get_keys():
51 for k in self._get_keys():
52 d[k] = getattr(self, k)
52 d[k] = getattr(self, k)
53 return d
53 return d
54
54
55 def get_appstruct(self):
55 def get_appstruct(self):
56 """return list with keys and values tupples corresponding
56 """return list with keys and values tupples corresponding
57 to this model data """
57 to this model data """
58
58
59 l = []
59 l = []
60 for k in self._get_keys():
60 for k in self._get_keys():
61 l.append((k, getattr(self, k),))
61 l.append((k, getattr(self, k),))
62 return l
62 return l
63
63
64 def populate_obj(self, populate_dict):
64 def populate_obj(self, populate_dict):
65 """populate model with data from given populate_dict"""
65 """populate model with data from given populate_dict"""
66
66
67 for k in self._get_keys():
67 for k in self._get_keys():
68 if k in populate_dict:
68 if k in populate_dict:
69 setattr(self, k, populate_dict[k])
69 setattr(self, k, populate_dict[k])
70
70
71 class RhodeCodeSettings(Base, BaseModel):
71 class RhodeCodeSettings(Base, BaseModel):
72 __tablename__ = 'rhodecode_settings'
72 __tablename__ = 'rhodecode_settings'
73 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
73 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
74 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
74 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
75 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
75 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
77
77
78 def __init__(self, k, v):
78 def __init__(self, k, v):
79 self.app_settings_name = k
79 self.app_settings_name = k
80 self.app_settings_value = v
80 self.app_settings_value = v
81
81
82 def __repr__(self):
82 def __repr__(self):
83 return "<%s('%s:%s')>" % (self.__class__.__name__,
83 return "<%s('%s:%s')>" % (self.__class__.__name__,
84 self.app_settings_name, self.app_settings_value)
84 self.app_settings_name, self.app_settings_value)
85
85
86 class RhodeCodeUi(Base, BaseModel):
86 class RhodeCodeUi(Base, BaseModel):
87 __tablename__ = 'rhodecode_ui'
87 __tablename__ = 'rhodecode_ui'
88 __table_args__ = {'useexisting':True}
88 __table_args__ = {'useexisting':True}
89 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
89 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
90 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
90 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
91 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
91 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
93 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
93 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
94
94
95
95
96 class User(Base, BaseModel):
96 class User(Base, BaseModel):
97 __tablename__ = 'users'
97 __tablename__ = 'users'
98 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
98 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
99 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
99 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
100 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
100 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
102 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
103 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
103 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
104 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
104 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
107 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
108 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
108 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
109
109
110 user_log = relation('UserLog', cascade='all')
110 user_log = relation('UserLog', cascade='all')
111 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
111 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
112
112
113 repositories = relation('Repository')
113 repositories = relation('Repository')
114 user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
114 user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
115
115
116 @property
116 @property
117 def full_contact(self):
117 def full_contact(self):
118 return '%s %s <%s>' % (self.name, self.lastname, self.email)
118 return '%s %s <%s>' % (self.name, self.lastname, self.email)
119
119
120
120
121 @property
121 @property
122 def is_admin(self):
122 def is_admin(self):
123 return self.admin
123 return self.admin
124
124
125 def __repr__(self):
125 def __repr__(self):
126 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
126 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
127 self.user_id, self.username)
127 self.user_id, self.username)
128
128
129 def update_lastlogin(self):
129 def update_lastlogin(self):
130 """Update user lastlogin"""
130 """Update user lastlogin"""
131
131
132 try:
132 try:
133 session = Session.object_session(self)
133 session = Session.object_session(self)
134 self.last_login = datetime.datetime.now()
134 self.last_login = datetime.datetime.now()
135 session.add(self)
135 session.add(self)
136 session.commit()
136 session.commit()
137 log.debug('updated user %s lastlogin', self.username)
137 log.debug('updated user %s lastlogin', self.username)
138 except (DatabaseError,):
138 except (DatabaseError,):
139 session.rollback()
139 session.rollback()
140
140
141
141
142 class UserLog(Base, BaseModel):
142 class UserLog(Base, BaseModel):
143 __tablename__ = 'user_logs'
143 __tablename__ = 'user_logs'
144 __table_args__ = {'useexisting':True}
144 __table_args__ = {'useexisting':True}
145 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
145 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
146 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
146 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
147 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
147 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
148 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
148 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
149 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
150 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
151 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
151 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
152
152
153 user = relation('User')
153 user = relation('User')
154 repository = relation('Repository')
154 repository = relation('Repository')
155
155
156 class Repository(Base, BaseModel):
156 class Repository(Base, BaseModel):
157 __tablename__ = 'repositories'
157 __tablename__ = 'repositories'
158 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
158 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
159 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
159 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
160 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
160 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
161 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
161 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
162 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
162 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
163 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
163 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
164 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
164 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
165 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
165 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
166 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
166 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
167 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
167 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
168
168
169 user = relation('User')
169 user = relation('User')
170 fork = relation('Repository', remote_side=repo_id)
170 fork = relation('Repository', remote_side=repo_id)
171 group = relation('Group')
171 group = relation('Group')
172 repo_to_perm = relation('RepoToPerm', cascade='all')
172 repo_to_perm = relation('RepoToPerm', cascade='all')
173 stats = relation('Statistics', cascade='all', uselist=False)
173 stats = relation('Statistics', cascade='all', uselist=False)
174
174
175 repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
175 repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
176
176
177 def __repr__(self):
177 def __repr__(self):
178 return "<%s('%s:%s')>" % (self.__class__.__name__,
178 return "<%s('%s:%s')>" % (self.__class__.__name__,
179 self.repo_id, self.repo_name)
179 self.repo_id, self.repo_name)
180
180
181 class Group(Base, BaseModel):
181 class Group(Base, BaseModel):
182 __tablename__ = 'groups'
182 __tablename__ = 'groups'
183 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
183 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
184
184
185 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
185 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
186 group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
186 group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
187 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
187 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
188
188
189 parent_group = relation('Group', remote_side=group_id)
189 parent_group = relation('Group', remote_side=group_id)
190
190
191
191
192 def __init__(self, group_name='', parent_group=None):
192 def __init__(self, group_name='', parent_group=None):
193 self.group_name = group_name
193 self.group_name = group_name
194 self.parent_group = parent_group
194 self.parent_group = parent_group
195
195
196 def __repr__(self):
196 def __repr__(self):
197 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
197 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
198 self.group_name)
198 self.group_name)
199
199
200 class Permission(Base, BaseModel):
200 class Permission(Base, BaseModel):
201 __tablename__ = 'permissions'
201 __tablename__ = 'permissions'
202 __table_args__ = {'useexisting':True}
202 __table_args__ = {'useexisting':True}
203 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
203 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
204 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
204 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
205 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
205 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
206
206
207 def __repr__(self):
207 def __repr__(self):
208 return "<%s('%s:%s')>" % (self.__class__.__name__,
208 return "<%s('%s:%s')>" % (self.__class__.__name__,
209 self.permission_id, self.permission_name)
209 self.permission_id, self.permission_name)
210
210
211 class RepoToPerm(Base, BaseModel):
211 class RepoToPerm(Base, BaseModel):
212 __tablename__ = 'repo_to_perm'
212 __tablename__ = 'repo_to_perm'
213 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
213 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
214 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
214 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
215 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
215 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
216 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
216 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
217 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
217 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
218
218
219 user = relation('User')
219 user = relation('User')
220 permission = relation('Permission')
220 permission = relation('Permission')
221 repository = relation('Repository')
221 repository = relation('Repository')
222
222
223 class UserToPerm(Base, BaseModel):
223 class UserToPerm(Base, BaseModel):
224 __tablename__ = 'user_to_perm'
224 __tablename__ = 'user_to_perm'
225 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
225 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
226 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
226 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
227 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
227 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
228 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
228 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
229
229
230 user = relation('User')
230 user = relation('User')
231 permission = relation('Permission')
231 permission = relation('Permission')
232
232
233 class GroupToPerm(Base, BaseModel):
233 class GroupToPerm(Base, BaseModel):
234 __tablename__ = 'group_to_perm'
234 __tablename__ = 'group_to_perm'
235 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
235 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
236
236
237 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
237 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
238 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
238 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
239 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
239 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
240 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
240 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
241
241
242 user = relation('User')
242 user = relation('User')
243 permission = relation('Permission')
243 permission = relation('Permission')
244 group = relation('Group')
244 group = relation('Group')
245
245
246 class Statistics(Base, BaseModel):
246 class Statistics(Base, BaseModel):
247 __tablename__ = 'statistics'
247 __tablename__ = 'statistics'
248 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
248 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
249 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
249 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
250 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
250 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
251 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
251 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
252 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
252 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
253 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
253 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
254 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
254 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
255
255
256 repository = relation('Repository', single_parent=True)
256 repository = relation('Repository', single_parent=True)
257
257
258 class UserFollowing(Base, BaseModel):
258 class UserFollowing(Base, BaseModel):
259 __tablename__ = 'user_followings'
259 __tablename__ = 'user_followings'
260 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
260 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
261 UniqueConstraint('user_id', 'follows_user_id')
261 UniqueConstraint('user_id', 'follows_user_id')
262 , {'useexisting':True})
262 , {'useexisting':True})
263
263
264 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
264 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
265 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
265 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
266 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
266 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
267 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
267 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
268
268
269 user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
269 user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
270
270
271 follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
271 follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
272 follows_repository = relation('Repository')
272 follows_repository = relation('Repository')
273
273
274 class CacheInvalidation(Base, BaseModel):
274 class CacheInvalidation(Base, BaseModel):
275 __tablename__ = 'cache_invalidation'
275 __tablename__ = 'cache_invalidation'
276 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
276 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
277 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
277 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
278 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
278 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
279 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
279 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
280 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
280 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
281
281
282
282
283 def __init__(self, cache_key, cache_args=''):
283 def __init__(self, cache_key, cache_args=''):
284 self.cache_key = cache_key
284 self.cache_key = cache_key
285 self.cache_args = cache_args
285 self.cache_args = cache_args
286 self.cache_active = False
286 self.cache_active = False
287
287
288 def __repr__(self):
288 def __repr__(self):
289 return "<%s('%s:%s')>" % (self.__class__.__name__,
289 return "<%s('%s:%s')>" % (self.__class__.__name__,
290 self.cache_id, self.cache_key)
290 self.cache_id, self.cache_key)
291
291
292 class DbMigrateVersion(Base, BaseModel):
292 class DbMigrateVersion(Base, BaseModel):
293 __tablename__ = 'db_migrate_version'
293 __tablename__ = 'db_migrate_version'
294 __table_args__ = {'useexisting':True}
294 __table_args__ = {'useexisting':True}
295 repository_id = Column('repository_id', String(250), primary_key=True)
295 repository_id = Column('repository_id', String(250), primary_key=True)
296 repository_path = Column('repository_path', Text)
296 repository_path = Column('repository_path', Text)
297 version = Column('version', Integer)
297 version = Column('version', Integer)
298
298
@@ -1,114 +1,114 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.permission
3 rhodecode.model.permission
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 permissions model for RhodeCode
6 permissions model for RhodeCode
7
7
8 :created_on: Aug 20, 2010
8 :created_on: Aug 20, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 from sqlalchemy.exc import DatabaseError
31 from sqlalchemy.exc import DatabaseError
32
32
33 from rhodecode.model import BaseModel
33 from rhodecode.model import BaseModel
34 from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
34 from rhodecode.model.db import User, Permission, UserToPerm, RepoToPerm
35 from rhodecode.model.caching_query import FromCache
35 from rhodecode.model.caching_query import FromCache
36
36
37 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
38
38
39
39
40 class PermissionModel(BaseModel):
40 class PermissionModel(BaseModel):
41 """Permissions model for RhodeCode
41 """Permissions model for RhodeCode
42 """
42 """
43
43
44 def get_permission(self, permission_id, cache=False):
44 def get_permission(self, permission_id, cache=False):
45 """Get's permissions by id
45 """Get's permissions by id
46
46
47 :param permission_id: id of permission to get from database
47 :param permission_id: id of permission to get from database
48 :param cache: use Cache for this query
48 :param cache: use Cache for this query
49 """
49 """
50 perm = self.sa.query(Permission)
50 perm = self.sa.query(Permission)
51 if cache:
51 if cache:
52 perm = perm.options(FromCache("sql_cache_short",
52 perm = perm.options(FromCache("sql_cache_short",
53 "get_permission_%s" % permission_id))
53 "get_permission_%s" % permission_id))
54 return perm.get(permission_id)
54 return perm.get(permission_id)
55
55
56 def get_permission_by_name(self, name, cache=False):
56 def get_permission_by_name(self, name, cache=False):
57 """Get's permissions by given name
57 """Get's permissions by given name
58
58
59 :param name: name to fetch
59 :param name: name to fetch
60 :param cache: Use cache for this query
60 :param cache: Use cache for this query
61 """
61 """
62 perm = self.sa.query(Permission)\
62 perm = self.sa.query(Permission)\
63 .filter(Permission.permission_name == name)
63 .filter(Permission.permission_name == name)
64 if cache:
64 if cache:
65 perm = perm.options(FromCache("sql_cache_short",
65 perm = perm.options(FromCache("sql_cache_short",
66 "get_permission_%s" % name))
66 "get_permission_%s" % name))
67 return perm.scalar()
67 return perm.scalar()
68
68
69 def update(self, form_result):
69 def update(self, form_result):
70 perm_user = self.sa.query(User)\
70 perm_user = self.sa.query(User)\
71 .filter(User.username == form_result['perm_user_name']).scalar()
71 .filter(User.username == form_result['perm_user_name']).scalar()
72 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
72 u2p = self.sa.query(UserToPerm).filter(UserToPerm.user == perm_user).all()
73 if len(u2p) != 3:
73 if len(u2p) != 3:
74 raise Exception('Defined: %s should be 3 permissions for default'
74 raise Exception('Defined: %s should be 3 permissions for default'
75 ' user. This should not happen please verify'
75 ' user. This should not happen please verify'
76 ' your database' % len(u2p))
76 ' your database' % len(u2p))
77
77
78 try:
78 try:
79 #stage 1 change defaults
79 #stage 1 change defaults
80 for p in u2p:
80 for p in u2p:
81 if p.permission.permission_name.startswith('repository.'):
81 if p.permission.permission_name.startswith('repository.'):
82 p.permission = self.get_permission_by_name(
82 p.permission = self.get_permission_by_name(
83 form_result['default_perm'])
83 form_result['default_perm'])
84 self.sa.add(p)
84 self.sa.add(p)
85
85
86 if p.permission.permission_name.startswith('hg.register.'):
86 if p.permission.permission_name.startswith('hg.register.'):
87 p.permission = self.get_permission_by_name(
87 p.permission = self.get_permission_by_name(
88 form_result['default_register'])
88 form_result['default_register'])
89 self.sa.add(p)
89 self.sa.add(p)
90
90
91 if p.permission.permission_name.startswith('hg.create.'):
91 if p.permission.permission_name.startswith('hg.create.'):
92 p.permission = self.get_permission_by_name(
92 p.permission = self.get_permission_by_name(
93 form_result['default_create'])
93 form_result['default_create'])
94 self.sa.add(p)
94 self.sa.add(p)
95
95
96 #stage 2 update all default permissions for repos if checked
96 #stage 2 update all default permissions for repos if checked
97 if form_result['overwrite_default'] == True:
97 if form_result['overwrite_default'] == True:
98 for r2p in self.sa.query(RepoToPerm)\
98 for r2p in self.sa.query(RepoToPerm)\
99 .filter(RepoToPerm.user == perm_user).all():
99 .filter(RepoToPerm.user == perm_user).all():
100 r2p.permission = self.get_permission_by_name(
100 r2p.permission = self.get_permission_by_name(
101 form_result['default_perm'])
101 form_result['default_perm'])
102 self.sa.add(r2p)
102 self.sa.add(r2p)
103
103
104 #stage 3 set anonymous access
104 #stage 3 set anonymous access
105 if perm_user.username == 'default':
105 if perm_user.username == 'default':
106 perm_user.active = bool(form_result['anonymous'])
106 perm_user.active = bool(form_result['anonymous'])
107 self.sa.add(perm_user)
107 self.sa.add(perm_user)
108
108
109
109
110 self.sa.commit()
110 self.sa.commit()
111 except (DatabaseError,):
111 except (DatabaseError,):
112 log.error(traceback.format_exc())
112 log.error(traceback.format_exc())
113 self.sa.rollback()
113 self.sa.rollback()
114 raise
114 raise
@@ -1,263 +1,263 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import os
27 import os
28 import shutil
28 import shutil
29 import logging
29 import logging
30 import traceback
30 import traceback
31 from datetime import datetime
31 from datetime import datetime
32
32
33 from pylons import app_globals as g
33 from pylons import app_globals as g
34
34
35 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
36 from rhodecode.model.caching_query import FromCache
36 from rhodecode.model.caching_query import FromCache
37 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
37 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
38 Statistics
38 Statistics
39 from rhodecode.model.user import UserModel
39 from rhodecode.model.user import UserModel
40
40
41 from vcs.backends import get_backend
41 from vcs.backends import get_backend
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45 class RepoModel(BaseModel):
45 class RepoModel(BaseModel):
46
46
47 def get(self, repo_id, cache=False):
47 def get(self, repo_id, cache=False):
48 repo = self.sa.query(Repository)\
48 repo = self.sa.query(Repository)\
49 .filter(Repository.repo_id == repo_id)
49 .filter(Repository.repo_id == repo_id)
50
50
51 if cache:
51 if cache:
52 repo = repo.options(FromCache("sql_cache_short",
52 repo = repo.options(FromCache("sql_cache_short",
53 "get_repo_%s" % repo_id))
53 "get_repo_%s" % repo_id))
54 return repo.scalar()
54 return repo.scalar()
55
55
56
56
57 def get_by_repo_name(self, repo_name, cache=False):
57 def get_by_repo_name(self, repo_name, cache=False):
58 repo = self.sa.query(Repository)\
58 repo = self.sa.query(Repository)\
59 .filter(Repository.repo_name == repo_name)
59 .filter(Repository.repo_name == repo_name)
60
60
61 if cache:
61 if cache:
62 repo = repo.options(FromCache("sql_cache_short",
62 repo = repo.options(FromCache("sql_cache_short",
63 "get_repo_%s" % repo_name))
63 "get_repo_%s" % repo_name))
64 return repo.scalar()
64 return repo.scalar()
65
65
66 def get_users_js(self):
66 def get_users_js(self):
67
67
68 users = self.sa.query(User).filter(User.active == True).all()
68 users = self.sa.query(User).filter(User.active == True).all()
69 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
69 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
70 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
70 users_array = '[%s];' % '\n'.join([u_tmpl % (u.user_id, u.name,
71 u.lastname, u.username)
71 u.lastname, u.username)
72 for u in users])
72 for u in users])
73 return users_array
73 return users_array
74
74
75
75
76 def update(self, repo_name, form_data):
76 def update(self, repo_name, form_data):
77 try:
77 try:
78 cur_repo = self.get_by_repo_name(repo_name, cache=False)
78 cur_repo = self.get_by_repo_name(repo_name, cache=False)
79 user_model = UserModel(self.sa)
79 user_model = UserModel(self.sa)
80
80
81 #update permissions
81 #update permissions
82 for username, perm in form_data['perms_updates']:
82 for username, perm in form_data['perms_updates']:
83 r2p = self.sa.query(RepoToPerm)\
83 r2p = self.sa.query(RepoToPerm)\
84 .filter(RepoToPerm.user == user_model.get_by_username(username))\
84 .filter(RepoToPerm.user == user_model.get_by_username(username))\
85 .filter(RepoToPerm.repository == cur_repo)\
85 .filter(RepoToPerm.repository == cur_repo)\
86 .one()
86 .one()
87
87
88 r2p.permission = self.sa.query(Permission)\
88 r2p.permission = self.sa.query(Permission)\
89 .filter(Permission.permission_name == perm)\
89 .filter(Permission.permission_name == perm)\
90 .scalar()
90 .scalar()
91 self.sa.add(r2p)
91 self.sa.add(r2p)
92
92
93 #set new permissions
93 #set new permissions
94 for username, perm in form_data['perms_new']:
94 for username, perm in form_data['perms_new']:
95 r2p = RepoToPerm()
95 r2p = RepoToPerm()
96 r2p.repository = cur_repo
96 r2p.repository = cur_repo
97 r2p.user = user_model.get_by_username(username, cache=False)
97 r2p.user = user_model.get_by_username(username, cache=False)
98
98
99 r2p.permission = self.sa.query(Permission)\
99 r2p.permission = self.sa.query(Permission)\
100 .filter(Permission.permission_name == perm)\
100 .filter(Permission.permission_name == perm)\
101 .scalar()
101 .scalar()
102 self.sa.add(r2p)
102 self.sa.add(r2p)
103
103
104 #update current repo
104 #update current repo
105 for k, v in form_data.items():
105 for k, v in form_data.items():
106 if k == 'user':
106 if k == 'user':
107 cur_repo.user = user_model.get(v)
107 cur_repo.user = user_model.get(v)
108 else:
108 else:
109 setattr(cur_repo, k, v)
109 setattr(cur_repo, k, v)
110
110
111 self.sa.add(cur_repo)
111 self.sa.add(cur_repo)
112
112
113 if repo_name != form_data['repo_name']:
113 if repo_name != form_data['repo_name']:
114 #rename our data
114 #rename our data
115 self.__rename_repo(repo_name, form_data['repo_name'])
115 self.__rename_repo(repo_name, form_data['repo_name'])
116
116
117 self.sa.commit()
117 self.sa.commit()
118 except:
118 except:
119 log.error(traceback.format_exc())
119 log.error(traceback.format_exc())
120 self.sa.rollback()
120 self.sa.rollback()
121 raise
121 raise
122
122
123 def create(self, form_data, cur_user, just_db=False, fork=False):
123 def create(self, form_data, cur_user, just_db=False, fork=False):
124 try:
124 try:
125 if fork:
125 if fork:
126 #force str since hg doesn't go with unicode
126 #force str since hg doesn't go with unicode
127 repo_name = str(form_data['fork_name'])
127 repo_name = str(form_data['fork_name'])
128 org_name = str(form_data['repo_name'])
128 org_name = str(form_data['repo_name'])
129
129
130 else:
130 else:
131 org_name = repo_name = str(form_data['repo_name'])
131 org_name = repo_name = str(form_data['repo_name'])
132 new_repo = Repository()
132 new_repo = Repository()
133 new_repo.enable_statistics = True
133 new_repo.enable_statistics = True
134 for k, v in form_data.items():
134 for k, v in form_data.items():
135 if k == 'repo_name':
135 if k == 'repo_name':
136 v = repo_name
136 v = repo_name
137 setattr(new_repo, k, v)
137 setattr(new_repo, k, v)
138
138
139 if fork:
139 if fork:
140 parent_repo = self.sa.query(Repository)\
140 parent_repo = self.sa.query(Repository)\
141 .filter(Repository.repo_name == org_name).scalar()
141 .filter(Repository.repo_name == org_name).scalar()
142 new_repo.fork = parent_repo
142 new_repo.fork = parent_repo
143
143
144 new_repo.user_id = cur_user.user_id
144 new_repo.user_id = cur_user.user_id
145 self.sa.add(new_repo)
145 self.sa.add(new_repo)
146
146
147 #create default permission
147 #create default permission
148 repo_to_perm = RepoToPerm()
148 repo_to_perm = RepoToPerm()
149 default = 'repository.read'
149 default = 'repository.read'
150 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
150 for p in UserModel(self.sa).get_by_username('default', cache=False).user_perms:
151 if p.permission.permission_name.startswith('repository.'):
151 if p.permission.permission_name.startswith('repository.'):
152 default = p.permission.permission_name
152 default = p.permission.permission_name
153 break
153 break
154
154
155 default_perm = 'repository.none' if form_data['private'] else default
155 default_perm = 'repository.none' if form_data['private'] else default
156
156
157 repo_to_perm.permission_id = self.sa.query(Permission)\
157 repo_to_perm.permission_id = self.sa.query(Permission)\
158 .filter(Permission.permission_name == default_perm)\
158 .filter(Permission.permission_name == default_perm)\
159 .one().permission_id
159 .one().permission_id
160
160
161 repo_to_perm.repository_id = new_repo.repo_id
161 repo_to_perm.repository_id = new_repo.repo_id
162 repo_to_perm.user_id = UserModel(self.sa)\
162 repo_to_perm.user_id = UserModel(self.sa)\
163 .get_by_username('default', cache=False).user_id
163 .get_by_username('default', cache=False).user_id
164
164
165 self.sa.add(repo_to_perm)
165 self.sa.add(repo_to_perm)
166 self.sa.commit()
166 self.sa.commit()
167
167
168
168
169 #now automatically start following this repository as owner
169 #now automatically start following this repository as owner
170 from rhodecode.model.scm import ScmModel
170 from rhodecode.model.scm import ScmModel
171 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
171 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
172 cur_user.user_id)
172 cur_user.user_id)
173
173
174 if not just_db:
174 if not just_db:
175 self.__create_repo(repo_name, form_data['repo_type'])
175 self.__create_repo(repo_name, form_data['repo_type'])
176 except:
176 except:
177 log.error(traceback.format_exc())
177 log.error(traceback.format_exc())
178 self.sa.rollback()
178 self.sa.rollback()
179 raise
179 raise
180
180
181 def create_fork(self, form_data, cur_user):
181 def create_fork(self, form_data, cur_user):
182 from rhodecode.lib.celerylib import tasks, run_task
182 from rhodecode.lib.celerylib import tasks, run_task
183 run_task(tasks.create_repo_fork, form_data, cur_user)
183 run_task(tasks.create_repo_fork, form_data, cur_user)
184
184
185 def delete(self, repo):
185 def delete(self, repo):
186 try:
186 try:
187 self.sa.delete(repo)
187 self.sa.delete(repo)
188 self.__delete_repo(repo)
188 self.__delete_repo(repo)
189 self.sa.commit()
189 self.sa.commit()
190 except:
190 except:
191 log.error(traceback.format_exc())
191 log.error(traceback.format_exc())
192 self.sa.rollback()
192 self.sa.rollback()
193 raise
193 raise
194
194
195 def delete_perm_user(self, form_data, repo_name):
195 def delete_perm_user(self, form_data, repo_name):
196 try:
196 try:
197 self.sa.query(RepoToPerm)\
197 self.sa.query(RepoToPerm)\
198 .filter(RepoToPerm.repository \
198 .filter(RepoToPerm.repository \
199 == self.get_by_repo_name(repo_name))\
199 == self.get_by_repo_name(repo_name))\
200 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
200 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
201 self.sa.commit()
201 self.sa.commit()
202 except:
202 except:
203 log.error(traceback.format_exc())
203 log.error(traceback.format_exc())
204 self.sa.rollback()
204 self.sa.rollback()
205 raise
205 raise
206
206
207 def delete_stats(self, repo_name):
207 def delete_stats(self, repo_name):
208 try:
208 try:
209 self.sa.query(Statistics)\
209 self.sa.query(Statistics)\
210 .filter(Statistics.repository == \
210 .filter(Statistics.repository == \
211 self.get_by_repo_name(repo_name)).delete()
211 self.get_by_repo_name(repo_name)).delete()
212 self.sa.commit()
212 self.sa.commit()
213 except:
213 except:
214 log.error(traceback.format_exc())
214 log.error(traceback.format_exc())
215 self.sa.rollback()
215 self.sa.rollback()
216 raise
216 raise
217
217
218
218
219 def __create_repo(self, repo_name, alias):
219 def __create_repo(self, repo_name, alias):
220 """
220 """
221 makes repository on filesystem
221 makes repository on filesystem
222 :param repo_name:
222 :param repo_name:
223 :param alias:
223 :param alias:
224 """
224 """
225 from rhodecode.lib.utils import check_repo
225 from rhodecode.lib.utils import check_repo
226 repo_path = os.path.join(g.base_path, repo_name)
226 repo_path = os.path.join(g.base_path, repo_name)
227 if check_repo(repo_name, g.base_path):
227 if check_repo(repo_name, g.base_path):
228 log.info('creating repo %s in %s', repo_name, repo_path)
228 log.info('creating repo %s in %s', repo_name, repo_path)
229 backend = get_backend(alias)
229 backend = get_backend(alias)
230 backend(repo_path, create=True)
230 backend(repo_path, create=True)
231
231
232 def __rename_repo(self, old, new):
232 def __rename_repo(self, old, new):
233 """
233 """
234 renames repository on filesystem
234 renames repository on filesystem
235 :param old: old name
235 :param old: old name
236 :param new: new name
236 :param new: new name
237 """
237 """
238 log.info('renaming repo from %s to %s', old, new)
238 log.info('renaming repo from %s to %s', old, new)
239
239
240 old_path = os.path.join(g.base_path, old)
240 old_path = os.path.join(g.base_path, old)
241 new_path = os.path.join(g.base_path, new)
241 new_path = os.path.join(g.base_path, new)
242 if os.path.isdir(new_path):
242 if os.path.isdir(new_path):
243 raise Exception('Was trying to rename to already existing dir %s',
243 raise Exception('Was trying to rename to already existing dir %s',
244 new_path)
244 new_path)
245 shutil.move(old_path, new_path)
245 shutil.move(old_path, new_path)
246
246
247 def __delete_repo(self, repo):
247 def __delete_repo(self, repo):
248 """
248 """
249 removes repo from filesystem, the removal is acctually made by
249 removes repo from filesystem, the removal is acctually made by
250 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
250 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
251 repository is no longer valid for rhodecode, can be undeleted later on
251 repository is no longer valid for rhodecode, can be undeleted later on
252 by reverting the renames on this repository
252 by reverting the renames on this repository
253 :param repo: repo object
253 :param repo: repo object
254 """
254 """
255 rm_path = os.path.join(g.base_path, repo.repo_name)
255 rm_path = os.path.join(g.base_path, repo.repo_name)
256 log.info("Removing %s", rm_path)
256 log.info("Removing %s", rm_path)
257 #disable hg/git
257 #disable hg/git
258 alias = repo.repo_type
258 alias = repo.repo_type
259 shutil.move(os.path.join(rm_path, '.%s' % alias),
259 shutil.move(os.path.join(rm_path, '.%s' % alias),
260 os.path.join(rm_path, 'rm__.%s' % alias))
260 os.path.join(rm_path, 'rm__.%s' % alias))
261 #disable repo
261 #disable repo
262 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
262 shutil.move(rm_path, os.path.join(g.base_path, 'rm__%s__%s' \
263 % (datetime.today(), repo.repo_name)))
263 % (datetime.today(), repo.repo_name)))
@@ -1,384 +1,384 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.scm
3 rhodecode.model.scm
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Scm model for RhodeCode
6 Scm model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27 import os
27 import os
28 import time
28 import time
29 import traceback
29 import traceback
30 import logging
30 import logging
31
31
32 from vcs import get_backend
32 from vcs import get_backend
33 from vcs.utils.helpers import get_scm
33 from vcs.utils.helpers import get_scm
34 from vcs.exceptions import RepositoryError, VCSError
34 from vcs.exceptions import RepositoryError, VCSError
35 from vcs.utils.lazy import LazyProperty
35 from vcs.utils.lazy import LazyProperty
36
36
37 from mercurial import ui
37 from mercurial import ui
38
38
39 from beaker.cache import cache_region, region_invalidate
39 from beaker.cache import cache_region, region_invalidate
40
40
41 from rhodecode import BACKENDS
41 from rhodecode import BACKENDS
42 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
43 from rhodecode.lib.auth import HasRepoPermissionAny
43 from rhodecode.lib.auth import HasRepoPermissionAny
44 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, action_logger
44 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, action_logger
45 from rhodecode.model import BaseModel
45 from rhodecode.model import BaseModel
46 from rhodecode.model.user import UserModel
46 from rhodecode.model.user import UserModel
47
47
48 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
48 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
49 UserFollowing, UserLog
49 UserFollowing, UserLog
50 from rhodecode.model.caching_query import FromCache
50 from rhodecode.model.caching_query import FromCache
51
51
52 from sqlalchemy.orm import joinedload
52 from sqlalchemy.orm import joinedload
53 from sqlalchemy.orm.session import make_transient
53 from sqlalchemy.orm.session import make_transient
54 from sqlalchemy.exc import DatabaseError
54 from sqlalchemy.exc import DatabaseError
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class UserTemp(object):
59 class UserTemp(object):
60 def __init__(self, user_id):
60 def __init__(self, user_id):
61 self.user_id = user_id
61 self.user_id = user_id
62
62
63 def __repr__(self):
63 def __repr__(self):
64 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
64 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
65
65
66 class RepoTemp(object):
66 class RepoTemp(object):
67 def __init__(self, repo_id):
67 def __init__(self, repo_id):
68 self.repo_id = repo_id
68 self.repo_id = repo_id
69
69
70 def __repr__(self):
70 def __repr__(self):
71 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
71 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
72
72
73 class ScmModel(BaseModel):
73 class ScmModel(BaseModel):
74 """Generic Scm Model
74 """Generic Scm Model
75 """
75 """
76
76
77 @LazyProperty
77 @LazyProperty
78 def repos_path(self):
78 def repos_path(self):
79 """Get's the repositories root path from database
79 """Get's the repositories root path from database
80 """
80 """
81
81
82 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
82 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
83
83
84 return q.ui_value
84 return q.ui_value
85
85
86 def repo_scan(self, repos_path, baseui):
86 def repo_scan(self, repos_path, baseui):
87 """Listing of repositories in given path. This path should not be a
87 """Listing of repositories in given path. This path should not be a
88 repository itself. Return a dictionary of repository objects
88 repository itself. Return a dictionary of repository objects
89
89
90 :param repos_path: path to directory containing repositories
90 :param repos_path: path to directory containing repositories
91 :param baseui: baseui instance to instantiate MercurialRepostitory with
91 :param baseui: baseui instance to instantiate MercurialRepostitory with
92 """
92 """
93
93
94 log.info('scanning for repositories in %s', repos_path)
94 log.info('scanning for repositories in %s', repos_path)
95
95
96 if not isinstance(baseui, ui.ui):
96 if not isinstance(baseui, ui.ui):
97 baseui = make_ui('db')
97 baseui = make_ui('db')
98 repos_list = {}
98 repos_list = {}
99
99
100 for name, path in get_filesystem_repos(repos_path, recursive=True):
100 for name, path in get_filesystem_repos(repos_path, recursive=True):
101 try:
101 try:
102 if repos_list.has_key(name):
102 if repos_list.has_key(name):
103 raise RepositoryError('Duplicate repository name %s '
103 raise RepositoryError('Duplicate repository name %s '
104 'found in %s' % (name, path))
104 'found in %s' % (name, path))
105 else:
105 else:
106
106
107 klass = get_backend(path[0])
107 klass = get_backend(path[0])
108
108
109 if path[0] == 'hg' and path[0] in BACKENDS.keys():
109 if path[0] == 'hg' and path[0] in BACKENDS.keys():
110 repos_list[name] = klass(path[1], baseui=baseui)
110 repos_list[name] = klass(path[1], baseui=baseui)
111
111
112 if path[0] == 'git' and path[0] in BACKENDS.keys():
112 if path[0] == 'git' and path[0] in BACKENDS.keys():
113 repos_list[name] = klass(path[1])
113 repos_list[name] = klass(path[1])
114 except OSError:
114 except OSError:
115 continue
115 continue
116
116
117 return repos_list
117 return repos_list
118
118
119 def get_repos(self, all_repos=None):
119 def get_repos(self, all_repos=None):
120 """Get all repos from db and for each repo create it's backend instance.
120 """Get all repos from db and for each repo create it's backend instance.
121 and fill that backed with information from database
121 and fill that backed with information from database
122
122
123 :param all_repos: give specific repositories list, good for filtering
123 :param all_repos: give specific repositories list, good for filtering
124 """
124 """
125
125
126 if all_repos is None:
126 if all_repos is None:
127 all_repos = self.sa.query(Repository)\
127 all_repos = self.sa.query(Repository)\
128 .order_by(Repository.repo_name).all()
128 .order_by(Repository.repo_name).all()
129
129
130 #get the repositories that should be invalidated
130 #get the repositories that should be invalidated
131 invalidation_list = [str(x.cache_key) for x in \
131 invalidation_list = [str(x.cache_key) for x in \
132 self.sa.query(CacheInvalidation.cache_key)\
132 self.sa.query(CacheInvalidation.cache_key)\
133 .filter(CacheInvalidation.cache_active == False)\
133 .filter(CacheInvalidation.cache_active == False)\
134 .all()]
134 .all()]
135
135
136 for r in all_repos:
136 for r in all_repos:
137
137
138 repo = self.get(r.repo_name, invalidation_list)
138 repo = self.get(r.repo_name, invalidation_list)
139
139
140 if repo is not None:
140 if repo is not None:
141 last_change = repo.last_change
141 last_change = repo.last_change
142 tip = h.get_changeset_safe(repo, 'tip')
142 tip = h.get_changeset_safe(repo, 'tip')
143
143
144 tmp_d = {}
144 tmp_d = {}
145 tmp_d['name'] = r.repo_name
145 tmp_d['name'] = r.repo_name
146 tmp_d['name_sort'] = tmp_d['name'].lower()
146 tmp_d['name_sort'] = tmp_d['name'].lower()
147 tmp_d['description'] = repo.dbrepo.description
147 tmp_d['description'] = repo.dbrepo.description
148 tmp_d['description_sort'] = tmp_d['description']
148 tmp_d['description_sort'] = tmp_d['description']
149 tmp_d['last_change'] = last_change
149 tmp_d['last_change'] = last_change
150 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
150 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
151 tmp_d['tip'] = tip.raw_id
151 tmp_d['tip'] = tip.raw_id
152 tmp_d['tip_sort'] = tip.revision
152 tmp_d['tip_sort'] = tip.revision
153 tmp_d['rev'] = tip.revision
153 tmp_d['rev'] = tip.revision
154 tmp_d['contact'] = repo.dbrepo.user.full_contact
154 tmp_d['contact'] = repo.dbrepo.user.full_contact
155 tmp_d['contact_sort'] = tmp_d['contact']
155 tmp_d['contact_sort'] = tmp_d['contact']
156 tmp_d['repo_archives'] = list(repo._get_archives())
156 tmp_d['repo_archives'] = list(repo._get_archives())
157 tmp_d['last_msg'] = tip.message
157 tmp_d['last_msg'] = tip.message
158 tmp_d['repo'] = repo
158 tmp_d['repo'] = repo
159 yield tmp_d
159 yield tmp_d
160
160
161 def get_repo(self, repo_name):
161 def get_repo(self, repo_name):
162 return self.get(repo_name)
162 return self.get(repo_name)
163
163
164 def get(self, repo_name, invalidation_list=None):
164 def get(self, repo_name, invalidation_list=None):
165 """Get's repository from given name, creates BackendInstance and
165 """Get's repository from given name, creates BackendInstance and
166 propagates it's data from database with all additional information
166 propagates it's data from database with all additional information
167
167
168 :param repo_name:
168 :param repo_name:
169 :param invalidation_list: if a invalidation list is given the get
169 :param invalidation_list: if a invalidation list is given the get
170 method should not manually check if this repository needs
170 method should not manually check if this repository needs
171 invalidation and just invalidate the repositories in list
171 invalidation and just invalidate the repositories in list
172
172
173 """
173 """
174 if not HasRepoPermissionAny('repository.read', 'repository.write',
174 if not HasRepoPermissionAny('repository.read', 'repository.write',
175 'repository.admin')(repo_name, 'get repo check'):
175 'repository.admin')(repo_name, 'get repo check'):
176 return
176 return
177
177
178 #======================================================================
178 #======================================================================
179 # CACHE FUNCTION
179 # CACHE FUNCTION
180 #======================================================================
180 #======================================================================
181 @cache_region('long_term')
181 @cache_region('long_term')
182 def _get_repo(repo_name):
182 def _get_repo(repo_name):
183
183
184 repo_path = os.path.join(self.repos_path, repo_name)
184 repo_path = os.path.join(self.repos_path, repo_name)
185
185
186 try:
186 try:
187 alias = get_scm(repo_path)[0]
187 alias = get_scm(repo_path)[0]
188
188
189 log.debug('Creating instance of %s repository', alias)
189 log.debug('Creating instance of %s repository', alias)
190 backend = get_backend(alias)
190 backend = get_backend(alias)
191 except VCSError:
191 except VCSError:
192 log.error(traceback.format_exc())
192 log.error(traceback.format_exc())
193 return
193 return
194
194
195 if alias == 'hg':
195 if alias == 'hg':
196 from pylons import app_globals as g
196 from pylons import app_globals as g
197 repo = backend(repo_path, create=False, baseui=g.baseui)
197 repo = backend(repo_path, create=False, baseui=g.baseui)
198 #skip hidden web repository
198 #skip hidden web repository
199 if repo._get_hidden():
199 if repo._get_hidden():
200 return
200 return
201 else:
201 else:
202 repo = backend(repo_path, create=False)
202 repo = backend(repo_path, create=False)
203
203
204 dbrepo = self.sa.query(Repository)\
204 dbrepo = self.sa.query(Repository)\
205 .options(joinedload(Repository.fork))\
205 .options(joinedload(Repository.fork))\
206 .options(joinedload(Repository.user))\
206 .options(joinedload(Repository.user))\
207 .filter(Repository.repo_name == repo_name)\
207 .filter(Repository.repo_name == repo_name)\
208 .scalar()
208 .scalar()
209
209
210 make_transient(dbrepo)
210 make_transient(dbrepo)
211 if dbrepo.user:
211 if dbrepo.user:
212 make_transient(dbrepo.user)
212 make_transient(dbrepo.user)
213 if dbrepo.fork:
213 if dbrepo.fork:
214 make_transient(dbrepo.fork)
214 make_transient(dbrepo.fork)
215
215
216 repo.dbrepo = dbrepo
216 repo.dbrepo = dbrepo
217 return repo
217 return repo
218
218
219 pre_invalidate = True
219 pre_invalidate = True
220 if invalidation_list is not None:
220 if invalidation_list is not None:
221 pre_invalidate = repo_name in invalidation_list
221 pre_invalidate = repo_name in invalidation_list
222
222
223 if pre_invalidate:
223 if pre_invalidate:
224 invalidate = self._should_invalidate(repo_name)
224 invalidate = self._should_invalidate(repo_name)
225
225
226 if invalidate:
226 if invalidate:
227 log.info('invalidating cache for repository %s', repo_name)
227 log.info('invalidating cache for repository %s', repo_name)
228 region_invalidate(_get_repo, None, repo_name)
228 region_invalidate(_get_repo, None, repo_name)
229 self._mark_invalidated(invalidate)
229 self._mark_invalidated(invalidate)
230
230
231 return _get_repo(repo_name)
231 return _get_repo(repo_name)
232
232
233
233
234
234
235 def mark_for_invalidation(self, repo_name):
235 def mark_for_invalidation(self, repo_name):
236 """Puts cache invalidation task into db for
236 """Puts cache invalidation task into db for
237 further global cache invalidation
237 further global cache invalidation
238
238
239 :param repo_name: this repo that should invalidation take place
239 :param repo_name: this repo that should invalidation take place
240 """
240 """
241
241
242 log.debug('marking %s for invalidation', repo_name)
242 log.debug('marking %s for invalidation', repo_name)
243 cache = self.sa.query(CacheInvalidation)\
243 cache = self.sa.query(CacheInvalidation)\
244 .filter(CacheInvalidation.cache_key == repo_name).scalar()
244 .filter(CacheInvalidation.cache_key == repo_name).scalar()
245
245
246 if cache:
246 if cache:
247 #mark this cache as inactive
247 #mark this cache as inactive
248 cache.cache_active = False
248 cache.cache_active = False
249 else:
249 else:
250 log.debug('cache key not found in invalidation db -> creating one')
250 log.debug('cache key not found in invalidation db -> creating one')
251 cache = CacheInvalidation(repo_name)
251 cache = CacheInvalidation(repo_name)
252
252
253 try:
253 try:
254 self.sa.add(cache)
254 self.sa.add(cache)
255 self.sa.commit()
255 self.sa.commit()
256 except (DatabaseError,):
256 except (DatabaseError,):
257 log.error(traceback.format_exc())
257 log.error(traceback.format_exc())
258 self.sa.rollback()
258 self.sa.rollback()
259
259
260
260
261 def toggle_following_repo(self, follow_repo_id, user_id):
261 def toggle_following_repo(self, follow_repo_id, user_id):
262
262
263 f = self.sa.query(UserFollowing)\
263 f = self.sa.query(UserFollowing)\
264 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
264 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
265 .filter(UserFollowing.user_id == user_id).scalar()
265 .filter(UserFollowing.user_id == user_id).scalar()
266
266
267 if f is not None:
267 if f is not None:
268
268
269 try:
269 try:
270 self.sa.delete(f)
270 self.sa.delete(f)
271 self.sa.commit()
271 self.sa.commit()
272 action_logger(UserTemp(user_id),
272 action_logger(UserTemp(user_id),
273 'stopped_following_repo',
273 'stopped_following_repo',
274 RepoTemp(follow_repo_id))
274 RepoTemp(follow_repo_id))
275 return
275 return
276 except:
276 except:
277 log.error(traceback.format_exc())
277 log.error(traceback.format_exc())
278 self.sa.rollback()
278 self.sa.rollback()
279 raise
279 raise
280
280
281
281
282 try:
282 try:
283 f = UserFollowing()
283 f = UserFollowing()
284 f.user_id = user_id
284 f.user_id = user_id
285 f.follows_repo_id = follow_repo_id
285 f.follows_repo_id = follow_repo_id
286 self.sa.add(f)
286 self.sa.add(f)
287 self.sa.commit()
287 self.sa.commit()
288 action_logger(UserTemp(user_id),
288 action_logger(UserTemp(user_id),
289 'started_following_repo',
289 'started_following_repo',
290 RepoTemp(follow_repo_id))
290 RepoTemp(follow_repo_id))
291 except:
291 except:
292 log.error(traceback.format_exc())
292 log.error(traceback.format_exc())
293 self.sa.rollback()
293 self.sa.rollback()
294 raise
294 raise
295
295
296 def toggle_following_user(self, follow_user_id , user_id):
296 def toggle_following_user(self, follow_user_id , user_id):
297 f = self.sa.query(UserFollowing)\
297 f = self.sa.query(UserFollowing)\
298 .filter(UserFollowing.follows_user_id == follow_user_id)\
298 .filter(UserFollowing.follows_user_id == follow_user_id)\
299 .filter(UserFollowing.user_id == user_id).scalar()
299 .filter(UserFollowing.user_id == user_id).scalar()
300
300
301 if f is not None:
301 if f is not None:
302 try:
302 try:
303 self.sa.delete(f)
303 self.sa.delete(f)
304 self.sa.commit()
304 self.sa.commit()
305 return
305 return
306 except:
306 except:
307 log.error(traceback.format_exc())
307 log.error(traceback.format_exc())
308 self.sa.rollback()
308 self.sa.rollback()
309 raise
309 raise
310
310
311 try:
311 try:
312 f = UserFollowing()
312 f = UserFollowing()
313 f.user_id = user_id
313 f.user_id = user_id
314 f.follows_user_id = follow_user_id
314 f.follows_user_id = follow_user_id
315 self.sa.add(f)
315 self.sa.add(f)
316 self.sa.commit()
316 self.sa.commit()
317 except:
317 except:
318 log.error(traceback.format_exc())
318 log.error(traceback.format_exc())
319 self.sa.rollback()
319 self.sa.rollback()
320 raise
320 raise
321
321
322 def is_following_repo(self, repo_name, user_id):
322 def is_following_repo(self, repo_name, user_id):
323 r = self.sa.query(Repository)\
323 r = self.sa.query(Repository)\
324 .filter(Repository.repo_name == repo_name).scalar()
324 .filter(Repository.repo_name == repo_name).scalar()
325
325
326 f = self.sa.query(UserFollowing)\
326 f = self.sa.query(UserFollowing)\
327 .filter(UserFollowing.follows_repository == r)\
327 .filter(UserFollowing.follows_repository == r)\
328 .filter(UserFollowing.user_id == user_id).scalar()
328 .filter(UserFollowing.user_id == user_id).scalar()
329
329
330 return f is not None
330 return f is not None
331
331
332 def is_following_user(self, username, user_id):
332 def is_following_user(self, username, user_id):
333 u = UserModel(self.sa).get_by_username(username)
333 u = UserModel(self.sa).get_by_username(username)
334
334
335 f = self.sa.query(UserFollowing)\
335 f = self.sa.query(UserFollowing)\
336 .filter(UserFollowing.follows_user == u)\
336 .filter(UserFollowing.follows_user == u)\
337 .filter(UserFollowing.user_id == user_id).scalar()
337 .filter(UserFollowing.user_id == user_id).scalar()
338
338
339 return f is not None
339 return f is not None
340
340
341 def get_followers(self, repo_id):
341 def get_followers(self, repo_id):
342 return self.sa.query(UserFollowing)\
342 return self.sa.query(UserFollowing)\
343 .filter(UserFollowing.follows_repo_id == repo_id).count()
343 .filter(UserFollowing.follows_repo_id == repo_id).count()
344
344
345 def get_forks(self, repo_id):
345 def get_forks(self, repo_id):
346 return self.sa.query(Repository)\
346 return self.sa.query(Repository)\
347 .filter(Repository.fork_id == repo_id).count()
347 .filter(Repository.fork_id == repo_id).count()
348
348
349
349
350 def get_unread_journal(self):
350 def get_unread_journal(self):
351 return self.sa.query(UserLog).count()
351 return self.sa.query(UserLog).count()
352
352
353
353
354 def _should_invalidate(self, repo_name):
354 def _should_invalidate(self, repo_name):
355 """Looks up database for invalidation signals for this repo_name
355 """Looks up database for invalidation signals for this repo_name
356
356
357 :param repo_name:
357 :param repo_name:
358 """
358 """
359
359
360 ret = self.sa.query(CacheInvalidation)\
360 ret = self.sa.query(CacheInvalidation)\
361 .options(FromCache('sql_cache_short',
361 .options(FromCache('sql_cache_short',
362 'get_invalidation_%s' % repo_name))\
362 'get_invalidation_%s' % repo_name))\
363 .filter(CacheInvalidation.cache_key == repo_name)\
363 .filter(CacheInvalidation.cache_key == repo_name)\
364 .filter(CacheInvalidation.cache_active == False)\
364 .filter(CacheInvalidation.cache_active == False)\
365 .scalar()
365 .scalar()
366
366
367 return ret
367 return ret
368
368
369 def _mark_invalidated(self, cache_key):
369 def _mark_invalidated(self, cache_key):
370 """ Marks all occurences of cache to invaldation as already invalidated
370 """ Marks all occurences of cache to invaldation as already invalidated
371
371
372 :param cache_key:
372 :param cache_key:
373 """
373 """
374
374
375 if cache_key:
375 if cache_key:
376 log.debug('marking %s as already invalidated', cache_key)
376 log.debug('marking %s as already invalidated', cache_key)
377 try:
377 try:
378 cache_key.cache_active = True
378 cache_key.cache_active = True
379 self.sa.add(cache_key)
379 self.sa.add(cache_key)
380 self.sa.commit()
380 self.sa.commit()
381 except (DatabaseError,):
381 except (DatabaseError,):
382 log.error(traceback.format_exc())
382 log.error(traceback.format_exc())
383 self.sa.rollback()
383 self.sa.rollback()
384
384
@@ -1,93 +1,93 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # Model for RhodeCode settings
3 # Model for RhodeCode settings
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2011 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 Nov 17, 2010
21 Created on Nov 17, 2010
22 Model for RhodeCode
22 Model for RhodeCode
23 :author: marcink
23 :author: marcink
24 """
24 """
25
25
26 from rhodecode.lib import helpers as h
26 from rhodecode.lib import helpers as h
27 from rhodecode.model import BaseModel
27 from rhodecode.model import BaseModel
28 from rhodecode.model.caching_query import FromCache
28 from rhodecode.model.caching_query import FromCache
29 from rhodecode.model.db import RhodeCodeSettings
29 from rhodecode.model.db import RhodeCodeSettings
30 from sqlalchemy.orm import joinedload
30 from sqlalchemy.orm import joinedload
31 import logging
31 import logging
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35 class SettingsModel(BaseModel):
35 class SettingsModel(BaseModel):
36 """
36 """
37 Settings model
37 Settings model
38 """
38 """
39
39
40 def get(self, settings_key, cache=False):
40 def get(self, settings_key, cache=False):
41 r = self.sa.query(RhodeCodeSettings)\
41 r = self.sa.query(RhodeCodeSettings)\
42 .filter(RhodeCodeSettings.app_settings_name == settings_key).scalar()
42 .filter(RhodeCodeSettings.app_settings_name == settings_key).scalar()
43 if cache:
43 if cache:
44 r = r.options(FromCache("sql_cache_short",
44 r = r.options(FromCache("sql_cache_short",
45 "get_setting_%s" % settings_key))
45 "get_setting_%s" % settings_key))
46 return r
46 return r
47
47
48 def get_app_settings(self, cache=False):
48 def get_app_settings(self, cache=False):
49 """Get's config from database, each config key is prefixed with
49 """Get's config from database, each config key is prefixed with
50 'rhodecode_' prefix, than global pylons config is updated with such
50 'rhodecode_' prefix, than global pylons config is updated with such
51 keys
51 keys
52 """
52 """
53
53
54 ret = self.sa.query(RhodeCodeSettings)
54 ret = self.sa.query(RhodeCodeSettings)
55
55
56 if cache:
56 if cache:
57 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
57 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
58
58
59 if not ret:
59 if not ret:
60 raise Exception('Could not get application settings !')
60 raise Exception('Could not get application settings !')
61 settings = {}
61 settings = {}
62 for each in ret:
62 for each in ret:
63 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
63 settings['rhodecode_' + each.app_settings_name] = each.app_settings_value
64
64
65 return settings
65 return settings
66
66
67 def get_ldap_settings(self):
67 def get_ldap_settings(self):
68 """
68 """
69 Returns ldap settings from database
69 Returns ldap settings from database
70 :returns:
70 :returns:
71 ldap_active
71 ldap_active
72 ldap_host
72 ldap_host
73 ldap_port
73 ldap_port
74 ldap_ldaps
74 ldap_ldaps
75 ldap_dn_user
75 ldap_dn_user
76 ldap_dn_pass
76 ldap_dn_pass
77 ldap_base_dn
77 ldap_base_dn
78 """
78 """
79
79
80 r = self.sa.query(RhodeCodeSettings)\
80 r = self.sa.query(RhodeCodeSettings)\
81 .filter(RhodeCodeSettings.app_settings_name\
81 .filter(RhodeCodeSettings.app_settings_name\
82 .startswith('ldap_'))\
82 .startswith('ldap_'))\
83 .all()
83 .all()
84
84
85 fd = {}
85 fd = {}
86
86
87 for row in r:
87 for row in r:
88 v = row.app_settings_value
88 v = row.app_settings_value
89 if v in ['0', '1']:
89 if v in ['0', '1']:
90 v = v == '1'
90 v = v == '1'
91 fd.update({row.app_settings_name:v})
91 fd.update({row.app_settings_name:v})
92
92
93 return fd
93 return fd
@@ -1,223 +1,223 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.rhodecode.model.user
3 package.rhodecode.model.user
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 users model for RhodeCode
6 users model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 from rhodecode.model import BaseModel
33 from rhodecode.model import BaseModel
34 from rhodecode.model.caching_query import FromCache
34 from rhodecode.model.caching_query import FromCache
35 from rhodecode.model.db import User
35 from rhodecode.model.db import User
36
36
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
38
38
39 from sqlalchemy.exc import DatabaseError
39 from sqlalchemy.exc import DatabaseError
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43 class UserModel(BaseModel):
43 class UserModel(BaseModel):
44
44
45 def get(self, user_id, cache=False):
45 def get(self, user_id, cache=False):
46 user = self.sa.query(User)
46 user = self.sa.query(User)
47 if cache:
47 if cache:
48 user = user.options(FromCache("sql_cache_short",
48 user = user.options(FromCache("sql_cache_short",
49 "get_user_%s" % user_id))
49 "get_user_%s" % user_id))
50 return user.get(user_id)
50 return user.get(user_id)
51
51
52
52
53 def get_by_username(self, username, cache=False, case_insensitive=False):
53 def get_by_username(self, username, cache=False, case_insensitive=False):
54
54
55 if case_insensitive:
55 if case_insensitive:
56 user = self.sa.query(User).filter(User.username.ilike(username))
56 user = self.sa.query(User).filter(User.username.ilike(username))
57 else:
57 else:
58 user = self.sa.query(User)\
58 user = self.sa.query(User)\
59 .filter(User.username == username)
59 .filter(User.username == username)
60 if cache:
60 if cache:
61 user = user.options(FromCache("sql_cache_short",
61 user = user.options(FromCache("sql_cache_short",
62 "get_user_%s" % username))
62 "get_user_%s" % username))
63 return user.scalar()
63 return user.scalar()
64
64
65 def create(self, form_data):
65 def create(self, form_data):
66 try:
66 try:
67 new_user = User()
67 new_user = User()
68 for k, v in form_data.items():
68 for k, v in form_data.items():
69 setattr(new_user, k, v)
69 setattr(new_user, k, v)
70
70
71 self.sa.add(new_user)
71 self.sa.add(new_user)
72 self.sa.commit()
72 self.sa.commit()
73 except:
73 except:
74 log.error(traceback.format_exc())
74 log.error(traceback.format_exc())
75 self.sa.rollback()
75 self.sa.rollback()
76 raise
76 raise
77
77
78 def create_ldap(self, username, password):
78 def create_ldap(self, username, password):
79 """
79 """
80 Checks if user is in database, if not creates this user marked
80 Checks if user is in database, if not creates this user marked
81 as ldap user
81 as ldap user
82 :param username:
82 :param username:
83 :param password:
83 :param password:
84 """
84 """
85 from rhodecode.lib.auth import get_crypt_password
85 from rhodecode.lib.auth import get_crypt_password
86 log.debug('Checking for such ldap account in RhodeCode database')
86 log.debug('Checking for such ldap account in RhodeCode database')
87 if self.get_by_username(username, case_insensitive=True) is None:
87 if self.get_by_username(username, case_insensitive=True) is None:
88 try:
88 try:
89 new_user = User()
89 new_user = User()
90 new_user.username = username.lower()#add ldap account always lowercase
90 new_user.username = username.lower()#add ldap account always lowercase
91 new_user.password = get_crypt_password(password)
91 new_user.password = get_crypt_password(password)
92 new_user.email = '%s@ldap.server' % username
92 new_user.email = '%s@ldap.server' % username
93 new_user.active = True
93 new_user.active = True
94 new_user.is_ldap = True
94 new_user.is_ldap = True
95 new_user.name = '%s@ldap' % username
95 new_user.name = '%s@ldap' % username
96 new_user.lastname = ''
96 new_user.lastname = ''
97
97
98
98
99 self.sa.add(new_user)
99 self.sa.add(new_user)
100 self.sa.commit()
100 self.sa.commit()
101 return True
101 return True
102 except (DatabaseError,):
102 except (DatabaseError,):
103 log.error(traceback.format_exc())
103 log.error(traceback.format_exc())
104 self.sa.rollback()
104 self.sa.rollback()
105 raise
105 raise
106 log.debug('this %s user exists skipping creation of ldap account',
106 log.debug('this %s user exists skipping creation of ldap account',
107 username)
107 username)
108 return False
108 return False
109
109
110 def create_registration(self, form_data):
110 def create_registration(self, form_data):
111 from rhodecode.lib.celerylib import tasks, run_task
111 from rhodecode.lib.celerylib import tasks, run_task
112 try:
112 try:
113 new_user = User()
113 new_user = User()
114 for k, v in form_data.items():
114 for k, v in form_data.items():
115 if k != 'admin':
115 if k != 'admin':
116 setattr(new_user, k, v)
116 setattr(new_user, k, v)
117
117
118 self.sa.add(new_user)
118 self.sa.add(new_user)
119 self.sa.commit()
119 self.sa.commit()
120 body = ('New user registration\n'
120 body = ('New user registration\n'
121 'username: %s\n'
121 'username: %s\n'
122 'email: %s\n')
122 'email: %s\n')
123 body = body % (form_data['username'], form_data['email'])
123 body = body % (form_data['username'], form_data['email'])
124
124
125 run_task(tasks.send_email, None,
125 run_task(tasks.send_email, None,
126 _('[RhodeCode] New User registration'),
126 _('[RhodeCode] New User registration'),
127 body)
127 body)
128 except:
128 except:
129 log.error(traceback.format_exc())
129 log.error(traceback.format_exc())
130 self.sa.rollback()
130 self.sa.rollback()
131 raise
131 raise
132
132
133 def update(self, user_id, form_data):
133 def update(self, user_id, form_data):
134 try:
134 try:
135 new_user = self.get(user_id, cache=False)
135 new_user = self.get(user_id, cache=False)
136 if new_user.username == 'default':
136 if new_user.username == 'default':
137 raise DefaultUserException(
137 raise DefaultUserException(
138 _("You can't Edit this user since it's"
138 _("You can't Edit this user since it's"
139 " crucial for entire application"))
139 " crucial for entire application"))
140
140
141 for k, v in form_data.items():
141 for k, v in form_data.items():
142 if k == 'new_password' and v != '':
142 if k == 'new_password' and v != '':
143 new_user.password = v
143 new_user.password = v
144 else:
144 else:
145 setattr(new_user, k, v)
145 setattr(new_user, k, v)
146
146
147 self.sa.add(new_user)
147 self.sa.add(new_user)
148 self.sa.commit()
148 self.sa.commit()
149 except:
149 except:
150 log.error(traceback.format_exc())
150 log.error(traceback.format_exc())
151 self.sa.rollback()
151 self.sa.rollback()
152 raise
152 raise
153
153
154 def update_my_account(self, user_id, form_data):
154 def update_my_account(self, user_id, form_data):
155 try:
155 try:
156 new_user = self.get(user_id, cache=False)
156 new_user = self.get(user_id, cache=False)
157 if new_user.username == 'default':
157 if new_user.username == 'default':
158 raise DefaultUserException(
158 raise DefaultUserException(
159 _("You can't Edit this user since it's"
159 _("You can't Edit this user since it's"
160 " crucial for entire application"))
160 " crucial for entire application"))
161 for k, v in form_data.items():
161 for k, v in form_data.items():
162 if k == 'new_password' and v != '':
162 if k == 'new_password' and v != '':
163 new_user.password = v
163 new_user.password = v
164 else:
164 else:
165 if k not in ['admin', 'active']:
165 if k not in ['admin', 'active']:
166 setattr(new_user, k, v)
166 setattr(new_user, k, v)
167
167
168 self.sa.add(new_user)
168 self.sa.add(new_user)
169 self.sa.commit()
169 self.sa.commit()
170 except:
170 except:
171 log.error(traceback.format_exc())
171 log.error(traceback.format_exc())
172 self.sa.rollback()
172 self.sa.rollback()
173 raise
173 raise
174
174
175 def delete(self, user_id):
175 def delete(self, user_id):
176 try:
176 try:
177 user = self.get(user_id, cache=False)
177 user = self.get(user_id, cache=False)
178 if user.username == 'default':
178 if user.username == 'default':
179 raise DefaultUserException(
179 raise DefaultUserException(
180 _("You can't remove this user since it's"
180 _("You can't remove this user since it's"
181 " crucial for entire application"))
181 " crucial for entire application"))
182 if user.repositories:
182 if user.repositories:
183 raise UserOwnsReposException(_('This user still owns %s '
183 raise UserOwnsReposException(_('This user still owns %s '
184 'repositories and cannot be '
184 'repositories and cannot be '
185 'removed. Switch owners or '
185 'removed. Switch owners or '
186 'remove those repositories') \
186 'remove those repositories') \
187 % user.repositories)
187 % user.repositories)
188 self.sa.delete(user)
188 self.sa.delete(user)
189 self.sa.commit()
189 self.sa.commit()
190 except:
190 except:
191 log.error(traceback.format_exc())
191 log.error(traceback.format_exc())
192 self.sa.rollback()
192 self.sa.rollback()
193 raise
193 raise
194
194
195 def reset_password(self, data):
195 def reset_password(self, data):
196 from rhodecode.lib.celerylib import tasks, run_task
196 from rhodecode.lib.celerylib import tasks, run_task
197 run_task(tasks.reset_user_password, data['email'])
197 run_task(tasks.reset_user_password, data['email'])
198
198
199
199
200 def fill_data(self, user):
200 def fill_data(self, user):
201 """
201 """
202 Fills user data with those from database and log out user if not
202 Fills user data with those from database and log out user if not
203 present in database
203 present in database
204 :param user:
204 :param user:
205 """
205 """
206
206
207 if not hasattr(user, 'user_id') or user.user_id is None:
207 if not hasattr(user, 'user_id') or user.user_id is None:
208 raise Exception('passed in user has to have the user_id attribute')
208 raise Exception('passed in user has to have the user_id attribute')
209
209
210
210
211 log.debug('filling auth user data')
211 log.debug('filling auth user data')
212 try:
212 try:
213 dbuser = self.get(user.user_id)
213 dbuser = self.get(user.user_id)
214 user.username = dbuser.username
214 user.username = dbuser.username
215 user.is_admin = dbuser.admin
215 user.is_admin = dbuser.admin
216 user.name = dbuser.name
216 user.name = dbuser.name
217 user.lastname = dbuser.lastname
217 user.lastname = dbuser.lastname
218 user.email = dbuser.email
218 user.email = dbuser.email
219 except:
219 except:
220 log.error(traceback.format_exc())
220 log.error(traceback.format_exc())
221 user.is_authenticated = False
221 user.is_authenticated = False
222
222
223 return user
223 return user
@@ -1,54 +1,54 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.websetup
3 rhodecode.websetup
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Weboperations and setup for rhodecode
6 Weboperations and setup for rhodecode
7
7
8 :created_on: Dec 11, 2010
8 :created_on: Dec 11, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import os
28 import os
29 import logging
29 import logging
30
30
31 from rhodecode.config.environment import load_environment
31 from rhodecode.config.environment import load_environment
32 from rhodecode.lib.db_manage import DbManage
32 from rhodecode.lib.db_manage import DbManage
33
33
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37 def setup_app(command, conf, vars):
37 def setup_app(command, conf, vars):
38 """Place any commands to setup rhodecode here"""
38 """Place any commands to setup rhodecode here"""
39 dbconf = conf['sqlalchemy.db1.url']
39 dbconf = conf['sqlalchemy.db1.url']
40 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=conf['here'], tests=False)
40 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=conf['here'], tests=False)
41 dbmanage.create_tables(override=True)
41 dbmanage.create_tables(override=True)
42 dbmanage.set_db_version()
42 dbmanage.set_db_version()
43 dbmanage.config_prompt(None)
43 dbmanage.config_prompt(None)
44 dbmanage.create_default_user()
44 dbmanage.create_default_user()
45 dbmanage.admin_prompt()
45 dbmanage.admin_prompt()
46 dbmanage.create_permissions()
46 dbmanage.create_permissions()
47 dbmanage.populate_default_permissions()
47 dbmanage.populate_default_permissions()
48
48
49 load_environment(conf.global_conf, conf.local_conf, initial=True)
49 load_environment(conf.global_conf, conf.local_conf, initial=True)
50
50
51
51
52
52
53
53
54
54
General Comments 0
You need to be logged in to leave comments. Login now