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.

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:

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.

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.