# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import base64 import logging import werkzeug from odoo import http, _ from odoo.exceptions import AccessError, UserError from odoo.http import request _logger = logging.getLogger(__name__) class WebsiteSlides(http.Controller): _slides_per_page = 12 _slides_per_list = 20 _order_by_criterion = { 'date': 'date_published desc', 'view': 'total_views desc', 'vote': 'likes desc', } def _set_viewed_slide(self, slide, view_mode): slide_key = '%s_%s' % (view_mode, request.session.sid) viewed_slides = request.session.setdefault(slide_key, list()) if slide.id not in viewed_slides: if view_mode == 'slide': slide.sudo().slide_views += 1 elif view_mode == 'embed': slide.sudo().embed_views += 1 viewed_slides.append(slide.id) request.session[slide_key] = viewed_slides return True def _get_slide_detail(self, slide): most_viewed_slides = slide.get_most_viewed_slides(self._slides_per_list) related_slides = slide.get_related_slides(self._slides_per_list) return { 'slide': slide, 'most_viewed_slides': most_viewed_slides, 'related_slides': related_slides, 'user': request.env.user, 'is_public_user': request.env.user == request.website.user_id, 'comments': slide.channel_id.can_see_full and slide.website_message_ids or [], 'private': not slide.channel_id.can_see_full, } # -------------------------------------------------- # MAIN / SEARCH # -------------------------------------------------- @http.route('/slides', type='http', auth="public", website=True) def slides_index(self, *args, **post): """ Returns a list of available channels: if only one is available, redirects directly to its slides """ channels = request.env['slide.channel'].search([], order='sequence, id') if not channels: return request.render("website_slides.channel_not_found") elif len(channels) == 1: return request.redirect("/slides/%s" % channels.id) return request.render('website_slides.channels', { 'channels': channels, 'user': request.env.user, 'is_public_user': request.env.user == request.website.user_id }) @http.route([ '''/slides//pdf_content''', type='http', auth="public", website=True) def slide_get_pdf_content(self, slide): response = werkzeug.wrappers.Response() response.data = slide.datas and slide.datas.decode('base64') or '' response.mimetype = 'application/pdf' return response @http.route('''/slides/slide/ cannot create mail.message ? if not post.get('comment'): return werkzeug.utils.redirect(request.httprequest.referrer + "#discuss") # public user: check or find author based on email, do not subscribe public user # and do not publish their comments by default to avoid direct spam if request.uid == request.website.user_id.id: if not post.get('email'): return werkzeug.utils.redirect(request.httprequest.referrer + "#discuss") # TDE FIXME: public user has no right to create mail.message, should # be investigated - using SUPERUSER_ID meanwhile contextual_slide = slide.sudo().with_context(mail_create_nosubcribe=True) # TDE FIXME: check in mail_thread, find partner from emails should maybe work as public user partner_id = slide.sudo()._find_partner_from_emails([post.get('email')])[0] if partner_id: partner = request.env['res.partner'].sudo().browse(partner_id) else: partner = request.env['res.partner'].sudo().create({ 'name': post.get('name', post['email']), 'email': post['email'] }) post_kwargs = { 'author_id': partner.id, 'website_published': False, 'email_from': partner.email, } # logged user: as usual, published by default else: contextual_slide = slide post_kwargs = {} contextual_slide.message_post( body=post['comment'], message_type='comment', subtype='mt_comment', **post_kwargs ) return werkzeug.utils.redirect(request.httprequest.referrer + "#discuss") @http.route('''/slides/slide//download''', type='http', auth="public", website=True) def slide_download(self, slide): if slide.download_security == 'public' or (slide.download_security == 'user' and request.session.uid): filecontent = base64.b64decode(slide.datas) disposition = 'attachment; filename=%s.pdf' % werkzeug.urls.url_quote(slide.name) return request.make_response( filecontent, [('Content-Type', 'application/pdf'), ('Content-Length', len(filecontent)), ('Content-Disposition', disposition)]) elif not request.session.uid and slide.download_security == 'user': return werkzeug.utils.redirect('/web?redirect=/slides/slide/%s' % (slide.id)) return request.render("website.403") @http.route('''/slides/slide//promote''', type='http', auth='user', website=True) def slide_set_promoted(self, slide): slide.channel_id.promoted_slide_id = slide.id return request.redirect("/slides/%s" % slide.channel_id.id) # JSONRPC @http.route('/slides/slide/like', type='json', auth="user", website=True) def slide_like(self, slide_id): slide = request.env['slide.slide'].browse(int(slide_id)) slide.likes += 1 return slide.likes @http.route('/slides/slide/dislike', type='json', auth="user", website=True) def slide_dislike(self, slide_id): slide = request.env['slide.slide'].browse(int(slide_id)) slide.dislikes += 1 return slide.dislikes @http.route(['/slides/slide/send_share_email'], type='json', auth='user', website=True) def slide_send_share_email(self, slide_id, email): slide = request.env['slide.slide'].browse(int(slide_id)) result = slide.send_share_email(email) return result @http.route('/slides/slide/overlay', type='json', auth="public", website=True) def slide_get_next_slides(self, slide_id): slide = request.env['slide.slide'].browse(int(slide_id)) slides_to_suggest = 9 def slide_mapped_dict(slide): return { 'img_src': '/web/image/slide.slide/%s/image_thumb' % (slide.id), 'caption': slide.name, 'url': slide.website_url } vals = map(slide_mapped_dict, slide.get_related_slides(slides_to_suggest)) add_more_slide = slides_to_suggest - len(vals) if max(add_more_slide, 0): vals += map(slide_mapped_dict, slide.get_most_viewed_slides(add_more_slide)) return vals # -------------------------------------------------- # TOOLS # -------------------------------------------------- @http.route(['/slides/dialog_preview'], type='json', auth='user', methods=['POST'], website=True) def dialog_preview(self, **data): Slide = request.env['slide.slide'] document_type, document_id = Slide._find_document_data_from_url(data['url']) preview = {} if not document_id: preview['error'] = _('Please enter valid youtube or google doc url') return preview existing_slide = Slide.search([('channel_id', '=', int(data['channel_id'])), ('document_id', '=', document_id)], limit=1) if existing_slide: preview['error'] = _('This video already exists in this channel click here to view it ') % existing_slide.id return preview values = Slide._parse_document_url(data['url'], only_preview_fields=True) if values.get('error'): preview['error'] = _('Could not fetch data from url. Document or access right not available.\nHere is the received response: %s') % values['error'] return preview return values @http.route(['/slides/add_slide'], type='json', auth='user', methods=['POST'], website=True) def create_slide(self, *args, **post): # check the size only when we upload a file. if post.get('datas'): file_size = len(post['datas']) * 3 / 4 # base64 if (file_size / 1024.0 / 1024.0) > 25: return {'error': _('File is too big. File size cannot exceed 25MB')} values = dict((fname, post[fname]) for fname in [ 'name', 'url', 'tag_ids', 'slide_type', 'channel_id', 'mime_type', 'datas', 'description', 'image', 'index_content', 'website_published'] if post.get(fname)) if post.get('category_id'): if post['category_id'][0] == 0: values['category_id'] = request.env['slide.category'].create({ 'name': post['category_id'][1]['name'], 'channel_id': values.get('channel_id')}).id else: values['category_id'] = post['category_id'][0] # handle exception during creation of slide and sent error notification to the client # otherwise client slide create dialog box continue processing even server fail to create a slide. try: slide_id = request.env['slide.slide'].create(values) except (UserError, AccessError) as e: _logger.error(e) return {'error': e.name} except Exception as e: _logger.error(e) return {'error': _('Internal server error, please try again later or contact administrator.\nHere is the error message: %s') % e.message} return {'url': "/slides/slide/%s" % (slide_id.id)} # -------------------------------------------------- # EMBED IN THIRD PARTY WEBSITES # -------------------------------------------------- @http.route('/slides/embed/', type='http', auth='public', website=True) def slides_embed(self, slide_id, page="1", **kw): # Note : don't use the 'model' in the route (use 'slide_id'), otherwise if public cannot access the embedded # slide, the error will be the website.403 page instead of the one of the website_slides.embed_slide. # Do not forget the rendering here will be displayed in the embedded iframe # determine if it is embedded from external web page referrer_url = request.httprequest.headers.get('Referer', '') base_url = request.env['ir.config_parameter'].get_param('web.base.url') is_embedded = referrer_url and not bool(base_url in referrer_url) or False # try accessing slide, and display to corresponding template try: slide = request.env['slide.slide'].browse(slide_id) if is_embedded: request.env['slide.embed'].sudo().add_embed_url(slide.id, referrer_url) values = self._get_slide_detail(slide) values['page'] = page values['is_embedded'] = is_embedded if not values.get('private'): self._set_viewed_slide(slide, 'embed') return request.render('website_slides.embed_slide', values) except AccessError: # TODO : please, make it clean one day, or find another secure way to detect # if the slide can be embedded, and properly display the error message. slide = request.env['slide.slide'].sudo().browse(slide_id) return request.render('website_slides.embed_slide_forbidden', {'slide': slide})