##// END OF EJS Templates
fixed #746 unicodeDedode errors on feed controllers
marcink -
r3292:2464ac75 beta
parent child Browse files
Show More
@@ -1,182 +1,182 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.feed
3 rhodecode.controllers.feed
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Feed controller for rhodecode
6 Feed controller for rhodecode
7
7
8 :created_on: Apr 23, 2010
8 :created_on: Apr 23, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 logging
26 import logging
27
27
28 from pylons import url, response, tmpl_context as c
28 from pylons import url, response, tmpl_context as c
29 from pylons.i18n.translation import _
29 from pylons.i18n.translation import _
30
30
31 from beaker.cache import cache_region, region_invalidate
31 from beaker.cache import cache_region, region_invalidate
32 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
32 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
33
33
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 from rhodecode.lib.base import BaseRepoController
36 from rhodecode.lib.base import BaseRepoController
37 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
37 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
38 from rhodecode.model.db import CacheInvalidation
38 from rhodecode.model.db import CacheInvalidation
39 from rhodecode.lib.utils2 import safe_int, str2bool
39 from rhodecode.lib.utils2 import safe_int, str2bool, safe_unicode
40
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 class FeedController(BaseRepoController):
44 class FeedController(BaseRepoController):
45
45
46 @LoginRequired(api_access=True)
46 @LoginRequired(api_access=True)
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
48 'repository.admin')
48 'repository.admin')
49 def __before__(self):
49 def __before__(self):
50 super(FeedController, self).__before__()
50 super(FeedController, self).__before__()
51 #common values for feeds
51 #common values for feeds
52 self.description = _('Changes on %s repository')
52 self.description = _('Changes on %s repository')
53 self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s')
53 self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s')
54 self.language = 'en-us'
54 self.language = 'en-us'
55 self.ttl = "5"
55 self.ttl = "5"
56 import rhodecode
56 import rhodecode
57 CONF = rhodecode.CONFIG
57 CONF = rhodecode.CONFIG
58 self.include_diff = str2bool(CONF.get('rss_include_diff', False))
58 self.include_diff = str2bool(CONF.get('rss_include_diff', False))
59 self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20))
59 self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20))
60 # we need to protect from parsing huge diffs here other way
60 # we need to protect from parsing huge diffs here other way
61 # we can kill the server
61 # we can kill the server
62 self.feed_diff_limit = safe_int(CONF.get('rss_cut_off_limit', 32 * 1024))
62 self.feed_diff_limit = safe_int(CONF.get('rss_cut_off_limit', 32 * 1024))
63
63
64 def _get_title(self, cs):
64 def _get_title(self, cs):
65 return "%s" % (
65 return "%s" % (
66 h.shorter(cs.message, 160)
66 h.shorter(cs.message, 160)
67 )
67 )
68
68
69 def __changes(self, cs):
69 def __changes(self, cs):
70 changes = []
70 changes = []
71 diff_processor = DiffProcessor(cs.diff(),
71 diff_processor = DiffProcessor(cs.diff(),
72 diff_limit=self.feed_diff_limit)
72 diff_limit=self.feed_diff_limit)
73 _parsed = diff_processor.prepare(inline_diff=False)
73 _parsed = diff_processor.prepare(inline_diff=False)
74 limited_diff = False
74 limited_diff = False
75 if isinstance(_parsed, LimitedDiffContainer):
75 if isinstance(_parsed, LimitedDiffContainer):
76 limited_diff = True
76 limited_diff = True
77
77
78 for st in _parsed:
78 for st in _parsed:
79 st.update({'added': st['stats'][0],
79 st.update({'added': st['stats'][0],
80 'removed': st['stats'][1]})
80 'removed': st['stats'][1]})
81 changes.append('\n %(operation)s %(filename)s '
81 changes.append('\n %(operation)s %(filename)s '
82 '(%(added)s lines added, %(removed)s lines removed)'
82 '(%(added)s lines added, %(removed)s lines removed)'
83 % st)
83 % st)
84 if limited_diff:
84 if limited_diff:
85 changes = changes + ['\n ' +
85 changes = changes + ['\n ' +
86 _('Changeset was too big and was cut off...')]
86 _('Changeset was too big and was cut off...')]
87 return diff_processor, changes
87 return diff_processor, changes
88
88
89 def __get_desc(self, cs):
89 def __get_desc(self, cs):
90 desc_msg = []
90 desc_msg = []
91 desc_msg.append('%s %s %s<br/>' % (h.person(cs.author),
91 desc_msg.append('%s %s %s<br/>' % (h.person(cs.author),
92 _('commited on'),
92 _('commited on'),
93 h.fmt_date(cs.date)))
93 h.fmt_date(cs.date)))
94 #branches, tags, bookmarks
94 #branches, tags, bookmarks
95 if cs.branch:
95 if cs.branch:
96 desc_msg.append('branch: %s<br/>' % cs.branch)
96 desc_msg.append('branch: %s<br/>' % cs.branch)
97 if h.is_hg(c.rhodecode_repo):
97 if h.is_hg(c.rhodecode_repo):
98 for book in cs.bookmarks:
98 for book in cs.bookmarks:
99 desc_msg.append('bookmark: %s<br/>' % book)
99 desc_msg.append('bookmark: %s<br/>' % book)
100 for tag in cs.tags:
100 for tag in cs.tags:
101 desc_msg.append('tag: %s<br/>' % tag)
101 desc_msg.append('tag: %s<br/>' % tag)
102 diff_processor, changes = self.__changes(cs)
102 diff_processor, changes = self.__changes(cs)
103 # rev link
103 # rev link
104 _url = url('changeset_home', repo_name=cs.repository.name,
104 _url = url('changeset_home', repo_name=cs.repository.name,
105 revision=cs.raw_id, qualified=True)
105 revision=cs.raw_id, qualified=True)
106 desc_msg.append('changesest: <a href="%s">%s</a>' % (_url, cs.raw_id[:8]))
106 desc_msg.append('changesest: <a href="%s">%s</a>' % (_url, cs.raw_id[:8]))
107
107
108 desc_msg.append('<pre>')
108 desc_msg.append('<pre>')
109 desc_msg.append(cs.message)
109 desc_msg.append(cs.message)
110 desc_msg.append('\n')
110 desc_msg.append('\n')
111 desc_msg.extend(changes)
111 desc_msg.extend(changes)
112 if self.include_diff:
112 if self.include_diff:
113 desc_msg.append('\n\n')
113 desc_msg.append('\n\n')
114 desc_msg.append(diff_processor.as_raw())
114 desc_msg.append(diff_processor.as_raw())
115 desc_msg.append('</pre>')
115 desc_msg.append('</pre>')
116 return desc_msg
116 return map(safe_unicode, desc_msg)
117
117
118 def atom(self, repo_name):
118 def atom(self, repo_name):
119 """Produce an atom-1.0 feed via feedgenerator module"""
119 """Produce an atom-1.0 feed via feedgenerator module"""
120
120
121 @cache_region('long_term')
121 @cache_region('long_term')
122 def _get_feed_from_cache(key):
122 def _get_feed_from_cache(key):
123 feed = Atom1Feed(
123 feed = Atom1Feed(
124 title=self.title % repo_name,
124 title=self.title % repo_name,
125 link=url('summary_home', repo_name=repo_name,
125 link=url('summary_home', repo_name=repo_name,
126 qualified=True),
126 qualified=True),
127 description=self.description % repo_name,
127 description=self.description % repo_name,
128 language=self.language,
128 language=self.language,
129 ttl=self.ttl
129 ttl=self.ttl
130 )
130 )
131
131
132 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
132 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
133 feed.add_item(title=self._get_title(cs),
133 feed.add_item(title=self._get_title(cs),
134 link=url('changeset_home', repo_name=repo_name,
134 link=url('changeset_home', repo_name=repo_name,
135 revision=cs.raw_id, qualified=True),
135 revision=cs.raw_id, qualified=True),
136 author_name=cs.author,
136 author_name=cs.author,
137 description=''.join(self.__get_desc(cs)),
137 description=''.join(self.__get_desc(cs)),
138 pubdate=cs.date,
138 pubdate=cs.date,
139 )
139 )
140
140
141 response.content_type = feed.mime_type
141 response.content_type = feed.mime_type
142 return feed.writeString('utf-8')
142 return feed.writeString('utf-8')
143
143
144 key = repo_name + '_ATOM'
144 key = repo_name + '_ATOM'
145 inv = CacheInvalidation.invalidate(key)
145 inv = CacheInvalidation.invalidate(key)
146 if inv is not None:
146 if inv is not None:
147 region_invalidate(_get_feed_from_cache, None, key)
147 region_invalidate(_get_feed_from_cache, None, key)
148 CacheInvalidation.set_valid(inv.cache_key)
148 CacheInvalidation.set_valid(inv.cache_key)
149 return _get_feed_from_cache(key)
149 return _get_feed_from_cache(key)
150
150
151 def rss(self, repo_name):
151 def rss(self, repo_name):
152 """Produce an rss2 feed via feedgenerator module"""
152 """Produce an rss2 feed via feedgenerator module"""
153
153
154 @cache_region('long_term')
154 @cache_region('long_term')
155 def _get_feed_from_cache(key):
155 def _get_feed_from_cache(key):
156 feed = Rss201rev2Feed(
156 feed = Rss201rev2Feed(
157 title=self.title % repo_name,
157 title=self.title % repo_name,
158 link=url('summary_home', repo_name=repo_name,
158 link=url('summary_home', repo_name=repo_name,
159 qualified=True),
159 qualified=True),
160 description=self.description % repo_name,
160 description=self.description % repo_name,
161 language=self.language,
161 language=self.language,
162 ttl=self.ttl
162 ttl=self.ttl
163 )
163 )
164
164
165 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
165 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
166 feed.add_item(title=self._get_title(cs),
166 feed.add_item(title=self._get_title(cs),
167 link=url('changeset_home', repo_name=repo_name,
167 link=url('changeset_home', repo_name=repo_name,
168 revision=cs.raw_id, qualified=True),
168 revision=cs.raw_id, qualified=True),
169 author_name=cs.author,
169 author_name=cs.author,
170 description=''.join(self.__get_desc(cs)),
170 description=''.join(self.__get_desc(cs)),
171 pubdate=cs.date,
171 pubdate=cs.date,
172 )
172 )
173
173
174 response.content_type = feed.mime_type
174 response.content_type = feed.mime_type
175 return feed.writeString('utf-8')
175 return feed.writeString('utf-8')
176
176
177 key = repo_name + '_RSS'
177 key = repo_name + '_RSS'
178 inv = CacheInvalidation.invalidate(key)
178 inv = CacheInvalidation.invalidate(key)
179 if inv is not None:
179 if inv is not None:
180 region_invalidate(_get_feed_from_cache, None, key)
180 region_invalidate(_get_feed_from_cache, None, key)
181 CacheInvalidation.set_valid(inv.cache_key)
181 CacheInvalidation.set_valid(inv.cache_key)
182 return _get_feed_from_cache(key)
182 return _get_feed_from_cache(key)
General Comments 0
You need to be logged in to leave comments. Login now