Creating button to (un)publish content and navigate between the backend and frontend
Intermediate
When you have online content on your website you usually want to be able to show or hide it at some point in time. If you're still in the process of writing the text for your content or if your content is outdated you'll want an option to quickly hide it on your website. While you're editing content or going through your content most of the time you'll want to check the content both in the backend and on the website. Thanks to Odoo there is an option to do both by inheriting the existing model 'website.published.mixin' and using these fields to create a smart button to quickly switch between the backend and frontend.
In this tutorial we will create a new model named 'tutorial' and website pages for all tutorial records. We'll add a smart button on the form to publish or unpublish records on the website. The smart button will also allow us to quickly open the related website page and to get back to the backend from the frontend.
We'll first have to inherit the model 'website.published.mixin'. This model is an abstract model, which means that we can inherit it to add the fields and functions that are available for this model on our model. Let us create a new model named 'tutorial' that inherits the published model. We'll add a name, description, author and preview_image field right away too:
# -*- coding: utf-8 -*-from odoo import models, fields, api, modules, _from odoo.addons.http_routing.models.ir_http import slugimport base64class Tutorial(models.Model): _name = 'tutorial' _inherit = ['website.published.mixin'] _description = 'Tutorial for a specific object' name = fields.Char(string='Tutorial name', required=True, copy=False) preview_image = fields.Binary("Image preview", copy=False) author_id = fields.Many2one('res.partner', string='Author', copy=False) description = fields.Text(string='Intro (preview) text', copy=False)
The next step is to override the default function '_compute_website_url'. In this function we have to set the field 'website_url', which should be pointing to the URL of your record in the website of Odoo. This means we need to build the URL part starting after the domain. Have a look at this code for now:
def _compute_website_url(self): super(Tutorial, self)._compute_website_url() for tutorial in self: tutorial.website_url = "/tutorial/%s" % slug(tutorial)
So, does this make sense to you? In this function we say that for every tutorial that is in the recordset we want to set the website_url to '/tutorial/tutorial-slug-url'. Slug is a builtin tool in Odoo, which slugifies URL's. It combines the name of the record with the ID of the record to create an unique, browsable, URL. The result will be '/tutorial/some-tutorial-1' for example. By setting this URL Odoo knows which URL in the frontend to open. Now let's continue with the views first.
Let's first create a menuitem, tree view and form view so that we can create our records. Since we have inherited the model 'website.published.mixin' we inherit the fields of this model. We can now show the fields 'website_published' and 'is_published' on the form so that you can see if the record is published or not and (later on) quickly open the record in the frontend. Add the field 'is_published' on the form and add a button with the field 'website_published' for quick navigation between the backend and frontend:
Notice how in the form view the field 'website_published' has the widget 'website_button' set. This tells Odoo that we want to use the widget to navigate between the backend/frontend and to show the text/info if the record is published or not.
First create an action to view all tutorials and a main menuitem. The action should reference our search view and the menuitem should reference the action:
Finally add a child menuitem for easy navigation:
This is all the code that we need to view and modify our records in the backend. We now need security rules to be able to view the content.
The next thing we need is security rules for this model or we won't be able to view/modify any record. Open up the 'ir.model.access.csv' file and add two security rules. One for logged in users so they can do all operations and one for public (website) users so they can view the content (which we will show on the website later on):
access_tutorial,tutorial,model_tutorial,base.group_portal,1,1,1,1public_access_tutorial,tutorial,model_tutorial,base.group_public,1,0,0,0
Tip: don't forget to import the ir.model.access.csv file in your __manifest__.py!
Now that we have the backend ready we should move to the frontend. Let's create two controller functions. The first one will show all active tutorials if we open the website and go to /tutorials. The second one will open a tutorial if the user clicked on a tutorial in the overview. Create a new file named 'tutorial.py' in the 'controllers' folder and create a route that gets all tutorials and makes them available:
# -*- coding: utf-8 -*-from odoo import httpfrom odoo.http import requestclass Tutorial(http.Controller): @http.route(["/tutorials"], type='http', auth="public", website=True) def tutorial_overview(self, **kw): # Get all tutorials from the database tutorials = http.request.env['tutorial'].search([("website_published", "=", True)]) # Return the tutorial overview page with all tutorial records return request.render('tutorial_website_published_button.tutorial_overview', {'tutorials': tutorials})
This controller will load all published tutorials from the database if you surf to yourwebsite.com/tutorials. Thanks to the domain in the search we'll only find published tutorials. Next we will load the XML record named "tutorial_overview" to show it to the user. Our next step is to create an XML record to show all tutorials on this page. Create a new file named 'tutorial_frontend_view.xml' in the 'views' folder of your module and create a new template. Let us show the name of the tutorial, the preview image, the description and the author. The name will be shown as a title and should create a link to a detail page: