##// END OF EJS Templates
merge with default
marcink -
r863:4c123ade merge default
parent child Browse files
Show More
@@ -1,106 +1,105 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.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 :created_on: Nov 26, 2010
8 :created_on: Nov 26, 2010
8 :author: marcink
9 :author: marcink
9 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
11 """
12 """
12 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
15 # 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.
16 #
17 #
17 # 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,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 # GNU General Public License for more details.
21 #
22 #
22 # 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
23 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
26 import logging
27 import logging
27 import formencode
28 import formencode
28 import traceback
29 import traceback
29
30
30 from formencode import htmlfill
31 from formencode import htmlfill
31
32
32 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
34 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
35
36
36 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
39 from rhodecode.lib.auth_ldap import LdapImportError
40 from rhodecode.lib.auth_ldap import LdapImportError
40 from rhodecode.model.settings import SettingsModel
41 from rhodecode.model.settings import SettingsModel
41 from rhodecode.model.forms import LdapSettingsForm
42 from rhodecode.model.forms import LdapSettingsForm
42 from sqlalchemy.exc import DatabaseError
43 from sqlalchemy.exc import DatabaseError
43
44
44 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
45
46
46
47
47
48
48 class LdapSettingsController(BaseController):
49 class LdapSettingsController(BaseController):
49
50
50 @LoginRequired()
51 @LoginRequired()
51 @HasPermissionAllDecorator('hg.admin')
52 @HasPermissionAllDecorator('hg.admin')
52 def __before__(self):
53 def __before__(self):
53 c.admin_user = session.get('admin_user')
54 c.admin_user = session.get('admin_user')
54 c.admin_username = session.get('admin_username')
55 c.admin_username = session.get('admin_username')
55 super(LdapSettingsController, self).__before__()
56 super(LdapSettingsController, self).__before__()
56
57
57 def index(self):
58 def index(self):
58 defaults = SettingsModel().get_ldap_settings()
59 defaults = SettingsModel().get_ldap_settings()
59
60
60 return htmlfill.render(
61 return htmlfill.render(
61 render('admin/ldap/ldap.html'),
62 render('admin/ldap/ldap.html'),
62 defaults=defaults,
63 defaults=defaults,
63 encoding="UTF-8",
64 encoding="UTF-8",
64 force_defaults=True,)
65 force_defaults=True,)
65
66
66 def ldap_settings(self):
67 def ldap_settings(self):
67 """
68 """POST ldap create and store ldap settings"""
68 POST ldap create and store ldap settings
69 """
70
69
71 settings_model = SettingsModel()
70 settings_model = SettingsModel()
72 _form = LdapSettingsForm()()
71 _form = LdapSettingsForm()()
73
72
74 try:
73 try:
75 form_result = _form.to_python(dict(request.POST))
74 form_result = _form.to_python(dict(request.POST))
76 try:
75 try:
77
76
78 for k, v in form_result.items():
77 for k, v in form_result.items():
79 if k.startswith('ldap_'):
78 if k.startswith('ldap_'):
80 setting = settings_model.get(k)
79 setting = settings_model.get(k)
81 setting.app_settings_value = v
80 setting.app_settings_value = v
82 self.sa.add(setting)
81 self.sa.add(setting)
83
82
84 self.sa.commit()
83 self.sa.commit()
85 h.flash(_('Ldap settings updated successfully'),
84 h.flash(_('Ldap settings updated successfully'),
86 category='success')
85 category='success')
87 except (DatabaseError,):
86 except (DatabaseError,):
88 raise
87 raise
89 except LdapImportError:
88 except LdapImportError:
90 h.flash(_('Unable to activate ldap. The "python-ldap" library '
89 h.flash(_('Unable to activate ldap. The "python-ldap" library '
91 'is missing.'), category='warning')
90 'is missing.'), category='warning')
92
91
93 except formencode.Invalid, errors:
92 except formencode.Invalid, errors:
94
93
95 return htmlfill.render(
94 return htmlfill.render(
96 render('admin/ldap/ldap.html'),
95 render('admin/ldap/ldap.html'),
97 defaults=errors.value,
96 defaults=errors.value,
98 errors=errors.error_dict or {},
97 errors=errors.error_dict or {},
99 prefix_error=False,
98 prefix_error=False,
100 encoding="UTF-8")
99 encoding="UTF-8")
101 except Exception:
100 except Exception:
102 log.error(traceback.format_exc())
101 log.error(traceback.format_exc())
103 h.flash(_('error occured during update of ldap settings'),
102 h.flash(_('error occurred during update of ldap settings'),
104 category='error')
103 category='error')
105
104
106 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-2010 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 occured 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-2010 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 occured 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 occured 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 occured 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 occured 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 exisitings 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,339 +1,340 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.rhodecode.controllers.admin.settings
3 rhodecode.controllers.admin.settings
4 ~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5 settings controller for rhodecode admin
6 settings controller for rhodecode admin
6
7
7 :created_on: Jul 14, 2010
8 :created_on: Jul 14, 2010
8 :author: marcink
9 :author: marcink
9 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
10 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
11 """
12 """
12 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
15 # 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.
16 #
17 #
17 # 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,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
21 # GNU General Public License for more details.
21 #
22 #
22 # 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
23 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
26
27
27 from formencode import htmlfill
28 from formencode import htmlfill
28 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
29 from pylons import request, session, tmpl_context as c, url, app_globals as g, \
29 config
30 config
30 from pylons.controllers.util import abort, redirect
31 from pylons.controllers.util import abort, redirect
31 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
32 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
34 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
34 HasPermissionAnyDecorator, NotAnonymous
35 HasPermissionAnyDecorator, NotAnonymous
35 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib.celerylib import tasks, run_task
37 from rhodecode.lib.celerylib import tasks, run_task
37 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
38 from rhodecode.lib.utils import repo2db_mapper, invalidate_cache, \
38 set_rhodecode_config
39 set_rhodecode_config
39 from rhodecode.model.db import RhodeCodeUi, Repository
40 from rhodecode.model.db import RhodeCodeUi, Repository
40 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
41 from rhodecode.model.forms import UserForm, ApplicationSettingsForm, \
41 ApplicationUiSettingsForm
42 ApplicationUiSettingsForm
42 from rhodecode.model.scm import ScmModel
43 from rhodecode.model.scm import ScmModel
43 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.user import UserModel
45 from rhodecode.model.user import UserModel
45 from sqlalchemy import func
46 from sqlalchemy import func
46 import formencode
47 import formencode
47 import logging
48 import logging
48 import traceback
49 import traceback
49
50
50 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
51
52
52
53
53 class SettingsController(BaseController):
54 class SettingsController(BaseController):
54 """REST Controller styled on the Atom Publishing Protocol"""
55 """REST Controller styled on the Atom Publishing Protocol"""
55 # To properly map this controller, ensure your config/routing.py
56 # To properly map this controller, ensure your config/routing.py
56 # file has a resource setup:
57 # file has a resource setup:
57 # map.resource('setting', 'settings', controller='admin/settings',
58 # map.resource('setting', 'settings', controller='admin/settings',
58 # path_prefix='/admin', name_prefix='admin_')
59 # path_prefix='/admin', name_prefix='admin_')
59
60
60
61
61 @LoginRequired()
62 @LoginRequired()
62 def __before__(self):
63 def __before__(self):
63 c.admin_user = session.get('admin_user')
64 c.admin_user = session.get('admin_user')
64 c.admin_username = session.get('admin_username')
65 c.admin_username = session.get('admin_username')
65 super(SettingsController, self).__before__()
66 super(SettingsController, self).__before__()
66
67
67
68
68 @HasPermissionAllDecorator('hg.admin')
69 @HasPermissionAllDecorator('hg.admin')
69 def index(self, format='html'):
70 def index(self, format='html'):
70 """GET /admin/settings: All items in the collection"""
71 """GET /admin/settings: All items in the collection"""
71 # url('admin_settings')
72 # url('admin_settings')
72
73
73 defaults = SettingsModel().get_app_settings()
74 defaults = SettingsModel().get_app_settings()
74 defaults.update(self.get_hg_ui_settings())
75 defaults.update(self.get_hg_ui_settings())
75 return htmlfill.render(
76 return htmlfill.render(
76 render('admin/settings/settings.html'),
77 render('admin/settings/settings.html'),
77 defaults=defaults,
78 defaults=defaults,
78 encoding="UTF-8",
79 encoding="UTF-8",
79 force_defaults=False
80 force_defaults=False
80 )
81 )
81
82
82 @HasPermissionAllDecorator('hg.admin')
83 @HasPermissionAllDecorator('hg.admin')
83 def create(self):
84 def create(self):
84 """POST /admin/settings: Create a new item"""
85 """POST /admin/settings: Create a new item"""
85 # url('admin_settings')
86 # url('admin_settings')
86
87
87 @HasPermissionAllDecorator('hg.admin')
88 @HasPermissionAllDecorator('hg.admin')
88 def new(self, format='html'):
89 def new(self, format='html'):
89 """GET /admin/settings/new: Form to create a new item"""
90 """GET /admin/settings/new: Form to create a new item"""
90 # url('admin_new_setting')
91 # url('admin_new_setting')
91
92
92 @HasPermissionAllDecorator('hg.admin')
93 @HasPermissionAllDecorator('hg.admin')
93 def update(self, setting_id):
94 def update(self, setting_id):
94 """PUT /admin/settings/setting_id: Update an existing item"""
95 """PUT /admin/settings/setting_id: Update an existing item"""
95 # Forms posted to this method should contain a hidden field:
96 # Forms posted to this method should contain a hidden field:
96 # <input type="hidden" name="_method" value="PUT" />
97 # <input type="hidden" name="_method" value="PUT" />
97 # Or using helpers:
98 # Or using helpers:
98 # h.form(url('admin_setting', setting_id=ID),
99 # h.form(url('admin_setting', setting_id=ID),
99 # method='put')
100 # method='put')
100 # url('admin_setting', setting_id=ID)
101 # url('admin_setting', setting_id=ID)
101 if setting_id == 'mapping':
102 if setting_id == 'mapping':
102 rm_obsolete = request.POST.get('destroy', False)
103 rm_obsolete = request.POST.get('destroy', False)
103 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
104 log.debug('Rescanning directories with destroy=%s', rm_obsolete)
104
105
105 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
106 initial = ScmModel().repo_scan(g.paths[0][1], g.baseui)
106 for repo_name in initial.keys():
107 for repo_name in initial.keys():
107 invalidate_cache('get_repo_cached_%s' % repo_name)
108 invalidate_cache('get_repo_cached_%s' % repo_name)
108
109
109 repo2db_mapper(initial, rm_obsolete)
110 repo2db_mapper(initial, rm_obsolete)
110
111
111 h.flash(_('Repositories successfully rescanned'), category='success')
112 h.flash(_('Repositories successfully rescanned'), category='success')
112
113
113 if setting_id == 'whoosh':
114 if setting_id == 'whoosh':
114 repo_location = self.get_hg_ui_settings()['paths_root_path']
115 repo_location = self.get_hg_ui_settings()['paths_root_path']
115 full_index = request.POST.get('full_index', False)
116 full_index = request.POST.get('full_index', False)
116 task = run_task(tasks.whoosh_index, repo_location, full_index)
117 task = run_task(tasks.whoosh_index, repo_location, full_index)
117
118
118 h.flash(_('Whoosh reindex task scheduled'), category='success')
119 h.flash(_('Whoosh reindex task scheduled'), category='success')
119 if setting_id == 'global':
120 if setting_id == 'global':
120
121
121 application_form = ApplicationSettingsForm()()
122 application_form = ApplicationSettingsForm()()
122 try:
123 try:
123 form_result = application_form.to_python(dict(request.POST))
124 form_result = application_form.to_python(dict(request.POST))
124 settings_model = SettingsModel()
125 settings_model = SettingsModel()
125 try:
126 try:
126 hgsettings1 = settings_model.get('title')
127 hgsettings1 = settings_model.get('title')
127 hgsettings1.app_settings_value = form_result['rhodecode_title']
128 hgsettings1.app_settings_value = form_result['rhodecode_title']
128
129
129 hgsettings2 = settings_model.get('realm')
130 hgsettings2 = settings_model.get('realm')
130 hgsettings2.app_settings_value = form_result['rhodecode_realm']
131 hgsettings2.app_settings_value = form_result['rhodecode_realm']
131
132
132
133
133 self.sa.add(hgsettings1)
134 self.sa.add(hgsettings1)
134 self.sa.add(hgsettings2)
135 self.sa.add(hgsettings2)
135 self.sa.commit()
136 self.sa.commit()
136 set_rhodecode_config(config)
137 set_rhodecode_config(config)
137 h.flash(_('Updated application settings'),
138 h.flash(_('Updated application settings'),
138 category='success')
139 category='success')
139
140
140 except:
141 except:
141 log.error(traceback.format_exc())
142 log.error(traceback.format_exc())
142 h.flash(_('error occurred during updating application settings'),
143 h.flash(_('error occurred during updating application settings'),
143 category='error')
144 category='error')
144
145
145 self.sa.rollback()
146 self.sa.rollback()
146
147
147
148
148 except formencode.Invalid, errors:
149 except formencode.Invalid, errors:
149 return htmlfill.render(
150 return htmlfill.render(
150 render('admin/settings/settings.html'),
151 render('admin/settings/settings.html'),
151 defaults=errors.value,
152 defaults=errors.value,
152 errors=errors.error_dict or {},
153 errors=errors.error_dict or {},
153 prefix_error=False,
154 prefix_error=False,
154 encoding="UTF-8")
155 encoding="UTF-8")
155
156
156 if setting_id == 'mercurial':
157 if setting_id == 'mercurial':
157 application_form = ApplicationUiSettingsForm()()
158 application_form = ApplicationUiSettingsForm()()
158 try:
159 try:
159 form_result = application_form.to_python(dict(request.POST))
160 form_result = application_form.to_python(dict(request.POST))
160
161
161 try:
162 try:
162
163
163 hgsettings1 = self.sa.query(RhodeCodeUi)\
164 hgsettings1 = self.sa.query(RhodeCodeUi)\
164 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
165 .filter(RhodeCodeUi.ui_key == 'push_ssl').one()
165 hgsettings1.ui_value = form_result['web_push_ssl']
166 hgsettings1.ui_value = form_result['web_push_ssl']
166
167
167 hgsettings2 = self.sa.query(RhodeCodeUi)\
168 hgsettings2 = self.sa.query(RhodeCodeUi)\
168 .filter(RhodeCodeUi.ui_key == '/').one()
169 .filter(RhodeCodeUi.ui_key == '/').one()
169 hgsettings2.ui_value = form_result['paths_root_path']
170 hgsettings2.ui_value = form_result['paths_root_path']
170
171
171
172
172 #HOOKS
173 #HOOKS
173 hgsettings3 = self.sa.query(RhodeCodeUi)\
174 hgsettings3 = self.sa.query(RhodeCodeUi)\
174 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
175 .filter(RhodeCodeUi.ui_key == 'changegroup.update').one()
175 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
176 hgsettings3.ui_active = bool(form_result['hooks_changegroup_update'])
176
177
177 hgsettings4 = self.sa.query(RhodeCodeUi)\
178 hgsettings4 = self.sa.query(RhodeCodeUi)\
178 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
179 .filter(RhodeCodeUi.ui_key == 'changegroup.repo_size').one()
179 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
180 hgsettings4.ui_active = bool(form_result['hooks_changegroup_repo_size'])
180
181
181 hgsettings5 = self.sa.query(RhodeCodeUi)\
182 hgsettings5 = self.sa.query(RhodeCodeUi)\
182 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
183 .filter(RhodeCodeUi.ui_key == 'pretxnchangegroup.push_logger').one()
183 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
184 hgsettings5.ui_active = bool(form_result['hooks_pretxnchangegroup_push_logger'])
184
185
185 hgsettings6 = self.sa.query(RhodeCodeUi)\
186 hgsettings6 = self.sa.query(RhodeCodeUi)\
186 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
187 .filter(RhodeCodeUi.ui_key == 'preoutgoing.pull_logger').one()
187 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
188 hgsettings6.ui_active = bool(form_result['hooks_preoutgoing_pull_logger'])
188
189
189
190
190 self.sa.add(hgsettings1)
191 self.sa.add(hgsettings1)
191 self.sa.add(hgsettings2)
192 self.sa.add(hgsettings2)
192 self.sa.add(hgsettings3)
193 self.sa.add(hgsettings3)
193 self.sa.add(hgsettings4)
194 self.sa.add(hgsettings4)
194 self.sa.add(hgsettings5)
195 self.sa.add(hgsettings5)
195 self.sa.add(hgsettings6)
196 self.sa.add(hgsettings6)
196 self.sa.commit()
197 self.sa.commit()
197
198
198 h.flash(_('Updated mercurial settings'),
199 h.flash(_('Updated mercurial settings'),
199 category='success')
200 category='success')
200
201
201 except:
202 except:
202 log.error(traceback.format_exc())
203 log.error(traceback.format_exc())
203 h.flash(_('error occurred during updating application settings'),
204 h.flash(_('error occurred during updating application settings'),
204 category='error')
205 category='error')
205
206
206 self.sa.rollback()
207 self.sa.rollback()
207
208
208
209
209 except formencode.Invalid, errors:
210 except formencode.Invalid, errors:
210 return htmlfill.render(
211 return htmlfill.render(
211 render('admin/settings/settings.html'),
212 render('admin/settings/settings.html'),
212 defaults=errors.value,
213 defaults=errors.value,
213 errors=errors.error_dict or {},
214 errors=errors.error_dict or {},
214 prefix_error=False,
215 prefix_error=False,
215 encoding="UTF-8")
216 encoding="UTF-8")
216
217
217
218
218
219
219 return redirect(url('admin_settings'))
220 return redirect(url('admin_settings'))
220
221
221 @HasPermissionAllDecorator('hg.admin')
222 @HasPermissionAllDecorator('hg.admin')
222 def delete(self, setting_id):
223 def delete(self, setting_id):
223 """DELETE /admin/settings/setting_id: Delete an existing item"""
224 """DELETE /admin/settings/setting_id: Delete an existing item"""
224 # Forms posted to this method should contain a hidden field:
225 # Forms posted to this method should contain a hidden field:
225 # <input type="hidden" name="_method" value="DELETE" />
226 # <input type="hidden" name="_method" value="DELETE" />
226 # Or using helpers:
227 # Or using helpers:
227 # h.form(url('admin_setting', setting_id=ID),
228 # h.form(url('admin_setting', setting_id=ID),
228 # method='delete')
229 # method='delete')
229 # url('admin_setting', setting_id=ID)
230 # url('admin_setting', setting_id=ID)
230
231
231 @HasPermissionAllDecorator('hg.admin')
232 @HasPermissionAllDecorator('hg.admin')
232 def show(self, setting_id, format='html'):
233 def show(self, setting_id, format='html'):
233 """GET /admin/settings/setting_id: Show a specific item"""
234 """GET /admin/settings/setting_id: Show a specific item"""
234 # url('admin_setting', setting_id=ID)
235 # url('admin_setting', setting_id=ID)
235
236
236 @HasPermissionAllDecorator('hg.admin')
237 @HasPermissionAllDecorator('hg.admin')
237 def edit(self, setting_id, format='html'):
238 def edit(self, setting_id, format='html'):
238 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
239 """GET /admin/settings/setting_id/edit: Form to edit an existing item"""
239 # url('admin_edit_setting', setting_id=ID)
240 # url('admin_edit_setting', setting_id=ID)
240
241
241 @NotAnonymous()
242 @NotAnonymous()
242 def my_account(self):
243 def my_account(self):
243 """
244 """
244 GET /_admin/my_account Displays info about my account
245 GET /_admin/my_account Displays info about my account
245 """
246 """
246 # url('admin_settings_my_account')
247 # url('admin_settings_my_account')
247
248
248 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
249 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
249 all_repos = self.sa.query(Repository)\
250 all_repos = self.sa.query(Repository)\
250 .filter(Repository.user_id == c.user.user_id)\
251 .filter(Repository.user_id == c.user.user_id)\
251 .order_by(func.lower(Repository.repo_name))\
252 .order_by(func.lower(Repository.repo_name))\
252 .all()
253 .all()
253
254
254 c.user_repos = ScmModel().get_repos(all_repos)
255 c.user_repos = ScmModel().get_repos(all_repos)
255
256
256 if c.user.username == 'default':
257 if c.user.username == 'default':
257 h.flash(_("You can't edit this user since it's"
258 h.flash(_("You can't edit this user since it's"
258 " crucial for entire application"), category='warning')
259 " crucial for entire application"), category='warning')
259 return redirect(url('users'))
260 return redirect(url('users'))
260
261
261 defaults = c.user.get_dict()
262 defaults = c.user.get_dict()
262 return htmlfill.render(
263 return htmlfill.render(
263 render('admin/users/user_edit_my_account.html'),
264 render('admin/users/user_edit_my_account.html'),
264 defaults=defaults,
265 defaults=defaults,
265 encoding="UTF-8",
266 encoding="UTF-8",
266 force_defaults=False
267 force_defaults=False
267 )
268 )
268
269
269 def my_account_update(self):
270 def my_account_update(self):
270 """PUT /_admin/my_account_update: Update an existing item"""
271 """PUT /_admin/my_account_update: Update an existing item"""
271 # Forms posted to this method should contain a hidden field:
272 # Forms posted to this method should contain a hidden field:
272 # <input type="hidden" name="_method" value="PUT" />
273 # <input type="hidden" name="_method" value="PUT" />
273 # Or using helpers:
274 # Or using helpers:
274 # h.form(url('admin_settings_my_account_update'),
275 # h.form(url('admin_settings_my_account_update'),
275 # method='put')
276 # method='put')
276 # url('admin_settings_my_account_update', id=ID)
277 # url('admin_settings_my_account_update', id=ID)
277 user_model = UserModel()
278 user_model = UserModel()
278 uid = c.rhodecode_user.user_id
279 uid = c.rhodecode_user.user_id
279 _form = UserForm(edit=True, old_data={'user_id':uid,
280 _form = UserForm(edit=True, old_data={'user_id':uid,
280 'email':c.rhodecode_user.email})()
281 'email':c.rhodecode_user.email})()
281 form_result = {}
282 form_result = {}
282 try:
283 try:
283 form_result = _form.to_python(dict(request.POST))
284 form_result = _form.to_python(dict(request.POST))
284 user_model.update_my_account(uid, form_result)
285 user_model.update_my_account(uid, form_result)
285 h.flash(_('Your account was updated successfully'),
286 h.flash(_('Your account was updated successfully'),
286 category='success')
287 category='success')
287
288
288 except formencode.Invalid, errors:
289 except formencode.Invalid, errors:
289 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
290 c.user = user_model.get(c.rhodecode_user.user_id, cache=False)
290 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
291 c.user = UserModel().get(c.rhodecode_user.user_id, cache=False)
291 all_repos = self.sa.query(Repository)\
292 all_repos = self.sa.query(Repository)\
292 .filter(Repository.user_id == c.user.user_id)\
293 .filter(Repository.user_id == c.user.user_id)\
293 .order_by(func.lower(Repository.repo_name))\
294 .order_by(func.lower(Repository.repo_name))\
294 .all()
295 .all()
295 c.user_repos = ScmModel().get_repos(all_repos)
296 c.user_repos = ScmModel().get_repos(all_repos)
296
297
297 return htmlfill.render(
298 return htmlfill.render(
298 render('admin/users/user_edit_my_account.html'),
299 render('admin/users/user_edit_my_account.html'),
299 defaults=errors.value,
300 defaults=errors.value,
300 errors=errors.error_dict or {},
301 errors=errors.error_dict or {},
301 prefix_error=False,
302 prefix_error=False,
302 encoding="UTF-8")
303 encoding="UTF-8")
303 except Exception:
304 except Exception:
304 log.error(traceback.format_exc())
305 log.error(traceback.format_exc())
305 h.flash(_('error occurred during update of user %s') \
306 h.flash(_('error occurred during update of user %s') \
306 % form_result.get('username'), category='error')
307 % form_result.get('username'), category='error')
307
308
308 return redirect(url('my_account'))
309 return redirect(url('my_account'))
309
310
310 @NotAnonymous()
311 @NotAnonymous()
311 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
312 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
312 def create_repository(self):
313 def create_repository(self):
313 """GET /_admin/create_repository: Form to create a new item"""
314 """GET /_admin/create_repository: Form to create a new item"""
314 new_repo = request.GET.get('repo', '')
315 new_repo = request.GET.get('repo', '')
315 c.new_repo = h.repo_name_slug(new_repo)
316 c.new_repo = h.repo_name_slug(new_repo)
316
317
317 return render('admin/repos/repo_add_create_repository.html')
318 return render('admin/repos/repo_add_create_repository.html')
318
319
319 def get_hg_ui_settings(self):
320 def get_hg_ui_settings(self):
320 ret = self.sa.query(RhodeCodeUi).all()
321 ret = self.sa.query(RhodeCodeUi).all()
321
322
322 if not ret:
323 if not ret:
323 raise Exception('Could not get application ui settings !')
324 raise Exception('Could not get application ui settings !')
324 settings = {}
325 settings = {}
325 for each in ret:
326 for each in ret:
326 k = each.ui_key
327 k = each.ui_key
327 v = each.ui_value
328 v = each.ui_value
328 if k == '/':
329 if k == '/':
329 k = 'root_path'
330 k = 'root_path'
330
331
331 if k.find('.') != -1:
332 if k.find('.') != -1:
332 k = k.replace('.', '_')
333 k = k.replace('.', '_')
333
334
334 if each.ui_section == 'hooks':
335 if each.ui_section == 'hooks':
335 v = each.ui_active
336 v = each.ui_active
336
337
337 settings[each.ui_section + '_' + k] = v
338 settings[each.ui_section + '_' + k] = v
338
339
339 return settings
340 return settings
@@ -1,172 +1,172 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-2010 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
33 from pylons import request, 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.exceptions import *
37 from rhodecode.lib.exceptions import *
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.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41
41
42 from rhodecode.model.db import User
42 from rhodecode.model.db import User
43 from rhodecode.model.forms import UserForm
43 from rhodecode.model.forms import UserForm
44 from rhodecode.model.user import UserModel
44 from rhodecode.model.user import UserModel
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48 class UsersController(BaseController):
48 class UsersController(BaseController):
49 """REST Controller styled on the Atom Publishing Protocol"""
49 """REST Controller styled on the Atom Publishing Protocol"""
50 # To properly map this controller, ensure your config/routing.py
50 # To properly map this controller, ensure your config/routing.py
51 # file has a resource setup:
51 # file has a resource setup:
52 # map.resource('user', 'users')
52 # map.resource('user', 'users')
53
53
54 @LoginRequired()
54 @LoginRequired()
55 @HasPermissionAllDecorator('hg.admin')
55 @HasPermissionAllDecorator('hg.admin')
56 def __before__(self):
56 def __before__(self):
57 c.admin_user = session.get('admin_user')
57 c.admin_user = session.get('admin_user')
58 c.admin_username = session.get('admin_username')
58 c.admin_username = session.get('admin_username')
59 super(UsersController, self).__before__()
59 super(UsersController, self).__before__()
60
60
61
61
62 def index(self, format='html'):
62 def index(self, format='html'):
63 """GET /users: All items in the collection"""
63 """GET /users: All items in the collection"""
64 # url('users')
64 # url('users')
65
65
66 c.users_list = self.sa.query(User).all()
66 c.users_list = self.sa.query(User).all()
67 return render('admin/users/users.html')
67 return render('admin/users/users.html')
68
68
69 def create(self):
69 def create(self):
70 """POST /users: Create a new item"""
70 """POST /users: Create a new item"""
71 # url('users')
71 # url('users')
72
72
73 user_model = UserModel()
73 user_model = UserModel()
74 login_form = UserForm()()
74 login_form = UserForm()()
75 try:
75 try:
76 form_result = login_form.to_python(dict(request.POST))
76 form_result = login_form.to_python(dict(request.POST))
77 user_model.create(form_result)
77 user_model.create(form_result)
78 h.flash(_('created user %s') % form_result['username'],
78 h.flash(_('created user %s') % form_result['username'],
79 category='success')
79 category='success')
80 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
80 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
81 except formencode.Invalid, errors:
81 except formencode.Invalid, errors:
82 return htmlfill.render(
82 return htmlfill.render(
83 render('admin/users/user_add.html'),
83 render('admin/users/user_add.html'),
84 defaults=errors.value,
84 defaults=errors.value,
85 errors=errors.error_dict or {},
85 errors=errors.error_dict or {},
86 prefix_error=False,
86 prefix_error=False,
87 encoding="UTF-8")
87 encoding="UTF-8")
88 except Exception:
88 except Exception:
89 log.error(traceback.format_exc())
89 log.error(traceback.format_exc())
90 h.flash(_('error occured during creation of user %s') \
90 h.flash(_('error occurred during creation of user %s') \
91 % request.POST.get('username'), category='error')
91 % request.POST.get('username'), category='error')
92 return redirect(url('users'))
92 return redirect(url('users'))
93
93
94 def new(self, format='html'):
94 def new(self, format='html'):
95 """GET /users/new: Form to create a new item"""
95 """GET /users/new: Form to create a new item"""
96 # url('new_user')
96 # url('new_user')
97 return render('admin/users/user_add.html')
97 return render('admin/users/user_add.html')
98
98
99 def update(self, id):
99 def update(self, id):
100 """PUT /users/id: Update an existing item"""
100 """PUT /users/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('user', id=ID),
104 # h.form(url('user', id=ID),
105 # method='put')
105 # method='put')
106 # url('user', id=ID)
106 # url('user', id=ID)
107 user_model = UserModel()
107 user_model = UserModel()
108 c.user = user_model.get(id)
108 c.user = user_model.get(id)
109
109
110 _form = UserForm(edit=True, old_data={'user_id':id,
110 _form = UserForm(edit=True, old_data={'user_id':id,
111 'email':c.user.email})()
111 'email':c.user.email})()
112 form_result = {}
112 form_result = {}
113 try:
113 try:
114 form_result = _form.to_python(dict(request.POST))
114 form_result = _form.to_python(dict(request.POST))
115 user_model.update(id, form_result)
115 user_model.update(id, form_result)
116 h.flash(_('User updated succesfully'), category='success')
116 h.flash(_('User updated succesfully'), category='success')
117
117
118 except formencode.Invalid, errors:
118 except formencode.Invalid, errors:
119 return htmlfill.render(
119 return htmlfill.render(
120 render('admin/users/user_edit.html'),
120 render('admin/users/user_edit.html'),
121 defaults=errors.value,
121 defaults=errors.value,
122 errors=errors.error_dict or {},
122 errors=errors.error_dict or {},
123 prefix_error=False,
123 prefix_error=False,
124 encoding="UTF-8")
124 encoding="UTF-8")
125 except Exception:
125 except Exception:
126 log.error(traceback.format_exc())
126 log.error(traceback.format_exc())
127 h.flash(_('error occurred during update of user %s') \
127 h.flash(_('error occurred during update of user %s') \
128 % form_result.get('username'), category='error')
128 % form_result.get('username'), category='error')
129
129
130 return redirect(url('users'))
130 return redirect(url('users'))
131
131
132 def delete(self, id):
132 def delete(self, id):
133 """DELETE /users/id: Delete an existing item"""
133 """DELETE /users/id: Delete an existing item"""
134 # Forms posted to this method should contain a hidden field:
134 # Forms posted to this method should contain a hidden field:
135 # <input type="hidden" name="_method" value="DELETE" />
135 # <input type="hidden" name="_method" value="DELETE" />
136 # Or using helpers:
136 # Or using helpers:
137 # h.form(url('user', id=ID),
137 # h.form(url('user', id=ID),
138 # method='delete')
138 # method='delete')
139 # url('user', id=ID)
139 # url('user', id=ID)
140 user_model = UserModel()
140 user_model = UserModel()
141 try:
141 try:
142 user_model.delete(id)
142 user_model.delete(id)
143 h.flash(_('sucessfully deleted user'), category='success')
143 h.flash(_('sucessfully deleted user'), category='success')
144 except (UserOwnsReposException, DefaultUserException), e:
144 except (UserOwnsReposException, DefaultUserException), e:
145 h.flash(str(e), category='warning')
145 h.flash(str(e), category='warning')
146 except Exception:
146 except Exception:
147 h.flash(_('An error occured during deletion of user'),
147 h.flash(_('An error occurred during deletion of user'),
148 category='error')
148 category='error')
149 return redirect(url('users'))
149 return redirect(url('users'))
150
150
151 def show(self, id, format='html'):
151 def show(self, id, format='html'):
152 """GET /users/id: Show a specific item"""
152 """GET /users/id: Show a specific item"""
153 # url('user', id=ID)
153 # url('user', id=ID)
154
154
155
155
156 def edit(self, id, format='html'):
156 def edit(self, id, format='html'):
157 """GET /users/id/edit: Form to edit an existing item"""
157 """GET /users/id/edit: Form to edit an existing item"""
158 # url('edit_user', id=ID)
158 # url('edit_user', id=ID)
159 c.user = self.sa.query(User).get(id)
159 c.user = self.sa.query(User).get(id)
160 if not c.user:
160 if not c.user:
161 return redirect(url('users'))
161 return redirect(url('users'))
162 if c.user.username == 'default':
162 if c.user.username == 'default':
163 h.flash(_("You can't edit this user"), category='warning')
163 h.flash(_("You can't edit this user"), category='warning')
164 return redirect(url('users'))
164 return redirect(url('users'))
165
165
166 defaults = c.user.get_dict()
166 defaults = c.user.get_dict()
167 return htmlfill.render(
167 return htmlfill.render(
168 render('admin/users/user_edit.html'),
168 render('admin/users/user_edit.html'),
169 defaults=defaults,
169 defaults=defaults,
170 encoding="UTF-8",
170 encoding="UTF-8",
171 force_defaults=False
171 force_defaults=False
172 )
172 )
@@ -1,47 +1,54 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # branches controller for pylons
3 rhodecode.controllers.branches
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 branches controller for rhodecode
7
8 :created_on: Apr 21, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
9 # 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.
10 #
17 #
11 # 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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
21 # GNU General Public License for more details.
15 #
22 #
16 # 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
17 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
20 """
27
21 Created on April 21, 2010
28 import logging
22 branches controller for pylons
29
23 @author: marcink
24 """
25 from pylons import tmpl_context as c
30 from pylons import tmpl_context as c
31
26 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController, render
28 from rhodecode.lib.utils import OrderedDict
34 from rhodecode.lib.utils import OrderedDict
29 from rhodecode.model.scm import ScmModel
35 from rhodecode.model.scm import ScmModel
30 import logging
36
31 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
32
38
33 class BranchesController(BaseController):
39 class BranchesController(BaseController):
34
40
35 @LoginRequired()
41 @LoginRequired()
36 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', 'repository.admin')
42 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 'repository.admin')
37 def __before__(self):
44 def __before__(self):
38 super(BranchesController, self).__before__()
45 super(BranchesController, self).__before__()
39
46
40 def index(self):
47 def index(self):
41 hg_model = ScmModel()
48 hg_model = ScmModel()
42 c.repo_info = hg_model.get_repo(c.repo_name)
49 c.repo_info = hg_model.get_repo(c.repo_name)
43 c.repo_branches = OrderedDict()
50 c.repo_branches = OrderedDict()
44 for name, hash_ in c.repo_info.branches.items():
51 for name, hash_ in c.repo_info.branches.items():
45 c.repo_branches[name] = c.repo_info.get_changeset(hash_)
52 c.repo_branches[name] = c.repo_info.get_changeset(hash_)
46
53
47 return render('branches/branches.html')
54 return render('branches/branches.html')
@@ -1,99 +1,106 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # changelog controller for pylons
3 rhodecode.controllers.changelog
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 changelog controller for rhodecode
7
8 :created_on: Apr 21, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
9 # 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.
10 #
17 #
11 # 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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
21 # GNU General Public License for more details.
15 #
22 #
16 # 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
17 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
20 """
27
21 Created on April 21, 2010
28 import logging
22 changelog controller for pylons
23 @author: marcink
24 """
25
29
26 try:
30 try:
27 import json
31 import json
28 except ImportError:
32 except ImportError:
29 #python 2.5 compatibility
33 #python 2.5 compatibility
30 import simplejson as json
34 import simplejson as json
35
31 from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev
36 from mercurial.graphmod import colored, CHANGESET, revisions as graph_rev
32 from pylons import request, session, tmpl_context as c
37 from pylons import request, session, tmpl_context as c
38
33 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
35 from rhodecode.model.scm import ScmModel
41 from rhodecode.model.scm import ScmModel
42
36 from webhelpers.paginate import Page
43 from webhelpers.paginate import Page
37 import logging
44
38 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
39
46
40 class ChangelogController(BaseController):
47 class ChangelogController(BaseController):
41
48
42 @LoginRequired()
49 @LoginRequired()
43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
50 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 'repository.admin')
51 'repository.admin')
45 def __before__(self):
52 def __before__(self):
46 super(ChangelogController, self).__before__()
53 super(ChangelogController, self).__before__()
47
54
48 def index(self):
55 def index(self):
49 limit = 100
56 limit = 100
50 default = 20
57 default = 20
51 if request.params.get('size'):
58 if request.params.get('size'):
52 try:
59 try:
53 int_size = int(request.params.get('size'))
60 int_size = int(request.params.get('size'))
54 except ValueError:
61 except ValueError:
55 int_size = default
62 int_size = default
56 int_size = int_size if int_size <= limit else limit
63 int_size = int_size if int_size <= limit else limit
57 c.size = int_size
64 c.size = int_size
58 session['changelog_size'] = c.size
65 session['changelog_size'] = c.size
59 session.save()
66 session.save()
60 else:
67 else:
61 c.size = int(session.get('changelog_size', default))
68 c.size = int(session.get('changelog_size', default))
62
69
63 changesets = ScmModel().get_repo(c.repo_name)
70 changesets = ScmModel().get_repo(c.repo_name)
64
71
65 p = int(request.params.get('page', 1))
72 p = int(request.params.get('page', 1))
66 c.total_cs = len(changesets)
73 c.total_cs = len(changesets)
67 c.pagination = Page(changesets, page=p, item_count=c.total_cs,
74 c.pagination = Page(changesets, page=p, item_count=c.total_cs,
68 items_per_page=c.size)
75 items_per_page=c.size)
69
76
70 self._graph(changesets, c.size, p)
77 self._graph(changesets, c.size, p)
71
78
72 return render('changelog/changelog.html')
79 return render('changelog/changelog.html')
73
80
74
81
75 def _graph(self, repo, size, p):
82 def _graph(self, repo, size, p):
76 revcount = size
83 revcount = size
77 if not repo.revisions or repo.alias == 'git':
84 if not repo.revisions or repo.alias == 'git':
78 c.jsdata = json.dumps([])
85 c.jsdata = json.dumps([])
79 return
86 return
80
87
81 max_rev = repo.revisions[-1]
88 max_rev = repo.revisions[-1]
82
89
83 offset = 1 if p == 1 else ((p - 1) * revcount + 1)
90 offset = 1 if p == 1 else ((p - 1) * revcount + 1)
84
91
85 rev_start = repo.revisions[(-1 * offset)]
92 rev_start = repo.revisions[(-1 * offset)]
86
93
87 revcount = min(max_rev, revcount)
94 revcount = min(max_rev, revcount)
88 rev_end = max(0, rev_start - revcount)
95 rev_end = max(0, rev_start - revcount)
89 dag = graph_rev(repo.repo, rev_start, rev_end)
96 dag = graph_rev(repo.repo, rev_start, rev_end)
90
97
91 c.dag = tree = list(colored(dag))
98 c.dag = tree = list(colored(dag))
92 data = []
99 data = []
93 for (id, type, ctx, vtx, edges) in tree:
100 for (id, type, ctx, vtx, edges) in tree:
94 if type != CHANGESET:
101 if type != CHANGESET:
95 continue
102 continue
96 data.append(('', vtx, edges))
103 data.append(('', vtx, edges))
97
104
98 c.jsdata = json.dumps(data)
105 c.jsdata = json.dumps(data)
99
106
@@ -1,110 +1,110 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.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-2010 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,80 +1,90 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # feed controller for pylons
3 rhodecode.controllers.feed
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Feed controller for rhodecode
7
8 :created_on: Apr 23, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
9 # 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.
10 #
17 #
11 # 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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
21 # GNU General Public License for more details.
15 #
22 #
16 # 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
17 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
20 """
27
21 Created on April 23, 2010
28 import logging
22 feed controller for pylons
29
23 @author: marcink
30 from pylons import url, response
24 """
31
25 from pylons import tmpl_context as c, url, response
32 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
26 from rhodecode.lib.base import BaseController, render
33 from rhodecode.lib.base import BaseController
27 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.scm import ScmModel
35
28 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
36 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
29 import logging
37
30 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
31
39
32 class FeedController(BaseController):
40 class FeedController(BaseController):
33
41
34 #secure it or not ?
42 @LoginRequired()
43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
44 'repository.admin')
35 def __before__(self):
45 def __before__(self):
36 super(FeedController, self).__before__()
46 super(FeedController, self).__before__()
37 #common values for feeds
47 #common values for feeds
38 self.description = 'Changes on %s repository'
48 self.description = 'Changes on %s repository'
39 self.title = "%s feed"
49 self.title = "%s feed"
40 self.language = 'en-us'
50 self.language = 'en-us'
41 self.ttl = "5"
51 self.ttl = "5"
42 self.feed_nr = 10
52 self.feed_nr = 10
43
53
44 def atom(self, repo_name):
54 def atom(self, repo_name):
45 """Produce an atom-1.0 feed via feedgenerator module"""
55 """Produce an atom-1.0 feed via feedgenerator module"""
46 feed = Atom1Feed(title=self.title % repo_name,
56 feed = Atom1Feed(title=self.title % repo_name,
47 link=url('summary_home', repo_name=repo_name, qualified=True),
57 link=url('summary_home', repo_name=repo_name, qualified=True),
48 description=self.description % repo_name,
58 description=self.description % repo_name,
49 language=self.language,
59 language=self.language,
50 ttl=self.ttl)
60 ttl=self.ttl)
51
61
52 changesets = ScmModel().get_repo(repo_name)
62 changesets = ScmModel().get_repo(repo_name)
53
63
54 for cs in changesets[:self.feed_nr]:
64 for cs in changesets[:self.feed_nr]:
55 feed.add_item(title=cs.message,
65 feed.add_item(title=cs.message,
56 link=url('changeset_home', repo_name=repo_name,
66 link=url('changeset_home', repo_name=repo_name,
57 revision=cs.raw_id, qualified=True),
67 revision=cs.raw_id, qualified=True),
58 description=str(cs.date))
68 description=str(cs.date))
59
69
60 response.content_type = feed.mime_type
70 response.content_type = feed.mime_type
61 return feed.writeString('utf-8')
71 return feed.writeString('utf-8')
62
72
63
73
64 def rss(self, repo_name):
74 def rss(self, repo_name):
65 """Produce an rss2 feed via feedgenerator module"""
75 """Produce an rss2 feed via feedgenerator module"""
66 feed = Rss201rev2Feed(title=self.title % repo_name,
76 feed = Rss201rev2Feed(title=self.title % repo_name,
67 link=url('summary_home', repo_name=repo_name, qualified=True),
77 link=url('summary_home', repo_name=repo_name, qualified=True),
68 description=self.description % repo_name,
78 description=self.description % repo_name,
69 language=self.language,
79 language=self.language,
70 ttl=self.ttl)
80 ttl=self.ttl)
71
81
72 changesets = ScmModel().get_repo(repo_name)
82 changesets = ScmModel().get_repo(repo_name)
73 for cs in changesets[:self.feed_nr]:
83 for cs in changesets[:self.feed_nr]:
74 feed.add_item(title=cs.message,
84 feed.add_item(title=cs.message,
75 link=url('changeset_home', repo_name=repo_name,
85 link=url('changeset_home', repo_name=repo_name,
76 revision=cs.raw_id, qualified=True),
86 revision=cs.raw_id, qualified=True),
77 description=str(cs.date))
87 description=str(cs.date))
78
88
79 response.content_type = feed.mime_type
89 response.content_type = feed.mime_type
80 return feed.writeString('utf-8')
90 return feed.writeString('utf-8')
@@ -1,58 +1,66 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # hg controller for pylons
3 rhodecode.controllers.home
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 Home controller for Rhodecode
7
8 :created_on: Feb 18, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
9 # 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.
10 #
17 #
11 # 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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
21 # GNU General Public License for more details.
15 #
22 #
16 # 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
17 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
20 """
27
21 Created on February 18, 2010
28 import logging
22 hg controller for pylons
23 @author: marcink
24 """
25 from operator import itemgetter
29 from operator import itemgetter
30
26 from pylons import tmpl_context as c, request
31 from pylons import tmpl_context as c, request
32
27 from rhodecode.lib.auth import LoginRequired
33 from rhodecode.lib.auth import LoginRequired
28 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
29 from rhodecode.model.scm import ScmModel
35 from rhodecode.model.scm import ScmModel
30 import logging
36
31 log = logging.getLogger(__name__)
37 log = logging.getLogger(__name__)
32
38
33 class HomeController(BaseController):
39 class HomeController(BaseController):
34
40
35 @LoginRequired()
41 @LoginRequired()
36 def __before__(self):
42 def __before__(self):
37 super(HomeController, self).__before__()
43 super(HomeController, self).__before__()
38
44
39 def index(self):
45 def index(self):
40 sortables = ['name', 'description', 'last_change', 'tip', 'contact']
46 sortables = ['name', 'description', 'last_change', 'tip', 'contact']
41 current_sort = request.GET.get('sort', 'name')
47 current_sort = request.GET.get('sort', 'name')
42 current_sort_slug = current_sort.replace('-', '')
48 current_sort_slug = current_sort.replace('-', '')
43
49
44 if current_sort_slug not in sortables:
50 if current_sort_slug not in sortables:
45 c.sort_by = 'name'
51 c.sort_by = 'name'
46 current_sort_slug = c.sort_by
52 current_sort_slug = c.sort_by
47 else:
53 else:
48 c.sort_by = current_sort
54 c.sort_by = current_sort
49 c.sort_slug = current_sort_slug
55 c.sort_slug = current_sort_slug
50 cached_repo_list = ScmModel().get_repos()
56 cached_repo_list = ScmModel().get_repos()
51
57
52 sort_key = current_sort_slug + '_sort'
58 sort_key = current_sort_slug + '_sort'
53 if c.sort_by.startswith('-'):
59 if c.sort_by.startswith('-'):
54 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=True)
60 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
61 reverse=True)
55 else:
62 else:
56 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=False)
63 c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key),
64 reverse=False)
57
65
58 return render('/index.html')
66 return render('/index.html')
@@ -1,93 +1,97 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # journal controller for pylons
3 rhodecode.controllers.journal
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 Journal controller for pylons
7
8 :created_on: Nov 21, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
9 # 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.
10 #
17 #
11 # 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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
21 # GNU General Public License for more details.
15 #
22 #
16 # 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
17 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
20 """
27
21 Created on November 21, 2010
28 import logging
22 journal controller for pylons
29 from sqlalchemy import or_
23 @author: marcink
24 """
25
30
26 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
27 from pylons.controllers.util import abort, redirect
32
28 from rhodecode.lib.auth import LoginRequired, NotAnonymous
33 from rhodecode.lib.auth import LoginRequired, NotAnonymous
29 from rhodecode.lib.base import BaseController, render
34 from rhodecode.lib.base import BaseController, render
30 from rhodecode.lib.helpers import get_token
35 from rhodecode.lib.helpers import get_token
31 from rhodecode.model.db import UserLog, UserFollowing
36 from rhodecode.model.db import UserLog, UserFollowing
32 from rhodecode.model.scm import ScmModel
37 from rhodecode.model.scm import ScmModel
33 from sqlalchemy import or_
38
34 import logging
39 from paste.httpexceptions import HTTPInternalServerError
35 from paste.httpexceptions import HTTPInternalServerError, HTTPNotFound
36
40
37 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
38
42
39 class JournalController(BaseController):
43 class JournalController(BaseController):
40
44
41
45
42 @LoginRequired()
46 @LoginRequired()
43 @NotAnonymous()
47 @NotAnonymous()
44 def __before__(self):
48 def __before__(self):
45 super(JournalController, self).__before__()
49 super(JournalController, self).__before__()
46
50
47 def index(self):
51 def index(self):
48 # Return a rendered template
52 # Return a rendered template
49
53
50 c.following = self.sa.query(UserFollowing)\
54 c.following = self.sa.query(UserFollowing)\
51 .filter(UserFollowing.user_id == c.rhodecode_user.user_id).all()
55 .filter(UserFollowing.user_id == c.rhodecode_user.user_id).all()
52
56
53 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
54 if x.follows_repository is not None]
58 if x.follows_repository is not None]
55 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
56 if x.follows_user is not None]
60 if x.follows_user is not None]
57
61
58 c.journal = self.sa.query(UserLog)\
62 c.journal = self.sa.query(UserLog)\
59 .filter(or_(
63 .filter(or_(
60 UserLog.repository_id.in_(repo_ids),
64 UserLog.repository_id.in_(repo_ids),
61 UserLog.user_id.in_(user_ids),
65 UserLog.user_id.in_(user_ids),
62 ))\
66 ))\
63 .order_by(UserLog.action_date.desc())\
67 .order_by(UserLog.action_date.desc())\
64 .limit(20)\
68 .limit(20)\
65 .all()
69 .all()
66 return render('/journal.html')
70 return render('/journal.html')
67
71
68 def toggle_following(self):
72 def toggle_following(self):
69
73
70 if request.POST.get('auth_token') == get_token():
74 if request.POST.get('auth_token') == get_token():
71 scm_model = ScmModel()
75 scm_model = ScmModel()
72
76
73 user_id = request.POST.get('follows_user_id')
77 user_id = request.POST.get('follows_user_id')
74 if user_id:
78 if user_id:
75 try:
79 try:
76 scm_model.toggle_following_user(user_id,
80 scm_model.toggle_following_user(user_id,
77 c.rhodecode_user.user_id)
81 c.rhodecode_user.user_id)
78 return 'ok'
82 return 'ok'
79 except:
83 except:
80 raise HTTPInternalServerError()
84 raise HTTPInternalServerError()
81
85
82 repo_id = request.POST.get('follows_repo_id')
86 repo_id = request.POST.get('follows_repo_id')
83 if repo_id:
87 if repo_id:
84 try:
88 try:
85 scm_model.toggle_following_repo(repo_id,
89 scm_model.toggle_following_repo(repo_id,
86 c.rhodecode_user.user_id)
90 c.rhodecode_user.user_id)
87 return 'ok'
91 return 'ok'
88 except:
92 except:
89 raise HTTPInternalServerError()
93 raise HTTPInternalServerError()
90
94
91
95
92
96
93 raise HTTPInternalServerError()
97 raise HTTPInternalServerError()
@@ -1,146 +1,152 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # login controller for pylons
3 rhodecode.controllers.login
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 Login controller for rhodeocode
7
8 :created_on: Apr 22, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
9 # 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.
10 #
17 #
11 # 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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
21 # GNU General Public License for more details.
15 #
22 #
16 # 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
17 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
20
27
21 """
28 import logging
22 Created on April 22, 2010
29 import formencode
23 login controller for pylons
30
24 @author: marcink
25 """
26 from formencode import htmlfill
31 from formencode import htmlfill
32
33 from pylons.i18n.translation import _
34 from pylons.controllers.util import abort, redirect
27 from pylons import request, response, session, tmpl_context as c, url
35 from pylons import request, response, session, tmpl_context as c, url
28 from pylons.controllers.util import abort, redirect
36
37 import rhodecode.lib.helpers as h
29 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
30 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
31 import rhodecode.lib.helpers as h
32 from pylons.i18n.translation import _
33 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
40 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
34 from rhodecode.model.user import UserModel
41 from rhodecode.model.user import UserModel
35 import formencode
42
36 import logging
37
43
38 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
39
45
40 class LoginController(BaseController):
46 class LoginController(BaseController):
41
47
42 def __before__(self):
48 def __before__(self):
43 super(LoginController, self).__before__()
49 super(LoginController, self).__before__()
44
50
45 def index(self):
51 def index(self):
46 #redirect if already logged in
52 #redirect if already logged in
47 c.came_from = request.GET.get('came_from', None)
53 c.came_from = request.GET.get('came_from', None)
48
54
49 if c.rhodecode_user.is_authenticated \
55 if c.rhodecode_user.is_authenticated \
50 and c.rhodecode_user.username != 'default':
56 and c.rhodecode_user.username != 'default':
51
57
52 return redirect(url('home'))
58 return redirect(url('home'))
53
59
54 if request.POST:
60 if request.POST:
55 #import Login Form validator class
61 #import Login Form validator class
56 login_form = LoginForm()
62 login_form = LoginForm()
57 try:
63 try:
58 c.form_result = login_form.to_python(dict(request.POST))
64 c.form_result = login_form.to_python(dict(request.POST))
59 username = c.form_result['username']
65 username = c.form_result['username']
60 user = UserModel().get_by_username(username, case_insensitive=True)
66 user = UserModel().get_by_username(username, case_insensitive=True)
61 auth_user = AuthUser()
67 auth_user = AuthUser()
62 auth_user.username = user.username
68 auth_user.username = user.username
63 auth_user.is_authenticated = True
69 auth_user.is_authenticated = True
64 auth_user.is_admin = user.admin
70 auth_user.is_admin = user.admin
65 auth_user.user_id = user.user_id
71 auth_user.user_id = user.user_id
66 auth_user.name = user.name
72 auth_user.name = user.name
67 auth_user.lastname = user.lastname
73 auth_user.lastname = user.lastname
68 session['rhodecode_user'] = auth_user
74 session['rhodecode_user'] = auth_user
69 session.save()
75 session.save()
70 log.info('user %s is now authenticated', username)
76 log.info('user %s is now authenticated', username)
71
77
72 user.update_lastlogin()
78 user.update_lastlogin()
73
79
74 if c.came_from:
80 if c.came_from:
75 return redirect(c.came_from)
81 return redirect(c.came_from)
76 else:
82 else:
77 return redirect(url('home'))
83 return redirect(url('home'))
78
84
79 except formencode.Invalid, errors:
85 except formencode.Invalid, errors:
80 return htmlfill.render(
86 return htmlfill.render(
81 render('/login.html'),
87 render('/login.html'),
82 defaults=errors.value,
88 defaults=errors.value,
83 errors=errors.error_dict or {},
89 errors=errors.error_dict or {},
84 prefix_error=False,
90 prefix_error=False,
85 encoding="UTF-8")
91 encoding="UTF-8")
86
92
87 return render('/login.html')
93 return render('/login.html')
88
94
89 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
95 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
90 'hg.register.manual_activate')
96 'hg.register.manual_activate')
91 def register(self):
97 def register(self):
92 user_model = UserModel()
98 user_model = UserModel()
93 c.auto_active = False
99 c.auto_active = False
94 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:
95 if perm.permission.permission_name == 'hg.register.auto_activate':
101 if perm.permission.permission_name == 'hg.register.auto_activate':
96 c.auto_active = True
102 c.auto_active = True
97 break
103 break
98
104
99 if request.POST:
105 if request.POST:
100
106
101 register_form = RegisterForm()()
107 register_form = RegisterForm()()
102 try:
108 try:
103 form_result = register_form.to_python(dict(request.POST))
109 form_result = register_form.to_python(dict(request.POST))
104 form_result['active'] = c.auto_active
110 form_result['active'] = c.auto_active
105 user_model.create_registration(form_result)
111 user_model.create_registration(form_result)
106 h.flash(_('You have successfully registered into rhodecode'),
112 h.flash(_('You have successfully registered into rhodecode'),
107 category='success')
113 category='success')
108 return redirect(url('login_home'))
114 return redirect(url('login_home'))
109
115
110 except formencode.Invalid, errors:
116 except formencode.Invalid, errors:
111 return htmlfill.render(
117 return htmlfill.render(
112 render('/register.html'),
118 render('/register.html'),
113 defaults=errors.value,
119 defaults=errors.value,
114 errors=errors.error_dict or {},
120 errors=errors.error_dict or {},
115 prefix_error=False,
121 prefix_error=False,
116 encoding="UTF-8")
122 encoding="UTF-8")
117
123
118 return render('/register.html')
124 return render('/register.html')
119
125
120 def password_reset(self):
126 def password_reset(self):
121 user_model = UserModel()
127 user_model = UserModel()
122 if request.POST:
128 if request.POST:
123
129
124 password_reset_form = PasswordResetForm()()
130 password_reset_form = PasswordResetForm()()
125 try:
131 try:
126 form_result = password_reset_form.to_python(dict(request.POST))
132 form_result = password_reset_form.to_python(dict(request.POST))
127 user_model.reset_password(form_result)
133 user_model.reset_password(form_result)
128 h.flash(_('Your new password was sent'),
134 h.flash(_('Your new password was sent'),
129 category='success')
135 category='success')
130 return redirect(url('login_home'))
136 return redirect(url('login_home'))
131
137
132 except formencode.Invalid, errors:
138 except formencode.Invalid, errors:
133 return htmlfill.render(
139 return htmlfill.render(
134 render('/password_reset.html'),
140 render('/password_reset.html'),
135 defaults=errors.value,
141 defaults=errors.value,
136 errors=errors.error_dict or {},
142 errors=errors.error_dict or {},
137 prefix_error=False,
143 prefix_error=False,
138 encoding="UTF-8")
144 encoding="UTF-8")
139
145
140 return render('/password_reset.html')
146 return render('/password_reset.html')
141
147
142 def logout(self):
148 def logout(self):
143 session['rhodecode_user'] = AuthUser()
149 session['rhodecode_user'] = AuthUser()
144 session.save()
150 session.save()
145 log.info('Logging out and setting user as Empty')
151 log.info('Logging out and setting user as Empty')
146 redirect(url('home'))
152 redirect(url('home'))
@@ -1,113 +1,120 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # search controller for pylons
3 rhodecode.controllers.search
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 Search controller for rhodecode
7
8 :created_on: Aug 7, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
9 # 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.
10 #
17 #
11 # 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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
21 # GNU General Public License for more details.
15 #
22 #
16 # 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
17 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
20 """
27 import logging
21 Created on Aug 7, 2010
28 import traceback
22 search controller for pylons
29
23 @author: marcink
30 from pylons.i18n.translation import _
24 """
25 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
26 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33
27 from rhodecode.lib.auth import LoginRequired
34 from rhodecode.lib.auth import LoginRequired
28 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
29 from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
36 from rhodecode.lib.indexers import SCHEMA, IDX_NAME, ResultWrapper
37
30 from webhelpers.paginate import Page
38 from webhelpers.paginate import Page
31 from webhelpers.util import update_params
39 from webhelpers.util import update_params
32 from pylons.i18n.translation import _
40
33 from whoosh.index import open_dir, EmptyIndexError
41 from whoosh.index import open_dir, EmptyIndexError
34 from whoosh.qparser import QueryParser, QueryParserError
42 from whoosh.qparser import QueryParser, QueryParserError
35 from whoosh.query import Phrase
43 from whoosh.query import Phrase
36 import logging
37 import traceback
38
44
39 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
40
46
41 class SearchController(BaseController):
47 class SearchController(BaseController):
42
48
43 @LoginRequired()
49 @LoginRequired()
44 def __before__(self):
50 def __before__(self):
45 super(SearchController, self).__before__()
51 super(SearchController, self).__before__()
46
52
47 def index(self, search_repo=None):
53 def index(self, search_repo=None):
48 c.repo_name = search_repo
54 c.repo_name = search_repo
49 c.formated_results = []
55 c.formated_results = []
50 c.runtime = ''
56 c.runtime = ''
51 c.cur_query = request.GET.get('q', None)
57 c.cur_query = request.GET.get('q', None)
52 c.cur_type = request.GET.get('type', 'source')
58 c.cur_type = request.GET.get('type', 'source')
53 c.cur_search = search_type = {'content':'content',
59 c.cur_search = search_type = {'content':'content',
54 'commit':'content',
60 'commit':'content',
55 'path':'path',
61 'path':'path',
56 'repository':'repository'}\
62 'repository':'repository'}\
57 .get(c.cur_type, 'content')
63 .get(c.cur_type, 'content')
58
64
59
65
60 if c.cur_query:
66 if c.cur_query:
61 cur_query = c.cur_query.lower()
67 cur_query = c.cur_query.lower()
62
68
63 if c.cur_query:
69 if c.cur_query:
64 p = int(request.params.get('page', 1))
70 p = int(request.params.get('page', 1))
65 highlight_items = set()
71 highlight_items = set()
66 try:
72 try:
67 idx = open_dir(config['app_conf']['index_dir']
73 idx = open_dir(config['app_conf']['index_dir']
68 , indexname=IDX_NAME)
74 , indexname=IDX_NAME)
69 searcher = idx.searcher()
75 searcher = idx.searcher()
70
76
71 qp = QueryParser(search_type, schema=SCHEMA)
77 qp = QueryParser(search_type, schema=SCHEMA)
72 if c.repo_name:
78 if c.repo_name:
73 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
79 cur_query = u'repository:%s %s' % (c.repo_name, cur_query)
74 try:
80 try:
75 query = qp.parse(unicode(cur_query))
81 query = qp.parse(unicode(cur_query))
76
82
77 if isinstance(query, Phrase):
83 if isinstance(query, Phrase):
78 highlight_items.update(query.words)
84 highlight_items.update(query.words)
79 else:
85 else:
80 for i in query.all_terms():
86 for i in query.all_terms():
81 if i[0] == 'content':
87 if i[0] == 'content':
82 highlight_items.add(i[1])
88 highlight_items.add(i[1])
83
89
84 matcher = query.matcher(searcher)
90 matcher = query.matcher(searcher)
85
91
86 log.debug(query)
92 log.debug(query)
87 log.debug(highlight_items)
93 log.debug(highlight_items)
88 results = searcher.search(query)
94 results = searcher.search(query)
89 res_ln = len(results)
95 res_ln = len(results)
90 c.runtime = '%s results (%.3f seconds)' \
96 c.runtime = '%s results (%.3f seconds)' \
91 % (res_ln, results.runtime)
97 % (res_ln, results.runtime)
92
98
93 def url_generator(**kw):
99 def url_generator(**kw):
94 return update_params("?q=%s&type=%s" \
100 return update_params("?q=%s&type=%s" \
95 % (c.cur_query, c.cur_search), **kw)
101 % (c.cur_query, c.cur_search), **kw)
96
102
97 c.formated_results = Page(
103 c.formated_results = Page(
98 ResultWrapper(search_type, searcher, matcher,
104 ResultWrapper(search_type, searcher, matcher,
99 highlight_items),
105 highlight_items),
100 page=p, item_count=res_ln,
106 page=p, item_count=res_ln,
101 items_per_page=10, url=url_generator)
107 items_per_page=10, url=url_generator)
102
108
103
109
104 except QueryParserError:
110 except QueryParserError:
105 c.runtime = _('Invalid search query. Try quoting it.')
111 c.runtime = _('Invalid search query. Try quoting it.')
106 searcher.close()
112 searcher.close()
107 except (EmptyIndexError, IOError):
113 except (EmptyIndexError, IOError):
108 log.error(traceback.format_exc())
114 log.error(traceback.format_exc())
109 log.error('Empty Index data')
115 log.error('Empty Index data')
110 c.runtime = _('There is no index to search in. Please run whoosh indexer')
116 c.runtime = _('There is no index to search in. '
117 'Please run whoosh indexer')
111
118
112 # Return a rendered template
119 # Return a rendered template
113 return render('/search/search.html')
120 return render('/search/search.html')
@@ -1,178 +1,184 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # settings controller for pylons
3 rhodecode.controllers.settings
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 Settings controller for rhodecode
7
8 :created_on: Jun 30, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
9 # 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.
10 #
17 #
11 # 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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
21 # GNU General Public License for more details.
15 #
22 #
16 # 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
17 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
20 """
27
21 Created on June 30, 2010
28 import logging
22 settings controller for pylons
29 import traceback
23 @author: marcink
30
24 """
31 import formencode
25 from formencode import htmlfill
32 from formencode import htmlfill
33
26 from pylons import tmpl_context as c, request, url
34 from pylons import tmpl_context as c, request, url
27 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
28 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
38 import rhodecode.lib.helpers as h
29 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator
30 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
31 from rhodecode.lib.utils import invalidate_cache, action_logger
41 from rhodecode.lib.utils import invalidate_cache, action_logger
32 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
42 from rhodecode.model.forms import RepoSettingsForm, RepoForkForm
33 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.repo import RepoModel
34 import formencode
35 import logging
36 import rhodecode.lib.helpers as h
37 import traceback
38
44
39 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
40
46
41 class SettingsController(BaseController):
47 class SettingsController(BaseController):
42
48
43 @LoginRequired()
49 @LoginRequired()
44 @HasRepoPermissionAllDecorator('repository.admin')
50 @HasRepoPermissionAllDecorator('repository.admin')
45 def __before__(self):
51 def __before__(self):
46 super(SettingsController, self).__before__()
52 super(SettingsController, self).__before__()
47
53
48 def index(self, repo_name):
54 def index(self, repo_name):
49 repo_model = RepoModel()
55 repo_model = RepoModel()
50 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)
51 if not repo:
57 if not repo:
52 h.flash(_('%s repository is not mapped to db perhaps'
58 h.flash(_('%s repository is not mapped to db perhaps'
53 ' it was created or renamed from the filesystem'
59 ' it was created or renamed from the file system'
54 ' please run the application again'
60 ' please run the application again'
55 ' in order to rescan repositories') % repo_name,
61 ' in order to rescan repositories') % repo_name,
56 category='error')
62 category='error')
57
63
58 return redirect(url('home'))
64 return redirect(url('home'))
59 defaults = c.repo_info.get_dict()
65 defaults = c.repo_info.get_dict()
60 defaults.update({'user':c.repo_info.user.username})
66 defaults.update({'user':c.repo_info.user.username})
61 c.users_array = repo_model.get_users_js()
67 c.users_array = repo_model.get_users_js()
62
68
63 for p in c.repo_info.repo_to_perm:
69 for p in c.repo_info.repo_to_perm:
64 defaults.update({'perm_%s' % p.user.username:
70 defaults.update({'perm_%s' % p.user.username:
65 p.permission.permission_name})
71 p.permission.permission_name})
66
72
67 return htmlfill.render(
73 return htmlfill.render(
68 render('settings/repo_settings.html'),
74 render('settings/repo_settings.html'),
69 defaults=defaults,
75 defaults=defaults,
70 encoding="UTF-8",
76 encoding="UTF-8",
71 force_defaults=False
77 force_defaults=False
72 )
78 )
73
79
74 def update(self, repo_name):
80 def update(self, repo_name):
75 repo_model = RepoModel()
81 repo_model = RepoModel()
76 changed_name = repo_name
82 changed_name = repo_name
77 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
83 _form = RepoSettingsForm(edit=True, old_data={'repo_name':repo_name})()
78 try:
84 try:
79 form_result = _form.to_python(dict(request.POST))
85 form_result = _form.to_python(dict(request.POST))
80 repo_model.update(repo_name, form_result)
86 repo_model.update(repo_name, form_result)
81 invalidate_cache('get_repo_cached_%s' % repo_name)
87 invalidate_cache('get_repo_cached_%s' % repo_name)
82 h.flash(_('Repository %s updated successfully' % repo_name),
88 h.flash(_('Repository %s updated successfully' % repo_name),
83 category='success')
89 category='success')
84 changed_name = form_result['repo_name']
90 changed_name = form_result['repo_name']
85 action_logger(self.rhodecode_user, 'user_updated_repo',
91 action_logger(self.rhodecode_user, 'user_updated_repo',
86 changed_name, '', self.sa)
92 changed_name, '', self.sa)
87 except formencode.Invalid, errors:
93 except formencode.Invalid, errors:
88 c.repo_info = repo_model.get_by_repo_name(repo_name)
94 c.repo_info = repo_model.get_by_repo_name(repo_name)
89 c.users_array = repo_model.get_users_js()
95 c.users_array = repo_model.get_users_js()
90 errors.value.update({'user':c.repo_info.user.username})
96 errors.value.update({'user':c.repo_info.user.username})
91 return htmlfill.render(
97 return htmlfill.render(
92 render('settings/repo_settings.html'),
98 render('settings/repo_settings.html'),
93 defaults=errors.value,
99 defaults=errors.value,
94 errors=errors.error_dict or {},
100 errors=errors.error_dict or {},
95 prefix_error=False,
101 prefix_error=False,
96 encoding="UTF-8")
102 encoding="UTF-8")
97 except Exception:
103 except Exception:
98 log.error(traceback.format_exc())
104 log.error(traceback.format_exc())
99 h.flash(_('error occurred during update of repository %s') \
105 h.flash(_('error occurred during update of repository %s') \
100 % repo_name, category='error')
106 % repo_name, category='error')
101
107
102 return redirect(url('repo_settings_home', repo_name=changed_name))
108 return redirect(url('repo_settings_home', repo_name=changed_name))
103
109
104
110
105
111
106 def delete(self, repo_name):
112 def delete(self, repo_name):
107 """DELETE /repos/repo_name: Delete an existing item"""
113 """DELETE /repos/repo_name: Delete an existing item"""
108 # Forms posted to this method should contain a hidden field:
114 # Forms posted to this method should contain a hidden field:
109 # <input type="hidden" name="_method" value="DELETE" />
115 # <input type="hidden" name="_method" value="DELETE" />
110 # Or using helpers:
116 # Or using helpers:
111 # h.form(url('repo_settings_delete', repo_name=ID),
117 # h.form(url('repo_settings_delete', repo_name=ID),
112 # method='delete')
118 # method='delete')
113 # url('repo_settings_delete', repo_name=ID)
119 # url('repo_settings_delete', repo_name=ID)
114
120
115 repo_model = RepoModel()
121 repo_model = RepoModel()
116 repo = repo_model.get_by_repo_name(repo_name)
122 repo = repo_model.get_by_repo_name(repo_name)
117 if not repo:
123 if not repo:
118 h.flash(_('%s repository is not mapped to db perhaps'
124 h.flash(_('%s repository is not mapped to db perhaps'
119 ' it was moved or renamed from the filesystem'
125 ' it was moved or renamed from the filesystem'
120 ' please run the application again'
126 ' please run the application again'
121 ' in order to rescan repositories') % repo_name,
127 ' in order to rescan repositories') % repo_name,
122 category='error')
128 category='error')
123
129
124 return redirect(url('home'))
130 return redirect(url('home'))
125 try:
131 try:
126 action_logger(self.rhodecode_user, 'user_deleted_repo',
132 action_logger(self.rhodecode_user, 'user_deleted_repo',
127 repo_name, '', self.sa)
133 repo_name, '', self.sa)
128 repo_model.delete(repo)
134 repo_model.delete(repo)
129 invalidate_cache('get_repo_cached_%s' % repo_name)
135 invalidate_cache('get_repo_cached_%s' % repo_name)
130 h.flash(_('deleted repository %s') % repo_name, category='success')
136 h.flash(_('deleted repository %s') % repo_name, category='success')
131 except Exception:
137 except Exception:
132 h.flash(_('An error occurred during deletion of %s') % repo_name,
138 h.flash(_('An error occurred during deletion of %s') % repo_name,
133 category='error')
139 category='error')
134
140
135 return redirect(url('home'))
141 return redirect(url('home'))
136
142
137 def fork(self, repo_name):
143 def fork(self, repo_name):
138 repo_model = RepoModel()
144 repo_model = RepoModel()
139 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)
140 if not repo:
146 if not repo:
141 h.flash(_('%s repository is not mapped to db perhaps'
147 h.flash(_('%s repository is not mapped to db perhaps'
142 ' it was created or renamed from the filesystem'
148 ' it was created or renamed from the file system'
143 ' please run the application again'
149 ' please run the application again'
144 ' in order to rescan repositories') % repo_name,
150 ' in order to rescan repositories') % repo_name,
145 category='error')
151 category='error')
146
152
147 return redirect(url('home'))
153 return redirect(url('home'))
148
154
149 return render('settings/repo_fork.html')
155 return render('settings/repo_fork.html')
150
156
151
157
152
158
153 def fork_create(self, repo_name):
159 def fork_create(self, repo_name):
154 repo_model = RepoModel()
160 repo_model = RepoModel()
155 c.repo_info = repo_model.get_by_repo_name(repo_name)
161 c.repo_info = repo_model.get_by_repo_name(repo_name)
156 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
162 _form = RepoForkForm(old_data={'repo_type':c.repo_info.repo_type})()
157 form_result = {}
163 form_result = {}
158 try:
164 try:
159 form_result = _form.to_python(dict(request.POST))
165 form_result = _form.to_python(dict(request.POST))
160 form_result.update({'repo_name':repo_name})
166 form_result.update({'repo_name':repo_name})
161 repo_model.create_fork(form_result, c.rhodecode_user)
167 repo_model.create_fork(form_result, c.rhodecode_user)
162 h.flash(_('forked %s repository as %s') \
168 h.flash(_('forked %s repository as %s') \
163 % (repo_name, form_result['fork_name']),
169 % (repo_name, form_result['fork_name']),
164 category='success')
170 category='success')
165 action_logger(self.rhodecode_user,
171 action_logger(self.rhodecode_user,
166 'user_forked_repo:%s' % form_result['fork_name'],
172 'user_forked_repo:%s' % form_result['fork_name'],
167 repo_name, '', self.sa)
173 repo_name, '', self.sa)
168 except formencode.Invalid, errors:
174 except formencode.Invalid, errors:
169 c.new_repo = errors.value['fork_name']
175 c.new_repo = errors.value['fork_name']
170 r = render('settings/repo_fork.html')
176 r = render('settings/repo_fork.html')
171
177
172 return htmlfill.render(
178 return htmlfill.render(
173 r,
179 r,
174 defaults=errors.value,
180 defaults=errors.value,
175 errors=errors.error_dict or {},
181 errors=errors.error_dict or {},
176 prefix_error=False,
182 prefix_error=False,
177 encoding="UTF-8")
183 encoding="UTF-8")
178 return redirect(url('home'))
184 return redirect(url('home'))
@@ -1,49 +1,56 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # shortlog controller for pylons
3 rhodecode.controllers.shortlog
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Shortlog controller for rhodecode
7
8 :created_on: Apr 18, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
9 # 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.
10 #
17 #
11 # 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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
21 # GNU General Public License for more details.
15 #
22 #
16 # 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
17 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
20 """
27
21 Created on April 18, 2010
28 import logging
22 shortlog controller for pylons
29
23 @author: marcink
24 """
25 from pylons import tmpl_context as c, request
30 from pylons import tmpl_context as c, request
31
32 from webhelpers.paginate import Page
33
26 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
34 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 from rhodecode.lib.base import BaseController, render
35 from rhodecode.lib.base import BaseController, render
28 from rhodecode.model.scm import ScmModel
36 from rhodecode.model.scm import ScmModel
29 from webhelpers.paginate import Page
37
30 import logging
31 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
32
39
33 class ShortlogController(BaseController):
40 class ShortlogController(BaseController):
34
41
35 @LoginRequired()
42 @LoginRequired()
36 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
43 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
37 'repository.admin')
44 'repository.admin')
38 def __before__(self):
45 def __before__(self):
39 super(ShortlogController, self).__before__()
46 super(ShortlogController, self).__before__()
40
47
41 def index(self):
48 def index(self):
42 p = int(request.params.get('page', 1))
49 p = int(request.params.get('page', 1))
43 repo = ScmModel().get_repo(c.repo_name)
50 repo = ScmModel().get_repo(c.repo_name)
44 c.repo_changesets = Page(repo, page=p, items_per_page=20)
51 c.repo_changesets = Page(repo, page=p, items_per_page=20)
45 c.shortlog_data = render('shortlog/shortlog_data.html')
52 c.shortlog_data = render('shortlog/shortlog_data.html')
46 if request.params.get('partial'):
53 if request.params.get('partial'):
47 return c.shortlog_data
54 return c.shortlog_data
48 r = render('shortlog/shortlog.html')
55 r = render('shortlog/shortlog.html')
49 return r
56 return r
@@ -1,144 +1,144 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 package.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-2010 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,47 +1,53 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # tags controller for pylons
3 rhodecode.controllers.tags
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 Tags controller for rhodecode
7
8 :created_on: Apr 21, 2010
9 :author: marcink
10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
12 """
6 # This program is free software; you can redistribute it and/or
13 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
9 # 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.
10 #
17 #
11 # 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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
21 # GNU General Public License for more details.
15 #
22 #
16 # 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
17 # along with this program; if not, write to the Free Software
24 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
20 """
27 import logging
21 Created on April 21, 2010
28
22 tags controller for pylons
23 @author: marcink
24 """
25 from pylons import tmpl_context as c
29 from pylons import tmpl_context as c
30
26 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
31 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 from rhodecode.lib.base import BaseController, render
32 from rhodecode.lib.base import BaseController, render
28 from rhodecode.lib.utils import OrderedDict
33 from rhodecode.lib.utils import OrderedDict
29 from rhodecode.model.scm import ScmModel
34 from rhodecode.model.scm import ScmModel
30 import logging
35
31 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
32
37
33 class TagsController(BaseController):
38 class TagsController(BaseController):
34
39
35 @LoginRequired()
40 @LoginRequired()
36 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write', 'repository.admin')
41 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
42 'repository.admin')
37 def __before__(self):
43 def __before__(self):
38 super(TagsController, self).__before__()
44 super(TagsController, self).__before__()
39
45
40 def index(self):
46 def index(self):
41 hg_model = ScmModel()
47 hg_model = ScmModel()
42 c.repo_info = hg_model.get_repo(c.repo_name)
48 c.repo_info = hg_model.get_repo(c.repo_name)
43 c.repo_tags = OrderedDict()
49 c.repo_tags = OrderedDict()
44 for name, hash_ in c.repo_info.tags.items():
50 for name, hash_ in c.repo_info.tags.items():
45 c.repo_tags[name] = c.repo_info.get_changeset(hash_)
51 c.repo_tags[name] = c.repo_info.get_changeset(hash_)
46
52
47 return render('tags/tags.html')
53 return render('tags/tags.html')
@@ -1,546 +1,546 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 import random
6 import random
7 import hashlib
7 import hashlib
8 from pygments.formatters import HtmlFormatter
8 from pygments.formatters import HtmlFormatter
9 from pygments import highlight as code_highlight
9 from pygments import highlight as code_highlight
10 from pylons import url, app_globals as g
10 from pylons import url, app_globals as g
11 from pylons.i18n.translation import _, ungettext
11 from pylons.i18n.translation import _, ungettext
12 from vcs.utils.annotate import annotate_highlight
12 from vcs.utils.annotate import annotate_highlight
13 from webhelpers.html import literal, HTML, escape
13 from webhelpers.html import literal, HTML, escape
14 from webhelpers.html.tools import *
14 from webhelpers.html.tools import *
15 from webhelpers.html.builder import make_tag
15 from webhelpers.html.builder import make_tag
16 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
16 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
17 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
17 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
18 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
18 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
19 password, textarea, title, ul, xml_declaration, radio
19 password, textarea, title, ul, xml_declaration, radio
20 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
20 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
21 mail_to, strip_links, strip_tags, tag_re
21 mail_to, strip_links, strip_tags, tag_re
22 from webhelpers.number import format_byte_size, format_bit_size
22 from webhelpers.number import format_byte_size, format_bit_size
23 from webhelpers.pylonslib import Flash as _Flash
23 from webhelpers.pylonslib import Flash as _Flash
24 from webhelpers.pylonslib.secure_form import secure_form
24 from webhelpers.pylonslib.secure_form import secure_form
25 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
25 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
26 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
26 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
27 replace_whitespace, urlify, truncate, wrap_paragraphs
27 replace_whitespace, urlify, truncate, wrap_paragraphs
28 from webhelpers.date import time_ago_in_words
28 from webhelpers.date import time_ago_in_words
29
29
30 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
30 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
31 convert_boolean_attrs, NotGiven
31 convert_boolean_attrs, NotGiven
32
32
33 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
33 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
34 _set_input_attrs(attrs, type, name, value)
34 _set_input_attrs(attrs, type, name, value)
35 _set_id_attr(attrs, id, name)
35 _set_id_attr(attrs, id, name)
36 convert_boolean_attrs(attrs, ["disabled"])
36 convert_boolean_attrs(attrs, ["disabled"])
37 return HTML.input(**attrs)
37 return HTML.input(**attrs)
38
38
39 reset = _reset
39 reset = _reset
40
40
41
41
42 def get_token():
42 def get_token():
43 """Return the current authentication token, creating one if one doesn't
43 """Return the current authentication token, creating one if one doesn't
44 already exist.
44 already exist.
45 """
45 """
46 token_key = "_authentication_token"
46 token_key = "_authentication_token"
47 from pylons import session
47 from pylons import session
48 if not token_key in session:
48 if not token_key in session:
49 try:
49 try:
50 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
50 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
51 except AttributeError: # Python < 2.4
51 except AttributeError: # Python < 2.4
52 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
52 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
53 session[token_key] = token
53 session[token_key] = token
54 if hasattr(session, 'save'):
54 if hasattr(session, 'save'):
55 session.save()
55 session.save()
56 return session[token_key]
56 return session[token_key]
57
57
58
58
59 #Custom helpers here :)
59 #Custom helpers here :)
60 class _Link(object):
60 class _Link(object):
61 '''
61 '''
62 Make a url based on label and url with help of url_for
62 Make a url based on label and url with help of url_for
63 :param label:name of link if not defined url is used
63 :param label:name of link if not defined url is used
64 :param url: the url for link
64 :param url: the url for link
65 '''
65 '''
66
66
67 def __call__(self, label='', *url_, **urlargs):
67 def __call__(self, label='', *url_, **urlargs):
68 if label is None or '':
68 if label is None or '':
69 label = url
69 label = url
70 link_fn = link_to(label, url(*url_, **urlargs))
70 link_fn = link_to(label, url(*url_, **urlargs))
71 return link_fn
71 return link_fn
72
72
73 link = _Link()
73 link = _Link()
74
74
75 class _GetError(object):
75 class _GetError(object):
76
76
77 def __call__(self, field_name, form_errors):
77 def __call__(self, field_name, form_errors):
78 tmpl = """<span class="error_msg">%s</span>"""
78 tmpl = """<span class="error_msg">%s</span>"""
79 if form_errors and form_errors.has_key(field_name):
79 if form_errors and form_errors.has_key(field_name):
80 return literal(tmpl % form_errors.get(field_name))
80 return literal(tmpl % form_errors.get(field_name))
81
81
82 get_error = _GetError()
82 get_error = _GetError()
83
83
84 def recursive_replace(str, replace=' '):
84 def recursive_replace(str, replace=' '):
85 """
85 """
86 Recursive replace of given sign to just one instance
86 Recursive replace of given sign to just one instance
87 :param str: given string
87 :param str: given string
88 :param replace:char to find and replace multiple instances
88 :param replace:char to find and replace multiple instances
89
89
90 Examples::
90 Examples::
91 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
91 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
92 'Mighty-Mighty-Bo-sstones'
92 'Mighty-Mighty-Bo-sstones'
93 """
93 """
94
94
95 if str.find(replace * 2) == -1:
95 if str.find(replace * 2) == -1:
96 return str
96 return str
97 else:
97 else:
98 str = str.replace(replace * 2, replace)
98 str = str.replace(replace * 2, replace)
99 return recursive_replace(str, replace)
99 return recursive_replace(str, replace)
100
100
101 class _ToolTip(object):
101 class _ToolTip(object):
102
102
103 def __call__(self, tooltip_title, trim_at=50):
103 def __call__(self, tooltip_title, trim_at=50):
104 """
104 """
105 Special function just to wrap our text into nice formatted autowrapped
105 Special function just to wrap our text into nice formatted autowrapped
106 text
106 text
107 :param tooltip_title:
107 :param tooltip_title:
108 """
108 """
109
109
110 return wrap_paragraphs(escape(tooltip_title), trim_at)\
110 return wrap_paragraphs(escape(tooltip_title), trim_at)\
111 .replace('\n', '<br/>')
111 .replace('\n', '<br/>')
112
112
113 def activate(self):
113 def activate(self):
114 """
114 """
115 Adds tooltip mechanism to the given Html all tooltips have to have
115 Adds tooltip mechanism to the given Html all tooltips have to have
116 set class tooltip and set attribute tooltip_title.
116 set class tooltip and set attribute tooltip_title.
117 Then a tooltip will be generated based on that
117 Then a tooltip will be generated based on that
118 All with yui js tooltip
118 All with yui js tooltip
119 """
119 """
120
120
121 js = '''
121 js = '''
122 YAHOO.util.Event.onDOMReady(function(){
122 YAHOO.util.Event.onDOMReady(function(){
123 function toolTipsId(){
123 function toolTipsId(){
124 var ids = [];
124 var ids = [];
125 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
125 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
126
126
127 for (var i = 0; i < tts.length; i++) {
127 for (var i = 0; i < tts.length; i++) {
128 //if element doesn't not have and id autgenerate one for tooltip
128 //if element doesn't not have and id autgenerate one for tooltip
129
129
130 if (!tts[i].id){
130 if (!tts[i].id){
131 tts[i].id='tt'+i*100;
131 tts[i].id='tt'+i*100;
132 }
132 }
133 ids.push(tts[i].id);
133 ids.push(tts[i].id);
134 }
134 }
135 return ids
135 return ids
136 };
136 };
137 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
137 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
138 context: toolTipsId(),
138 context: toolTipsId(),
139 monitorresize:false,
139 monitorresize:false,
140 xyoffset :[0,0],
140 xyoffset :[0,0],
141 autodismissdelay:300000,
141 autodismissdelay:300000,
142 hidedelay:5,
142 hidedelay:5,
143 showdelay:20,
143 showdelay:20,
144 });
144 });
145
145
146 //Mouse Over event disabled for new repositories since they don't
146 //Mouse Over event disabled for new repositories since they don't
147 //have last commit message
147 //have last commit message
148 myToolTips.contextMouseOverEvent.subscribe(
148 myToolTips.contextMouseOverEvent.subscribe(
149 function(type, args) {
149 function(type, args) {
150 var context = args[0];
150 var context = args[0];
151 var txt = context.getAttribute('tooltip_title');
151 var txt = context.getAttribute('tooltip_title');
152 if(txt){
152 if(txt){
153 return true;
153 return true;
154 }
154 }
155 else{
155 else{
156 return false;
156 return false;
157 }
157 }
158 });
158 });
159
159
160
160
161 // Set the text for the tooltip just before we display it. Lazy method
161 // Set the text for the tooltip just before we display it. Lazy method
162 myToolTips.contextTriggerEvent.subscribe(
162 myToolTips.contextTriggerEvent.subscribe(
163 function(type, args) {
163 function(type, args) {
164
164
165
165
166 var context = args[0];
166 var context = args[0];
167
167
168 var txt = context.getAttribute('tooltip_title');
168 var txt = context.getAttribute('tooltip_title');
169 this.cfg.setProperty("text", txt);
169 this.cfg.setProperty("text", txt);
170
170
171
171
172 // positioning of tooltip
172 // positioning of tooltip
173 var tt_w = this.element.clientWidth;
173 var tt_w = this.element.clientWidth;
174 var tt_h = this.element.clientHeight;
174 var tt_h = this.element.clientHeight;
175
175
176 var context_w = context.offsetWidth;
176 var context_w = context.offsetWidth;
177 var context_h = context.offsetHeight;
177 var context_h = context.offsetHeight;
178
178
179 var pos_x = YAHOO.util.Dom.getX(context);
179 var pos_x = YAHOO.util.Dom.getX(context);
180 var pos_y = YAHOO.util.Dom.getY(context);
180 var pos_y = YAHOO.util.Dom.getY(context);
181
181
182 var display_strategy = 'top';
182 var display_strategy = 'top';
183 var xy_pos = [0,0];
183 var xy_pos = [0,0];
184 switch (display_strategy){
184 switch (display_strategy){
185
185
186 case 'top':
186 case 'top':
187 var cur_x = (pos_x+context_w/2)-(tt_w/2);
187 var cur_x = (pos_x+context_w/2)-(tt_w/2);
188 var cur_y = pos_y-tt_h-4;
188 var cur_y = pos_y-tt_h-4;
189 xy_pos = [cur_x,cur_y];
189 xy_pos = [cur_x,cur_y];
190 break;
190 break;
191 case 'bottom':
191 case 'bottom':
192 var cur_x = (pos_x+context_w/2)-(tt_w/2);
192 var cur_x = (pos_x+context_w/2)-(tt_w/2);
193 var cur_y = pos_y+context_h+4;
193 var cur_y = pos_y+context_h+4;
194 xy_pos = [cur_x,cur_y];
194 xy_pos = [cur_x,cur_y];
195 break;
195 break;
196 case 'left':
196 case 'left':
197 var cur_x = (pos_x-tt_w-4);
197 var cur_x = (pos_x-tt_w-4);
198 var cur_y = pos_y-((tt_h/2)-context_h/2);
198 var cur_y = pos_y-((tt_h/2)-context_h/2);
199 xy_pos = [cur_x,cur_y];
199 xy_pos = [cur_x,cur_y];
200 break;
200 break;
201 case 'right':
201 case 'right':
202 var cur_x = (pos_x+context_w+4);
202 var cur_x = (pos_x+context_w+4);
203 var cur_y = pos_y-((tt_h/2)-context_h/2);
203 var cur_y = pos_y-((tt_h/2)-context_h/2);
204 xy_pos = [cur_x,cur_y];
204 xy_pos = [cur_x,cur_y];
205 break;
205 break;
206 default:
206 default:
207 var cur_x = (pos_x+context_w/2)-(tt_w/2);
207 var cur_x = (pos_x+context_w/2)-(tt_w/2);
208 var cur_y = pos_y-tt_h-4;
208 var cur_y = pos_y-tt_h-4;
209 xy_pos = [cur_x,cur_y];
209 xy_pos = [cur_x,cur_y];
210 break;
210 break;
211
211
212 }
212 }
213
213
214 this.cfg.setProperty("xy",xy_pos);
214 this.cfg.setProperty("xy",xy_pos);
215
215
216 });
216 });
217
217
218 //Mouse out
218 //Mouse out
219 myToolTips.contextMouseOutEvent.subscribe(
219 myToolTips.contextMouseOutEvent.subscribe(
220 function(type, args) {
220 function(type, args) {
221 var context = args[0];
221 var context = args[0];
222
222
223 });
223 });
224 });
224 });
225 '''
225 '''
226 return literal(js)
226 return literal(js)
227
227
228 tooltip = _ToolTip()
228 tooltip = _ToolTip()
229
229
230 class _FilesBreadCrumbs(object):
230 class _FilesBreadCrumbs(object):
231
231
232 def __call__(self, repo_name, rev, paths):
232 def __call__(self, repo_name, rev, paths):
233 url_l = [link_to(repo_name, url('files_home',
233 url_l = [link_to(repo_name, url('files_home',
234 repo_name=repo_name,
234 repo_name=repo_name,
235 revision=rev, f_path=''))]
235 revision=rev, f_path=''))]
236 paths_l = paths.split('/')
236 paths_l = paths.split('/')
237
237
238 for cnt, p in enumerate(paths_l):
238 for cnt, p in enumerate(paths_l):
239 if p != '':
239 if p != '':
240 url_l.append(link_to(p, url('files_home',
240 url_l.append(link_to(p, url('files_home',
241 repo_name=repo_name,
241 repo_name=repo_name,
242 revision=rev,
242 revision=rev,
243 f_path='/'.join(paths_l[:cnt + 1]))))
243 f_path='/'.join(paths_l[:cnt + 1]))))
244
244
245 return literal('/'.join(url_l))
245 return literal('/'.join(url_l))
246
246
247 files_breadcrumbs = _FilesBreadCrumbs()
247 files_breadcrumbs = _FilesBreadCrumbs()
248 class CodeHtmlFormatter(HtmlFormatter):
248 class CodeHtmlFormatter(HtmlFormatter):
249
249
250 def wrap(self, source, outfile):
250 def wrap(self, source, outfile):
251 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
251 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
252
252
253 def _wrap_code(self, source):
253 def _wrap_code(self, source):
254 for cnt, it in enumerate(source):
254 for cnt, it in enumerate(source):
255 i, t = it
255 i, t = it
256 t = '<div id="#S-%s">%s</div>' % (cnt + 1, t)
256 t = '<div id="#S-%s">%s</div>' % (cnt + 1, t)
257 yield i, t
257 yield i, t
258 def pygmentize(filenode, **kwargs):
258 def pygmentize(filenode, **kwargs):
259 """
259 """
260 pygmentize function using pygments
260 pygmentize function using pygments
261 :param filenode:
261 :param filenode:
262 """
262 """
263 return literal(code_highlight(filenode.content,
263 return literal(code_highlight(filenode.content,
264 filenode.lexer, CodeHtmlFormatter(**kwargs)))
264 filenode.lexer, CodeHtmlFormatter(**kwargs)))
265
265
266 def pygmentize_annotation(filenode, **kwargs):
266 def pygmentize_annotation(filenode, **kwargs):
267 """
267 """
268 pygmentize function for annotation
268 pygmentize function for annotation
269 :param filenode:
269 :param filenode:
270 """
270 """
271
271
272 color_dict = {}
272 color_dict = {}
273 def gen_color():
273 def gen_color():
274 """generator for getting 10k of evenly distibuted colors using hsv color
274 """generator for getting 10k of evenly distibuted colors using hsv color
275 and golden ratio.
275 and golden ratio.
276 """
276 """
277 import colorsys
277 import colorsys
278 n = 10000
278 n = 10000
279 golden_ratio = 0.618033988749895
279 golden_ratio = 0.618033988749895
280 h = 0.22717784590367374
280 h = 0.22717784590367374
281 #generate 10k nice web friendly colors in the same order
281 #generate 10k nice web friendly colors in the same order
282 for c in xrange(n):
282 for c in xrange(n):
283 h += golden_ratio
283 h += golden_ratio
284 h %= 1
284 h %= 1
285 HSV_tuple = [h, 0.95, 0.95]
285 HSV_tuple = [h, 0.95, 0.95]
286 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
286 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
287 yield map(lambda x:str(int(x * 256)), RGB_tuple)
287 yield map(lambda x:str(int(x * 256)), RGB_tuple)
288
288
289 cgenerator = gen_color()
289 cgenerator = gen_color()
290
290
291 def get_color_string(cs):
291 def get_color_string(cs):
292 if color_dict.has_key(cs):
292 if color_dict.has_key(cs):
293 col = color_dict[cs]
293 col = color_dict[cs]
294 else:
294 else:
295 col = color_dict[cs] = cgenerator.next()
295 col = color_dict[cs] = cgenerator.next()
296 return "color: rgb(%s)! important;" % (', '.join(col))
296 return "color: rgb(%s)! important;" % (', '.join(col))
297
297
298 def url_func(changeset):
298 def url_func(changeset):
299 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
299 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
300 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
300 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
301
301
302 tooltip_html = tooltip_html % (changeset.author,
302 tooltip_html = tooltip_html % (changeset.author,
303 changeset.date,
303 changeset.date,
304 tooltip(changeset.message))
304 tooltip(changeset.message))
305 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
305 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
306 short_id(changeset.raw_id))
306 short_id(changeset.raw_id))
307 uri = link_to(
307 uri = link_to(
308 lnk_format,
308 lnk_format,
309 url('changeset_home', repo_name=changeset.repository.name,
309 url('changeset_home', repo_name=changeset.repository.name,
310 revision=changeset.raw_id),
310 revision=changeset.raw_id),
311 style=get_color_string(changeset.raw_id),
311 style=get_color_string(changeset.raw_id),
312 class_='tooltip',
312 class_='tooltip',
313 tooltip_title=tooltip_html
313 tooltip_title=tooltip_html
314 )
314 )
315
315
316 uri += '\n'
316 uri += '\n'
317 return uri
317 return uri
318 return literal(annotate_highlight(filenode, url_func, **kwargs))
318 return literal(annotate_highlight(filenode, url_func, **kwargs))
319
319
320 def repo_name_slug(value):
320 def repo_name_slug(value):
321 """Return slug of name of repository
321 """Return slug of name of repository
322 This function is called on each creation/modification
322 This function is called on each creation/modification
323 of repository to prevent bad names in repo
323 of repository to prevent bad names in repo
324 """
324 """
325 slug = remove_formatting(value)
325 slug = remove_formatting(value)
326 slug = strip_tags(slug)
326 slug = strip_tags(slug)
327
327
328 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
328 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
329 slug = slug.replace(c, '-')
329 slug = slug.replace(c, '-')
330 slug = recursive_replace(slug, '-')
330 slug = recursive_replace(slug, '-')
331 slug = collapse(slug, '-')
331 slug = collapse(slug, '-')
332 return slug
332 return slug
333
333
334 def get_changeset_safe(repo, rev):
334 def get_changeset_safe(repo, rev):
335 from vcs.backends.base import BaseRepository
335 from vcs.backends.base import BaseRepository
336 from vcs.exceptions import RepositoryError
336 from vcs.exceptions import RepositoryError
337 if not isinstance(repo, BaseRepository):
337 if not isinstance(repo, BaseRepository):
338 raise Exception('You must pass an Repository '
338 raise Exception('You must pass an Repository '
339 'object as first argument got %s', type(repo))
339 'object as first argument got %s', type(repo))
340
340
341 try:
341 try:
342 cs = repo.get_changeset(rev)
342 cs = repo.get_changeset(rev)
343 except RepositoryError:
343 except RepositoryError:
344 from rhodecode.lib.utils import EmptyChangeset
344 from rhodecode.lib.utils import EmptyChangeset
345 cs = EmptyChangeset()
345 cs = EmptyChangeset()
346 return cs
346 return cs
347
347
348
348
349 flash = _Flash()
349 flash = _Flash()
350
350
351
351
352 #==============================================================================
352 #==============================================================================
353 # MERCURIAL FILTERS available via h.
353 # MERCURIAL FILTERS available via h.
354 #==============================================================================
354 #==============================================================================
355 from mercurial import util
355 from mercurial import util
356 from mercurial.templatefilters import person as _person
356 from mercurial.templatefilters import person as _person
357
357
358
358
359
359
360 def _age(curdate):
360 def _age(curdate):
361 """turns a datetime into an age string."""
361 """turns a datetime into an age string."""
362
362
363 if not curdate:
363 if not curdate:
364 return ''
364 return ''
365
365
366 from datetime import timedelta, datetime
366 from datetime import timedelta, datetime
367
367
368 agescales = [("year", 3600 * 24 * 365),
368 agescales = [("year", 3600 * 24 * 365),
369 ("month", 3600 * 24 * 30),
369 ("month", 3600 * 24 * 30),
370 ("day", 3600 * 24),
370 ("day", 3600 * 24),
371 ("hour", 3600),
371 ("hour", 3600),
372 ("minute", 60),
372 ("minute", 60),
373 ("second", 1), ]
373 ("second", 1), ]
374
374
375 age = datetime.now() - curdate
375 age = datetime.now() - curdate
376 age_seconds = (age.days * agescales[2][1]) + age.seconds
376 age_seconds = (age.days * agescales[2][1]) + age.seconds
377 pos = 1
377 pos = 1
378 for scale in agescales:
378 for scale in agescales:
379 if scale[1] <= age_seconds:
379 if scale[1] <= age_seconds:
380 if pos == 6:pos = 5
380 if pos == 6:pos = 5
381 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
381 return time_ago_in_words(curdate, agescales[pos][0]) + ' ' + _('ago')
382 pos += 1
382 pos += 1
383
383
384 return _('just now')
384 return _('just now')
385
385
386 age = lambda x:_age(x)
386 age = lambda x:_age(x)
387 capitalize = lambda x: x.capitalize()
387 capitalize = lambda x: x.capitalize()
388 email = util.email
388 email = util.email
389 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
389 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
390 person = lambda x: _person(x)
390 person = lambda x: _person(x)
391 short_id = lambda x: x[:12]
391 short_id = lambda x: x[:12]
392
392
393
393
394 def bool2icon(value):
394 def bool2icon(value):
395 """
395 """
396 Returns True/False values represented as small html image of true/false
396 Returns True/False values represented as small html image of true/false
397 icons
397 icons
398 :param value: bool value
398 :param value: bool value
399 """
399 """
400
400
401 if value is True:
401 if value is True:
402 return HTML.tag('img', src="/images/icons/accept.png", alt=_('True'))
402 return HTML.tag('img', src="/images/icons/accept.png", alt=_('True'))
403
403
404 if value is False:
404 if value is False:
405 return HTML.tag('img', src="/images/icons/cancel.png", alt=_('False'))
405 return HTML.tag('img', src="/images/icons/cancel.png", alt=_('False'))
406
406
407 return value
407 return value
408
408
409
409
410 def action_parser(user_log):
410 def action_parser(user_log):
411 """
411 """
412 This helper will map the specified string action into translated
412 This helper will map the specified string action into translated
413 fancy names with icons and links
413 fancy names with icons and links
414
414
415 @param action:
415 @param action:
416 """
416 """
417 action = user_log.action
417 action = user_log.action
418 action_params = ' '
418 action_params = ' '
419
419
420 x = action.split(':')
420 x = action.split(':')
421
421
422 if len(x) > 1:
422 if len(x) > 1:
423 action, action_params = x
423 action, action_params = x
424
424
425 def get_cs_links():
425 def get_cs_links():
426 if action == 'push':
426 if action == 'push':
427 revs_limit = 5
427 revs_limit = 1000
428 revs = action_params.split(',')
428 revs = action_params.split(',')
429 cs_links = " " + ', '.join ([link(rev,
429 cs_links = " " + ', '.join ([link(rev,
430 url('changeset_home',
430 url('changeset_home',
431 repo_name=user_log.repository.repo_name,
431 repo_name=user_log.repository.repo_name,
432 revision=rev)) for rev in revs[:revs_limit] ])
432 revision=rev)) for rev in revs[:revs_limit] ])
433 if len(revs) > revs_limit:
433 if len(revs) > revs_limit:
434 uniq_id = revs[0]
434 uniq_id = revs[0]
435 html_tmpl = ('<span> %s '
435 html_tmpl = ('<span> %s '
436 '<a class="show_more" id="_%s" href="#">%s</a> '
436 '<a class="show_more" id="_%s" href="#">%s</a> '
437 '%s</span>')
437 '%s</span>')
438 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
438 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
439 % (len(revs) - revs_limit),
439 % (len(revs) - revs_limit),
440 _('revisions'))
440 _('revisions'))
441
441
442 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
442 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
443 cs_links += html_tmpl % (uniq_id, ', '.join([link(rev,
443 cs_links += html_tmpl % (uniq_id, ', '.join([link(rev,
444 url('changeset_home',
444 url('changeset_home',
445 repo_name=user_log.repository.repo_name,
445 repo_name=user_log.repository.repo_name,
446 revision=rev)) for rev in revs[:revs_limit] ]))
446 revision=rev)) for rev in revs[:revs_limit] ]))
447
447
448 return cs_links
448 return cs_links
449 return ''
449 return ''
450
450
451 def get_fork_name():
451 def get_fork_name():
452 if action == 'user_forked_repo':
452 if action == 'user_forked_repo':
453 from rhodecode.model.scm import ScmModel
453 from rhodecode.model.scm import ScmModel
454 repo_name = action_params
454 repo_name = action_params
455 repo = ScmModel().get(repo_name)
455 repo = ScmModel().get(repo_name)
456 if repo is None:
456 if repo is None:
457 return repo_name
457 return repo_name
458 return link_to(action_params, url('summary_home',
458 return link_to(action_params, url('summary_home',
459 repo_name=repo.name,),
459 repo_name=repo.name,),
460 title=repo.dbrepo.description)
460 title=repo.dbrepo.description)
461 return ''
461 return ''
462 map = {'user_deleted_repo':_('User [deleted] repository'),
462 map = {'user_deleted_repo':_('User [deleted] repository'),
463 'user_created_repo':_('User [created] repository'),
463 'user_created_repo':_('User [created] repository'),
464 'user_forked_repo':_('User [forked] repository as: %s') % get_fork_name(),
464 'user_forked_repo':_('User [forked] repository as: %s') % get_fork_name(),
465 'user_updated_repo':_('User [updated] repository'),
465 'user_updated_repo':_('User [updated] repository'),
466 'admin_deleted_repo':_('Admin [delete] repository'),
466 'admin_deleted_repo':_('Admin [delete] repository'),
467 'admin_created_repo':_('Admin [created] repository'),
467 'admin_created_repo':_('Admin [created] repository'),
468 'admin_forked_repo':_('Admin [forked] repository'),
468 'admin_forked_repo':_('Admin [forked] repository'),
469 'admin_updated_repo':_('Admin [updated] repository'),
469 'admin_updated_repo':_('Admin [updated] repository'),
470 'push':_('[Pushed] %s') % get_cs_links(),
470 'push':_('[Pushed] %s') % get_cs_links(),
471 'pull':_('[Pulled]'),
471 'pull':_('[Pulled]'),
472 'started_following_repo':_('User [started following] repository'),
472 'started_following_repo':_('User [started following] repository'),
473 'stopped_following_repo':_('User [stopped following] repository'),
473 'stopped_following_repo':_('User [stopped following] repository'),
474 }
474 }
475
475
476 action_str = map.get(action, action)
476 action_str = map.get(action, action)
477 return literal(action_str.replace('[', '<span class="journal_highlight">')\
477 return literal(action_str.replace('[', '<span class="journal_highlight">')\
478 .replace(']', '</span>'))
478 .replace(']', '</span>'))
479
479
480 def action_parser_icon(user_log):
480 def action_parser_icon(user_log):
481 action = user_log.action
481 action = user_log.action
482 action_params = None
482 action_params = None
483 x = action.split(':')
483 x = action.split(':')
484
484
485 if len(x) > 1:
485 if len(x) > 1:
486 action, action_params = x
486 action, action_params = x
487
487
488 tmpl = """<img src="/images/icons/%s" alt="%s"/>"""
488 tmpl = """<img src="/images/icons/%s" alt="%s"/>"""
489 map = {'user_deleted_repo':'database_delete.png',
489 map = {'user_deleted_repo':'database_delete.png',
490 'user_created_repo':'database_add.png',
490 'user_created_repo':'database_add.png',
491 'user_forked_repo':'arrow_divide.png',
491 'user_forked_repo':'arrow_divide.png',
492 'user_updated_repo':'database_edit.png',
492 'user_updated_repo':'database_edit.png',
493 'admin_deleted_repo':'database_delete.png',
493 'admin_deleted_repo':'database_delete.png',
494 'admin_created_repo':'database_ddd.png',
494 'admin_created_repo':'database_ddd.png',
495 'admin_forked_repo':'arrow_divide.png',
495 'admin_forked_repo':'arrow_divide.png',
496 'admin_updated_repo':'database_edit.png',
496 'admin_updated_repo':'database_edit.png',
497 'push':'script_add.png',
497 'push':'script_add.png',
498 'pull':'down_16.png',
498 'pull':'down_16.png',
499 'started_following_repo':'heart_add.png',
499 'started_following_repo':'heart_add.png',
500 'stopped_following_repo':'heart_delete.png',
500 'stopped_following_repo':'heart_delete.png',
501 }
501 }
502 return literal(tmpl % (map.get(action, action), action))
502 return literal(tmpl % (map.get(action, action), action))
503
503
504
504
505 #==============================================================================
505 #==============================================================================
506 # PERMS
506 # PERMS
507 #==============================================================================
507 #==============================================================================
508 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
508 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
509 HasRepoPermissionAny, HasRepoPermissionAll
509 HasRepoPermissionAny, HasRepoPermissionAll
510
510
511 #==============================================================================
511 #==============================================================================
512 # GRAVATAR URL
512 # GRAVATAR URL
513 #==============================================================================
513 #==============================================================================
514 import hashlib
514 import hashlib
515 import urllib
515 import urllib
516 from pylons import request
516 from pylons import request
517
517
518 def gravatar_url(email_address, size=30):
518 def gravatar_url(email_address, size=30):
519 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
519 ssl_enabled = 'https' == request.environ.get('HTTP_X_URL_SCHEME')
520 default = 'identicon'
520 default = 'identicon'
521 baseurl_nossl = "http://www.gravatar.com/avatar/"
521 baseurl_nossl = "http://www.gravatar.com/avatar/"
522 baseurl_ssl = "https://secure.gravatar.com/avatar/"
522 baseurl_ssl = "https://secure.gravatar.com/avatar/"
523 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
523 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
524
524
525
525
526 # construct the url
526 # construct the url
527 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
527 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
528 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
528 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
529
529
530 return gravatar_url
530 return gravatar_url
531
531
532 def safe_unicode(str):
532 def safe_unicode(str):
533 """safe unicode function. In case of UnicodeDecode error we try to return
533 """safe unicode function. In case of UnicodeDecode error we try to return
534 unicode with errors replace, if this failes we return unicode with
534 unicode with errors replace, if this failes we return unicode with
535 string_escape decoding """
535 string_escape decoding """
536
536
537 try:
537 try:
538 u_str = unicode(str)
538 u_str = unicode(str)
539 except UnicodeDecodeError:
539 except UnicodeDecodeError:
540 try:
540 try:
541 u_str = unicode(str, 'utf-8', 'replace')
541 u_str = unicode(str, 'utf-8', 'replace')
542 except UnicodeDecodeError:
542 except UnicodeDecodeError:
543 #incase we have a decode error just represent as byte string
543 #incase we have a decode error just represent as byte string
544 u_str = unicode(str(str).encode('string_escape'))
544 u_str = unicode(str(str).encode('string_escape'))
545
545
546 return u_str
546 return u_str
@@ -1,614 +1,614 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-2010 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
47 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog
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, initial=False):
113 def get_repos(path, recursive=False, initial=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 :param prefix:
117 :param prefix:
117 :param path:
118 :param path:
118 :param recursive:
119 :param recursive:
119 :param initial:
120 :param initial:
120 """
121 """
121 from vcs.utils.helpers import get_scm
122 from vcs.utils.helpers import get_scm
122 from vcs.exceptions import VCSError
123 from vcs.exceptions import VCSError
123
124
124 try:
125 try:
125 scm = get_scm(path)
126 scm = get_scm(path)
126 except:
127 except:
127 pass
128 pass
128 else:
129 else:
129 raise Exception('The given path %s should not be a repository got %s',
130 raise Exception('The given path %s should not be a repository got %s',
130 path, scm)
131 path, scm)
131
132
132 for dirpath in os.listdir(path):
133 for dirpath in os.listdir(path):
133 try:
134 try:
134 yield dirpath, get_scm(os.path.join(path, dirpath))
135 yield dirpath, get_scm(os.path.join(path, dirpath))
135 except VCSError:
136 except VCSError:
136 pass
137 pass
137
138
138 def check_repo_fast(repo_name, base_path):
139 def check_repo_fast(repo_name, base_path):
139 """
140 """
140 Check given path for existance of directory
141 Check given path for existence of directory
141 :param repo_name:
142 :param repo_name:
142 :param base_path:
143 :param base_path:
143
144
144 :return False: if this directory is present
145 :return False: if this directory is present
145 """
146 """
146 if os.path.isdir(os.path.join(base_path, repo_name)):return False
147 if os.path.isdir(os.path.join(base_path, repo_name)):return False
147 return True
148 return True
148
149
149 def check_repo(repo_name, base_path, verify=True):
150 def check_repo(repo_name, base_path, verify=True):
150
151
151 repo_path = os.path.join(base_path, repo_name)
152 repo_path = os.path.join(base_path, repo_name)
152
153
153 try:
154 try:
154 if not check_repo_fast(repo_name, base_path):
155 if not check_repo_fast(repo_name, base_path):
155 return False
156 return False
156 r = hg.repository(ui.ui(), repo_path)
157 r = hg.repository(ui.ui(), repo_path)
157 if verify:
158 if verify:
158 hg.verify(r)
159 hg.verify(r)
159 #here we hnow that repo exists it was verified
160 #here we hnow that repo exists it was verified
160 log.info('%s repo is already created', repo_name)
161 log.info('%s repo is already created', repo_name)
161 return False
162 return False
162 except RepoError:
163 except RepoError:
163 #it means that there is no valid repo there...
164 #it means that there is no valid repo there...
164 log.info('%s repo is free for creation', repo_name)
165 log.info('%s repo is free for creation', repo_name)
165 return True
166 return True
166
167
167 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
168 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
168 while True:
169 while True:
169 ok = raw_input(prompt)
170 ok = raw_input(prompt)
170 if ok in ('y', 'ye', 'yes'): return True
171 if ok in ('y', 'ye', 'yes'): return True
171 if ok in ('n', 'no', 'nop', 'nope'): return False
172 if ok in ('n', 'no', 'nop', 'nope'): return False
172 retries = retries - 1
173 retries = retries - 1
173 if retries < 0: raise IOError
174 if retries < 0: raise IOError
174 print complaint
175 print complaint
175
176
176 #propagated from mercurial documentation
177 #propagated from mercurial documentation
177 ui_sections = ['alias', 'auth',
178 ui_sections = ['alias', 'auth',
178 'decode/encode', 'defaults',
179 'decode/encode', 'defaults',
179 'diff', 'email',
180 'diff', 'email',
180 'extensions', 'format',
181 'extensions', 'format',
181 'merge-patterns', 'merge-tools',
182 'merge-patterns', 'merge-tools',
182 'hooks', 'http_proxy',
183 'hooks', 'http_proxy',
183 'smtp', 'patch',
184 'smtp', 'patch',
184 'paths', 'profiling',
185 'paths', 'profiling',
185 'server', 'trusted',
186 'server', 'trusted',
186 'ui', 'web', ]
187 'ui', 'web', ]
187
188
188 def make_ui(read_from='file', path=None, checkpaths=True):
189 def make_ui(read_from='file', path=None, checkpaths=True):
189 """
190 """
190 A function that will read python rc files or database
191 A function that will read python rc files or database
191 and make an mercurial ui object from read options
192 and make an mercurial ui object from read options
192
193
193 :param path: path to mercurial config file
194 :param path: path to mercurial config file
194 :param checkpaths: check the path
195 :param checkpaths: check the path
195 :param read_from: read from 'file' or 'db'
196 :param read_from: read from 'file' or 'db'
196 """
197 """
197
198
198 baseui = ui.ui()
199 baseui = ui.ui()
199
200
200 #clean the baseui object
201 #clean the baseui object
201 baseui._ocfg = config.config()
202 baseui._ocfg = config.config()
202 baseui._ucfg = config.config()
203 baseui._ucfg = config.config()
203 baseui._tcfg = config.config()
204 baseui._tcfg = config.config()
204
205
205 if read_from == 'file':
206 if read_from == 'file':
206 if not os.path.isfile(path):
207 if not os.path.isfile(path):
207 log.warning('Unable to read config file %s' % path)
208 log.warning('Unable to read config file %s' % path)
208 return False
209 return False
209 log.debug('reading hgrc from %s', path)
210 log.debug('reading hgrc from %s', path)
210 cfg = config.config()
211 cfg = config.config()
211 cfg.read(path)
212 cfg.read(path)
212 for section in ui_sections:
213 for section in ui_sections:
213 for k, v in cfg.items(section):
214 for k, v in cfg.items(section):
214 log.debug('settings ui from file[%s]%s:%s', section, k, v)
215 log.debug('settings ui from file[%s]%s:%s', section, k, v)
215 baseui.setconfig(section, k, v)
216 baseui.setconfig(section, k, v)
216
217
217
218
218 elif read_from == 'db':
219 elif read_from == 'db':
219 sa = meta.Session()
220 sa = meta.Session()
220 ret = sa.query(RhodeCodeUi)\
221 ret = sa.query(RhodeCodeUi)\
221 .options(FromCache("sql_cache_short",
222 .options(FromCache("sql_cache_short",
222 "get_hg_ui_settings")).all()
223 "get_hg_ui_settings")).all()
223
224
224 hg_ui = ret
225 hg_ui = ret
225 for ui_ in hg_ui:
226 for ui_ in hg_ui:
226 if ui_.ui_active:
227 if ui_.ui_active:
227 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
228 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
228 ui_.ui_key, ui_.ui_value)
229 ui_.ui_key, ui_.ui_value)
229 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
230 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
230
231
231 meta.Session.remove()
232 meta.Session.remove()
232 return baseui
233 return baseui
233
234
234
235
235 def set_rhodecode_config(config):
236 def set_rhodecode_config(config):
236 """
237 """Updates pylons config with new settings from database
237 Updates pylons config with new settings from database
238
238 :param config:
239 :param config:
239 """
240 """
240 from rhodecode.model.settings import SettingsModel
241 from rhodecode.model.settings import SettingsModel
241 hgsettings = SettingsModel().get_app_settings()
242 hgsettings = SettingsModel().get_app_settings()
242
243
243 for k, v in hgsettings.items():
244 for k, v in hgsettings.items():
244 config[k] = v
245 config[k] = v
245
246
246 def invalidate_cache(cache_key, *args):
247 def invalidate_cache(cache_key, *args):
247 """
248 """Puts cache invalidation task into db for
248 Puts cache invalidation task into db for
249 further global cache invalidation
249 further global cache invalidation
250 """
250 """
251
251 from rhodecode.model.scm import ScmModel
252 from rhodecode.model.scm import ScmModel
252
253
253 if cache_key.startswith('get_repo_cached_'):
254 if cache_key.startswith('get_repo_cached_'):
254 name = cache_key.split('get_repo_cached_')[-1]
255 name = cache_key.split('get_repo_cached_')[-1]
255 ScmModel().mark_for_invalidation(name)
256 ScmModel().mark_for_invalidation(name)
256
257
257 class EmptyChangeset(BaseChangeset):
258 class EmptyChangeset(BaseChangeset):
258 """
259 """
259 An dummy empty changeset. It's possible to pass hash when creating
260 An dummy empty changeset. It's possible to pass hash when creating
260 an EmptyChangeset
261 an EmptyChangeset
261 """
262 """
262
263
263 def __init__(self, cs='0' * 40):
264 def __init__(self, cs='0' * 40):
264 self._empty_cs = cs
265 self._empty_cs = cs
265 self.revision = -1
266 self.revision = -1
266 self.message = ''
267 self.message = ''
267 self.author = ''
268 self.author = ''
268 self.date = ''
269 self.date = ''
269
270
270 @LazyProperty
271 @LazyProperty
271 def raw_id(self):
272 def raw_id(self):
272 """
273 """Returns raw string identifying this changeset, useful for web
273 Returns raw string identifying this changeset, useful for web
274 representation.
274 representation.
275 """
275 """
276
276 return self._empty_cs
277 return self._empty_cs
277
278
278 @LazyProperty
279 @LazyProperty
279 def short_id(self):
280 def short_id(self):
280 return self.raw_id[:12]
281 return self.raw_id[:12]
281
282
282 def get_file_changeset(self, path):
283 def get_file_changeset(self, path):
283 return self
284 return self
284
285
285 def get_file_content(self, path):
286 def get_file_content(self, path):
286 return u''
287 return u''
287
288
288 def get_file_size(self, path):
289 def get_file_size(self, path):
289 return 0
290 return 0
290
291
291 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
292 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
292 """
293 """maps all found repositories into db
293 maps all found repositories into db
294 """
294 """
295
295
296 sa = meta.Session()
296 sa = meta.Session()
297 rm = RepoModel()
297 rm = RepoModel()
298 user = sa.query(User).filter(User.admin == True).first()
298 user = sa.query(User).filter(User.admin == True).first()
299
299
300 for name, repo in initial_repo_list.items():
300 for name, repo in initial_repo_list.items():
301 if not rm.get_by_repo_name(name, cache=False):
301 if not rm.get_by_repo_name(name, cache=False):
302 log.info('repository %s not found creating default', name)
302 log.info('repository %s not found creating default', name)
303
303
304 form_data = {
304 form_data = {
305 'repo_name':name,
305 'repo_name':name,
306 'repo_type':repo.alias,
306 'repo_type':repo.alias,
307 'description':repo.description \
307 'description':repo.description \
308 if repo.description != 'unknown' else \
308 if repo.description != 'unknown' else \
309 '%s repository' % name,
309 '%s repository' % name,
310 'private':False
310 'private':False
311 }
311 }
312 rm.create(form_data, user, just_db=True)
312 rm.create(form_data, user, just_db=True)
313
313
314 if remove_obsolete:
314 if remove_obsolete:
315 #remove from database those repositories that are not in the filesystem
315 #remove from database those repositories that are not in the filesystem
316 for repo in sa.query(Repository).all():
316 for repo in sa.query(Repository).all():
317 if repo.repo_name not in initial_repo_list.keys():
317 if repo.repo_name not in initial_repo_list.keys():
318 sa.delete(repo)
318 sa.delete(repo)
319 sa.commit()
319 sa.commit()
320
320
321 class OrderedDict(dict, DictMixin):
321 class OrderedDict(dict, DictMixin):
322
322
323 def __init__(self, *args, **kwds):
323 def __init__(self, *args, **kwds):
324 if len(args) > 1:
324 if len(args) > 1:
325 raise TypeError('expected at most 1 arguments, got %d' % len(args))
325 raise TypeError('expected at most 1 arguments, got %d' % len(args))
326 try:
326 try:
327 self.__end
327 self.__end
328 except AttributeError:
328 except AttributeError:
329 self.clear()
329 self.clear()
330 self.update(*args, **kwds)
330 self.update(*args, **kwds)
331
331
332 def clear(self):
332 def clear(self):
333 self.__end = end = []
333 self.__end = end = []
334 end += [None, end, end] # sentinel node for doubly linked list
334 end += [None, end, end] # sentinel node for doubly linked list
335 self.__map = {} # key --> [key, prev, next]
335 self.__map = {} # key --> [key, prev, next]
336 dict.clear(self)
336 dict.clear(self)
337
337
338 def __setitem__(self, key, value):
338 def __setitem__(self, key, value):
339 if key not in self:
339 if key not in self:
340 end = self.__end
340 end = self.__end
341 curr = end[1]
341 curr = end[1]
342 curr[2] = end[1] = self.__map[key] = [key, curr, end]
342 curr[2] = end[1] = self.__map[key] = [key, curr, end]
343 dict.__setitem__(self, key, value)
343 dict.__setitem__(self, key, value)
344
344
345 def __delitem__(self, key):
345 def __delitem__(self, key):
346 dict.__delitem__(self, key)
346 dict.__delitem__(self, key)
347 key, prev, next = self.__map.pop(key)
347 key, prev, next = self.__map.pop(key)
348 prev[2] = next
348 prev[2] = next
349 next[1] = prev
349 next[1] = prev
350
350
351 def __iter__(self):
351 def __iter__(self):
352 end = self.__end
352 end = self.__end
353 curr = end[2]
353 curr = end[2]
354 while curr is not end:
354 while curr is not end:
355 yield curr[0]
355 yield curr[0]
356 curr = curr[2]
356 curr = curr[2]
357
357
358 def __reversed__(self):
358 def __reversed__(self):
359 end = self.__end
359 end = self.__end
360 curr = end[1]
360 curr = end[1]
361 while curr is not end:
361 while curr is not end:
362 yield curr[0]
362 yield curr[0]
363 curr = curr[1]
363 curr = curr[1]
364
364
365 def popitem(self, last=True):
365 def popitem(self, last=True):
366 if not self:
366 if not self:
367 raise KeyError('dictionary is empty')
367 raise KeyError('dictionary is empty')
368 if last:
368 if last:
369 key = reversed(self).next()
369 key = reversed(self).next()
370 else:
370 else:
371 key = iter(self).next()
371 key = iter(self).next()
372 value = self.pop(key)
372 value = self.pop(key)
373 return key, value
373 return key, value
374
374
375 def __reduce__(self):
375 def __reduce__(self):
376 items = [[k, self[k]] for k in self]
376 items = [[k, self[k]] for k in self]
377 tmp = self.__map, self.__end
377 tmp = self.__map, self.__end
378 del self.__map, self.__end
378 del self.__map, self.__end
379 inst_dict = vars(self).copy()
379 inst_dict = vars(self).copy()
380 self.__map, self.__end = tmp
380 self.__map, self.__end = tmp
381 if inst_dict:
381 if inst_dict:
382 return (self.__class__, (items,), inst_dict)
382 return (self.__class__, (items,), inst_dict)
383 return self.__class__, (items,)
383 return self.__class__, (items,)
384
384
385 def keys(self):
385 def keys(self):
386 return list(self)
386 return list(self)
387
387
388 setdefault = DictMixin.setdefault
388 setdefault = DictMixin.setdefault
389 update = DictMixin.update
389 update = DictMixin.update
390 pop = DictMixin.pop
390 pop = DictMixin.pop
391 values = DictMixin.values
391 values = DictMixin.values
392 items = DictMixin.items
392 items = DictMixin.items
393 iterkeys = DictMixin.iterkeys
393 iterkeys = DictMixin.iterkeys
394 itervalues = DictMixin.itervalues
394 itervalues = DictMixin.itervalues
395 iteritems = DictMixin.iteritems
395 iteritems = DictMixin.iteritems
396
396
397 def __repr__(self):
397 def __repr__(self):
398 if not self:
398 if not self:
399 return '%s()' % (self.__class__.__name__,)
399 return '%s()' % (self.__class__.__name__,)
400 return '%s(%r)' % (self.__class__.__name__, self.items())
400 return '%s(%r)' % (self.__class__.__name__, self.items())
401
401
402 def copy(self):
402 def copy(self):
403 return self.__class__(self)
403 return self.__class__(self)
404
404
405 @classmethod
405 @classmethod
406 def fromkeys(cls, iterable, value=None):
406 def fromkeys(cls, iterable, value=None):
407 d = cls()
407 d = cls()
408 for key in iterable:
408 for key in iterable:
409 d[key] = value
409 d[key] = value
410 return d
410 return d
411
411
412 def __eq__(self, other):
412 def __eq__(self, other):
413 if isinstance(other, OrderedDict):
413 if isinstance(other, OrderedDict):
414 return len(self) == len(other) and self.items() == other.items()
414 return len(self) == len(other) and self.items() == other.items()
415 return dict.__eq__(self, other)
415 return dict.__eq__(self, other)
416
416
417 def __ne__(self, other):
417 def __ne__(self, other):
418 return not self == other
418 return not self == other
419
419
420
420
421 #set cache regions for beaker so celery can utilise it
421 #set cache regions for beaker so celery can utilise it
422 def add_cache(settings):
422 def add_cache(settings):
423 cache_settings = {'regions':None}
423 cache_settings = {'regions':None}
424 for key in settings.keys():
424 for key in settings.keys():
425 for prefix in ['beaker.cache.', 'cache.']:
425 for prefix in ['beaker.cache.', 'cache.']:
426 if key.startswith(prefix):
426 if key.startswith(prefix):
427 name = key.split(prefix)[1].strip()
427 name = key.split(prefix)[1].strip()
428 cache_settings[name] = settings[key].strip()
428 cache_settings[name] = settings[key].strip()
429 if cache_settings['regions']:
429 if cache_settings['regions']:
430 for region in cache_settings['regions'].split(','):
430 for region in cache_settings['regions'].split(','):
431 region = region.strip()
431 region = region.strip()
432 region_settings = {}
432 region_settings = {}
433 for key, value in cache_settings.items():
433 for key, value in cache_settings.items():
434 if key.startswith(region):
434 if key.startswith(region):
435 region_settings[key.split('.')[1]] = value
435 region_settings[key.split('.')[1]] = value
436 region_settings['expire'] = int(region_settings.get('expire',
436 region_settings['expire'] = int(region_settings.get('expire',
437 60))
437 60))
438 region_settings.setdefault('lock_dir',
438 region_settings.setdefault('lock_dir',
439 cache_settings.get('lock_dir'))
439 cache_settings.get('lock_dir'))
440 if 'type' not in region_settings:
440 if 'type' not in region_settings:
441 region_settings['type'] = cache_settings.get('type',
441 region_settings['type'] = cache_settings.get('type',
442 'memory')
442 'memory')
443 beaker.cache.cache_regions[region] = region_settings
443 beaker.cache.cache_regions[region] = region_settings
444
444
445 def get_current_revision():
445 def get_current_revision():
446 """
446 """Returns tuple of (number, id) from repository containing this package
447 Returns tuple of (number, id) from repository containing this package
448 or None if repository could not be found.
447 or None if repository could not be found.
449 """
448 """
449
450 try:
450 try:
451 from vcs import get_repo
451 from vcs import get_repo
452 from vcs.utils.helpers import get_scm
452 from vcs.utils.helpers import get_scm
453 from vcs.exceptions import RepositoryError, VCSError
453 from vcs.exceptions import RepositoryError, VCSError
454 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
454 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
455 scm = get_scm(repopath)[0]
455 scm = get_scm(repopath)[0]
456 repo = get_repo(path=repopath, alias=scm)
456 repo = get_repo(path=repopath, alias=scm)
457 tip = repo.get_changeset()
457 tip = repo.get_changeset()
458 return (tip.revision, tip.short_id)
458 return (tip.revision, tip.short_id)
459 except (ImportError, RepositoryError, VCSError), err:
459 except (ImportError, RepositoryError, VCSError), err:
460 logging.debug("Cannot retrieve rhodecode's revision. Original error "
460 logging.debug("Cannot retrieve rhodecode's revision. Original error "
461 "was: %s" % err)
461 "was: %s" % err)
462 return None
462 return None
463
463
464 #===============================================================================
464 #===============================================================================
465 # TEST FUNCTIONS AND CREATORS
465 # TEST FUNCTIONS AND CREATORS
466 #===============================================================================
466 #===============================================================================
467 def create_test_index(repo_location, full_index):
467 def create_test_index(repo_location, full_index):
468 """Makes default test index
468 """Makes default test index
469 :param repo_location:
469 :param repo_location:
470 :param full_index:
470 :param full_index:
471 """
471 """
472 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
472 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
473 from rhodecode.lib.pidlock import DaemonLock, LockHeld
473 from rhodecode.lib.pidlock import DaemonLock, LockHeld
474 import shutil
474 import shutil
475
475
476 index_location = os.path.join(repo_location, 'index')
476 index_location = os.path.join(repo_location, 'index')
477 if os.path.exists(index_location):
477 if os.path.exists(index_location):
478 shutil.rmtree(index_location)
478 shutil.rmtree(index_location)
479
479
480 try:
480 try:
481 l = DaemonLock()
481 l = DaemonLock()
482 WhooshIndexingDaemon(index_location=index_location,
482 WhooshIndexingDaemon(index_location=index_location,
483 repo_location=repo_location)\
483 repo_location=repo_location)\
484 .run(full_index=full_index)
484 .run(full_index=full_index)
485 l.release()
485 l.release()
486 except LockHeld:
486 except LockHeld:
487 pass
487 pass
488
488
489 def create_test_env(repos_test_path, config):
489 def create_test_env(repos_test_path, config):
490 """Makes a fresh database and
490 """Makes a fresh database and
491 install test repository into tmp dir
491 install test repository into tmp dir
492 """
492 """
493 from rhodecode.lib.db_manage import DbManage
493 from rhodecode.lib.db_manage import DbManage
494 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
494 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
495 HG_FORK, GIT_FORK, TESTS_TMP_PATH
495 HG_FORK, GIT_FORK, TESTS_TMP_PATH
496 import tarfile
496 import tarfile
497 import shutil
497 import shutil
498 from os.path import dirname as dn, join as jn, abspath
498 from os.path import dirname as dn, join as jn, abspath
499
499
500 log = logging.getLogger('TestEnvCreator')
500 log = logging.getLogger('TestEnvCreator')
501 # create logger
501 # create logger
502 log.setLevel(logging.DEBUG)
502 log.setLevel(logging.DEBUG)
503 log.propagate = True
503 log.propagate = True
504 # create console handler and set level to debug
504 # create console handler and set level to debug
505 ch = logging.StreamHandler()
505 ch = logging.StreamHandler()
506 ch.setLevel(logging.DEBUG)
506 ch.setLevel(logging.DEBUG)
507
507
508 # create formatter
508 # create formatter
509 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
509 formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
510
510
511 # add formatter to ch
511 # add formatter to ch
512 ch.setFormatter(formatter)
512 ch.setFormatter(formatter)
513
513
514 # add ch to logger
514 # add ch to logger
515 log.addHandler(ch)
515 log.addHandler(ch)
516
516
517 #PART ONE create db
517 #PART ONE create db
518 dbconf = config['sqlalchemy.db1.url']
518 dbconf = config['sqlalchemy.db1.url']
519 log.debug('making test db %s', dbconf)
519 log.debug('making test db %s', dbconf)
520
520
521 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
521 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
522 tests=True)
522 tests=True)
523 dbmanage.create_tables(override=True)
523 dbmanage.create_tables(override=True)
524 dbmanage.config_prompt(repos_test_path)
524 dbmanage.config_prompt(repos_test_path)
525 dbmanage.create_default_user()
525 dbmanage.create_default_user()
526 dbmanage.admin_prompt()
526 dbmanage.admin_prompt()
527 dbmanage.create_permissions()
527 dbmanage.create_permissions()
528 dbmanage.populate_default_permissions()
528 dbmanage.populate_default_permissions()
529
529
530 #PART TWO make test repo
530 #PART TWO make test repo
531 log.debug('making test vcs repositories')
531 log.debug('making test vcs repositories')
532
532
533 #remove old one from previos tests
533 #remove old one from previos tests
534 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
534 for r in [HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, HG_FORK, GIT_FORK]:
535
535
536 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
536 if os.path.isdir(jn(TESTS_TMP_PATH, r)):
537 log.debug('removing %s', r)
537 log.debug('removing %s', r)
538 shutil.rmtree(jn(TESTS_TMP_PATH, r))
538 shutil.rmtree(jn(TESTS_TMP_PATH, r))
539
539
540 #CREATE DEFAULT HG REPOSITORY
540 #CREATE DEFAULT HG REPOSITORY
541 cur_dir = dn(dn(abspath(__file__)))
541 cur_dir = dn(dn(abspath(__file__)))
542 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
542 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
543 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
543 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
544 tar.close()
544 tar.close()
545
545
546
546
547 #==============================================================================
547 #==============================================================================
548 # PASTER COMMANDS
548 # PASTER COMMANDS
549 #==============================================================================
549 #==============================================================================
550
550
551 class BasePasterCommand(Command):
551 class BasePasterCommand(Command):
552 """
552 """
553 Abstract Base Class for paster commands.
553 Abstract Base Class for paster commands.
554
554
555 The celery commands are somewhat aggressive about loading
555 The celery commands are somewhat aggressive about loading
556 celery.conf, and since our module sets the `CELERY_LOADER`
556 celery.conf, and since our module sets the `CELERY_LOADER`
557 environment variable to our loader, we have to bootstrap a bit and
557 environment variable to our loader, we have to bootstrap a bit and
558 make sure we've had a chance to load the pylons config off of the
558 make sure we've had a chance to load the pylons config off of the
559 command line, otherwise everything fails.
559 command line, otherwise everything fails.
560 """
560 """
561 min_args = 1
561 min_args = 1
562 min_args_error = "Please provide a paster config file as an argument."
562 min_args_error = "Please provide a paster config file as an argument."
563 takes_config_file = 1
563 takes_config_file = 1
564 requires_config_file = True
564 requires_config_file = True
565
565
566 def notify_msg(self, msg, log=False):
566 def notify_msg(self, msg, log=False):
567 """Make a notification to user, additionally if logger is passed
567 """Make a notification to user, additionally if logger is passed
568 it logs this action using given logger
568 it logs this action using given logger
569
569
570 :param msg: message that will be printed to user
570 :param msg: message that will be printed to user
571 :param log: logging instance, to use to additionally log this message
571 :param log: logging instance, to use to additionally log this message
572
572
573 """
573 """
574 print msg
574 print msg
575 if log and isinstance(log, logging):
575 if log and isinstance(log, logging):
576 log(msg)
576 log(msg)
577
577
578
578
579 def run(self, args):
579 def run(self, args):
580 """
580 """
581 Overrides Command.run
581 Overrides Command.run
582
582
583 Checks for a config file argument and loads it.
583 Checks for a config file argument and loads it.
584 """
584 """
585 if len(args) < self.min_args:
585 if len(args) < self.min_args:
586 raise BadCommand(
586 raise BadCommand(
587 self.min_args_error % {'min_args': self.min_args,
587 self.min_args_error % {'min_args': self.min_args,
588 'actual_args': len(args)})
588 'actual_args': len(args)})
589
589
590 # Decrement because we're going to lob off the first argument.
590 # Decrement because we're going to lob off the first argument.
591 # @@ This is hacky
591 # @@ This is hacky
592 self.min_args -= 1
592 self.min_args -= 1
593 self.bootstrap_config(args[0])
593 self.bootstrap_config(args[0])
594 self.update_parser()
594 self.update_parser()
595 return super(BasePasterCommand, self).run(args[1:])
595 return super(BasePasterCommand, self).run(args[1:])
596
596
597 def update_parser(self):
597 def update_parser(self):
598 """
598 """
599 Abstract method. Allows for the class's parser to be updated
599 Abstract method. Allows for the class's parser to be updated
600 before the superclass's `run` method is called. Necessary to
600 before the superclass's `run` method is called. Necessary to
601 allow options/arguments to be passed through to the underlying
601 allow options/arguments to be passed through to the underlying
602 celery command.
602 celery command.
603 """
603 """
604 raise NotImplementedError("Abstract Method.")
604 raise NotImplementedError("Abstract Method.")
605
605
606 def bootstrap_config(self, conf):
606 def bootstrap_config(self, conf):
607 """
607 """
608 Loads the pylons configuration.
608 Loads the pylons configuration.
609 """
609 """
610 from pylons import config as pylonsconfig
610 from pylons import config as pylonsconfig
611
611
612 path_to_ini_file = os.path.realpath(conf)
612 path_to_ini_file = os.path.realpath(conf)
613 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
613 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
614 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
614 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
General Comments 0
You need to be logged in to leave comments. Login now