2018-01-16 06:58:15 +01:00
# -*- coding: utf-8 -*-
2018-01-16 11:34:37 +01:00
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
2018-01-16 06:58:15 +01:00
import base64
import logging
import werkzeug
2018-01-16 11:34:37 +01:00
from flectra import http , _
from flectra . exceptions import AccessError , UserError
from flectra . http import request
from flectra . addons . http_routing . models . ir_http import slug
from flectra . addons . website . models . ir_http import sitemap_qs2dom
2018-01-16 06:58:15 +01:00
_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
"""
2018-01-01 11:46:47 +01:00
domain = [ ]
if not request . env . user . has_group ( ' website.group_website_designer ' ) :
domain + = [ ' | ' , ( ' website_ids ' , ' = ' , False ) , ( " website_ids " , " in " , request . website . id ) ]
channels = request . env [ ' slide.channel ' ] . search ( domain , order = ' sequence, id ' )
2018-01-16 06:58:15 +01:00
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
} )
def sitemap_slide ( env , rule , qs ) :
Channel = env [ ' slide.channel ' ]
dom = sitemap_qs2dom ( qs = qs , route = ' /slides/ ' , field = Channel . _rec_name )
for channel in Channel . search ( dom ) :
loc = ' /slides/ %s ' % slug ( channel )
if not qs or qs . lower ( ) in loc :
yield { ' loc ' : loc }
@http.route ( [
''' /slides/<model( " slide.channel " ):channel> ''' ,
''' /slides/<model( " slide.channel " ):channel>/page/<int:page> ''' ,
''' /slides/<model( " slide.channel " ):channel>/<string:slide_type> ''' ,
''' /slides/<model( " slide.channel " ):channel>/<string:slide_type>/page/<int:page> ''' ,
''' /slides/<model( " slide.channel " ):channel>/tag/<model( " slide.tag " ):tag> ''' ,
''' /slides/<model( " slide.channel " ):channel>/tag/<model( " slide.tag " ):tag>/page/<int:page> ''' ,
''' /slides/<model( " slide.channel " ):channel>/category/<model( " slide.category " ):category> ''' ,
''' /slides/<model( " slide.channel " ):channel>/category/<model( " slide.category " ):category>/page/<int:page> ''' ,
''' /slides/<model( " slide.channel " ):channel>/category/<model( " slide.category " ):category>/<string:slide_type> ''' ,
''' /slides/<model( " slide.channel " ):channel>/category/<model( " slide.category " ):category>/<string:slide_type>/page/<int:page> ''' ] ,
type = ' http ' , auth = " public " , website = True , sitemap = sitemap_slide )
def channel ( self , channel , category = None , tag = None , page = 1 , slide_type = None , sorting = ' creation ' , search = None , * * kw ) :
user = request . env . user
Slide = request . env [ ' slide.slide ' ]
domain = [ ( ' channel_id ' , ' = ' , channel . id ) ]
pager_url = " /slides/ %s " % ( channel . id )
pager_args = { }
if search :
domain + = [
' | ' , ' | ' ,
( ' name ' , ' ilike ' , search ) ,
( ' description ' , ' ilike ' , search ) ,
( ' index_content ' , ' ilike ' , search ) ]
pager_args [ ' search ' ] = search
else :
if category :
domain + = [ ( ' category_id ' , ' = ' , category . id ) ]
pager_url + = " /category/ %s " % category . id
elif tag :
domain + = [ ( ' tag_ids.id ' , ' = ' , tag . id ) ]
pager_url + = " /tag/ %s " % tag . id
if slide_type :
domain + = [ ( ' slide_type ' , ' = ' , slide_type ) ]
pager_url + = " / %s " % slide_type
if not sorting or sorting not in self . _order_by_criterion :
sorting = ' date '
order = self . _order_by_criterion [ sorting ]
pager_args [ ' sorting ' ] = sorting
pager_count = Slide . search_count ( domain )
pager = request . website . pager ( url = pager_url , total = pager_count , page = page ,
step = self . _slides_per_page , scope = self . _slides_per_page ,
url_args = pager_args )
slides = Slide . search ( domain , limit = self . _slides_per_page , offset = pager [ ' offset ' ] , order = order )
values = {
' channel ' : channel ,
' category ' : category ,
' slides ' : slides ,
' tag ' : tag ,
' slide_type ' : slide_type ,
' sorting ' : sorting ,
' user ' : user ,
' pager ' : pager ,
' is_public_user ' : user == request . website . user_id ,
' display_channel_settings ' : not request . httprequest . cookies . get ( ' slides_channel_ %s ' % ( channel . id ) , False ) and channel . can_see_full ,
}
if search :
values [ ' search ' ] = search
return request . render ( ' website_slides.slides_search ' , values )
# Display uncategorized slides
if not slide_type and not category :
category_datas = [ ]
for category in Slide . read_group ( domain , [ ' category_id ' ] , [ ' category_id ' ] ) :
category_id , name = category . get ( ' category_id ' ) or ( False , _ ( ' Uncategorized ' ) )
category_datas . append ( {
' id ' : category_id ,
' name ' : name ,
' total ' : category [ ' category_id_count ' ] ,
' slides ' : Slide . search ( category [ ' __domain ' ] , limit = 4 , offset = 0 , order = order )
} )
values . update ( {
' category_datas ' : category_datas ,
} )
return request . render ( ' website_slides.home ' , values )
# --------------------------------------------------
# SLIDE.SLIDE CONTOLLERS
# --------------------------------------------------
@http.route ( ''' /slides/slide/<model( " slide.slide " , " [( ' channel_id.can_see ' , ' = ' , True)] " ):slide> ''' , type = ' http ' , auth = " public " , website = True )
def slide_view ( self , slide , * * kwargs ) :
values = self . _get_slide_detail ( slide )
if not values . get ( ' private ' ) :
self . _set_viewed_slide ( slide , ' slide ' )
return request . render ( ' website_slides.slide_detail_view ' , values )
@http.route ( ''' /slides/slide/<model( " slide.slide " ):slide>/pdf_content ''' ,
type = ' http ' , auth = " public " , website = True , sitemap = False )
def slide_get_pdf_content ( self , slide ) :
response = werkzeug . wrappers . Response ( )
response . data = slide . datas and base64 . b64decode ( slide . datas ) or b ' '
response . mimetype = ' application/pdf '
return response
@http.route ( ''' /slides/slide/<model( " slide.slide " ):slide>/download ''' , type = ' http ' , auth = " public " , website = True )
def slide_download ( self , slide , sitemap = False ) :
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/<model( " slide.slide " ):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 = [ slide_mapped_dict ( s ) for s in slide . get_related_slides ( slides_to_suggest ) ]
add_more_slide = slides_to_suggest - len ( vals )
if max ( add_more_slide , 0 ) :
vals . extend ( slide_mapped_dict ( s ) for s in 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 <a target= " _blank " href= " /slides/slide/ %s " >click here to view it </a> ' ) % 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. \n Here 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. \n Here is the error message: %s ' ) % e }
return { ' url ' : " /slides/slide/ %s " % ( slide_id . id ) }
# --------------------------------------------------
# EMBED IN THIRD PARTY WEBSITES
# --------------------------------------------------
@http.route ( ' /slides/embed/<int:slide_id> ' , type = ' http ' , auth = ' public ' , website = True , sitemap = False )
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 ' ] . sudo ( ) . 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 } )