Creating button to (un)publish content and navigate between the backend and frontend

< / / / / / >
Intermediate
Framework Website
V13.0
V 13.0
+/- 14 minutes
Written by Yenthe Van Ginneken
(0)

Quick scroll

1. Introduction

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.

2. Inheriting the abstract model and function

2.1 Inheriting the model

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)                                            

2.2 inheriting the function

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.

3. Creating the backend

3.1 Creating the views

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.

3.2 Creating the menus and action

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.

4. Creating security rules

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!

5. Creating a controller

5.1 Tutorials overview

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:

                                                                                                

This view will show all published tutorials under eachother with first the title, then the image and finally the text with the author. Notice how we've added a t-attf-href with a slug in the code. This will automatically convert the title of the blog into an URL that contains the title. In this URL you will also find the ID of the record at the end. This is so that we can open the detail page and handle this in the controller.

5.2 Tutorial detail

So we have an overview page with tutorials and clickable links. The next step is to handle users clicking through to the detail page of a tutorial. Open your controller again and let us add a second route that opens the detail view for the clicked tutorial:

                            @http.route(['/tutorial/<model("tutorial"):tutorial>'],                type='http', auth='public', website=True)    def tutorial_details(self, tutorial, **kw):        return request.render('tutorial_website_published_button.tutorial_detail_page',                              {'tutorial': tutorial})                                            

Finally, let us add a simple template named 'tutorial_detail_page' in our file 'tutorial_frontend_view.xml'. Let us just show the title and description for this demo:

                                                                                                

5.3 specifying the return URL

By default Odoo finds the way from the frontend to the backend but there are two downsides. The backend view that is loaded never has a breadcrumb and it always opens the record in the website app. As you usually quickly want to switch between records in the backend you'll probably want to add support for breadcrumbs. Luckily we can solve this by creating an XML record that sets an action on the 'edit in backend' option:

                                                                                                

Right now there is no out of the box solution to load your views in another module then website but there is an ongoing discussion about this on Github so it might become available in upcoming versions.

Great job, that's it! If you now install this module, add a tutorial and publish it on the website you'll see it on https://yourwebsite.com/tutorials. If you open the tutorial it's detail page you will see that you're able to directly go back to the backend (if you're logged in):

Edit in backend example

6. Conclusion

Being able to quickly open the record in the frontend or backend can be a very handy feature, especially if you have users creating new content often. Odoo knows this and has made an abstract model so that you can quickly switch and (un)publish content from the frontend too. If you're writing content that changes a lot or that you want to (un)publish this is a must have feature on your custom model.