[IMP] mail,portal,project: add preview to portal link
Before this commit, when the user shares a portal link, the receiver has to click on the link to understand what it is about. This commit uses new key called `preview_object` inside `portal.frontend_layout` template. When it is defined, it will add some useful information for the preview link. This commit also defines `preview_object` in portal view of project and task to be able to have the preview link to prevent the user from having to click on the link to understand what it is about. task-3186692 closes odoo/odoo#113622 Related: odoo/enterprise#37514 Signed-off-by: Xavier Bol (xbo) <xbo@odoo.com>
This commit is contained in:
committed by
Xavier Bol (xbo)
parent
7f35c48ef5
commit
ed268f81f4
@@ -19,6 +19,7 @@ class LinkPreview(models.Model):
|
||||
source_url = fields.Char('URL', required=True)
|
||||
og_type = fields.Char('Type')
|
||||
og_title = fields.Char('Title')
|
||||
og_site_name = fields.Char('Site name')
|
||||
og_image = fields.Char('Image')
|
||||
og_description = fields.Text('Description')
|
||||
og_mimetype = fields.Char('MIME type')
|
||||
@@ -97,5 +98,6 @@ class LinkPreview(models.Model):
|
||||
'og_mimetype': preview.og_mimetype,
|
||||
'og_title': preview.og_title,
|
||||
'og_type': preview.og_type,
|
||||
'og_site_name': preview.og_site_name,
|
||||
'source_url': preview.source_url,
|
||||
} for preview in self]
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
<h6 class="card-title mb-0 me-2" t-attf-class="{{ props.linkPreview.og_description ? 'text-truncate' : 'overflow-hidden' }}">
|
||||
<a t-att-href="props.linkPreview.source_url" target="_blank" t-out="props.linkPreview.og_title"/>
|
||||
</h6>
|
||||
<span t-if="props.linkPreview.og_site_name" t-out="props.linkPreview.og_site_name"/>
|
||||
<p t-if="props.linkPreview.og_description" class="o-mail-LinkPreviewCard-description card-text mb-0 mt-2 text-muted small overflow-hidden" t-out="props.linkPreview.og_description"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -39,6 +39,8 @@ export class LinkPreview extends Record {
|
||||
/** @type {string} */
|
||||
og_type;
|
||||
/** @type {string} */
|
||||
og_site_name;
|
||||
/** @type {string} */
|
||||
source_url;
|
||||
|
||||
get imageUrl() {
|
||||
@@ -56,6 +58,7 @@ export class LinkPreview extends Record {
|
||||
get isCard() {
|
||||
return !this.isImage && !this.isVideo;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
LinkPreview.register();
|
||||
|
||||
@@ -115,6 +115,7 @@ class TestLinkPreview(MailCommon):
|
||||
'og_mimetype': False,
|
||||
'og_title': self.og_title,
|
||||
'og_type': False,
|
||||
'og_site_name': False,
|
||||
'source_url': self.source_url,
|
||||
}] for link_preview in message.link_preview_ids
|
||||
}
|
||||
@@ -134,6 +135,7 @@ class TestLinkPreview(MailCommon):
|
||||
'og_mimetype': None,
|
||||
'og_title': self.og_title,
|
||||
'og_type': None,
|
||||
'og_site_name': None,
|
||||
'source_url': self.source_url,
|
||||
},
|
||||
{
|
||||
@@ -142,6 +144,7 @@ class TestLinkPreview(MailCommon):
|
||||
'og_mimetype': None,
|
||||
'og_title': self.title,
|
||||
'og_type': None,
|
||||
'og_site_name': None,
|
||||
'source_url': self.source_url,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -73,6 +73,7 @@ def get_link_preview_from_html(url, response):
|
||||
return False
|
||||
og_description = tree.xpath('//meta[@property="og:description"]/@content')
|
||||
og_type = tree.xpath('//meta[@property="og:type"]/@content')
|
||||
og_site_name = tree.xpath('//meta[@property="og:site_name"]/@content')
|
||||
og_image = tree.xpath('//meta[@property="og:image"]/@content')
|
||||
og_mimetype = tree.xpath('//meta[@property="og:image:type"]/@content')
|
||||
return {
|
||||
@@ -81,5 +82,6 @@ def get_link_preview_from_html(url, response):
|
||||
'og_mimetype': og_mimetype[0] if og_mimetype else None,
|
||||
'og_title': og_title,
|
||||
'og_type': og_type[0] if og_type else None,
|
||||
'og_site_name': og_site_name[0] if og_site_name else None,
|
||||
'source_url': url,
|
||||
}
|
||||
|
||||
@@ -35,6 +35,28 @@
|
||||
</div>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//head/meta" position="after">
|
||||
<t t-if="preview_object">
|
||||
<!-- Remove seo_object to not define og and twitter tags twice when wesbite is installed -->
|
||||
<t t-set="seo_object" t-value="False"/>
|
||||
<t t-set="company" t-value="preview_object.company_id or request.env.company"/>
|
||||
<t t-set="not_uses_default_logo" t-value="company and not company.uses_default_logo"/>
|
||||
<meta property="og:title" t-att-content="preview_object.name"/>
|
||||
<meta property="og:description" t-att-content="preview_object.description.striptags() if preview_object.description else ''"/>
|
||||
<meta property="og:site_name" t-att-content="company.name if company else ''"/>
|
||||
<t t-if="not_uses_default_logo">
|
||||
<meta property="og:image" t-attf-content="/web/binary/company_logo?company={{ company.id }}"/>
|
||||
</t>
|
||||
<meta property="og:image:width" content="300"/>
|
||||
<meta property="og:image:height" content="200"/>
|
||||
<meta name="twitter:card" content="summary_large_image"/>
|
||||
<meta property="twitter:title" t-att-content="preview_object.name"/>
|
||||
<meta property="twitter:description" t-att-content="preview_object.description.striptags() if preview_object.description else ''"/>
|
||||
<t t-if="not_uses_default_logo">
|
||||
<meta property="twitter:image" t-attf-content="/web/binary/company_logo?company={{ company.id }}"/>
|
||||
</t>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Added by another template so that it can be disabled if needed -->
|
||||
|
||||
@@ -46,6 +46,7 @@ class ProjectCustomerPortal(CustomerPortal):
|
||||
pager=pager,
|
||||
project=project,
|
||||
task_url=f'projects/{project.id}/task',
|
||||
preview_object=project,
|
||||
)
|
||||
|
||||
if not groupby:
|
||||
@@ -255,6 +256,7 @@ class ProjectCustomerPortal(CustomerPortal):
|
||||
'user': request.env.user,
|
||||
'project_accessible': project_accessible,
|
||||
'task_link_section': [],
|
||||
'preview_object': task,
|
||||
}
|
||||
|
||||
values = self._get_page_view_values(task, access_token, values, history, False, **kwargs)
|
||||
|
||||
@@ -120,7 +120,29 @@
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="task_link_preview_front_end" inherit_id="portal.frontend_layout" primary="True">
|
||||
<xpath expr="//t[@t-if='not_uses_default_logo'][1]" position="before">
|
||||
<t t-if="preview_object.displayed_image_id">
|
||||
<meta property="og:image" t-attf-content="/web/image/{{ preview_object.displayed_image_id.id }}/300x200?access_token={{ preview_object.displayed_image_id.generate_access_token()[0] }}"/>
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//t[@t-if='not_uses_default_logo'][2]" position="before">
|
||||
<t t-if="preview_object.displayed_image_id">
|
||||
<meta property="twitter:image" t-attf-content="/web/image/{{ preview_object.displayed_image_id.id }}/300x200?access_token={{ preview_object.displayed_image_id.generate_access_token()[0] }}"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="task_link_preview_portal_layout" inherit_id="portal.portal_layout" primary="True">
|
||||
<xpath expr="//t[@t-call='portal.frontend_layout']" position="attributes">
|
||||
<attribute name="t-call">project.task_link_preview_front_end</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="portal_my_task" name="My Task" inherit_id="portal.portal_sidebar" primary="True">
|
||||
<xpath expr="//t[@t-call='portal.portal_layout']" position="attributes">
|
||||
<attribute name="t-call">project.task_link_preview_portal_layout</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[hasclass('o_portal_sidebar')]" position="inside">
|
||||
<t t-set="title" t-value="task.name"/>
|
||||
<t t-set="o_portal_fullwidth_alert" groups="project.group_project_user">
|
||||
|
||||
Reference in New Issue
Block a user