[MERGE] : with trunk and resolve conflicts
[odoo/odoo.git] / addons / website_blog / controllers / main.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import datetime
23 import werkzeug
24
25 from openerp import tools
26 from openerp.addons.web import http
27 from openerp.addons.web.http import request
28 from openerp.addons.website.models.website import slug
29 from openerp.osv.orm import browse_record
30 from openerp.tools.translate import _
31 from openerp import SUPERUSER_ID
32 from openerp.tools import html2plaintext
33
34
35 class QueryURL(object):
36     def __init__(self, path='', path_args=None, **args):
37         self.path = path
38         self.args = args
39         self.path_args = set(path_args or [])
40
41     def __call__(self, path=None, path_args=None, **kw):
42         path = path or self.path
43         for k, v in self.args.items():
44             kw.setdefault(k, v)
45         path_args = set(path_args or []).union(self.path_args)
46         paths, fragments = [], []
47         for key, value in kw.items():
48             if value and key in path_args:
49                 if isinstance(value, browse_record):
50                     paths.append((key, slug(value)))
51                 else:
52                     paths.append((key, value))
53             elif value:
54                 if isinstance(value, list) or isinstance(value, set):
55                     fragments.append(werkzeug.url_encode([(key, item) for item in value]))
56                 else:
57                     fragments.append(werkzeug.url_encode([(key, value)]))
58         for key, value in paths:
59             path += '/' + key + '/%s' % value
60         if fragments:
61             path += '?' + '&'.join(fragments)
62         return path
63
64
65 class WebsiteBlog(http.Controller):
66     _blog_post_per_page = 20
67     _post_comment_per_page = 10
68
69     def nav_list(self):
70         blog_post_obj = request.registry['blog.post']
71         groups = blog_post_obj.read_group(request.cr, request.uid, [], ['name', 'create_date'],
72             groupby="create_date", orderby="create_date asc", context=request.context)
73         for group in groups:
74             begin_date = datetime.datetime.strptime(group['__domain'][0][2], tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
75             end_date = datetime.datetime.strptime(group['__domain'][1][2], tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
76             group['date_begin'] = '%s' % datetime.date.strftime(begin_date, tools.DEFAULT_SERVER_DATE_FORMAT)
77             group['date_end'] = '%s' % datetime.date.strftime(end_date, tools.DEFAULT_SERVER_DATE_FORMAT)
78         return groups
79
80     @http.route([
81         '/blog',
82         '/blog/page/<int:page>',
83     ], type='http', auth="public", website=True, multilang=True)
84     def blogs(self, page=1, **post):
85         cr, uid, context = request.cr, request.uid, request.context
86         blog_obj = request.registry['blog.post']
87         total = blog_obj.search(cr, uid, [], count=True, context=context)
88         pager = request.website.pager(
89             url='/blog',
90             total=total,
91             page=page,
92             step=self._blog_post_per_page,
93         )
94         post_ids = blog_obj.search(cr, uid, [], offset=(page-1)*self._blog_post_per_page, limit=self._blog_post_per_page, context=context)
95         posts = blog_obj.browse(cr, uid, post_ids, context=context)
96         blog_url = QueryURL('', ['blog', 'tag'])
97         return request.website.render("website_blog.latest_blogs", {
98             'posts': posts,
99             'pager': pager,
100             'blog_url': blog_url,
101         })
102
103     @http.route([
104         '/blog/<model("blog.blog"):blog>',
105         '/blog/<model("blog.blog"):blog>/page/<int:page>',
106         '/blog/<model("blog.blog"):blog>/tag/<model("blog.tag"):tag>',
107         '/blog/<model("blog.blog"):blog>/tag/<model("blog.tag"):tag>/page/<int:page>',
108     ], type='http', auth="public", website=True, multilang=True)
109     def blog(self, blog=None, tag=None, page=1, **opt):
110         """ Prepare all values to display the blog.
111
112         :param blog: blog currently browsed.
113         :param tag: tag that is currently used to filter blog posts
114         :param integer page: current page of the pager. Can be the blog or
115                             post pager.
116         :param date: date currently used to filter blog posts (dateBegin_dateEnd)
117
118         :return dict values: values for the templates, containing
119
120          - 'blog_posts': list of browse records that are the posts to display
121                          in a given blog, if not blog_post_id
122          - 'blog': browse of the current blog, if blog_id
123          - 'blogs': list of browse records of blogs
124          - 'pager': the pager to display posts pager in a blog
125          - 'tag': current tag, if tag_id
126          - 'nav_list': a dict [year][month] for archives navigation
127         """
128         date_begin, date_end = opt.get('date_begin'), opt.get('date_end')
129
130         cr, uid, context = request.cr, request.uid, request.context
131         blog_post_obj = request.registry['blog.post']
132
133         blog_obj = request.registry['blog.blog']
134         blog_ids = blog_obj.search(cr, uid, [], order="create_date asc", context=context)
135         blogs = blog_obj.browse(cr, uid, blog_ids, context=context)
136
137         domain = []
138         if blog:
139             domain += [('blog_id', '=', blog.id)]
140         if tag:
141             domain += [('tag_ids', 'in', tag.id)]
142         if date_begin and date_end:
143             domain += [("create_date", ">=", date_begin), ("create_date", "<=", date_end)]
144
145         blog_url = QueryURL('', ['blog', 'tag'], blog=blog, tag=tag, date_begin=date_begin, date_end=date_end)
146         post_url = QueryURL('', ['blogpost'], tag_id=tag and tag.id or None, date_begin=date_begin, date_end=date_end)
147
148         blog_post_ids = blog_post_obj.search(cr, uid, domain, order="create_date asc", context=context)
149         blog_posts = blog_post_obj.browse(cr, uid, blog_post_ids, context=context)
150
151         pager = request.website.pager(
152             url=blog_url(),
153             total=len(blog_posts),
154             page=page,
155             step=self._blog_post_per_page,
156         )
157         pager_begin = (page - 1) * self._blog_post_per_page
158         pager_end = page * self._blog_post_per_page
159         blog_posts = blog_posts[pager_begin:pager_end]
160
161         tag_obj = request.registry['blog.tag']
162         tag_ids = tag_obj.search(cr, uid, [], context=context)
163         tags = tag_obj.browse(cr, uid, tag_ids, context=context)
164
165         values = {
166             'blog': blog,
167             'blogs': blogs,
168             'tags': tags,
169             'tag': tag,
170             'blog_posts': blog_posts,
171             'pager': pager,
172             'nav_list': self.nav_list(),
173             'blog_url': blog_url,
174             'post_url': post_url,
175             'date': date_begin,
176         }
177         response = request.website.render("website_blog.blog_post_short", values)
178         return response
179
180     @http.route([
181         '/blog/<model("blog.blog"):blog>/post/<model("blog.post"):blog_post>',
182     ], type='http', auth="public", website=True, multilang=True)
183     def blog_post(self, blog, blog_post, tag_id=None, page=1, enable_editor=None, **post):
184         """ Prepare all values to display the blog.
185
186         :param blog_post: blog post currently browsed. If not set, the user is
187                           browsing the blog and a post pager is calculated.
188                           If set the user is reading the blog post and a
189                           comments pager is calculated.
190         :param blog: blog currently browsed.
191         :param tag: tag that is currently used to filter blog posts
192         :param integer page: current page of the pager. Can be the blog or
193                             post pager.
194         :param date: date currently used to filter blog posts (dateBegin_dateEnd)
195
196          - 'enable_editor': editor control
197
198         :return dict values: values for the templates, containing
199
200          - 'blog_post': browse of the current post, if blog_post_id
201          - 'blog': browse of the current blog, if blog_id
202          - 'blogs': list of browse records of blogs
203          - 'pager': the pager to display comments pager in a blog post
204          - 'tag': current tag, if tag_id
205          - 'nav_list': a dict [year][month] for archives navigation
206          - 'next_blog': next blog post , display in footer
207         """
208         date_begin, date_end = post.get('date_begin'), post.get('date_end')
209
210         pager_url = "/blogpost/%s" % blog_post.id
211
212         pager = request.website.pager(
213             url=pager_url,
214             total=len(blog_post.website_message_ids),
215             page=page,
216             step=self._post_comment_per_page,
217             scope=7
218         )
219         pager_begin = (page - 1) * self._post_comment_per_page
220         pager_end = page * self._post_comment_per_page
221         blog_post.website_message_ids = blog_post.website_message_ids[pager_begin:pager_end]
222
223         tag = None
224         if tag_id:
225             tag = request.registry['blog.tag'].browse(request.cr, request.uid, int(tag_id), context=request.context)
226         post_url = QueryURL('', ['blogpost'], blogpost=blog_post, tag_id=tag_id, date_begin=date_begin, date_end=date_end)
227         blog_url = QueryURL('', ['blog', 'tag'], blog=blog_post.blog_id, tag=tag, date_begin=date_begin, date_end=date_end)
228
229         cr, uid, context = request.cr, request.uid, request.context
230         if not blog_post.blog_id.id==blog.id:
231             return request.redirect("/blog/%s/post/%s" % (blog_post.blog_id.id, blog_post.id))
232         blog_post_obj = request.registry.get('blog.post')
233
234         blog_obj = request.registry['blog.blog']
235         blog_ids = blog_obj.search(cr, uid, [], context=context)
236         blogs = blog_obj.browse(cr, uid, blog_ids, context=context)
237
238         tag_obj = request.registry['blog.tag']
239         tag_ids = tag_obj.search(cr, uid, [], context=context)
240         tags = tag_obj.browse(cr, uid, tag_ids, context=context)
241
242         # Find next Post
243         visited_blogs = request.httprequest.cookies.get('visited_blogs') or ''
244         visited_ids = filter(None, visited_blogs.split(','))
245         visited_ids = map(lambda x: int(x), visited_ids)
246         if blog_post.id not in visited_ids:
247             visited_ids.append(blog_post.id)
248         next_post_id = blog_post_obj.search(cr, uid, [
249             ('id', 'not in', visited_ids),
250             ], order='ranking desc', limit=1, context=context)
251         next_post = next_post_id and blog_post_obj.browse(cr, uid, next_post_id[0], context=context) or False
252
253         values = {
254             'blog': blog,
255             'blogs': blogs,
256             'tags': tags,
257             'tag': tag,
258             'blog_post': blog_post,
259             'main_object': blog_post,
260             'nav_list': self.nav_list(),
261             'enable_editor': enable_editor,
262             'next_post' : next_post,
263             'date': date_begin,
264             'post_url': post_url,
265             'blog_url': blog_url,
266         }
267         response = request.website.render("website_blog.blog_post_complete", values)
268         response.set_cookie('visited_blogs', ','.join(map(str, visited_ids)))
269
270         request.session[request.session_id] = request.session.get(request.session_id, [])
271         if not (blog_post.id in request.session[request.session_id]):
272             request.session[request.session_id].append(blog_post.id)
273             # Increase counter
274             blog_post_obj.write(cr, SUPERUSER_ID, [blog_post.id], {
275                 'visits': blog_post.visits+1,
276             },context=context)
277         return response
278
279     def _blog_post_message(self, user, blog_post_id=0, **post):
280         cr, uid, context = request.cr, request.uid, request.context
281         blog_post = request.registry['blog.post']
282         partner_obj = request.registry['res.partner']
283         thread_obj = request.registry['mail.thread']
284         website = request.registry['website']
285
286         public_id = website.get_public_user(cr, uid, context)
287         if uid != public_id:
288             partner_ids = [user.partner_id.id]
289         else:
290             partner_ids = blog_post._find_partner_from_emails(
291                 cr, SUPERUSER_ID, 0, [post.get('email')], context=context)
292             if not partner_ids or not partner_ids[0]:
293                 partner_ids = [partner_obj.create(cr, SUPERUSER_ID, {'name': post.get('name'), 'email': post.get('email')}, context=context)]
294
295         message_id = blog_post.message_post(
296             cr, SUPERUSER_ID, int(blog_post_id),
297             body=post.get('comment'),
298             type='comment',
299             subtype='mt_comment',
300             author_id=partner_ids[0],
301             discussion=post.get('discussion'),
302             context=dict(context, mail_create_nosubcribe=True))
303         return message_id
304
305     @http.route(['/blogpost/comment'], type='http', auth="public", methods=['POST'], website=True)
306     def blog_post_comment(self, blog_post_id=0, **post):
307         cr, uid, context = request.cr, request.uid, request.context
308         if post.get('comment'):
309             user = request.registry['res.users'].browse(cr, uid, uid, context=context)
310             blog_post = request.registry['blog.post']
311             blog_post.check_access_rights(cr, uid, 'read')
312             self._blog_post_message(user, blog_post_id, **post)
313         return werkzeug.utils.redirect(request.httprequest.referrer + "#comments")
314
315     def _get_discussion_detail(self, ids, publish=False, **post):
316         cr, uid, context = request.cr, request.uid, request.context
317         values = []
318         mail_obj = request.registry.get('mail.message')
319         for message in mail_obj.browse(cr, SUPERUSER_ID, ids, context=context):
320             values.append({
321                 "id": message.id,
322                 "author_name": message.author_id.name,
323                 "author_image": message.author_id.image and \
324                     ("data:image/png;base64,%s" % message.author_id.image) or \
325                     '/website_blog/static/src/img/anonymous.png',
326                 "date": message.date,
327                 'body': html2plaintext(message.body),
328                 'website_published' : message.website_published,
329                 'publish' : publish,
330             })
331         return values
332
333     @http.route(['/blogpost/post_discussion'], type='json', auth="public", website=True)
334     def post_discussion(self, blog_post_id=0, **post):
335         cr, uid, context = request.cr, request.uid, request.context
336         publish = request.registry['res.users'].has_group(cr, uid, 'base.group_website_publisher')
337         user = request.registry['res.users'].browse(cr, uid, uid, context=context)
338         id = self._blog_post_message(user, blog_post_id, **post)
339         return self._get_discussion_detail([id], publish, **post)
340     
341     @http.route('/blogpost/new', type='http', auth="public", website=True, multilang=True)
342     def blog_post_create(self, blog_id, **post):
343         cr, uid, context = request.cr, request.uid, request.context
344         create_context = dict(context, mail_create_nosubscribe=True)
345         new_blog_post_id = request.registry['blog.post'].create(cr, uid, {
346                 'blog_id': blog_id,
347                 'name': _("Blog Post Title"),
348                 'sub_title': _("Subtitle"),
349                 'content': '',
350                 'website_published': False,
351             }, context=create_context)
352         return werkzeug.utils.redirect("/blog/%s/post/%s/?enable_editor=1" % (blog_id, new_blog_post_id))
353
354     @http.route('/blogpost/duplicate', type='http', auth="public", website=True)
355     def blog_post_copy(self, blog_post_id, **post):
356         """ Duplicate a blog.
357
358         :param blog_post_id: id of the blog post currently browsed.
359
360         :return redirect to the new blog created
361         """
362         cr, uid, context = request.cr, request.uid, request.context
363         create_context = dict(context, mail_create_nosubscribe=True)
364         nid = request.registry['blog.post'].copy(cr, uid, blog_post_id, {}, context=create_context)
365         post = request.registry['blog.post'].browse(cr, uid, nid, context)
366         return werkzeug.utils.redirect("/blog/%s/post/%s/?enable_editor=1" % (post.blog_id.id, nid))
367
368     @http.route('/blogpost/get_discussion/', type='json', auth="public", website=True)
369     def discussion(self, post_id=0, discussion=None, count=False, **post):
370         cr, uid, context = request.cr, request.uid, request.context
371         mail_obj = request.registry.get('mail.message')
372         domain = [('res_id', '=', int(post_id)) ,('model','=','blog.post'), ('discussion', '=', discussion)]
373         #check current user belongs to website publisher group
374         publish = request.registry['res.users'].has_group(cr, uid, 'base.group_website_publisher')
375         if not publish:
376             domain.append(('website_published', '=', True))
377         ids = mail_obj.search(cr, SUPERUSER_ID, domain, count=count)
378         if count:
379             return ids
380         return self._get_discussion_detail(ids, publish, **post)
381
382     @http.route('/blogpsot/change_background', type='json', auth="public", website=True)
383     def change_bg(self, post_id=0, image=None, **post):
384         post_obj = request.registry.get('blog.post')
385         values = {'content_image' : image}
386
387         ids = post_obj.write(request.cr, request.uid, [int(post_id)], values, request.context)
388         return []
389
390     @http.route('/blog/get_user/', type='json', auth="public", website=True)
391     def get_user(self, **post):
392         return [False if request.session.uid else True]