[IMP] website_slides: refactor slide.slide embed detection

This commit slightly changes the embed detection for slides.

Currently, we use the same URL to embed slide content within the e-learning
module and when the end-user wants to embed that content on an external
website.

However, we want to know whether the slide is embedded externally or not to
show different controls and update some statistics.
The code previously relied on the presence of the "referer" header to achieve
that detection, if the referer URL comes from a domain that is not this Odoo
website -> then it's embedded externally.

The issue with that strategy is that it's not very reliable, indeed some modern
browsers now completely avoid passing the referer header (mostly for security
and privacy reasons).

Instead, we change this to let users embed on external websites with a
*different* URL.

When the embed comes from '/embed_external/', we now easily know that it comes
from an external embed and don't have to do some further and potentially tricky
processing.

We introduced a computed 'embed_code_external' field that is computed the same
way as the embed_code except for that small route distinction.

This also easily solves any issue that could come with multi-website /
different root URLs etc.

Task-2654009

Part-of: odoo/odoo#77736
This commit is contained in:
Aurélien Warnon
2021-11-24 10:18:17 +00:00
parent 61d7af7611
commit 1ab9455a9a
4 changed files with 45 additions and 17 deletions
+25 -11
View File
@@ -1133,22 +1133,36 @@ class WebsiteSlides(WebsiteProfile):
@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
return self._slide_embed(slide_id, page=page, is_external_embed=False, **kw)
@http.route('/slides/embed_external/<int:slide_id>', type='http', auth='public', website=True, sitemap=False)
def slides_embed_external(self, slide_id, page="1", **kw):
return self._slide_embed(slide_id, page=page, is_external_embed=True, **kw)
def _slide_embed(self, slide_id, page="1", is_external_embed=False, **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
Try accessing slide, and display to corresponding template.
When the content is embedded *externally*, meaning on a third party website, we do some
additional steps like displaying sharing controls and also updating some KPIs. """
# try accessing slide, and display to corresponding template
try:
slide = request.env['slide.slide'].browse(slide_id)
# determine if it is embedded from external web page
referrer_url = request.httprequest.headers.get('Referer', '')
base_url = slide.get_base_url()
is_embedded = referrer_url and not bool(base_url in referrer_url) or False
if is_embedded:
request.env['slide.embed'].sudo()._add_embed_url(slide.id, referrer_url)
referer_url = request.httprequest.headers.get('Referer', '')
if is_external_embed and referer_url:
request.env['slide.embed'].sudo()._add_embed_url(
slide.id,
referer_url
)
values = self._get_slide_detail(slide)
values['page'] = page
values['is_embedded'] = is_embedded
values['is_external_embed'] = is_external_embed
self._set_viewed_slide(slide)
return request.render('website_slides.embed_slide', values)
except AccessError: # TODO : please, make it clean one day, or find another secure way to detect
+14 -3
View File
@@ -10,6 +10,7 @@ import PyPDF2
import json
from dateutil.relativedelta import relativedelta
from markupsafe import Markup
from PIL import Image
from werkzeug import urls
@@ -165,6 +166,8 @@ class Slide(models.Model):
dislikes = fields.Integer('Dislikes', compute='_compute_user_info', store=True, compute_sudo=False)
user_vote = fields.Integer('User vote', compute='_compute_user_info', compute_sudo=False)
embed_code = fields.Html('Embed Code', readonly=True, compute='_compute_embed_code', sanitize=False)
embed_code_external = fields.Html('External Embed Code', readonly=True, compute='_compute_embed_code', sanitize=False,
help="Same as 'Embed Code' but used to embed the content on an external website.")
# views
embedcount_ids = fields.One2many('slide.embed', 'slide_id', string="Embed Count")
slide_views = fields.Integer('# of Website Views', store=True, compute="_compute_slide_views")
@@ -320,26 +323,34 @@ class Slide(models.Model):
@api.depends('document_id', 'slide_type', 'mime_type')
def _compute_embed_code(self):
base_url = request and request.httprequest.url_root
for record in self:
embed_code_external = False
if not base_url:
base_url = record.get_base_url()
if base_url[-1] == '/':
base_url = base_url[:-1]
if record.datas and (not record.document_id or record.slide_type in ['document', 'presentation']):
slide_url = base_url + url_for('/slides/embed/%s?page=1' % record.id)
record.embed_code = '<iframe src="%s" class="o_wslides_iframe_viewer" allowFullScreen="true" height="%s" width="%s" frameborder="0"></iframe>' % (slide_url, 315, 420)
slide_url_external = base_url + url_for('/slides/embed_external/%s?page=1' % record.id)
base_embed_code = Markup('<iframe src="%s" class="o_wslides_iframe_viewer" allowFullScreen="true" height="%s" width="%s" frameborder="0"></iframe>')
record.embed_code = base_embed_code % (slide_url, 315, 420)
embed_code_external = base_embed_code % (slide_url_external, 315, 420)
elif record.slide_type == 'video' and record.document_id:
if not record.mime_type:
# embed youtube video
query = urls.url_parse(record.url).query
query = query + '&theme=light' if query else 'theme=light'
record.embed_code = '<iframe src="//www.youtube-nocookie.com/embed/%s?%s" allowFullScreen="true" frameborder="0"></iframe>' % (record.document_id, query)
record.embed_code = Markup('<iframe src="//www.youtube-nocookie.com/embed/%s?%s" allowFullScreen="true" frameborder="0"></iframe>') % (record.document_id, query)
else:
# embed google doc video
record.embed_code = '<iframe src="//drive.google.com/file/d/%s/preview" allowFullScreen="true" frameborder="0"></iframe>' % (record.document_id)
record.embed_code = Markup('<iframe src="//drive.google.com/file/d/%s/preview" allowFullScreen="true" frameborder="0"></iframe>') % (record.document_id)
else:
record.embed_code = False
record.embed_code_external = embed_code_external or record.embed_code
@api.onchange('url')
def _on_change_url(self):
self.ensure_one()
@@ -552,7 +552,10 @@
<div class="oe_slide_js_embed_code_widget mt-4">
<h5 class="mt0">Embed in your website</h5>
<div class="form-group">
<textarea class="form-control slide_embed_code" readonly="readonly" onClick="this.select();"><t t-esc="slide.embed_code"/></textarea>
<textarea class="form-control slide_embed_code" readonly="readonly" onClick="this.select();">
<!-- Use the external embedding URL here, see python controller '_slide_embed' method for more details. -->
<t t-esc="slide.embed_code_external"/>
</textarea>
</div>
<div class="form-group d-flex" t-if="slide.slide_type in ('presentation', 'document')">
<div class="form-text p-0 col-xs-5 col-sm-5 col-md-5 col-lg-5">Select page to start with</div>
@@ -17,7 +17,7 @@
<body>
<div id="PDFViewer" class="o_wslides_fs_pdf_viewer d-flex flex-column h-100">
<!-- PDF Viewer Header : contains the name, and the share links -->
<div t-if="is_embedded" class="oe_slides_share_bar">
<div t-if="is_external_embed" class="oe_slides_share_bar">
<div class="container-fluid">
<div class="row align-items-center">
<div class="col">
@@ -41,7 +41,7 @@
t-attf-data-slideurl="/slides/slide/#{slug(slide)}/pdf_content"
t-attf-data-downloadable="#{False}"
t-att-data-defaultpage="page">
<t t-if="is_embedded">
<t t-if="is_external_embed">
<div id="slide_share" class="oe_slide_embed_option" style="display: none;">
<t t-call="website_slides.slide_share_modal">
<t t-set="record" t-value="slide"/>