We will be adding the kanban view to the Todo tasks with a new addon module. It would be simpler to add it directly to the todo_ui
module. However, for a clearer explanation, we will use a new module and avoid too many, possibly confusing, changes in the already created files.
We will name this new addon module as todo_kanban
and create the usual initial files. Edit the descriptor file todo_kanban/__manifest__.py
as follows:
{'name': 'To-Do Kanban', 'description': 'Kanban board for to-do tasks.', 'author': 'Daniel Reis', 'depends': ['todo_ui'], 'data': ['views/todo_view.xml'] }
Also add an empty todo_kanban/__init__.py
file, to make the directory Python importable, as required for Odoo addon modules.
Next, create the XML file where our shiny new kanban view will go and set kanban as the default view on the to-do task's window action. This should be in todo_kanban/views/todo_view.xml
, containing the following code:
<?xml version="1.0"?>
<odoo>
<!-- Add Kanban view mode to the menu Action: -->
<act_window id="todo_app.action_todo_task" name="To-Do Tasks"
res_model="todo.task" view_mode="kanban,tree,form,calendar,graph,pivot"
context="{'search_default_filter_my_tasks': True}" />
<!-- Add Kanban view -->
<record id="To-do Task Kanban" model="ir.ui.view">
<field name="model">todo.task</field>
<field name="arch" type="xml">
<kanban>
<!-- Empty for now, but the Kanban will go here! -->
</kanban>
</field>
</record>
</odoo>
Now we have the basic skeleton for our module in place.
Before starting with the kanban views, we need to add a couple of fields to the to-do tasks model.
Other than stages, a few more fields are useful and frequently used in kanban boards.
priority
lets users organize their work items, signaling what should be addressed first.kanban_state
signals whether a task is ready to move to the next stage or is blocked for some reason. At the model definition layer, both are selection fields. At the view layer, they have specific widgets for them that can be used on form and kanban views.color
is used to store the color the kanban card should display, and can be set using a color picker menu available on kanban views.To add these fields to our model, we will add a models/todo_task_model.py
file.
But first, we will need to make it importable, and edit the todo_kanban/__init__.py
file to import the models
subdirectory:
from . import models
Then create the models/__init__.py
file with:
from . import todo_task
Now let's edit the models/todo_task.py
file:
from odoo import models, fields class TodoTask(models.Model): _inherit = 'todo.task' color = fields.Integer('Color Index') priority = fields.Selection( [('0', 'Low'), ('1', 'Normal'), ('2', 'High')], 'Priority', default='1') kanban_state = fields.Selection( [('normal', 'In Progress'), ('blocked', 'Blocked'), ('done', 'Ready for next stage')], 'Kanban State', default='normal')
Now we can work on the kanban view.
The kanban view architecture has a <kanban>
top element and the following basic structure:
<kanban default_group_by="stage_id" class="o_kanban_small_column" > <!-- Fields to use in expressions... --> <field name="stage_id" /> <field name="color" /> <field name="kanban_state" /> <field name="priority" /> <field name="is_done" /> <field name="message_partner_ids" /> <!-- (...add other used fields). --> <templates> <t t-name="kanban-box"> <!-- HTML QWeb template... --> </t> </templates> </kanban>
Notice the default_group_by="stage_id"
attribute used in the <kanban>
element. We used it so that, by default, the kanban cards are grouped by stage like kanban boards should. In simple card list kanbans, such as the one in Contacts, we don't need this and would instead just use a simple <kanban>
opening tag.
The <kanban>
top element supports a few interesting attributes:
default_group_by
sets the field to use for the default column groups.default_order
sets a default order to use for the kanban items.quick_create="false"
disables the quick create option (the large plus sign), available at the top of each column to create new items by providing just a title description. The false value is a JavaScript literal, and must be in lowercase.class
adds a CSS class to the root element of the rendered kanban view. A relevant class is o_kanban_small_column
, making columns somewhat more compact than the default. Additional classes may be made available by module provided custom CSS.We then see a list of fields used in templates. To be exact, only fields used exclusively in QWeb expressions need to be declared here, to ensure that their data is fetched from the server.
Next, we have a <templates>
element, containing one or more QWeb templates to generate the used HTML fragments. We must have one template named kanban-box
, that will render the kanban cards. Additional templates can be also added, usually to define HTML fragments to be reused in the main template.
These templates use standard HTML and the QWeb templating language. QWeb provides special directives, that are processed to dynamically generate the final HTML to be presented.
Odoo uses the Twitter Bootstrap 3 web style library, so those style classes are generally available wherever HTML can be rendered. You can learn more about Bootstrap at https://getbootstrap.com
We will now have a closer look at the QWeb templates to use in the kanban views.
The main content area of a kanban card is defined inside the kanban-box
template. This content area can also have a footer sub-container.
For a single footer, we would use a <div>
element at the bottom of the kanban box, with the oe_kanban_footer
CSS class. This class will automatically split its inner elements with flexible spaces, making explicit left- and right- alignment inside it superfluous.
A button opening an action menu may also be featured at the card's top-right corner. As an alternative, the Bootstrap provided classes pull-left
and pull-right
can be used to add left or right aligned elements anywhere in the card, including in the oe_kanban_footer
footer.
Here is our first iteration on the QWeb template for our kanban card:
<!-- Define the kanban-box template --> <t t-name="kanban-box"> <!-- Set the Kanban Card color: --> <div t-attf-class="#{kanban_color(record.color.raw_value)} oe_kanban_global_click"> <div class="o_dropdown_kanban dropdown"> <!-- Top-right drop down menu here... --> </div> <div class="oe_kanban_content"> <div class="oe_kanban_footer"> <div> <!-- Left hand footer... --> </div> <div> <!-- Right hand footer... --> </div> </div> </div> <!-- oe_kanban_content --> <div class="oe_clear"/> </div> <!-- kanban color --> </t>
This lays out the overall structure for the kanban card. You may notice that the color
field is being used in the top <div>
element to dynamically set the card's color. We will explain the t-attf
QWeb directive in more detail in one of the next sections.
Now let's work on the main content area, and choose what to place there:
<!-- Content elements and fields go here... -->
<div>
<field name="tag_ids" />
</div>
<div>
<strong>
<a type="open"><field name="name" /></a>
</strong>
</div>
<ul>
<li><field name="user_id" /></li>
<li><field name="date_deadline" /></li>
</ul>
Most of this template is regular HTML, but we also see the <field>
element used to render field values, and the type
attribute used in regular form view buttons, used here in an <a>
anchor tag.
On the left-hand footer, we will insert the priority widget:
<div>
<!-- Left hand footer... -->
<field name="priority" widget="priority"/>
</div>
Here we can see the priority
field added, just like we would do in a form view.
On the right-hand footer we will place the kanban state widget and the avatar for the owner of the to-do task:
<div>
<!-- Right hand footer... -->
<field name="kanban_state" widget="kanban_state_selection"/>
<img t-att- t-att-src="kanban_image(
'res.users', 'image_small', record.user_id.raw_value)"
width="24" height="24" class="oe_kanban_avatar pull-right" />
</div>
The kanban state is added using a <field>
element, just like in regular form views. The user avatar image is inserted using the HTML <img>
tag. The image content is dynamically generated using the QWeb t-att-
directive, that we will explain in a moment.
Sometimes we want to have a small representative image to be shown on the card, like in the Contacts example. For reference, this can be done by adding the following as the first content element:
<img t-att-src="kanban_image( 'res.partner', 'image_medium', record.id.value)" class="o_kanban_image"/>
Kanban cards can have an option menu, placed at the top-right. Usual actions are to edit or delete the record, but it's possible to have any action that can be called from a button. We also have a widget to set the card's color available.
The following is a baseline HTML code for the option menu to be added at the top of the oe_kanban_content
element:
<div class="o_dropdown_kanban dropdown"> <!-- Top-right drop down menu here... --> <a class="dropdown-toggle btn" data-toggle="dropdown" href="#"> <span class="fa fa-bars fa-lg"/> </a> <ul class="dropdown-menu" role="menu" aria-labelledby="dLabel"> <!-- Edit and Delete actions, if available: --> <t t-if="widget.editable"> <li><a type="edit">Edit</a></li> </t> <t t-if="widget.deletable"> <li><a type="delete">Delete</a></li> </t> <!-- Call a server-side Model method: --> <t t-if="!record.is_done.value"> <li><a name="do_toggle_done" type="object">Set as Done</a> </li> </t> <!-- Color picker option: --> <li> <ul class="oe_kanban_colorpicker" data-field="color"/> </li> </ul> </div>
Notice that the above won't work unless we have <field name="is_done" />
somewhere in the view, because it is used in one of the expressions. If we don't need to use it inside the template, we can declare it before the <templates>
element, as we did when defining the <kanban>
view.
The drop-down menu is basically an HTML list of the <a>
elements. Some options, such as Edit and Delete, are made available only if certain conditions are met. This is done with the t-if
QWeb directive. Later in this chapter, we explain this and other QWeb directives in more detail.
The widget
global variable represents the current KanbanRecord()
JavaScript object responsible for the rendering of the current kanban card. Two particularly useful properties are widget.editable
and widget.deletable
to inspect if the actions are available.
We can also see how to show or hide an option depending on the record field values. The Set as Done option will only be displayed if the is_done
field is not set.
The last option adds the color picker special widget using the color
data field to select and change the card's background color.
In QWeb templates, the <a>
tag for links can have a type
attribute. It sets the type of action the link will perform so that links can act just like the buttons in regular forms. So in addition to the <button>
elements, the <a>
tags can also be used to run Odoo actions.
As in form views, the action type can be action
or object
, and it should be accompanied by a name
attribute, identifying the specific action to execute. Additionally, the following action types are also available:
open
opens the corresponding form viewedit
opens the corresponding form view directly in edit modedelete
deletes the record and removes the item from the kanban view