We want our customers to be able to access restricted contents on our Odoo website based on whether they bought one or multiple products from our store. This could be access to an individual digital product (comic in our case) or something like a all-access-pass which grants access to all restricted content.
When a user makes a purchase from our store and they want to be able to access restricted content later on (instead of just using the file we sent them) they need to register as a user.
We also need a mapping between website pages and products from the store that grant access. Having a user in our database together with their purchase history we can then decide whether they can access certain pages or not. Visitors who don't have a user account don't get access to the restricted content.
In order to implement this logic we create a custom module.
from odoo import api, models, fields, Command
class PageProductMapping(models.Model):
_name = 'product_page_access.page_product_mapping'
_description = 'Page Product Mapping'
# for each page there is one list of products that grant access to it
page = fields.('website.page', ondelete = 'cascade', string='Page')
# the product where the user is redirected to if access is not granted to the page
link_out_product = fields.('product.product', string='Product')
# products that grant access to the page
products = fields.('product.product', string='Alternative Products')
_sql_constraints = [
('page_uniq', 'unique(page)', 'Each page can only be mapped once.'),
]We add a basic list view for our model and add a menuitem under the eCommerce menuitem in the website backend settings. I skip this part here for the sake of brevity. See the view definition in the repo.
With the shortened controller implementation for the path /restricted-content/... below we can implement our business logic.
class RestrictedPageController(http.Controller):
@http.route(
"/restricted-content/<string:slug>",
type="http",
auth="user",
website=True,
sitemap=False,
priority=99,
)
def restricted_page(self, slug, **kw):
page = (
request.env["website.page"]
.()
.([("url", "=", "/restricted-content/%s" % slug)], limit=1)
)
if not page:
return request.()
# Load the product_page_access mapping for this page
mapping = (
request.env["product_page_access.page_product_mapping"]
.()
.([("page", "=", page.id)], limit=1)
)
if not mapping:
# no allowlist defined → redirect to the home page
return request.("/")
if not mapping.products:
# empty list → redirect
return request.("/")
# Check if the user ever bought at least one of them
user = request.env.user
has_access = (
request.env["sale.order.line"]
.()
.(
[
("order_id.partner_id", "child_of", user.partner_id.id),
("order_id.state", "in", ("sale", "done")),
'|',
("product_id", "in", mapping.products.ids),
("product_id", "=", mapping.link_out_product.id)
],
limit=1,
)
)
if not has_access:
# Redirect to the shop so the user can buy
_logger.(
"restricted_page: user_id=%s has_access=%s", user.id, has_access
)
return request.(mapping.link_out_product.website_url)
# Everything fine – render the page
return request.(page.key, {})There you have it. With the custom controller above and the mapping between pages and products that grant access to them we can serve our restricted content just to customers who have bought access. There is certainly room for improvement. For example instead of using a fixed link_out_product we could redirect the user to a page that displays all products options that grant access to the page sorted in a way that could be tailored to the customer.
Also it might make sense to integrate our restricted content with the subscriptions addon module provided by Odoo in case we can gain a loyal customer base.