##// END OF EJS Templates
pep8ify
marcink -
r1307:c1516b35 beta
parent child Browse files
Show More
@@ -1,138 +1,137 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.ldap_settings
3 rhodecode.controllers.admin.ldap_settings
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 ldap controller for RhodeCode
6 ldap controller for RhodeCode
7
7
8 :created_on: Nov 26, 2010
8 :created_on: Nov 26, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import logging
25 import logging
26 import formencode
26 import formencode
27 import traceback
27 import traceback
28
28
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
32 from pylons.controllers.util import abort, redirect
32 from pylons.controllers.util import abort, redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from sqlalchemy.exc import DatabaseError
35 from sqlalchemy.exc import DatabaseError
36
36
37 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.base import BaseController, render
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
40 from rhodecode.lib.exceptions import LdapImportError
40 from rhodecode.lib.exceptions import LdapImportError
41 from rhodecode.model.forms import LdapSettingsForm
41 from rhodecode.model.forms import LdapSettingsForm
42 from rhodecode.model.db import RhodeCodeSettings
42 from rhodecode.model.db import RhodeCodeSettings
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class LdapSettingsController(BaseController):
47 class LdapSettingsController(BaseController):
48
48
49 search_scope_choices = [('BASE', _('BASE'),),
49 search_scope_choices = [('BASE', _('BASE'),),
50 ('ONELEVEL', _('ONELEVEL'),),
50 ('ONELEVEL', _('ONELEVEL'),),
51 ('SUBTREE', _('SUBTREE'),),
51 ('SUBTREE', _('SUBTREE'),),
52 ]
52 ]
53 search_scope_default = 'SUBTREE'
53 search_scope_default = 'SUBTREE'
54
54
55 tls_reqcert_choices = [('NEVER', _('NEVER'),),
55 tls_reqcert_choices = [('NEVER', _('NEVER'),),
56 ('ALLOW', _('ALLOW'),),
56 ('ALLOW', _('ALLOW'),),
57 ('TRY', _('TRY'),),
57 ('TRY', _('TRY'),),
58 ('DEMAND', _('DEMAND'),),
58 ('DEMAND', _('DEMAND'),),
59 ('HARD', _('HARD'),),
59 ('HARD', _('HARD'),),
60 ]
60 ]
61 tls_reqcert_default = 'DEMAND'
61 tls_reqcert_default = 'DEMAND'
62
62
63 tls_kind_choices = [('PLAIN', _('No encryption'),),
63 tls_kind_choices = [('PLAIN', _('No encryption'),),
64 ('LDAPS', _('LDAPS connection'),),
64 ('LDAPS', _('LDAPS connection'),),
65 ('START_TLS', _('START_TLS on LDAP connection'),)
65 ('START_TLS', _('START_TLS on LDAP connection'),)
66 ]
66 ]
67
67
68 tls_kind_default = 'PLAIN'
68 tls_kind_default = 'PLAIN'
69
69
70 @LoginRequired()
70 @LoginRequired()
71 @HasPermissionAllDecorator('hg.admin')
71 @HasPermissionAllDecorator('hg.admin')
72 def __before__(self):
72 def __before__(self):
73 c.admin_user = session.get('admin_user')
73 c.admin_user = session.get('admin_user')
74 c.admin_username = session.get('admin_username')
74 c.admin_username = session.get('admin_username')
75 c.search_scope_choices = self.search_scope_choices
75 c.search_scope_choices = self.search_scope_choices
76 c.tls_reqcert_choices = self.tls_reqcert_choices
76 c.tls_reqcert_choices = self.tls_reqcert_choices
77 c.tls_kind_choices = self.tls_kind_choices
77 c.tls_kind_choices = self.tls_kind_choices
78
78
79 c.search_scope_cur = self.search_scope_default
79 c.search_scope_cur = self.search_scope_default
80 c.tls_reqcert_cur = self.tls_reqcert_default
80 c.tls_reqcert_cur = self.tls_reqcert_default
81 c.tls_kind_cur = self.tls_kind_default
81 c.tls_kind_cur = self.tls_kind_default
82
82
83 super(LdapSettingsController, self).__before__()
83 super(LdapSettingsController, self).__before__()
84
84
85 def index(self):
85 def index(self):
86 defaults = RhodeCodeSettings.get_ldap_settings()
86 defaults = RhodeCodeSettings.get_ldap_settings()
87 c.search_scope_cur = defaults.get('ldap_search_scope')
87 c.search_scope_cur = defaults.get('ldap_search_scope')
88 c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert')
88 c.tls_reqcert_cur = defaults.get('ldap_tls_reqcert')
89 c.tls_kind_cur = defaults.get('ldap_tls_kind')
89 c.tls_kind_cur = defaults.get('ldap_tls_kind')
90
90
91 return htmlfill.render(
91 return htmlfill.render(
92 render('admin/ldap/ldap.html'),
92 render('admin/ldap/ldap.html'),
93 defaults=defaults,
93 defaults=defaults,
94 encoding="UTF-8",
94 encoding="UTF-8",
95 force_defaults=True,)
95 force_defaults=True,)
96
96
97 def ldap_settings(self):
97 def ldap_settings(self):
98 """POST ldap create and store ldap settings"""
98 """POST ldap create and store ldap settings"""
99
99
100 _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
100 _form = LdapSettingsForm([x[0] for x in self.tls_reqcert_choices],
101 [x[0] for x in self.search_scope_choices],
101 [x[0] for x in self.search_scope_choices],
102 [x[0] for x in self.tls_kind_choices])()
102 [x[0] for x in self.tls_kind_choices])()
103
103
104 try:
104 try:
105 form_result = _form.to_python(dict(request.POST))
105 form_result = _form.to_python(dict(request.POST))
106 try:
106 try:
107
107
108 for k, v in form_result.items():
108 for k, v in form_result.items():
109 if k.startswith('ldap_'):
109 if k.startswith('ldap_'):
110 setting = RhodeCodeSettings.get_by_name(k)
110 setting = RhodeCodeSettings.get_by_name(k)
111 setting.app_settings_value = v
111 setting.app_settings_value = v
112 self.sa.add(setting)
112 self.sa.add(setting)
113
113
114 self.sa.commit()
114 self.sa.commit()
115 h.flash(_('Ldap settings updated successfully'),
115 h.flash(_('Ldap settings updated successfully'),
116 category='success')
116 category='success')
117 except (DatabaseError,):
117 except (DatabaseError,):
118 raise
118 raise
119 except LdapImportError:
119 except LdapImportError:
120 h.flash(_('Unable to activate ldap. The "python-ldap" library '
120 h.flash(_('Unable to activate ldap. The "python-ldap" library '
121 'is missing.'), category='warning')
121 'is missing.'), category='warning')
122
122
123 except formencode.Invalid, errors:
123 except formencode.Invalid, errors:
124 e = errors.error_dict or {}
124 e = errors.error_dict or {}
125
125
126
127 return htmlfill.render(
126 return htmlfill.render(
128 render('admin/ldap/ldap.html'),
127 render('admin/ldap/ldap.html'),
129 defaults=errors.value,
128 defaults=errors.value,
130 errors=e,
129 errors=e,
131 prefix_error=False,
130 prefix_error=False,
132 encoding="UTF-8")
131 encoding="UTF-8")
133 except Exception:
132 except Exception:
134 log.error(traceback.format_exc())
133 log.error(traceback.format_exc())
135 h.flash(_('error occurred during update of ldap settings'),
134 h.flash(_('error occurred during update of ldap settings'),
136 category='error')
135 category='error')
137
136
138 return redirect(url('ldap_home'))
137 return redirect(url('ldap_home'))
@@ -1,405 +1,406 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.files
3 rhodecode.controllers.files
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Files controller for RhodeCode
6 Files controller for RhodeCode
7
7
8 :created_on: Apr 21, 2010
8 :created_on: Apr 21, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import mimetypes
28 import mimetypes
29 import traceback
29 import traceback
30
30
31 from pylons import request, response, session, tmpl_context as c, url
31 from pylons import request, response, session, tmpl_context as c, url
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34
34
35 from vcs.backends import ARCHIVE_SPECS
35 from vcs.backends import ARCHIVE_SPECS
36 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
36 from vcs.exceptions import RepositoryError, ChangesetDoesNotExistError, \
37 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
37 EmptyRepositoryError, ImproperArchiveTypeError, VCSError
38 from vcs.nodes import FileNode, NodeKind
38 from vcs.nodes import FileNode, NodeKind
39 from vcs.utils import diffs as differ
39 from vcs.utils import diffs as differ
40
40
41 from rhodecode.lib import convert_line_endings, detect_mode
41 from rhodecode.lib import convert_line_endings, detect_mode
42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
43 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.base import BaseRepoController, render
44 from rhodecode.lib.utils import EmptyChangeset
44 from rhodecode.lib.utils import EmptyChangeset
45 import rhodecode.lib.helpers as h
45 import rhodecode.lib.helpers as h
46 from rhodecode.model.repo import RepoModel
46 from rhodecode.model.repo import RepoModel
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 class FilesController(BaseRepoController):
51 class FilesController(BaseRepoController):
52
52
53 @LoginRequired()
53 @LoginRequired()
54 def __before__(self):
54 def __before__(self):
55 super(FilesController, self).__before__()
55 super(FilesController, self).__before__()
56 c.cut_off_limit = self.cut_off_limit
56 c.cut_off_limit = self.cut_off_limit
57
57
58 def __get_cs_or_redirect(self, rev, repo_name):
58 def __get_cs_or_redirect(self, rev, repo_name):
59 """
59 """
60 Safe way to get changeset if error occur it redirects to tip with
60 Safe way to get changeset if error occur it redirects to tip with
61 proper message
61 proper message
62
62
63 :param rev: revision to fetch
63 :param rev: revision to fetch
64 :param repo_name: repo name to redirect after
64 :param repo_name: repo name to redirect after
65 """
65 """
66
66
67 try:
67 try:
68 return c.rhodecode_repo.get_changeset(rev)
68 return c.rhodecode_repo.get_changeset(rev)
69 except EmptyRepositoryError, e:
69 except EmptyRepositoryError, e:
70 h.flash(_('There are no files yet'), category='warning')
70 h.flash(_('There are no files yet'), category='warning')
71 redirect(h.url('summary_home', repo_name=repo_name))
71 redirect(h.url('summary_home', repo_name=repo_name))
72
72
73 except RepositoryError, e:
73 except RepositoryError, e:
74 h.flash(str(e), category='warning')
74 h.flash(str(e), category='warning')
75 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
75 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
76
76
77 def __get_filenode_or_redirect(self, repo_name, cs, path):
77 def __get_filenode_or_redirect(self, repo_name, cs, path):
78 """
78 """
79 Returns file_node, if error occurs or given path is directory,
79 Returns file_node, if error occurs or given path is directory,
80 it'll redirect to top level path
80 it'll redirect to top level path
81
81
82 :param repo_name: repo_name
82 :param repo_name: repo_name
83 :param cs: given changeset
83 :param cs: given changeset
84 :param path: path to lookup
84 :param path: path to lookup
85 """
85 """
86
86
87 try:
87 try:
88 file_node = cs.get_node(path)
88 file_node = cs.get_node(path)
89 if file_node.is_dir():
89 if file_node.is_dir():
90 raise RepositoryError('given path is a directory')
90 raise RepositoryError('given path is a directory')
91 except RepositoryError, e:
91 except RepositoryError, e:
92 h.flash(str(e), category='warning')
92 h.flash(str(e), category='warning')
93 redirect(h.url('files_home', repo_name=repo_name,
93 redirect(h.url('files_home', repo_name=repo_name,
94 revision=cs.raw_id))
94 revision=cs.raw_id))
95
95
96 return file_node
96 return file_node
97
97
98 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
98 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
99 'repository.admin')
99 'repository.admin')
100 def index(self, repo_name, revision, f_path):
100 def index(self, repo_name, revision, f_path):
101 #reditect to given revision from form if given
101 #reditect to given revision from form if given
102 post_revision = request.POST.get('at_rev', None)
102 post_revision = request.POST.get('at_rev', None)
103 if post_revision:
103 if post_revision:
104 cs = self.__get_cs_or_redirect(post_revision, repo_name)
104 cs = self.__get_cs_or_redirect(post_revision, repo_name)
105 redirect(url('files_home', repo_name=c.repo_name,
105 redirect(url('files_home', repo_name=c.repo_name,
106 revision=cs.raw_id, f_path=f_path))
106 revision=cs.raw_id, f_path=f_path))
107
107
108 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
108 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
109 c.branch = request.GET.get('branch', None)
109 c.branch = request.GET.get('branch', None)
110 c.f_path = f_path
110 c.f_path = f_path
111
111
112 cur_rev = c.changeset.revision
112 cur_rev = c.changeset.revision
113
113
114 #prev link
114 #prev link
115 try:
115 try:
116 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
116 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
117 c.url_prev = url('files_home', repo_name=c.repo_name,
117 c.url_prev = url('files_home', repo_name=c.repo_name,
118 revision=prev_rev.raw_id, f_path=f_path)
118 revision=prev_rev.raw_id, f_path=f_path)
119 if c.branch:
119 if c.branch:
120 c.url_prev += '?branch=%s' % c.branch
120 c.url_prev += '?branch=%s' % c.branch
121 except (ChangesetDoesNotExistError, VCSError):
121 except (ChangesetDoesNotExistError, VCSError):
122 c.url_prev = '#'
122 c.url_prev = '#'
123
123
124 #next link
124 #next link
125 try:
125 try:
126 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
126 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
127 c.url_next = url('files_home', repo_name=c.repo_name,
127 c.url_next = url('files_home', repo_name=c.repo_name,
128 revision=next_rev.raw_id, f_path=f_path)
128 revision=next_rev.raw_id, f_path=f_path)
129 if c.branch:
129 if c.branch:
130 c.url_next += '?branch=%s' % c.branch
130 c.url_next += '?branch=%s' % c.branch
131 except (ChangesetDoesNotExistError, VCSError):
131 except (ChangesetDoesNotExistError, VCSError):
132 c.url_next = '#'
132 c.url_next = '#'
133
133
134 #files or dirs
134 #files or dirs
135 try:
135 try:
136 c.files_list = c.changeset.get_node(f_path)
136 c.files_list = c.changeset.get_node(f_path)
137
137
138 if c.files_list.is_file():
138 if c.files_list.is_file():
139 c.file_history = self._get_node_history(c.changeset, f_path)
139 c.file_history = self._get_node_history(c.changeset, f_path)
140 else:
140 else:
141 c.file_history = []
141 c.file_history = []
142 except RepositoryError, e:
142 except RepositoryError, e:
143 h.flash(str(e), category='warning')
143 h.flash(str(e), category='warning')
144 redirect(h.url('files_home', repo_name=repo_name,
144 redirect(h.url('files_home', repo_name=repo_name,
145 revision=revision))
145 revision=revision))
146
146
147 return render('files/files.html')
147 return render('files/files.html')
148
148
149 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
149 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
150 'repository.admin')
150 'repository.admin')
151 def rawfile(self, repo_name, revision, f_path):
151 def rawfile(self, repo_name, revision, f_path):
152 cs = self.__get_cs_or_redirect(revision, repo_name)
152 cs = self.__get_cs_or_redirect(revision, repo_name)
153 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
153 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
154
154
155 response.content_disposition = 'attachment; filename=%s' % \
155 response.content_disposition = 'attachment; filename=%s' % \
156 f_path.split(os.sep)[-1].encode('utf8', 'replace')
156 f_path.split(os.sep)[-1].encode('utf8', 'replace')
157
157
158 response.content_type = file_node.mimetype
158 response.content_type = file_node.mimetype
159 return file_node.content
159 return file_node.content
160
160
161 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
161 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
162 'repository.admin')
162 'repository.admin')
163 def raw(self, repo_name, revision, f_path):
163 def raw(self, repo_name, revision, f_path):
164 cs = self.__get_cs_or_redirect(revision, repo_name)
164 cs = self.__get_cs_or_redirect(revision, repo_name)
165 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
165 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
166
166
167 raw_mimetype_mapping = {
167 raw_mimetype_mapping = {
168 # map original mimetype to a mimetype used for "show as raw"
168 # map original mimetype to a mimetype used for "show as raw"
169 # you can also provide a content-disposition to override the
169 # you can also provide a content-disposition to override the
170 # default "attachment" disposition.
170 # default "attachment" disposition.
171 # orig_type: (new_type, new_dispo)
171 # orig_type: (new_type, new_dispo)
172
172
173 # show images inline:
173 # show images inline:
174 'image/x-icon': ('image/x-icon', 'inline'),
174 'image/x-icon': ('image/x-icon', 'inline'),
175 'image/png': ('image/png', 'inline'),
175 'image/png': ('image/png', 'inline'),
176 'image/gif': ('image/gif', 'inline'),
176 'image/gif': ('image/gif', 'inline'),
177 'image/jpeg': ('image/jpeg', 'inline'),
177 'image/jpeg': ('image/jpeg', 'inline'),
178 'image/svg+xml': ('image/svg+xml', 'inline'),
178 'image/svg+xml': ('image/svg+xml', 'inline'),
179 }
179 }
180
180
181 mimetype = file_node.mimetype
181 mimetype = file_node.mimetype
182 try:
182 try:
183 mimetype, dispo = raw_mimetype_mapping[mimetype]
183 mimetype, dispo = raw_mimetype_mapping[mimetype]
184 except KeyError:
184 except KeyError:
185 # we don't know anything special about this, handle it safely
185 # we don't know anything special about this, handle it safely
186 if file_node.is_binary:
186 if file_node.is_binary:
187 # do same as download raw for binary files
187 # do same as download raw for binary files
188 mimetype, dispo = 'application/octet-stream', 'attachment'
188 mimetype, dispo = 'application/octet-stream', 'attachment'
189 else:
189 else:
190 # do not just use the original mimetype, but force text/plain,
190 # do not just use the original mimetype, but force text/plain,
191 # otherwise it would serve text/html and that might be unsafe.
191 # otherwise it would serve text/html and that might be unsafe.
192 # Note: underlying vcs library fakes text/plain mimetype if the
192 # Note: underlying vcs library fakes text/plain mimetype if the
193 # mimetype can not be determined and it thinks it is not
193 # mimetype can not be determined and it thinks it is not
194 # binary.This might lead to erroneous text display in some
194 # binary.This might lead to erroneous text display in some
195 # cases, but helps in other cases, like with text files
195 # cases, but helps in other cases, like with text files
196 # without extension.
196 # without extension.
197 mimetype, dispo = 'text/plain', 'inline'
197 mimetype, dispo = 'text/plain', 'inline'
198
198
199 if dispo == 'attachment':
199 if dispo == 'attachment':
200 dispo = 'attachment; filename=%s' % \
200 dispo = 'attachment; filename=%s' % \
201 f_path.split(os.sep)[-1].encode('utf8', 'replace')
201 f_path.split(os.sep)[-1].encode('utf8', 'replace')
202
202
203 response.content_disposition = dispo
203 response.content_disposition = dispo
204 response.content_type = mimetype
204 response.content_type = mimetype
205 return file_node.content
205 return file_node.content
206
206
207 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
207 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
208 'repository.admin')
208 'repository.admin')
209 def annotate(self, repo_name, revision, f_path):
209 def annotate(self, repo_name, revision, f_path):
210 c.cs = self.__get_cs_or_redirect(revision, repo_name)
210 c.cs = self.__get_cs_or_redirect(revision, repo_name)
211 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
211 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
212
212
213 c.file_history = self._get_node_history(c.cs, f_path)
213 c.file_history = self._get_node_history(c.cs, f_path)
214 c.f_path = f_path
214 c.f_path = f_path
215 return render('files/files_annotate.html')
215 return render('files/files_annotate.html')
216
216
217 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
217 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
218 def edit(self, repo_name, revision, f_path):
218 def edit(self, repo_name, revision, f_path):
219 r_post = request.POST
219 r_post = request.POST
220
220
221 if c.rhodecode_repo.alias == 'hg':
221 if c.rhodecode_repo.alias == 'hg':
222 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
222 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
223 elif c.rhodecode_repo.alias == 'git':
223 elif c.rhodecode_repo.alias == 'git':
224 from vcs.backends.git import GitInMemoryChangeset as IMC
224 from vcs.backends.git import GitInMemoryChangeset as IMC
225
225
226 c.cs = self.__get_cs_or_redirect(revision, repo_name)
226 c.cs = self.__get_cs_or_redirect(revision, repo_name)
227 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
227 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
228
228
229 c.file_history = self._get_node_history(c.cs, f_path)
229 c.file_history = self._get_node_history(c.cs, f_path)
230 c.f_path = f_path
230 c.f_path = f_path
231
231
232 if r_post:
232 if r_post:
233
233
234 old_content = c.file.content
234 old_content = c.file.content
235 sl = old_content.splitlines(1)
235 sl = old_content.splitlines(1)
236 first_line = sl[0] if sl else ''
236 first_line = sl[0] if sl else ''
237 # modes: 0 - Unix, 1 - Mac, 2 - DOS
237 # modes: 0 - Unix, 1 - Mac, 2 - DOS
238 mode = detect_mode(first_line, 0)
238 mode = detect_mode(first_line, 0)
239 content = convert_line_endings(r_post.get('content'), mode)
239 content = convert_line_endings(r_post.get('content'), mode)
240
240
241 message = r_post.get('message') or (_('Edited %s via RhodeCode')
241 message = r_post.get('message') or (_('Edited %s via RhodeCode')
242 % (f_path))
242 % (f_path))
243
243
244 if content == old_content:
244 if content == old_content:
245 h.flash(_('No changes'),
245 h.flash(_('No changes'),
246 category='warning')
246 category='warning')
247 return redirect(url('changeset_home', repo_name=c.repo_name,
247 return redirect(url('changeset_home', repo_name=c.repo_name,
248 revision='tip'))
248 revision='tip'))
249
249
250 try:
250 try:
251 # decoding here will force that we have proper encoded values
252 # in any other case this will throw exceptions and deny commit
251 content = content.encode('utf8')
253 content = content.encode('utf8')
252 message = message.encode('utf8')
254 message = message.encode('utf8')
253 path = f_path.encode('utf8')
255 path = f_path.encode('utf8')
254 author = self.rhodecode_user.full_contact.encode('utf8')
256 author = self.rhodecode_user.full_contact.encode('utf8')
255 m = IMC(c.rhodecode_repo)
257 m = IMC(c.rhodecode_repo)
256 m.change(FileNode(path, content))
258 m.change(FileNode(path, content))
257 m.commit(message=message,
259 m.commit(message=message,
258 author=author,
260 author=author,
259 parents=[c.cs], branch=c.cs.branch)
261 parents=[c.cs], branch=c.cs.branch)
260 h.flash(_('Successfully committed to %s' % f_path),
262 h.flash(_('Successfully committed to %s' % f_path),
261 category='success')
263 category='success')
262
264
263 except Exception, e:
265 except Exception, e:
264 log.error(traceback.format_exc())
266 log.error(traceback.format_exc())
265 h.flash(_('Error occurred during commit'), category='error')
267 h.flash(_('Error occurred during commit'), category='error')
266 raise
267 return redirect(url('changeset_home',
268 return redirect(url('changeset_home',
268 repo_name=c.repo_name, revision='tip'))
269 repo_name=c.repo_name, revision='tip'))
269
270
270 return render('files/files_edit.html')
271 return render('files/files_edit.html')
271
272
272 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
273 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
273 'repository.admin')
274 'repository.admin')
274 def archivefile(self, repo_name, fname):
275 def archivefile(self, repo_name, fname):
275
276
276 fileformat = None
277 fileformat = None
277 revision = None
278 revision = None
278 ext = None
279 ext = None
279
280
280 for a_type, ext_data in ARCHIVE_SPECS.items():
281 for a_type, ext_data in ARCHIVE_SPECS.items():
281 archive_spec = fname.split(ext_data[1])
282 archive_spec = fname.split(ext_data[1])
282 if len(archive_spec) == 2 and archive_spec[1] == '':
283 if len(archive_spec) == 2 and archive_spec[1] == '':
283 fileformat = a_type or ext_data[1]
284 fileformat = a_type or ext_data[1]
284 revision = archive_spec[0]
285 revision = archive_spec[0]
285 ext = ext_data[1]
286 ext = ext_data[1]
286
287
287 try:
288 try:
288 dbrepo = RepoModel().get_by_repo_name(repo_name)
289 dbrepo = RepoModel().get_by_repo_name(repo_name)
289 if dbrepo.enable_downloads is False:
290 if dbrepo.enable_downloads is False:
290 return _('downloads disabled')
291 return _('downloads disabled')
291
292
292 cs = c.rhodecode_repo.get_changeset(revision)
293 cs = c.rhodecode_repo.get_changeset(revision)
293 content_type = ARCHIVE_SPECS[fileformat][0]
294 content_type = ARCHIVE_SPECS[fileformat][0]
294 except ChangesetDoesNotExistError:
295 except ChangesetDoesNotExistError:
295 return _('Unknown revision %s') % revision
296 return _('Unknown revision %s') % revision
296 except EmptyRepositoryError:
297 except EmptyRepositoryError:
297 return _('Empty repository')
298 return _('Empty repository')
298 except (ImproperArchiveTypeError, KeyError):
299 except (ImproperArchiveTypeError, KeyError):
299 return _('Unknown archive type')
300 return _('Unknown archive type')
300
301
301 response.content_type = content_type
302 response.content_type = content_type
302 response.content_disposition = 'attachment; filename=%s-%s%s' \
303 response.content_disposition = 'attachment; filename=%s-%s%s' \
303 % (repo_name, revision, ext)
304 % (repo_name, revision, ext)
304
305
305 return cs.get_chunked_archive(stream=None, kind=fileformat)
306 return cs.get_chunked_archive(stream=None, kind=fileformat)
306
307
307 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
308 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
308 'repository.admin')
309 'repository.admin')
309 def diff(self, repo_name, f_path):
310 def diff(self, repo_name, f_path):
310 diff1 = request.GET.get('diff1')
311 diff1 = request.GET.get('diff1')
311 diff2 = request.GET.get('diff2')
312 diff2 = request.GET.get('diff2')
312 c.action = request.GET.get('diff')
313 c.action = request.GET.get('diff')
313 c.no_changes = diff1 == diff2
314 c.no_changes = diff1 == diff2
314 c.f_path = f_path
315 c.f_path = f_path
315 c.big_diff = False
316 c.big_diff = False
316
317
317 try:
318 try:
318 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
319 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
319 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
320 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
320 node1 = c.changeset_1.get_node(f_path)
321 node1 = c.changeset_1.get_node(f_path)
321 else:
322 else:
322 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
323 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
323 node1 = FileNode('.', '', changeset=c.changeset_1)
324 node1 = FileNode('.', '', changeset=c.changeset_1)
324
325
325 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
326 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
326 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
327 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
327 node2 = c.changeset_2.get_node(f_path)
328 node2 = c.changeset_2.get_node(f_path)
328 else:
329 else:
329 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
330 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
330 node2 = FileNode('.', '', changeset=c.changeset_2)
331 node2 = FileNode('.', '', changeset=c.changeset_2)
331 except RepositoryError:
332 except RepositoryError:
332 return redirect(url('files_home',
333 return redirect(url('files_home',
333 repo_name=c.repo_name, f_path=f_path))
334 repo_name=c.repo_name, f_path=f_path))
334
335
335 if c.action == 'download':
336 if c.action == 'download':
336 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
337 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
337 format='gitdiff')
338 format='gitdiff')
338
339
339 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
340 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
340 response.content_type = 'text/plain'
341 response.content_type = 'text/plain'
341 response.content_disposition = 'attachment; filename=%s' \
342 response.content_disposition = 'attachment; filename=%s' \
342 % diff_name
343 % diff_name
343 return diff.raw_diff()
344 return diff.raw_diff()
344
345
345 elif c.action == 'raw':
346 elif c.action == 'raw':
346 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
347 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
347 format='gitdiff')
348 format='gitdiff')
348 response.content_type = 'text/plain'
349 response.content_type = 'text/plain'
349 return diff.raw_diff()
350 return diff.raw_diff()
350
351
351 elif c.action == 'diff':
352 elif c.action == 'diff':
352 if node1.is_binary or node2.is_binary:
353 if node1.is_binary or node2.is_binary:
353 c.cur_diff = _('Binary file')
354 c.cur_diff = _('Binary file')
354 elif node1.size > self.cut_off_limit or \
355 elif node1.size > self.cut_off_limit or \
355 node2.size > self.cut_off_limit:
356 node2.size > self.cut_off_limit:
356 c.cur_diff = ''
357 c.cur_diff = ''
357 c.big_diff = True
358 c.big_diff = True
358 else:
359 else:
359 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
360 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
360 format='gitdiff')
361 format='gitdiff')
361 c.cur_diff = diff.as_html()
362 c.cur_diff = diff.as_html()
362 else:
363 else:
363
364
364 #default option
365 #default option
365 if node1.is_binary or node2.is_binary:
366 if node1.is_binary or node2.is_binary:
366 c.cur_diff = _('Binary file')
367 c.cur_diff = _('Binary file')
367 elif node1.size > self.cut_off_limit or \
368 elif node1.size > self.cut_off_limit or \
368 node2.size > self.cut_off_limit:
369 node2.size > self.cut_off_limit:
369 c.cur_diff = ''
370 c.cur_diff = ''
370 c.big_diff = True
371 c.big_diff = True
371
372
372 else:
373 else:
373 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
374 diff = differ.DiffProcessor(differ.get_gitdiff(node1, node2),
374 format='gitdiff')
375 format='gitdiff')
375 c.cur_diff = diff.as_html()
376 c.cur_diff = diff.as_html()
376
377
377 if not c.cur_diff and not c.big_diff:
378 if not c.cur_diff and not c.big_diff:
378 c.no_changes = True
379 c.no_changes = True
379 return render('files/file_diff.html')
380 return render('files/file_diff.html')
380
381
381 def _get_node_history(self, cs, f_path):
382 def _get_node_history(self, cs, f_path):
382 changesets = cs.get_file_history(f_path)
383 changesets = cs.get_file_history(f_path)
383 hist_l = []
384 hist_l = []
384
385
385 changesets_group = ([], _("Changesets"))
386 changesets_group = ([], _("Changesets"))
386 branches_group = ([], _("Branches"))
387 branches_group = ([], _("Branches"))
387 tags_group = ([], _("Tags"))
388 tags_group = ([], _("Tags"))
388
389
389 for chs in changesets:
390 for chs in changesets:
390 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
391 n_desc = 'r%s:%s' % (chs.revision, chs.short_id)
391 changesets_group[0].append((chs.raw_id, n_desc,))
392 changesets_group[0].append((chs.raw_id, n_desc,))
392
393
393 hist_l.append(changesets_group)
394 hist_l.append(changesets_group)
394
395
395 for name, chs in c.rhodecode_repo.branches.items():
396 for name, chs in c.rhodecode_repo.branches.items():
396 #chs = chs.split(':')[-1]
397 #chs = chs.split(':')[-1]
397 branches_group[0].append((chs, name),)
398 branches_group[0].append((chs, name),)
398 hist_l.append(branches_group)
399 hist_l.append(branches_group)
399
400
400 for name, chs in c.rhodecode_repo.tags.items():
401 for name, chs in c.rhodecode_repo.tags.items():
401 #chs = chs.split(':')[-1]
402 #chs = chs.split(':')[-1]
402 tags_group[0].append((chs, name),)
403 tags_group[0].append((chs, name),)
403 hist_l.append(tags_group)
404 hist_l.append(tags_group)
404
405
405 return hist_l
406 return hist_l
@@ -1,166 +1,175 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.__init__
3 rhodecode.lib.__init__
4 ~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Some simple helper functions
6 Some simple helper functions
7
7
8 :created_on: Jan 5, 2011
8 :created_on: Jan 5, 2011
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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26
26
27
28 def __get_lem():
27 def __get_lem():
29 from pygments import lexers
28 from pygments import lexers
30 from string import lower
29 from string import lower
31 from collections import defaultdict
30 from collections import defaultdict
32
31
33 d = defaultdict(lambda: [])
32 d = defaultdict(lambda: [])
34
33
35 def __clean(s):
34 def __clean(s):
36 s = s.lstrip('*')
35 s = s.lstrip('*')
37 s = s.lstrip('.')
36 s = s.lstrip('.')
38
37
39 if s.find('[') != -1:
38 if s.find('[') != -1:
40 exts = []
39 exts = []
41 start, stop = s.find('['), s.find(']')
40 start, stop = s.find('['), s.find(']')
42
41
43 for suffix in s[start + 1:stop]:
42 for suffix in s[start + 1:stop]:
44 exts.append(s[:s.find('[')] + suffix)
43 exts.append(s[:s.find('[')] + suffix)
45 return map(lower, exts)
44 return map(lower, exts)
46 else:
45 else:
47 return map(lower, [s])
46 return map(lower, [s])
48
47
49 for lx, t in sorted(lexers.LEXERS.items()):
48 for lx, t in sorted(lexers.LEXERS.items()):
50 m = map(__clean, t[-2])
49 m = map(__clean, t[-2])
51 if m:
50 if m:
52 m = reduce(lambda x, y: x + y, m)
51 m = reduce(lambda x, y: x + y, m)
53 for ext in m:
52 for ext in m:
54 desc = lx.replace('Lexer', '')
53 desc = lx.replace('Lexer', '')
55 d[ext].append(desc)
54 d[ext].append(desc)
56
55
57 return dict(d)
56 return dict(d)
58
57
59 # language map is also used by whoosh indexer, which for those specified
58 # language map is also used by whoosh indexer, which for those specified
60 # extensions will index it's content
59 # extensions will index it's content
61 LANGUAGES_EXTENSIONS_MAP = __get_lem()
60 LANGUAGES_EXTENSIONS_MAP = __get_lem()
62
61
63 #Additional mappings that are not present in the pygments lexers
62 # Additional mappings that are not present in the pygments lexers
64 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
63 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
65 ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
64 ADDITIONAL_MAPPINGS = {'xaml': 'XAML'}
66
65
67 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
66 LANGUAGES_EXTENSIONS_MAP.update(ADDITIONAL_MAPPINGS)
68
67
68
69 def str2bool(_str):
69 def str2bool(_str):
70 """
70 """
71 returs True/False value from given string, it tries to translate the
71 returs True/False value from given string, it tries to translate the
72 string into boolean
72 string into boolean
73
73
74 :param _str: string value to translate into boolean
74 :param _str: string value to translate into boolean
75 :rtype: boolean
75 :rtype: boolean
76 :returns: boolean from given string
76 :returns: boolean from given string
77 """
77 """
78 if _str is None:
78 if _str is None:
79 return False
79 return False
80 if _str in (True, False):
80 if _str in (True, False):
81 return _str
81 return _str
82 _str = str(_str).strip().lower()
82 _str = str(_str).strip().lower()
83 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
83 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
84
84
85
85 def convert_line_endings(temp, mode):
86 def convert_line_endings(temp, mode):
86 from string import replace
87 from string import replace
87 #modes: 0 - Unix, 1 - Mac, 2 - DOS
88 #modes: 0 - Unix, 1 - Mac, 2 - DOS
88 if mode == 0:
89 if mode == 0:
89 temp = replace(temp, '\r\n', '\n')
90 temp = replace(temp, '\r\n', '\n')
90 temp = replace(temp, '\r', '\n')
91 temp = replace(temp, '\r', '\n')
91 elif mode == 1:
92 elif mode == 1:
92 temp = replace(temp, '\r\n', '\r')
93 temp = replace(temp, '\r\n', '\r')
93 temp = replace(temp, '\n', '\r')
94 temp = replace(temp, '\n', '\r')
94 elif mode == 2:
95 elif mode == 2:
95 import re
96 import re
96 temp = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", temp)
97 temp = re.sub("\r(?!\n)|(?<!\r)\n", "\r\n", temp)
97 return temp
98 return temp
98
99
99
100
100 def detect_mode(line, default):
101 def detect_mode(line, default):
102 """
103 Detects line break for given line, if line break couldn't be found
104 given default value is returned
105
106 :param line: str line
107 :param default: default
108 :rtype: int
109 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
110 """
101 if line.endswith('\r\n'):
111 if line.endswith('\r\n'):
102 return 2
112 return 2
103 elif line.endswith('\n'):
113 elif line.endswith('\n'):
104 return 0
114 return 0
105 elif line.endswith('\r'):
115 elif line.endswith('\r'):
106 return 1
116 return 1
107 else:
117 else:
108 return default
118 return default
109
119
120
110 def generate_api_key(username, salt=None):
121 def generate_api_key(username, salt=None):
111 """
122 """
112 Generates unique API key for given username,if salt is not given
123 Generates unique API key for given username,if salt is not given
113 it'll be generated from some random string
124 it'll be generated from some random string
114
125
115 :param username: username as string
126 :param username: username as string
116 :param salt: salt to hash generate KEY
127 :param salt: salt to hash generate KEY
117 :rtype: str
128 :rtype: str
118 :returns: sha1 hash from username+salt
129 :returns: sha1 hash from username+salt
119 """
130 """
120 from tempfile import _RandomNameSequence
131 from tempfile import _RandomNameSequence
121 import hashlib
132 import hashlib
122
133
123 if salt is None:
134 if salt is None:
124 salt = _RandomNameSequence().next()
135 salt = _RandomNameSequence().next()
125
136
126 return hashlib.sha1(username + salt).hexdigest()
137 return hashlib.sha1(username + salt).hexdigest()
127
138
128
139
129 def safe_unicode(_str, from_encoding='utf8'):
140 def safe_unicode(_str, from_encoding='utf8'):
130 """
141 """
131 safe unicode function. In case of UnicodeDecode error we try to return
142 safe unicode function. In case of UnicodeDecode error we try to return
132 unicode with errors replace
143 unicode with errors replace
133
144
134 :param _str: string to decode
145 :param _str: string to decode
135 :rtype: unicode
146 :rtype: unicode
136 :returns: unicode object
147 :returns: unicode object
137 """
148 """
138
149
139 if isinstance(_str, unicode):
150 if isinstance(_str, unicode):
140 return _str
151 return _str
141
152
142 try:
153 try:
143 u_str = unicode(_str, from_encoding)
154 u_str = unicode(_str, from_encoding)
144 except UnicodeDecodeError:
155 except UnicodeDecodeError:
145 u_str = unicode(_str, from_encoding, 'replace')
156 u_str = unicode(_str, from_encoding, 'replace')
146
157
147 return u_str
158 return u_str
148
159
149
160
150 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
161 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
151 """
162 """
152 Custom engine_from_config functions that makes sure we use NullPool for
163 Custom engine_from_config functions that makes sure we use NullPool for
153 file based sqlite databases. This prevents errors on sqlite.
164 file based sqlite databases. This prevents errors on sqlite.
154
165
155 """
166 """
156 from sqlalchemy import engine_from_config as efc
167 from sqlalchemy import engine_from_config as efc
157 from sqlalchemy.pool import NullPool
168 from sqlalchemy.pool import NullPool
158
169
159 url = configuration[prefix + 'url']
170 url = configuration[prefix + 'url']
160
171
161 if url.startswith('sqlite'):
172 if url.startswith('sqlite'):
162 kwargs.update({'poolclass':NullPool})
173 kwargs.update({'poolclass': NullPool})
163
174
164 return efc(configuration, prefix, **kwargs)
175 return efc(configuration, prefix, **kwargs)
165
166
@@ -1,19 +1,20 b''
1 """The application's Globals object"""
1 """The application's Globals object"""
2
2
3 from beaker.cache import CacheManager
3 from beaker.cache import CacheManager
4 from beaker.util import parse_cache_config_options
4 from beaker.util import parse_cache_config_options
5
5
6
6 class Globals(object):
7 class Globals(object):
7 """Globals acts as a container for objects available throughout the
8 """Globals acts as a container for objects available throughout the
8 life of the application
9 life of the application
9
10
10 """
11 """
11
12
12 def __init__(self, config):
13 def __init__(self, config):
13 """One instance of Globals is created during application
14 """One instance of Globals is created during application
14 initialization and is available during requests via the
15 initialization and is available during requests via the
15 'app_globals' variable
16 'app_globals' variable
16
17
17 """
18 """
18 self.cache = CacheManager(**parse_cache_config_options(config))
19 self.cache = CacheManager(**parse_cache_config_options(config))
19 self.available_permissions = None # propagated after init_model
20 self.available_permissions = None # propagated after init_model
@@ -1,104 +1,101 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # mercurial repository backup manager
3 rhodecode.lib.backup_manager
4 # Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Mercurial repositories backup manager, it allows to backups all
7 repositories and send it to backup server using RSA key via ssh.
8
9 :created_on: Feb 28, 2010
10 :copyright: (c) 2010 by marcink.
11 :license: LICENSE_NAME, see LICENSE_FILE for more details.
12 """
6 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18
25
19 """
26 import os
20 Created on Feb 28, 2010
27 import sys
21 Mercurial repositories backup manager
22 @author: marcink
23 """
24
25
28
26 import logging
29 import logging
27 import tarfile
30 import tarfile
28 import os
29 import datetime
31 import datetime
30 import sys
31 import subprocess
32 import subprocess
33
32 logging.basicConfig(level=logging.DEBUG,
34 logging.basicConfig(level=logging.DEBUG,
33 format="%(asctime)s %(levelname)-5.5s %(message)s")
35 format="%(asctime)s %(levelname)-5.5s %(message)s")
34
36
37
35 class BackupManager(object):
38 class BackupManager(object):
36 def __init__(self, repos_location, rsa_key, backup_server):
39 def __init__(self, repos_location, rsa_key, backup_server):
37 today = datetime.datetime.now().weekday() + 1
40 today = datetime.datetime.now().weekday() + 1
38 self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
41 self.backup_file_name = "mercurial_repos.%s.tar.gz" % today
39
42
40 self.id_rsa_path = self.get_id_rsa(rsa_key)
43 self.id_rsa_path = self.get_id_rsa(rsa_key)
41 self.repos_path = self.get_repos_path(repos_location)
44 self.repos_path = self.get_repos_path(repos_location)
42 self.backup_server = backup_server
45 self.backup_server = backup_server
43
46
44 self.backup_file_path = '/tmp'
47 self.backup_file_path = '/tmp'
45
48
46 logging.info('starting backup for %s', self.repos_path)
49 logging.info('starting backup for %s', self.repos_path)
47 logging.info('backup target %s', self.backup_file_path)
50 logging.info('backup target %s', self.backup_file_path)
48
51
49
50 def get_id_rsa(self, rsa_key):
52 def get_id_rsa(self, rsa_key):
51 if not os.path.isfile(rsa_key):
53 if not os.path.isfile(rsa_key):
52 logging.error('Could not load id_rsa key file in %s', rsa_key)
54 logging.error('Could not load id_rsa key file in %s', rsa_key)
53 sys.exit()
55 sys.exit()
54 return rsa_key
56 return rsa_key
55
57
56 def get_repos_path(self, path):
58 def get_repos_path(self, path):
57 if not os.path.isdir(path):
59 if not os.path.isdir(path):
58 logging.error('Wrong location for repositories in %s', path)
60 logging.error('Wrong location for repositories in %s', path)
59 sys.exit()
61 sys.exit()
60 return path
62 return path
61
63
62 def backup_repos(self):
64 def backup_repos(self):
63 bckp_file = os.path.join(self.backup_file_path, self.backup_file_name)
65 bckp_file = os.path.join(self.backup_file_path, self.backup_file_name)
64 tar = tarfile.open(bckp_file, "w:gz")
66 tar = tarfile.open(bckp_file, "w:gz")
65
67
66 for dir_name in os.listdir(self.repos_path):
68 for dir_name in os.listdir(self.repos_path):
67 logging.info('backing up %s', dir_name)
69 logging.info('backing up %s', dir_name)
68 tar.add(os.path.join(self.repos_path, dir_name), dir_name)
70 tar.add(os.path.join(self.repos_path, dir_name), dir_name)
69 tar.close()
71 tar.close()
70 logging.info('finished backup of mercurial repositories')
72 logging.info('finished backup of mercurial repositories')
71
73
72
73
74 def transfer_files(self):
74 def transfer_files(self):
75 params = {
75 params = {
76 'id_rsa_key': self.id_rsa_path,
76 'id_rsa_key': self.id_rsa_path,
77 'backup_file':os.path.join(self.backup_file_path,
77 'backup_file': os.path.join(self.backup_file_path,
78 self.backup_file_name),
78 self.backup_file_name),
79 'backup_server':self.backup_server
79 'backup_server': self.backup_server
80 }
80 }
81 cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params,
81 cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params,
82 '%(backup_file)s' % params,
82 '%(backup_file)s' % params,
83 '%(backup_server)s' % params]
83 '%(backup_server)s' % params]
84
84
85 subprocess.call(cmd)
85 subprocess.call(cmd)
86 logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4])
86 logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4])
87
87
88
89 def rm_file(self):
88 def rm_file(self):
90 logging.info('Removing file %s', self.backup_file_name)
89 logging.info('Removing file %s', self.backup_file_name)
91 os.remove(os.path.join(self.backup_file_path, self.backup_file_name))
90 os.remove(os.path.join(self.backup_file_path, self.backup_file_name))
92
91
93
94
95 if __name__ == "__main__":
92 if __name__ == "__main__":
96
93
97 repo_location = '/home/repo_path'
94 repo_location = '/home/repo_path'
98 backup_server = 'root@192.168.1.100:/backups/mercurial'
95 backup_server = 'root@192.168.1.100:/backups/mercurial'
99 rsa_key = '/home/id_rsa'
96 rsa_key = '/home/id_rsa'
100
97
101 B_MANAGER = BackupManager(repo_location, rsa_key, backup_server)
98 B_MANAGER = BackupManager(repo_location, rsa_key, backup_server)
102 B_MANAGER.backup_repos()
99 B_MANAGER.backup_repos()
103 B_MANAGER.transfer_files()
100 B_MANAGER.transfer_files()
104 B_MANAGER.rm_file()
101 B_MANAGER.rm_file()
@@ -1,79 +1,81 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import copy
5 import copy
6
6
7 from pylons import config, tmpl_context as c, request, session
7 from pylons import config, tmpl_context as c, request, session
8 from pylons.controllers import WSGIController
8 from pylons.controllers import WSGIController
9 from pylons.templating import render_mako as render
9 from pylons.templating import render_mako as render
10
10
11 from rhodecode import __version__
11 from rhodecode import __version__
12 from rhodecode.lib.auth import AuthUser
12 from rhodecode.lib.auth import AuthUser
13 from rhodecode.lib.utils import get_repo_slug
13 from rhodecode.lib.utils import get_repo_slug
14 from rhodecode.model import meta
14 from rhodecode.model import meta
15 from rhodecode.model.scm import ScmModel
15 from rhodecode.model.scm import ScmModel
16 from rhodecode import BACKENDS
16 from rhodecode import BACKENDS
17
17
18
18 class BaseController(WSGIController):
19 class BaseController(WSGIController):
19
20
20 def __before__(self):
21 def __before__(self):
21 c.rhodecode_version = __version__
22 c.rhodecode_version = __version__
22 c.rhodecode_name = config.get('rhodecode_title')
23 c.rhodecode_name = config.get('rhodecode_title')
23 c.ga_code = config.get('rhodecode_ga_code')
24 c.ga_code = config.get('rhodecode_ga_code')
24 c.repo_name = get_repo_slug(request)
25 c.repo_name = get_repo_slug(request)
25 c.backends = BACKENDS.keys()
26 c.backends = BACKENDS.keys()
26 self.cut_off_limit = int(config.get('cut_off_limit'))
27 self.cut_off_limit = int(config.get('cut_off_limit'))
27
28
28 self.sa = meta.Session()
29 self.sa = meta.Session()
29 self.scm_model = ScmModel(self.sa)
30 self.scm_model = ScmModel(self.sa)
30 c.cached_repo_list = self.scm_model.get_repos()
31 c.cached_repo_list = self.scm_model.get_repos()
31 #c.unread_journal = scm_model.get_unread_journal()
32 #c.unread_journal = scm_model.get_unread_journal()
32
33
33 def __call__(self, environ, start_response):
34 def __call__(self, environ, start_response):
34 """Invoke the Controller"""
35 """Invoke the Controller"""
35 # WSGIController.__call__ dispatches to the Controller method
36 # WSGIController.__call__ dispatches to the Controller method
36 # the request is routed to. This routing information is
37 # the request is routed to. This routing information is
37 # available in environ['pylons.routes_dict']
38 # available in environ['pylons.routes_dict']
38 try:
39 try:
39 #putting this here makes sure that we update permissions every time
40 # putting this here makes sure that we update permissions each time
40 api_key = request.GET.get('api_key')
41 api_key = request.GET.get('api_key')
41 user_id = getattr(session.get('rhodecode_user'), 'user_id', None)
42 user_id = getattr(session.get('rhodecode_user'), 'user_id', None)
42 self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key)
43 self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key)
43 self.rhodecode_user.set_authenticated(
44 self.rhodecode_user.set_authenticated(
44 getattr(session.get('rhodecode_user'),
45 getattr(session.get('rhodecode_user'),
45 'is_authenticated', False))
46 'is_authenticated', False))
46 session['rhodecode_user'] = self.rhodecode_user
47 session['rhodecode_user'] = self.rhodecode_user
47 session.save()
48 session.save()
48 return WSGIController.__call__(self, environ, start_response)
49 return WSGIController.__call__(self, environ, start_response)
49 finally:
50 finally:
50 meta.Session.remove()
51 meta.Session.remove()
51
52
52
53
53 class BaseRepoController(BaseController):
54 class BaseRepoController(BaseController):
54 """
55 """
55 Base class for controllers responsible for loading all needed data
56 Base class for controllers responsible for loading all needed data
56 for those controllers, loaded items are
57 for those controllers, loaded items are
57
58
58 c.rhodecode_repo: instance of scm repository (taken from cache)
59 c.rhodecode_repo: instance of scm repository (taken from cache)
59
60
60 """
61 """
61
62
62 def __before__(self):
63 def __before__(self):
63 super(BaseRepoController, self).__before__()
64 super(BaseRepoController, self).__before__()
64 if c.repo_name:
65 if c.repo_name:
65
66
66 r, dbrepo = self.scm_model.get(c.repo_name, retval='repo')
67 r, dbrepo = self.scm_model.get(c.repo_name, retval='repo')
67
68
68 if r is not None:
69 if r is not None:
69 c.repository_followers = self.scm_model.get_followers(c.repo_name)
70 c.repository_followers = \
71 self.scm_model.get_followers(c.repo_name)
70 c.repository_forks = self.scm_model.get_forks(c.repo_name)
72 c.repository_forks = self.scm_model.get_forks(c.repo_name)
71 else:
73 else:
72 c.repository_followers = 0
74 c.repository_followers = 0
73 c.repository_forks = 0
75 c.repository_forks = 0
74
76
75 # Since RhodeCode uses heavy memory caching we make a deepcopy
77 # Since RhodeCode uses heavy memory caching we make a deepcopy
76 # of object taken from cache. This way we lose reference to cached
78 # of object taken from cache. This way we lose reference to cached
77 # instance in memory and keep it relatively small even for
79 # instance in memory and keep it relatively small even for
78 # very large number of changesets
80 # very large number of changesets
79 c.rhodecode_repo = copy.copy(r)
81 c.rhodecode_repo = copy.copy(r)
@@ -1,82 +1,85 b''
1
1
2 import logging
2 import logging
3
3
4 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
4 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
5
5
6 # Sequences
6 # Sequences
7 RESET_SEQ = "\033[0m"
7 RESET_SEQ = "\033[0m"
8 COLOR_SEQ = "\033[1;%dm"
8 COLOR_SEQ = "\033[1;%dm"
9 BOLD_SEQ = "\033[1m"
9 BOLD_SEQ = "\033[1m"
10
10
11 COLORS = {
11 COLORS = {
12 'CRITICAL': MAGENTA, # level 50
12 'CRITICAL': MAGENTA,
13 'ERROR': RED, # level 40
13 'ERROR': RED,
14 'WARNING': CYAN, # level 30
14 'WARNING': CYAN,
15 'INFO': GREEN, # level 20
15 'INFO': GREEN,
16 'DEBUG': BLUE, # level 10
16 'DEBUG': BLUE,
17 'SQL' : YELLOW
17 'SQL': YELLOW
18 }
18 }
19
19
20
20 def one_space_trim(s):
21 def one_space_trim(s):
21 if s.find(" ") == -1:
22 if s.find(" ") == -1:
22 return s
23 return s
23 else:
24 else:
24 s = s.replace(' ', ' ')
25 s = s.replace(' ', ' ')
25 return one_space_trim(s)
26 return one_space_trim(s)
26
27
28
27 def format_sql(sql):
29 def format_sql(sql):
28 sql = sql.replace('\n', '')
30 sql = sql.replace('\n', '')
29 sql = one_space_trim(sql)
31 sql = one_space_trim(sql)
30 sql = sql\
32 sql = sql\
31 .replace(',', ',\n\t')\
33 .replace(',', ',\n\t')\
32 .replace('SELECT', '\n\tSELECT \n\t')\
34 .replace('SELECT', '\n\tSELECT \n\t')\
33 .replace('UPDATE', '\n\tUPDATE \n\t')\
35 .replace('UPDATE', '\n\tUPDATE \n\t')\
34 .replace('DELETE', '\n\tDELETE \n\t')\
36 .replace('DELETE', '\n\tDELETE \n\t')\
35 .replace('FROM', '\n\tFROM')\
37 .replace('FROM', '\n\tFROM')\
36 .replace('ORDER BY', '\n\tORDER BY')\
38 .replace('ORDER BY', '\n\tORDER BY')\
37 .replace('LIMIT', '\n\tLIMIT')\
39 .replace('LIMIT', '\n\tLIMIT')\
38 .replace('WHERE', '\n\tWHERE')\
40 .replace('WHERE', '\n\tWHERE')\
39 .replace('AND', '\n\tAND')\
41 .replace('AND', '\n\tAND')\
40 .replace('LEFT', '\n\tLEFT')\
42 .replace('LEFT', '\n\tLEFT')\
41 .replace('INNER', '\n\tINNER')\
43 .replace('INNER', '\n\tINNER')\
42 .replace('INSERT', '\n\tINSERT')\
44 .replace('INSERT', '\n\tINSERT')\
43 .replace('DELETE', '\n\tDELETE')
45 .replace('DELETE', '\n\tDELETE')
44 return sql
46 return sql
45
47
48
46 class ColorFormatter(logging.Formatter):
49 class ColorFormatter(logging.Formatter):
47
50
48 def __init__(self, *args, **kwargs):
51 def __init__(self, *args, **kwargs):
49 # can't do super(...) here because Formatter is an old school class
52 # can't do super(...) here because Formatter is an old school class
50 logging.Formatter.__init__(self, *args, **kwargs)
53 logging.Formatter.__init__(self, *args, **kwargs)
51
54
52 def format(self, record):
55 def format(self, record):
53 """
56 """
54 Changes record's levelname to use with COLORS enum
57 Changes record's levelname to use with COLORS enum
55 """
58 """
56
59
57 levelname = record.levelname
60 levelname = record.levelname
58 start = COLOR_SEQ % (COLORS[levelname])
61 start = COLOR_SEQ % (COLORS[levelname])
59 def_record = logging.Formatter.format(self, record)
62 def_record = logging.Formatter.format(self, record)
60 end = RESET_SEQ
63 end = RESET_SEQ
61
64
62 colored_record = start + def_record + end
65 colored_record = start + def_record + end
63 return colored_record
66 return colored_record
64
67
65
68
66 class ColorFormatterSql(logging.Formatter):
69 class ColorFormatterSql(logging.Formatter):
67
70
68 def __init__(self, *args, **kwargs):
71 def __init__(self, *args, **kwargs):
69 # can't do super(...) here because Formatter is an old school class
72 # can't do super(...) here because Formatter is an old school class
70 logging.Formatter.__init__(self, *args, **kwargs)
73 logging.Formatter.__init__(self, *args, **kwargs)
71
74
72 def format(self, record):
75 def format(self, record):
73 """
76 """
74 Changes record's levelname to use with COLORS enum
77 Changes record's levelname to use with COLORS enum
75 """
78 """
76
79
77 start = COLOR_SEQ % (COLORS['SQL'])
80 start = COLOR_SEQ % (COLORS['SQL'])
78 def_record = format_sql(logging.Formatter.format(self, record))
81 def_record = format_sql(logging.Formatter.format(self, record))
79 end = RESET_SEQ
82 end = RESET_SEQ
80
83
81 colored_record = start + def_record + end
84 colored_record = start + def_record + end
82 return colored_record
85 return colored_record
@@ -1,30 +1,47 b''
1 #!/usr/bin/env python
1 # -*- coding: utf-8 -*-
2 # encoding: utf-8
2 """
3 # Custom Exceptions modules
3 rhodecode.lib.exceptions
4 # Copyright (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5 #
5
6 Set of custom exceptions used in RhodeCode
7
8 :created_on: Nov 17, 2010
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
6 # This program is free software: you can redistribute it and/or modify
12 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
13 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
14 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
15 # (at your option) any later version.
10 #
16 #
11 # This program is distributed in the hope that it will be useful,
17 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
20 # GNU General Public License for more details.
15 #
21 #
16 # You should have received a copy of the GNU General Public License
22 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 """
24
19 Created on Nov 17, 2010
25
20 Custom Exceptions modules
26 class LdapUsernameError(Exception):
21 @author: marcink
27 pass
22 """
28
29
30 class LdapPasswordError(Exception):
31 pass
32
23
33
24 class LdapUsernameError(Exception):pass
34 class LdapConnectionError(Exception):
25 class LdapPasswordError(Exception):pass
35 pass
26 class LdapConnectionError(Exception):pass
36
27 class LdapImportError(Exception):pass
37
38 class LdapImportError(Exception):
39 pass
28
40
29 class DefaultUserException(Exception):pass
41
30 class UserOwnsReposException(Exception):pass
42 class DefaultUserException(Exception):
43 pass
44
45
46 class UserOwnsReposException(Exception):
47 pass
@@ -1,113 +1,116 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.hooks
3 rhodecode.lib.hooks
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Hooks runned by rhodecode
6 Hooks runned by rhodecode
7
7
8 :created_on: Aug 6, 2010
8 :created_on: Aug 6, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 import os
25 import os
26 import sys
26 import sys
27 import getpass
27 import getpass
28
28
29 from mercurial.cmdutil import revrange
29 from mercurial.cmdutil import revrange
30 from mercurial.node import nullrev
30 from mercurial.node import nullrev
31
31
32 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
33 from rhodecode.lib.utils import action_logger
33 from rhodecode.lib.utils import action_logger
34
34
35
35 def repo_size(ui, repo, hooktype=None, **kwargs):
36 def repo_size(ui, repo, hooktype=None, **kwargs):
36 """Presents size of repository after push
37 """Presents size of repository after push
37
38
38 :param ui:
39 :param ui:
39 :param repo:
40 :param repo:
40 :param hooktype:
41 :param hooktype:
41 """
42 """
42
43
43 if hooktype != 'changegroup':
44 if hooktype != 'changegroup':
44 return False
45 return False
45 size_hg, size_root = 0, 0
46 size_hg, size_root = 0, 0
46 for path, dirs, files in os.walk(repo.root):
47 for path, dirs, files in os.walk(repo.root):
47 if path.find('.hg') != -1:
48 if path.find('.hg') != -1:
48 for f in files:
49 for f in files:
49 try:
50 try:
50 size_hg += os.path.getsize(os.path.join(path, f))
51 size_hg += os.path.getsize(os.path.join(path, f))
51 except OSError:
52 except OSError:
52 pass
53 pass
53 else:
54 else:
54 for f in files:
55 for f in files:
55 try:
56 try:
56 size_root += os.path.getsize(os.path.join(path, f))
57 size_root += os.path.getsize(os.path.join(path, f))
57 except OSError:
58 except OSError:
58 pass
59 pass
59
60
60 size_hg_f = h.format_byte_size(size_hg)
61 size_hg_f = h.format_byte_size(size_hg)
61 size_root_f = h.format_byte_size(size_root)
62 size_root_f = h.format_byte_size(size_root)
62 size_total_f = h.format_byte_size(size_root + size_hg)
63 size_total_f = h.format_byte_size(size_root + size_hg)
63 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
64 sys.stdout.write('Repository size .hg:%s repo:%s total:%s\n' \
64 % (size_hg_f, size_root_f, size_total_f))
65 % (size_hg_f, size_root_f, size_total_f))
65
66
67
66 def log_pull_action(ui, repo, **kwargs):
68 def log_pull_action(ui, repo, **kwargs):
67 """Logs user last pull action
69 """Logs user last pull action
68
70
69 :param ui:
71 :param ui:
70 :param repo:
72 :param repo:
71 """
73 """
72
74
73 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
75 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
74 username = extra_params['username']
76 username = extra_params['username']
75 repository = extra_params['repository']
77 repository = extra_params['repository']
76 action = 'pull'
78 action = 'pull'
77
79
78 action_logger(username, action, repository, extra_params['ip'])
80 action_logger(username, action, repository, extra_params['ip'])
79
81
80 return 0
82 return 0
81
83
84
82 def log_push_action(ui, repo, **kwargs):
85 def log_push_action(ui, repo, **kwargs):
83 """Maps user last push action to new changeset id, from mercurial
86 """Maps user last push action to new changeset id, from mercurial
84
87
85 :param ui:
88 :param ui:
86 :param repo:
89 :param repo:
87 """
90 """
88
91
89 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
92 extra_params = dict(repo.ui.configitems('rhodecode_extras'))
90 username = extra_params['username']
93 username = extra_params['username']
91 repository = extra_params['repository']
94 repository = extra_params['repository']
92 action = extra_params['action'] + ':%s'
95 action = extra_params['action'] + ':%s'
93 node = kwargs['node']
96 node = kwargs['node']
94
97
95 def get_revs(repo, rev_opt):
98 def get_revs(repo, rev_opt):
96 if rev_opt:
99 if rev_opt:
97 revs = revrange(repo, rev_opt)
100 revs = revrange(repo, rev_opt)
98
101
99 if len(revs) == 0:
102 if len(revs) == 0:
100 return (nullrev, nullrev)
103 return (nullrev, nullrev)
101 return (max(revs), min(revs))
104 return (max(revs), min(revs))
102 else:
105 else:
103 return (len(repo) - 1, 0)
106 return (len(repo) - 1, 0)
104
107
105 stop, start = get_revs(repo, [node + ':'])
108 stop, start = get_revs(repo, [node + ':'])
106
109
107 revs = (str(repo[r]) for r in xrange(start, stop + 1))
110 revs = (str(repo[r]) for r in xrange(start, stop + 1))
108
111
109 action = action % ','.join(revs)
112 action = action % ','.join(revs)
110
113
111 action_logger(username, action, repository, extra_params['ip'])
114 action_logger(username, action, repository, extra_params['ip'])
112
115
113 return 0
116 return 0
@@ -1,133 +1,142 b''
1 import os, time
1 import os
2 import sys
2 import sys
3 import time
4 import errno
5
3 from warnings import warn
6 from warnings import warn
4 from multiprocessing.util import Finalize
7 from multiprocessing.util import Finalize
5 import errno
6
8
7 from rhodecode import __platform__, PLATFORM_WIN
9 from rhodecode import __platform__, PLATFORM_WIN
8
10
9 if __platform__ in PLATFORM_WIN:
11 if __platform__ in PLATFORM_WIN:
10 import ctypes
12 import ctypes
13
11 def kill(pid):
14 def kill(pid):
12 """kill function for Win32"""
15 """kill function for Win32"""
13 kernel32 = ctypes.windll.kernel32
16 kernel32 = ctypes.windll.kernel32
14 handle = kernel32.OpenProcess(1, 0, pid)
17 handle = kernel32.OpenProcess(1, 0, pid)
15 return (0 != kernel32.TerminateProcess(handle, 0))
18 return (0 != kernel32.TerminateProcess(handle, 0))
16
19
17 else:
20 else:
18 kill = os.kill
21 kill = os.kill
19
22
20 class LockHeld(Exception):pass
23
24 class LockHeld(Exception):
25 pass
21
26
22
27
23 class DaemonLock(object):
28 class DaemonLock(object):
24 """daemon locking
29 """daemon locking
25 USAGE:
30 USAGE:
26 try:
31 try:
27 l = DaemonLock(desc='test lock')
32 l = DaemonLock(desc='test lock')
28 main()
33 main()
29 l.release()
34 l.release()
30 except LockHeld:
35 except LockHeld:
31 sys.exit(1)
36 sys.exit(1)
32 """
37 """
33
38
34 def __init__(self, file=None, callbackfn=None,
39 def __init__(self, file=None, callbackfn=None,
35 desc='daemon lock', debug=False):
40 desc='daemon lock', debug=False):
36
41
37 self.pidfile = file if file else os.path.join(os.path.dirname(__file__),
42 self.pidfile = file if file else os.path.join(
43 os.path.dirname(__file__),
38 'running.lock')
44 'running.lock')
39 self.callbackfn = callbackfn
45 self.callbackfn = callbackfn
40 self.desc = desc
46 self.desc = desc
41 self.debug = debug
47 self.debug = debug
42 self.held = False
48 self.held = False
43 #run the lock automatically !
49 #run the lock automatically !
44 self.lock()
50 self.lock()
45 self._finalize = Finalize(self, DaemonLock._on_finalize,
51 self._finalize = Finalize(self, DaemonLock._on_finalize,
46 args=(self, debug), exitpriority=10)
52 args=(self, debug), exitpriority=10)
47
53
48 @staticmethod
54 @staticmethod
49 def _on_finalize(lock, debug):
55 def _on_finalize(lock, debug):
50 if lock.held:
56 if lock.held:
51 if debug:
57 if debug:
52 print 'leck held finilazing and running lock.release()'
58 print 'leck held finilazing and running lock.release()'
53 lock.release()
59 lock.release()
54
60
55
56 def lock(self):
61 def lock(self):
57 """locking function, if lock is present it will raise LockHeld exception
62 """
63 locking function, if lock is present it
64 will raise LockHeld exception
58 """
65 """
59 lockname = '%s' % (os.getpid())
66 lockname = '%s' % (os.getpid())
60 if self.debug:
67 if self.debug:
61 print 'running lock'
68 print 'running lock'
62 self.trylock()
69 self.trylock()
63 self.makelock(lockname, self.pidfile)
70 self.makelock(lockname, self.pidfile)
64 return True
71 return True
65
72
66 def trylock(self):
73 def trylock(self):
67 running_pid = False
74 running_pid = False
68 if self.debug:
75 if self.debug:
69 print 'checking for already running process'
76 print 'checking for already running process'
70 try:
77 try:
71 pidfile = open(self.pidfile, "r")
78 pidfile = open(self.pidfile, "r")
72 pidfile.seek(0)
79 pidfile.seek(0)
73 running_pid = int(pidfile.readline())
80 running_pid = int(pidfile.readline())
74
81
75 pidfile.close()
82 pidfile.close()
76
83
77 if self.debug:
84 if self.debug:
78 print 'lock file present running_pid: %s, checking for execution'\
85 print ('lock file present running_pid: %s, '
79 % running_pid
86 'checking for execution') % running_pid
80 # Now we check the PID from lock file matches to the current
87 # Now we check the PID from lock file matches to the current
81 # process PID
88 # process PID
82 if running_pid:
89 if running_pid:
83 try:
90 try:
84 kill(running_pid, 0)
91 kill(running_pid, 0)
85 except OSError, exc:
92 except OSError, exc:
86 if exc.errno in (errno.ESRCH, errno.EPERM):
93 if exc.errno in (errno.ESRCH, errno.EPERM):
87 print "Lock File is there but the program is not running"
94 print ("Lock File is there but"
95 " the program is not running")
88 print "Removing lock file for the: %s" % running_pid
96 print "Removing lock file for the: %s" % running_pid
89 self.release()
97 self.release()
90 else:
98 else:
91 raise
99 raise
92 else:
100 else:
93 print "You already have an instance of the program running"
101 print "You already have an instance of the program running"
94 print "It is running as process %s" % running_pid
102 print "It is running as process %s" % running_pid
95 raise LockHeld()
103 raise LockHeld()
96
104
97 except IOError, e:
105 except IOError, e:
98 if e.errno != 2:
106 if e.errno != 2:
99 raise
107 raise
100
108
101 def release(self):
109 def release(self):
102 """releases the pid by removing the pidfile
110 """releases the pid by removing the pidfile
103 """
111 """
104 if self.debug:
112 if self.debug:
105 print 'trying to release the pidlock'
113 print 'trying to release the pidlock'
106
114
107 if self.callbackfn:
115 if self.callbackfn:
108 #execute callback function on release
116 #execute callback function on release
109 if self.debug:
117 if self.debug:
110 print 'executing callback function %s' % self.callbackfn
118 print 'executing callback function %s' % self.callbackfn
111 self.callbackfn()
119 self.callbackfn()
112 try:
120 try:
113 if self.debug:
121 if self.debug:
114 print 'removing pidfile %s' % self.pidfile
122 print 'removing pidfile %s' % self.pidfile
115 os.remove(self.pidfile)
123 os.remove(self.pidfile)
116 self.held = False
124 self.held = False
117 except OSError, e:
125 except OSError, e:
118 if self.debug:
126 if self.debug:
119 print 'removing pidfile failed %s' % e
127 print 'removing pidfile failed %s' % e
120 pass
128 pass
121
129
122 def makelock(self, lockname, pidfile):
130 def makelock(self, lockname, pidfile):
123 """
131 """
124 this function will make an actual lock
132 this function will make an actual lock
133
125 :param lockname: acctual pid of file
134 :param lockname: acctual pid of file
126 :param pidfile: the file to write the pid in
135 :param pidfile: the file to write the pid in
127 """
136 """
128 if self.debug:
137 if self.debug:
129 print 'creating a file %s and pid: %s' % (pidfile, lockname)
138 print 'creating a file %s and pid: %s' % (pidfile, lockname)
130 pidfile = open(self.pidfile, "wb")
139 pidfile = open(self.pidfile, "wb")
131 pidfile.write(lockname)
140 pidfile.write(lockname)
132 pidfile.close
141 pidfile.close
133 self.held = True
142 self.held = True
@@ -1,51 +1,53 b''
1 from __future__ import with_statement
1 from __future__ import with_statement
2
2
3 import cProfile
3 import cProfile
4 import pstats
4 import pstats
5 import cgi
5 import cgi
6 import pprint
6 import pprint
7 import threading
7 import threading
8
8
9 from StringIO import StringIO
9 from StringIO import StringIO
10
10
11
11 class ProfilingMiddleware(object):
12 class ProfilingMiddleware(object):
12 def __init__(self, app):
13 def __init__(self, app):
13 self.lock = threading.Lock()
14 self.lock = threading.Lock()
14 self.app = app
15 self.app = app
15
16
16
17 def __call__(self, environ, start_response):
17 def __call__(self, environ, start_response):
18 with self.lock:
18 with self.lock:
19 profiler = cProfile.Profile()
19 profiler = cProfile.Profile()
20
20 def run_app(*a, **kw):
21 def run_app(*a, **kw):
21 self.response = self.app(environ, start_response)
22 self.response = self.app(environ, start_response)
22
23
23 profiler.runcall(run_app, environ, start_response)
24 profiler.runcall(run_app, environ, start_response)
24
25
25 profiler.snapshot_stats()
26 profiler.snapshot_stats()
26
27
27 stats = pstats.Stats(profiler)
28 stats = pstats.Stats(profiler)
28 stats.sort_stats('cumulative')
29 stats.sort_stats('cumulative')
29
30
30 # Redirect output
31 # Redirect output
31 out = StringIO()
32 out = StringIO()
32 stats.stream = out
33 stats.stream = out
33
34
34 stats.print_stats()
35 stats.print_stats()
35
36
36 resp = ''.join(self.response)
37 resp = ''.join(self.response)
37
38
38 # Lets at least only put this on html-like responses.
39 # Lets at least only put this on html-like responses.
39 if resp.strip().startswith('<'):
40 if resp.strip().startswith('<'):
40 ## The profiling info is just appended to the response.
41 ## The profiling info is just appended to the response.
41 ## Browsers don't mind this.
42 ## Browsers don't mind this.
42 resp += '<pre style="text-align:left; border-top: 4px dashed red; padding: 1em;">'
43 resp += ('<pre style="text-align:left; '
44 'border-top: 4px dashed red; padding: 1em;">')
43 resp += cgi.escape(out.getvalue(), True)
45 resp += cgi.escape(out.getvalue(), True)
44
46
45 output = StringIO()
47 output = StringIO()
46 pprint.pprint(environ, output, depth=3)
48 pprint.pprint(environ, output, depth=3)
47
49
48 resp += cgi.escape(output.getvalue(), True)
50 resp += cgi.escape(output.getvalue(), True)
49 resp += '</pre>'
51 resp += '</pre>'
50
52
51 return resp
53 return resp
@@ -1,143 +1,143 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.smtp_mailer
3 rhodecode.lib.smtp_mailer
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Simple smtp mailer used in RhodeCode
6 Simple smtp mailer used in RhodeCode
7
7
8 :created_on: Sep 13, 2010
8 :created_on: Sep 13, 2010
9 :copyright: (c) 2011 by marcink.
9 :copyright: (c) 2011 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
11 """
12
12
13 import logging
13 import logging
14 import smtplib
14 import smtplib
15 import mimetypes
15 import mimetypes
16 from socket import sslerror
16 from socket import sslerror
17
17
18 from email.mime.multipart import MIMEMultipart
18 from email.mime.multipart import MIMEMultipart
19 from email.mime.image import MIMEImage
19 from email.mime.image import MIMEImage
20 from email.mime.audio import MIMEAudio
20 from email.mime.audio import MIMEAudio
21 from email.mime.base import MIMEBase
21 from email.mime.base import MIMEBase
22 from email.mime.text import MIMEText
22 from email.mime.text import MIMEText
23 from email.utils import formatdate
23 from email.utils import formatdate
24 from email import encoders
24 from email import encoders
25
25
26
26 class SmtpMailer(object):
27 class SmtpMailer(object):
27 """SMTP mailer class
28 """SMTP mailer class
28
29
29 mailer = SmtpMailer(mail_from, user, passwd, mail_server, mail_port, ssl, tls)
30 mailer = SmtpMailer(mail_from, user, passwd, mail_server,
31 mail_port, ssl, tls)
30 mailer.send(recipients, subject, body, attachment_files)
32 mailer.send(recipients, subject, body, attachment_files)
31
33
32 :param recipients might be a list of string or single string
34 :param recipients might be a list of string or single string
33 :param attachment_files is a dict of {filename:location}
35 :param attachment_files is a dict of {filename:location}
34 it tries to guess the mimetype and attach the file
36 it tries to guess the mimetype and attach the file
35
37
36 """
38 """
37
39
38 def __init__(self, mail_from, user, passwd, mail_server,
40 def __init__(self, mail_from, user, passwd, mail_server,
39 mail_port=None, ssl=False, tls=False, debug=False):
41 mail_port=None, ssl=False, tls=False, debug=False):
40
42
41 self.mail_from = mail_from
43 self.mail_from = mail_from
42 self.mail_server = mail_server
44 self.mail_server = mail_server
43 self.mail_port = mail_port
45 self.mail_port = mail_port
44 self.user = user
46 self.user = user
45 self.passwd = passwd
47 self.passwd = passwd
46 self.ssl = ssl
48 self.ssl = ssl
47 self.tls = tls
49 self.tls = tls
48 self.debug = debug
50 self.debug = debug
49
51
50 def send(self, recipients=[], subject='', body='', attachment_files=None):
52 def send(self, recipients=[], subject='', body='', attachment_files=None):
51
53
52 if isinstance(recipients, basestring):
54 if isinstance(recipients, basestring):
53 recipients = [recipients]
55 recipients = [recipients]
54 if self.ssl:
56 if self.ssl:
55 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
57 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
56 else:
58 else:
57 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
59 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
58
60
59 if self.tls:
61 if self.tls:
60 smtp_serv.ehlo()
62 smtp_serv.ehlo()
61 smtp_serv.starttls()
63 smtp_serv.starttls()
62
64
63 if self.debug:
65 if self.debug:
64 smtp_serv.set_debuglevel(1)
66 smtp_serv.set_debuglevel(1)
65
67
66 smtp_serv.ehlo()
68 smtp_serv.ehlo()
67
69
68 #if server requires authorization you must provide login and password
70 #if server requires authorization you must provide login and password
69 #but only if we have them
71 #but only if we have them
70 if self.user and self.passwd:
72 if self.user and self.passwd:
71 smtp_serv.login(self.user, self.passwd)
73 smtp_serv.login(self.user, self.passwd)
72
74
73
74 date_ = formatdate(localtime=True)
75 date_ = formatdate(localtime=True)
75 msg = MIMEMultipart()
76 msg = MIMEMultipart()
76 msg['From'] = self.mail_from
77 msg['From'] = self.mail_from
77 msg['To'] = ','.join(recipients)
78 msg['To'] = ','.join(recipients)
78 msg['Date'] = date_
79 msg['Date'] = date_
79 msg['Subject'] = subject
80 msg['Subject'] = subject
80 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
81 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
81
82
82 msg.attach(MIMEText(body))
83 msg.attach(MIMEText(body))
83
84
84 if attachment_files:
85 if attachment_files:
85 self.__atach_files(msg, attachment_files)
86 self.__atach_files(msg, attachment_files)
86
87
87 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
88 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
88 logging.info('MAIL SEND TO: %s' % recipients)
89 logging.info('MAIL SEND TO: %s' % recipients)
89
90
90 try:
91 try:
91 smtp_serv.quit()
92 smtp_serv.quit()
92 except sslerror:
93 except sslerror:
93 # sslerror is raised in tls connections on closing sometimes
94 # sslerror is raised in tls connections on closing sometimes
94 pass
95 pass
95
96
96
97
98 def __atach_files(self, msg, attachment_files):
97 def __atach_files(self, msg, attachment_files):
99 if isinstance(attachment_files, dict):
98 if isinstance(attachment_files, dict):
100 for f_name, msg_file in attachment_files.items():
99 for f_name, msg_file in attachment_files.items():
101 ctype, encoding = mimetypes.guess_type(f_name)
100 ctype, encoding = mimetypes.guess_type(f_name)
102 logging.info("guessing file %s type based on %s" , ctype, f_name)
101 logging.info("guessing file %s type based on %s", ctype,
102 f_name)
103 if ctype is None or encoding is not None:
103 if ctype is None or encoding is not None:
104 # No guess could be made, or the file is encoded (compressed), so
104 # No guess could be made, or the file is encoded
105 # use a generic bag-of-bits type.
105 # (compressed), so use a generic bag-of-bits type.
106 ctype = 'application/octet-stream'
106 ctype = 'application/octet-stream'
107 maintype, subtype = ctype.split('/', 1)
107 maintype, subtype = ctype.split('/', 1)
108 if maintype == 'text':
108 if maintype == 'text':
109 # Note: we should handle calculating the charset
109 # Note: we should handle calculating the charset
110 file_part = MIMEText(self.get_content(msg_file),
110 file_part = MIMEText(self.get_content(msg_file),
111 _subtype=subtype)
111 _subtype=subtype)
112 elif maintype == 'image':
112 elif maintype == 'image':
113 file_part = MIMEImage(self.get_content(msg_file),
113 file_part = MIMEImage(self.get_content(msg_file),
114 _subtype=subtype)
114 _subtype=subtype)
115 elif maintype == 'audio':
115 elif maintype == 'audio':
116 file_part = MIMEAudio(self.get_content(msg_file),
116 file_part = MIMEAudio(self.get_content(msg_file),
117 _subtype=subtype)
117 _subtype=subtype)
118 else:
118 else:
119 file_part = MIMEBase(maintype, subtype)
119 file_part = MIMEBase(maintype, subtype)
120 file_part.set_payload(self.get_content(msg_file))
120 file_part.set_payload(self.get_content(msg_file))
121 # Encode the payload using Base64
121 # Encode the payload using Base64
122 encoders.encode_base64(msg)
122 encoders.encode_base64(msg)
123 # Set the filename parameter
123 # Set the filename parameter
124 file_part.add_header('Content-Disposition', 'attachment',
124 file_part.add_header('Content-Disposition', 'attachment',
125 filename=f_name)
125 filename=f_name)
126 file_part.add_header('Content-Type', ctype, name=f_name)
126 file_part.add_header('Content-Type', ctype, name=f_name)
127 msg.attach(file_part)
127 msg.attach(file_part)
128 else:
128 else:
129 raise Exception('Attachment files should be'
129 raise Exception('Attachment files should be'
130 'a dict in format {"filename":"filepath"}')
130 'a dict in format {"filename":"filepath"}')
131
131
132 def get_content(self, msg_file):
132 def get_content(self, msg_file):
133 """Get content based on type, if content is a string do open first
133 """Get content based on type, if content is a string do open first
134 else just read because it's a probably open file object
134 else just read because it's a probably open file object
135
135
136 :param msg_file:
136 :param msg_file:
137 """
137 """
138 if isinstance(msg_file, str):
138 if isinstance(msg_file, str):
139 return open(msg_file, "rb").read()
139 return open(msg_file, "rb").read()
140 else:
140 else:
141 #just for safe seek to 0
141 #just for safe seek to 0
142 msg_file.seek(0)
142 msg_file.seek(0)
143 return msg_file.read()
143 return msg_file.read()
@@ -1,28 +1,30 b''
1 from sqlalchemy.interfaces import ConnectionProxy
1 from sqlalchemy.interfaces import ConnectionProxy
2 import time
2 import time
3 import logging
3 import logging
4 log = logging.getLogger('timerproxy')
4 log = logging.getLogger('timerproxy')
5
5
6 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
6 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = xrange(30, 38)
7
7
8
8 def color_sql(sql):
9 def color_sql(sql):
9 COLOR_SEQ = "\033[1;%dm"
10 COLOR_SEQ = "\033[1;%dm"
10 COLOR_SQL = YELLOW
11 COLOR_SQL = YELLOW
11 normal = '\x1b[0m'
12 normal = '\x1b[0m'
12 return COLOR_SEQ % COLOR_SQL + sql + normal
13 return COLOR_SEQ % COLOR_SQL + sql + normal
13
14
15
14 class TimerProxy(ConnectionProxy):
16 class TimerProxy(ConnectionProxy):
15
17
16 def __init__(self):
18 def __init__(self):
17 super(TimerProxy, self).__init__()
19 super(TimerProxy, self).__init__()
18
20
19 def cursor_execute(self, execute, cursor, statement, parameters,
21 def cursor_execute(self, execute, cursor, statement, parameters,
20 context, executemany):
22 context, executemany):
21
23
22 now = time.time()
24 now = time.time()
23 try:
25 try:
24 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
26 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
25 return execute(cursor, statement, parameters, context)
27 return execute(cursor, statement, parameters, context)
26 finally:
28 finally:
27 total = time.time() - now
29 total = time.time() - now
28 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
30 log.info(color_sql("<<<<< TOTAL TIME: %f <<<<<" % total))
General Comments 0
You need to be logged in to leave comments. Login now