Odoo18-Base/addons/marketing_card/controllers/marketing_card.py

113 lines
4.1 KiB
Python
Raw Permalink Normal View History

2025-01-06 10:57:38 +07:00
import base64
from urllib.parse import quote
from werkzeug.exceptions import BadRequest
from odoo.http import Controller, content_disposition, request, route
# from https://github.com/monperrus/crawler-user-agents
SOCIAL_NETWORK_USER_AGENTS = (
# Facebook
'Facebot',
'facebookexternalhit',
# Twitter
'Twitterbot',
# LinkedIn
'LinkedInBot',
# Whatsapp
'WhatsApp',
# Pinterest
'Pinterest',
'Pinterestbot',
)
def _is_crawler(request):
"""Returns True if the request is made by a social network crawler."""
return any(
short_crawler_name in request.httprequest.user_agent.string
for short_crawler_name in SOCIAL_NETWORK_USER_AGENTS
)
def _get_card_from_url(card_id, card_slug):
"""Helper to support both legacy card id url and new slug urls"""
if card_slug:
card_id = request.env['ir.http']._unslug(card_slug)[1]
if not card_id:
raise request.not_found()
card = request.env['card.card'].browse(card_id).exists()
if not card:
raise BadRequest()
return card
class MarketingCardController(Controller):
@route([
'/cards/<string:card_slug>/card.jpg',
'/cards/<int:card_id>/card.jpg',
], type='http', auth='public', sitemap=False, website=True)
def card_campaign_image(self, card_id=None, card_slug=None):
card = _get_card_from_url(card_id, card_slug)
if _is_crawler(request) and card.share_status != 'shared':
card.sudo().share_status = 'shared'
if not card.image:
raise request.not_found()
image_bytes = base64.b64decode(card.image)
return request.make_response(image_bytes, [
('Content-Type', ' image/jpeg'),
('Content-Length', len(image_bytes)),
('Content-Disposition', content_disposition('card.jpg')),
])
@route([
'/cards/<string:card_slug>/preview',
'/cards/<int:card_id>/preview',
], type='http', auth='public', sitemap=False, website=True)
def card_campaign_preview(self, card_id=None, card_slug=None):
"""Route for users to preview their card and share it on their social platforms."""
card = _get_card_from_url(card_id, card_slug)
if not card.share_status:
card.sudo().share_status = 'visited'
campaign_sudo = card.sudo().campaign_id
return request.render('marketing_card.card_campaign_preview', {
'card': card,
'campaign': campaign_sudo,
'quote': quote,
})
@route([
'/cards/<string:card_slug>/redirect',
'/cards/<int:card_id>/redirect',
], type='http', auth='public', sitemap=False, website=True)
def card_campaign_redirect(self, card_id=None, card_slug=None):
"""Route to redirect users to the target url, or display the opengraph embed text for web crawlers.
When a user posts a link on an application supporting opengraph, the application will follow
the link to fetch specific meta tags on the web page to get preview information such as a preview card.
The "crawler" performing that action usually has a specific user agent.
As we cannot necessarily control the target url of the campaign we must return a different
result when a social network crawler is visiting the URL to get preview information.
From the perspective of the crawler, this url is an empty page with opengraph tags.
For all other user agents, it's a simple redirection url.
Keeping an up-to-date list of user agents for each supported target website is imperative
for this app to work.
"""
card = _get_card_from_url(card_id, card_slug)
campaign_sudo = card.sudo().campaign_id
redirect_url = campaign_sudo.link_tracker_id.short_url or campaign_sudo.target_url or campaign_sudo.get_base_url()
if _is_crawler(request):
return request.render('marketing_card.card_campaign_crawler', {
'image_url': card._get_card_url(),
'post_text': campaign_sudo.post_suggestion,
'target_name': card.display_name or '',
})
return request.redirect(redirect_url)