PageRenderTime 61ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/doc/howtos/themes.rst

https://gitlab.com/padjis/mapan
ReStructuredText | 1041 lines | 710 code | 331 blank | 0 comment | 0 complexity | 4e9f84fc38ed4ea29c14e5d94f9270e9 MD5 | raw file
  1. :banner: banners/build_a_theme.jpg
  2. =====================
  3. Theme Tutorial
  4. =====================
  5. .. rst-class:: lead
  6. Odoo celebrates freedom. Freedom for the designer to go further and
  7. freedom for the user to customize everything according to their needs.
  8. Ready to create your own theme? Great. Here are some things you should know before you begin. This tutorial is a guide to creating an Odoo theme.
  9. .. image:: theme_tutorial_assets/img/Intro.jpg
  10. An introduction for web designers
  11. =================================
  12. If you are a web designer using Odoo for the first time, you are in the right place.
  13. This introduction will outline the basics of Odoo theme creation.
  14. .. note::
  15. Odoos team has created a framework thats powerful and easy to use. Theres no need to know special syntaxes to use this set of tools.
  16. From common CMS to Odoo
  17. -----------------------
  18. .. note::
  19. If you always think and work in the same way, youll probably get the same results. If you want something completely new, then try something different.
  20. ..
  21. Where is my header.php file?
  22. This is usually the first question from a web designer used to working with Wordpress or Joomla and coming to Odoo for the first time.
  23. .. container:: row
  24. .. container:: col-sm-4
  25. .. image:: theme_tutorial_assets/img/cms.jpg
  26. .. container:: col-sm-7
  27. Indeed, when using common CMSs, you have to code several files (like header.php, page.php, post.php, etc.) in order to create a basic structure for your website. With those systems, this base structure acts as a design foundation that you have to update over time to ensure compatibility within your CMS. So, even after you have spent hours coding the files, you have not even started on the design yet.
  28. This **does not** apply to creating Odoo themes.
  29. .. note::
  30. We think that theme design should be simple (and powerful). When we created our Website Builder, we decided to start from scratch instead of relying on what already existed. This approach gave us the freedom to focus on the things that are really important for designers: styles, content and the logic behind them. No more struggling with technical stuff.
  31. Odoo default theme structure
  32. ----------------------------
  33. .. container:: row
  34. .. container:: col-sm-8
  35. Odoo comes with a default theme structure.
  36. It is a very basic theme that provides minimal structure and layout. When you create a new theme, you are actually extending this.
  37. Indeed its always enabled in your setup and it acts exactly like the CMSs base structure we mentioned above, except that you dont have to create or maintain it.
  38. It will upgrade automatically within your Odoo installation and, since it is included in the Website Builder module, everything is smoothly integrated by default.
  39. As a result, you are totally free to focus on design while this structure does the job of providing integrations and functionality.
  40. .. container:: col-sm-4
  41. .. image:: theme_tutorial_assets/img/def_structure.jpg
  42. .. container:: row
  43. .. container:: col-md-6
  44. **Main features:**
  45. * Basic layouts for pages, blog and eCommerce
  46. * Website Builder integration
  47. * Basic Snippets
  48. * Automatic Less/Sass compiling
  49. * Automatic Js and CSS minification and combination
  50. .. container:: col-md-6
  51. **Main technologies:**
  52. * Twitter Bootstrap
  53. * jQuery
  54. * jQuery UI
  55. * underscore.js
  56. Thinking "modular"
  57. ==================
  58. An Odoo theme is not a folder containing HTML or PHP files, its a modular framework written in XML. Never worked with XML files before? Dont worry, after following the tutorial, youll be able to create your first theme with only basic knowledge of HTML.
  59. Using classical web design workflows, you usually code the layout of the entire page. The result of this is a static web page. You can update the content, of course, but your client will need you to work on making even basic changes.
  60. Creating themes for Odoo is a total change of perspective. Instead of defining the complete layout for a page, you can create blocks (snippets) at let the user choose where to drag&drop them, creating the page layout on their own.
  61. We call this modular design.
  62. Imagine an Odoo theme as a list of elements and options that you have to create and style.
  63. As a designer, your goal is to style these elements in order to achieve a wonderful result, regardless of where the end user chooses to place them.
  64. Lets take a tour of our list elements:
  65. .. container:: row
  66. .. figure:: theme_tutorial_assets/img/snippet.jpg
  67. :figclass: col-sm-6
  68. Snippets (or building-blocks)
  69. A piece of HTML code. The user will drag&drop, modify and combine them using our built-in Website Builder interface. You can define sets of options and styles for each snippet. The user will choose from them according to their needs.
  70. .. figure:: theme_tutorial_assets/img/page.jpg
  71. :figclass: col-sm-6
  72. Pages
  73. These are normal web pages, except that they will be editable by the final user and that you can define an empty area that the user can fill by dragging snippets into it.
  74. .. raw:: html
  75. <div class="clearfix themes"></div>
  76. .. container:: row
  77. .. figure:: theme_tutorial_assets/img/styles.jpg
  78. :figclass: col-sm-6
  79. Styles
  80. Styles are defined using standard CSS files (or Less/Sass). You can define a style as **default** or **optional**. The default styles are always active in your theme, the optional styles can be enabled or disabled by the user.
  81. .. figure:: theme_tutorial_assets/img/functionalities.jpg
  82. :figclass: col-sm-6
  83. Functionalities
  84. Thanks to Odoos modularity, everything can be personalized even more. This means there are endless possibilities for your creativity. Adding functionalities is easy and its simple to provide the end user with customizable options.
  85. Odoo's XML files, an overview
  86. -----------------------------
  87. Any Odoo XML file starts with encoding specifications.
  88. After that, you have to write your code inside a ``<odoo>`` tag.
  89. .. code-block:: xml
  90. [XML]
  91. <?xml version="1.0" encoding="utf-8" ?>
  92. <odoo>
  93.   ## YOUR CODE HERE
  94. </odoo>
  95. Almost every element and option that you create has to be placed inside a ``<template>`` tag, like in this example.
  96. .. code-block:: xml
  97. [XML]
  98. <template id="my_title" name="My title">
  99. <h1>This is an HTML block</h1>
  100. <h2 class="lead">And this is a subtitle</h2>
  101. </template>
  102. .. important::
  103. don't misunderstand what ``template`` means. A template tag only
  104. defines a piece of html code or options - but it does not
  105. necessarily coincide with a visual arrangement of elements.
  106. The previous code defines a title, but it will not be displayed
  107. anywhere because that *template* is not associated with any part of
  108. the **Odoo default structure**. In order to do that you can use
  109. **xpath**, **qWeb** or a combination of both.
  110. Keep reading the tutorial to learn to how properly extend it with your own code.
  111. Update your theme
  112. -----------------
  113. .. container:: row
  114. .. container:: col-sm-6
  115. Since XML files are only loaded when you install the theme, you will have to force reloading every time you make changes on an xml file.
  116. To do that, click on the Upgrade button in the modules page.
  117. .. image:: theme_tutorial_assets/img/restart.png
  118. .. container:: col-sm-5
  119. .. image:: theme_tutorial_assets/img/upgrade_module.png
  120. Create a theme module
  121. ======================
  122. Odoos themes are packaged like modules. Even if you are designing a very simple website for your company or client, you need to package the theme like an Odoo module.
  123. ``main folder``
  124. Create a folder and name it like this: ``theme_`` followed by your
  125. theme's name.
  126. ``__manifest__.py``
  127. Create an empty document and save it to your folder as
  128. ``__manifest__.py``. This will contain the configuration info for
  129. your theme.
  130. ``__init__.py``
  131. Create another empty file and name it ``__init__.py``. It's a
  132. mandatory system file. Create and leave it blank.
  133. ``views`` and ``static`` folders
  134. Create them in the main folder. In ``views`` you'll place your xml
  135. files that define your snippets, your pages and your
  136. options. ``static`` folder is the right place for your style ,
  137. images and custom js code.
  138. .. important::
  139. Use two underscore characters at the beginning
  140. and two at the end of odoo and init file names.
  141. The final result should be something like this:
  142. .. image:: theme_tutorial_assets/img/folder.jpg
  143. Edit ``__manifest__.py``
  144. ------------------------
  145. Open the ``__manifest__.py`` you created and copy/paste the following:
  146. .. code-block:: python
  147. {
  148. 'name':'Tutorial theme',
  149. 'description': 'A description for your theme.',
  150. 'version':'1.0',
  151. 'author':'Your name',
  152. 'data': [
  153. ],
  154. 'category': 'Theme/Creative',
  155. 'depends': ['website', 'website_theme_install'],
  156. }
  157. Replace the first four propertys values with anything you like.
  158. These values will be used to identify your new theme in Odoos backend.
  159. The ``data`` property will contain the xml files list. Right now its empty, but we will add any new files created.
  160. ``category`` defines your module category (always Theme) and, after a slash, the subcategory. You can use one subcategory from the Odoo Apps categories list. (https://www.odoo.com/apps/themes)
  161. ``depends`` specifies the modules needed by our theme to work properly. For our tutorial theme, we only need website and website_theme_install to install/update. If you need blogging or eCommerce features as well, you have to add those modules too.
  162. .. code-block:: python
  163. ...
  164. 'depends': ['website', 'website_theme_install', 'website_blog', 'sale'],
  165. ...
  166. Installing your theme
  167. ---------------------
  168. To install your theme, you just place your theme folder inside addons in your Odoo installation.
  169. After that, navigate to the Odoo **Website** module, go to
  170. :menuselection:`Configuration --> Settings`.
  171. Under **Website** section click the **Choose a theme** button, then hover over
  172. your theme and click **Use this theme**.
  173. Structure of an Odoo page
  174. =========================
  175. An Odoo page is the visual result of a combination of 2 kind of elements, **cross-pages** and **unique**.
  176. By default, Odoo provides you with a **Header** and a **Footer** (cross-pages) and a unique main element that contains the content that makes your page unique.
  177. .. note::
  178. Cross-pages elements will be the same on every page. Unique elements are related to a specific page only.
  179. .. image:: theme_tutorial_assets/img/page_structure.jpg
  180. To inspect the default layout, simply create a new page using the
  181. Website Builder. Click on :menuselection:`Content --> New Page` and
  182. add a page name. Inspect the page using your browser.
  183. .. code-block:: html
  184. <div id=wrapwrap>
  185. <header />
  186. <main />
  187. <footer />
  188. </div>
  189. Extend the default Header
  190. -------------------------
  191. By default, Odoo header contains a responsive navigation menu and the companys logo. You can easily add new elements or style the existing one.
  192. To do so, create a **layout.xml** file in your **views** folder and add the default Odoo xml markup.
  193. .. code-block:: xml
  194. <?xml version="1.0" encoding="utf-8" ?>
  195. <odoo>
  196. </odoo>
  197. Create a new template into the ``<odoo>`` tag, copy-pasting the following
  198. code.
  199. .. code-block:: xml
  200. <!-- Customize header -->
  201. <template id="custom_header" inherit_id="website.layout" name="Custom Header">
  202. <!-- Assign an id -->
  203. <xpath expr="//div[@id='wrapwrap']/header" position="attributes">
  204. <attribute name="id">my_header</attribute>
  205. </xpath>
  206. <!-- Add an element after the top menu -->
  207. <xpath expr="//div[@id='wrapwrap']/header/nav" position="after">
  208. <div class="container">
  209. <div class="alert alert-info mt16" role="alert">
  210. <strong>Welcome</strong> in our website!
  211. </div>
  212. </div>
  213. </xpath>
  214. </template>
  215. The first xpath will add the id ``my_header`` to the header. Its the best option if you want to
  216. target css rules to that element and avoid these affecting other content on the page.
  217. .. warning::
  218. Be careful replacing default elements attributes. As your theme will extend the default one,
  219. your changes will take priority in any future Odoos update.
  220. The second xpath will add a welcome message just after the navigation menu.
  221. The last step is to add layout.xml to the list of xml files used by
  222. the theme. To do that, edit your ``__manifest__.py`` file like this
  223. .. code-block:: python
  224. 'data': [ 'views/layout.xml' ],
  225. Update your theme
  226. .. image:: theme_tutorial_assets/img/restart.png
  227. Great! We successfully added an id to the
  228. header and an element after the navigation menu. These changes will be
  229. applied to each page of the website.
  230. .. image:: theme_tutorial_assets/img/after-menu.png
  231. :class: shadow-0
  232. Create a specific page layout
  233. =============================
  234. Imagine that we want to create a specific layout for a Services page.
  235. For this page, we need to add a list of services to the top and give the client the possibility of setting the rest of the pages layout using snippets.
  236. Inside your *views* folder, create a **pages.xml** file and add the
  237. default Odoo markup. Inside ``<odoo>``, instead of defining a ``<template>``,
  238. we will create a *page* object.
  239. .. code-block:: xml
  240. <?xml version="1.0" encoding="utf-8" ?>
  241. <odoo>
  242. <!-- === Services Page === -->
  243. <record id="services_page" model="website.page">
  244. <field name="name">Services page</field>
  245. <field name="website_published">True</field>
  246. <field name="url">/services</field>
  247. <field name="type">qweb</field>
  248. <field name="key">theme_tutorial.services_page</field>
  249. <field name="arch" type="xml">
  250. <t t-name="theme_tutorial.services_page_template">
  251. <h1>Our Services</h1>
  252. <ul class="services">
  253. <li>Cloud Hosting</li>
  254. <li>Support</li>
  255. <li>Unlimited space</li>
  256. </ul>
  257. </t>
  258. </field>
  259. </record>
  260. </odoo>
  261. As you can see, pages come with many additional properties like the *name* or
  262. the *url* where it is reachable.
  263. We successfully created a new page layout, but we haven't told the
  264. system **how to use it**. To do that, we can use **QWeb**. Wrap the
  265. html code into a ``<t>`` tag, like in this example.
  266. .. code-block:: xml
  267. <!-- === Services Page === -->
  268. <record id="services_page" model="website.page">
  269. <field name="name">Services page</field>
  270. <field name="website_published">True</field>
  271. <field name="url">/services</field>
  272. <field name="type">qweb</field>
  273. <field name="key">theme_tutorial.services_page</field>
  274. <field name="arch" type="xml">
  275. <t t-name="theme_tutorial.services_page_template">
  276. <t t-call="website.layout">
  277. <div id="wrap">
  278. <div class="container">
  279. <h1>Our Services</h1>
  280. <ul class="services">
  281. <li>Cloud Hosting</li>
  282. <li>Support</li>
  283. <li>Unlimited space</li>
  284. </ul>
  285. </div>
  286. </div>
  287. </t>
  288. </t>
  289. </field>
  290. </record>
  291. Using ``<t t-call="website.layout">`` we will extend the Odoo
  292. default page layout with our code.
  293. As you can see, we wrapped our code into two ``<div>``, one with ID ``wrap`` and the other one with class ``container``. This is to provide a minimal layout.
  294. The next step is to add an empty area that the user
  295. can fill with snippets. To achieve this, just create a ``div`` with
  296. ``oe_structure`` class just before closing the ``div#wrap`` element.
  297. .. code-block:: xml
  298. <?xml version="1.0" encoding="utf-8" ?>
  299. <odoo>
  300. <!-- === Services Page === -->
  301. <record id="services_page" model="website.page">
  302. <field name="name">Services page</field>
  303. <field name="website_published">True</field>
  304. <field name="url">/services</field>
  305. <field name="type">qweb</field>
  306. <field name="key">theme_tutorial.services_page</field>
  307. <field name="arch" type="xml">
  308. <t t-name="theme_tutorial.services_page_template">
  309. <t t-call="website.layout">
  310. <div id="wrap">
  311. <div class="container">
  312. <h1>Our Services</h1>
  313. <ul class="services">
  314. <li>Cloud Hosting</li>
  315. <li>Support</li>
  316. <li>Unlimited space</li>
  317. </ul>
  318. <!-- === Snippets' area === -->
  319. <div class="oe_structure" />
  320. </div>
  321. </div>
  322. </t>
  323. </t>
  324. </field>
  325. </record>
  326. </odoo>
  327. .. tip::
  328. You can create as many snippet areas as you like and place them anywhere in your pages.
  329. It is worth mentioning there is an alternative to create pages using the
  330. ``<template>`` directive we saw before.
  331. .. code-block:: xml
  332. <?xml version="1.0" encoding="utf-8" ?>
  333. <odoo>
  334. <!-- === Services Page === -->
  335. <template id="services_page_template">
  336. <t t-call="website.layout">
  337. <div id="wrap">
  338. <div class="container">
  339. <h1>Our Services</h1>
  340. <ul class="services">
  341. <li>Cloud Hosting</li>
  342. <li>Support</li>
  343. <li>Unlimited space</li>
  344. </ul>
  345. <!-- === Snippets' area === -->
  346. <div class="oe_structure" />
  347. </div>
  348. </div>
  349. </t>
  350. </template>
  351. <record id="services_page" model="website.page">
  352. <field name="name">Services page</field>
  353. <field name="website_published">True</field>
  354. <field name="url">/services</field>
  355. <field name="view_id" ref="services_page_template"/>
  356. </record>
  357. </odoo>
  358. This would allow your page content to be further customized using ``<xpath>``.
  359. Our page is almost ready. Now all we have to do is add **pages.xml** in our **__manifest__.py** file
  360. .. code-block:: python
  361. 'data': [
  362. 'views/layout.xml',
  363. 'views/pages.xml'
  364. ],
  365. Update your theme
  366. .. image:: theme_tutorial_assets/img/restart.png
  367. Great, our Services page is ready and youll be able to access it by navigating to ``<yourwebsite>/services`` (the URL we chose above).
  368. You will notice that it's possible to drag/drop snippets underneath the
  369. *Our Services* list.
  370. .. image:: theme_tutorial_assets/img/services_page_nostyle.png
  371. :class: shadow-0
  372. Now let's go back to our *pages.xml* and, after our page template,
  373. copy/paste the following code.
  374. .. code-block:: xml
  375. <record id="services_page_link" model="website.menu">
  376. <field name="name">Services</field>
  377. <field name="page_id" ref="services_page"/>
  378. <field name="parent_id" ref="website.main_menu" />
  379. <field name="sequence" type="int">99</field>
  380. </record>
  381. This code will add a link to the main menu, referring to the page we created.
  382. .. image:: theme_tutorial_assets/img/services_page_menu.png
  383. :class: shadow-0
  384. The **sequence** attribute defines the links position in the top menu.
  385. In our example, we set the value to ``99`` in order to place it last. I you want to place it in a particular position, you have to replace the value according to your needs.
  386. As you can see inspecting the *data.xml* file in the ``website`` module, the **Home** link is set to ``10`` and the **Contact** us one is set to ``60`` by default.
  387. If, for example, you want to place your link in the **middle**, you can set your links sequence value to ``40``.
  388. Add Styles
  389. ==========
  390. Odoo includes Bootstrap by default. This means that you can take advantage of all Bootstrap styles and layout functionalities out of the box.
  391. Of course Bootstrap is not enough if you want to provide a unique design. The following steps will guide you through how to add custom styles to your theme.
  392. The final result won't be pretty, but will provide you with enough information to build upon on your own.
  393. Lets start by creating an empty file called **style.scss** and place it in a folder called **scss** in your static folder.
  394. The following rules will style our *Services* page. Copy and paste it, then save the file.
  395. .. as of Pygments 2.2, the Less lexer can't handle inline comments or nested
  396. rules so use scss instead, it's not quite perfect but it doesn't trigger
  397. warnings/errors
  398. .. code-block:: scss
  399. .services {
  400. background: #EAEAEA;
  401. padding: 1em;
  402. margin: 2em 0 3em;
  403. li {
  404. display: block;
  405. position: relative;
  406. background-color: #16a085;
  407. color: #FFF;
  408. padding: 2em;
  409. text-align: center;
  410. margin-bottom: 1em;
  411. font-size: 1.5em;
  412. }
  413. }
  414. Our file is ready but it is not included in our theme yet.
  415. Lets navigate to the view folder and create an XML file called *assets.xml*. Add the default Odoo xml markup and copy/paste the following code. Remember to replace ``theme folder`` with your themes main folder name.
  416. .. code-block:: xml
  417. <template id="mystyle" name="My style" inherit_id="website.assets_frontend">
  418. <xpath expr="link[last()]" position="after">
  419. <link rel="stylesheet" type="text/scss" href="/theme folder/static/scss/style.scss"/>
  420. </xpath>
  421. </template>
  422. We just created a template specifying our scss file. As you can see,
  423. our template has a special attribute called ``inherit_id``. This
  424. attribute tells Odoo that our template is referring to another one in
  425. order to operate.
  426. In this case, we are referring to ``assets_frontend`` template,
  427. located in the ``website`` module. ``assets_frontend`` specifies the
  428. list of assets loaded by the website builder and our goal is to add
  429. our scss file to this list.
  430. This can be achieved using xpath with the attributes
  431. ``expr="link[last()]"`` and ``position="after"``, which means "*take my
  432. style file and place it after the last link in the list of the
  433. assets*".
  434. Placing it after the last one, we ensure that our file will
  435. be loaded at the end and take priority.
  436. Finally add **assets.xml** in your **__manifest__.py** file.
  437. Update your theme
  438. .. image:: theme_tutorial_assets/img/restart.png
  439. Our scss file is now included in our theme, it will be automatically compiled, minified and combined with all Odoos assets.
  440. .. image:: theme_tutorial_assets/img/services_page_styled.png
  441. :class: shadow-0
  442. Create Snippets
  443. ===============
  444. Since snippets are how users design and layout pages, they are the most important element of your design.
  445. Lets create a snippet for our Service page. The snippet will display three testimonials and it will be editable by the end user using the Website Builder UI.
  446. Navigate to the view folder and create an XML file called **snippets.xml**.
  447. Add the default Odoo xml markup and copy/paste the following code.
  448. The template contains the HTML markup that will be displayed by the snippet.
  449. .. code-block:: xml
  450. <template id="snippet_testimonial" name="Testimonial snippet">
  451. <section class="snippet_testimonial">
  452. <div class="container text-center">
  453. <div class="row">
  454. <div class="col-lg-4">
  455. <img alt="client" class="rounded-circle" src="/theme_tutorial/static/src/img/client_1.jpg"/>
  456. <h3>Client Name</h3>
  457. <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
  458. </div>
  459. <div class="col-lg-4">
  460. <img alt="client" class="rounded-circle" src="/theme_tutorial/static/src/img/client_2.jpg"/>
  461. <h3>Client Name</h3>
  462. <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
  463. </div>
  464. <div class="col-lg-4">
  465. <img alt="client" class="rounded-circle" src="/theme_tutorial/static/src/img/client_3.jpg"/>
  466. <h3>Client Name</h3>
  467. <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
  468. </div>
  469. </div>
  470. </div>
  471. </section>
  472. </template>
  473. As you can see, we used Bootstrap default classes for our three columns. Its not just about layout, these classes **will be triggered by the Website Builder to make them resizable by the user**.
  474. The previous code will create the snippets content, but we still need to place it into the editor bar, so the user will be able to drag&drop it into the page. Copy/paste this template in your **snippets.xml** file.
  475. .. code-block:: xml
  476. <template id="place_into_bar" inherit_id="website.snippets" name="Place into bar">
  477. <xpath expr="//div[@id='snippet_structure']/div[@class='o_panel_body']" position="inside">
  478. <t t-snippet="theme_tutorial.snippet_testimonial"
  479. t-thumbnail="/theme_tutorial/static/src/img/ui/snippet_thumb.jpg"/>
  480. </xpath>
  481. </template>
  482. .. rst-class:: col-sm-6
  483. Using xpath, we are targeting a particular element with id
  484. ``snippet_structure``. This means that the snippet will appear in the
  485. Structure tab. If you want to change the destination tab, you have just to replace the ``id`` value in the xpath expression.
  486. .. image:: theme_tutorial_assets/img/snippet_bar.png
  487. :class: col-sm-6 shadow-0
  488. ============ ==================================
  489. Tab Name Xpath expression
  490. ============ ==================================
  491. Structure ``//div[@id='snippet_structure']``
  492. Content ``//div[@id='snippet_content']``
  493. Feature ``//div[@id='snippet_feature']``
  494. Effect ``//div[@id='snippet_effect']``
  495. ============ ==================================
  496. The ``<t>`` tag will call our snippet's template and will assign a thumbnail placed in the img folder.
  497. You can now drag your snippet from the snippet bar, drop it in your page and see the result.
  498. .. image:: theme_tutorial_assets/img/snippet_default.png
  499. Snippet options
  500. ===============
  501. Options allow publishers to edit a snippets appearance using the Website Builders UI.
  502. Using Website Builder functionalities, you can create snippet options easily and automatically add them to the UI.
  503. Options group properties
  504. -------------------------
  505. Options are wrapped in groups. Groups can have properties that define how the included options will interact with the user interface.
  506. ``data-selector="[css selector(s)]"``
  507. Bind all the options included into the group to a particular element.
  508. ``data-js=" custom method name "``
  509. Is used to bind custom Javascript methods.
  510. ``data-drop-in="[css selector(s)]"``
  511. Defines the list of elements where the snippet can be dropped into.
  512. ``data-drop-near="[css selector(s)]"``
  513. Defines the list of elements that the snippet can be dropped beside.
  514. Default option methods
  515. -----------------------
  516. Options apply standard CSS classes to the snippet. Depending on the method that you choose, the UI will behave differently.
  517. ``data-select-class="[class name]"``
  518. More data-select-class in the same group defines a list of classes that the user can choose to apply. Only one option can be enabled at a time.
  519. ``data-toggle-class="[class name]"``
  520. The data-toggle-class is used to apply one or more CSS classes from the list to a snippet. Multiple selections can be applied at once.
  521. Let's demonstrate how default options work with a basic example.
  522. We start by adding a new file in our views folder - name it **options.xml** and add the default Odoo XML markup. Create a new template copy/pasting the following
  523. .. code-block:: xml
  524. <template id="snippet_testimonial_opt" name="Snippet Testimonial Options" inherit_id="website.snippet_options">
  525. <xpath expr="//div[@data-js='background']" position="after">
  526. <div data-selector=".snippet_testimonial"> <!-- Options group -->
  527. <div class="dropdown-submenu">
  528. <a href="#" class="dropdown-item">Your Option</a>
  529. <div class="dropdown-menu"><!-- Options list -->
  530. <a href="#" class="dropdown-item" data-select-class="opt_shadow">Shadow Images</a>
  531. <a href="#" class="dropdown-item" data-select-class="opt_grey_bg">Grey Bg</a>
  532. <a href="#" class="dropdown-item" data-select-class="">None</a>
  533. </div>
  534. </div>
  535. </div>
  536. </xpath>
  537. </template>
  538. .. note::
  539. The previous template will inherit the default **snippet_options template** adding our options after the **background** options (xpath expr attribute).
  540. To place your options in a particular order, inspect the **snippet_options template** from the **website module** and add your options before/after the desired position.
  541. As you can see, we wrapped all our options inside a DIV tag that will
  542. group our options and that will target them to the right selector
  543. (``data-selector=".snippet_testimonial"``).
  544. To define our options we applied ``data-select-class`` attributes to the
  545. ``li`` elements. When the user selects an option, the class contained in
  546. the attribute will automatically be applied to the element.
  547. Since ``selectClass`` method avoids multiple selections, the last "empty"
  548. option will reset the snippet to default.
  549. Add **options.xml** to ``__manifest__.py`` and update your theme.
  550. .. image:: theme_tutorial_assets/img/restart.png
  551. Dropping our snippet onto the page, you will notice that our new options are automatically added to the customize menu. Inspecting the page, you will also notice that the class will be applied to the element when selecting an option.
  552. .. image:: theme_tutorial_assets/img/snippet_options.png
  553. Lets create some css rules in order to provide a visual feedback for our options. Open our **style.scss** file and add the following
  554. .. code-block:: scss
  555. .snippet_testimonial {
  556. border: 1px solid #EAEAEA;
  557. padding: 20px;
  558. }
  559. // These lines will add a default style for our snippet. Now let's create our custom rules for the options.
  560. .snippet_testimonial {
  561. border: 1px solid #EAEAEA;
  562. padding: 20px;
  563. &.opt_shadow img {
  564. box-shadow: 0 2px 5px rgba(51, 51, 51, 0.4);
  565. }
  566. &.opt_grey_bg {
  567. border: none;
  568. background-color: #EAEAEA;
  569. }
  570. }
  571. .. image:: theme_tutorial_assets/img/snippet_options2.png
  572. :class: shadow-0
  573. Great! We successfully created options for our snippet.
  574. Any time the publisher clicks on an option, the system will add the class specified in the data-select-class attribute.
  575. By replacing ``data-select-class`` with ``data-toggle-class`` you will be able to select
  576. more classes at the same time.
  577. Javascript Options
  578. ------------------
  579. ``data-select-class`` and ``data-toggle-class`` are great if you need to perform
  580. simple class change operations. But what if your snippets customization needs something more?
  581. As we said before, ``data-js`` propriety can be assigned to an options group in order to define a custom method. Lets create one for our *testimonials snippet* by adding a ``data-js`` attribute to the options group div that we created earlier.
  582. .. code-block:: xml
  583. <div data-js="snippet_testimonial_options" data-selector=".snippet_testimonial">
  584. [...]
  585. </div>
  586. Done. From now on, the Website Builder will look for a
  587. ``snippet_testimonial_options`` method each time the publisher enters in edit
  588. mode.
  589. Let's go one step further by creating a javascript file, name
  590. it **tutorial_editor.js** and place it into the **static** folder. Copy/paste
  591. the following code
  592. .. code-block:: javascript
  593. odoo.define(function (require) {
  594. var options = require('web_editor.snippets.options');
  595. });
  596. Great, we successfully created our javascript editor file. This file will contain all the javascript functions used by our snippets in edit mode. Lets create a new function for our testimonial snippet using the ``snippet_testimonial_options`` method that we created before.
  597. .. code-block:: javascript
  598. odoo.define(function (require) {
  599. var options = require('web_editor.snippets.options');
  600. options.registry.snippet_testimonial_options = options.Class.extend({
  601. onFocus: function () {
  602. alert("On focus!")
  603. },
  604. });
  605. });
  606. As you will notice, we used a method called ``onFocus`` to trigger our function. The Website Builder provides several events you can use to trigger your custom functions.
  607. =========================== ==================================
  608. Event Description
  609. =========================== ==================================
  610. ``start`` Fires when the publisher selects the snippet for the first time in an editing session or when the snippet is drag-dropped into the page
  611. ``onFocus`` Fires each time the snippet is selected by the user or when the snippet is drag-dropped into the page.
  612. ``onBlur`` This event occurs when a snippet loses focus.
  613. ``onClone`` Fires just after a snippet is duplicated.
  614. ``onRemove`` It occurs just before that the snippet is removed.
  615. ``onBuilt`` Fires just after that the snippet is drag and dropped into a drop zone. When this event is triggered, the content is already inserted in the page.
  616. ``cleanForSave`` It trigger before the publisher save the page.
  617. =========================== ==================================
  618. Lets add our new javascript files to the editor assets list.
  619. Go back to **assets.xml** and create a new template like the previous one.
  620. This time we have to inherit ``assets_editor`` instead of ``assets_frontend``.
  621. .. code-block:: xml
  622. <template id="my_js" inherit_id="website.assets_editor" name="My Js">
  623. <xpath expr="script[last()]" position="after">
  624. <script type="text/javascript" src="/theme_tutorial/static/src/js/tutorial_editor.js" />
  625. </xpath>
  626. </template>
  627. Update your theme
  628. .. image:: theme_tutorial_assets/img/restart.png
  629. Lets test our new javascript function. Enter in Edit mode and drop into the page.
  630. You should now see the javascript alert that we bound on the ``onFocus`` event.
  631. If you close it, then click outside of your snippet and then click in it again, the event will trigger again.
  632. .. image:: theme_tutorial_assets/img/snippet_custom_method.png
  633. :class: shadow-0
  634. Editing Reference Guide
  635. =======================
  636. Basically all the elements in a page can be edited by the publisher.
  637. Besides that, some element types and css classes will trigger special Website Builder functionalities when edited.
  638. Layout
  639. ------
  640. ``<section />``
  641. Any section element can be edited like a block of content. The publisher can move or duplicate it. Its also possible to set a background image or color. Section is the standard main container of any snippet.
  642. ``.row > .col-lg-*``
  643. Any large bootstrap columns directly descending from a .row element, will be resizable by the publisher.
  644. ``contenteditable="False"``
  645. This attribute will prevent editing to the element and all its children.
  646. ``contenteditable="True"``
  647. Apply it to an element inside a contenteditable="False" element in order to create an exception and make the element and its children editable.
  648. ``<a href=# />``
  649. In Edit Mode, any link can be edited and styled. Using the Link Modal its also possible to replace it with a button.
  650. Media
  651. -----
  652. ``<span class=fa />``
  653. Pictogram elements. Editing this element will open the Pictogram library to replace the icon. Its also possible to transform the elements using CSS.
  654. ``<img />``
  655. Once clicked, the Image Library will open and you can replace images. Transformation is also possible for this kind of element.
  656. .. code-block:: html
  657. <div class="media_iframe_video" data-src="[your url]" >
  658. <div class="css_editable_mode_display"/>
  659. <div class="media_iframe_video_size"/>
  660. <iframe src="[your url]"/>
  661. </div>
  662. This html structure will create an ``<iframe>`` element editable by the publisher.
  663. SEO best practice
  664. =================
  665. Facilitate content insertion
  666. ----------------------------
  667. Modern search engine algorithms increasingly focus on content, which means there is less focus on **keyword saturation** and more focus on whether or not the content is **actually relevant to the keywords**.
  668. As content is so important for SEO, you should concentrate on giving publishers the tools to easily insert it. It is important that your snippets are content-responsive, meaning that they should fit the publishers content regardless of size.
  669. Lets have a look to this example of a classic two column snippet, implemented in two different ways.
  670. .. container:: row
  671. .. container:: col-sm-7
  672. .. image:: theme_tutorial_assets/img/seo_snippet_wrong.png
  673. .. container:: col-sm-5
  674. **Bad**
  675. Using fixed image, the publisher will be forced to limit the text in order to follow the layout.
  676. .. container:: row
  677. .. container:: col-sm-7
  678. .. image:: theme_tutorial_assets/img/seo_snippet_good.png
  679. .. container:: col-sm-5
  680. **Good**
  681. Using background images that fit the column height, the publisher will be free to add the content regardless of the images height.
  682. Page segmentation
  683. -----------------
  684. Basically, page segmentation means that a page is divided into several separate parts and these parts are treated as separate entries by search engines.
  685. When you design pages or snippets, you should be sure to use the right tags in order to facilitate search engine indexing.
  686. ``<article>``
  687. Specifies an independent block of content. Within it should be a piece of self-contained content that should make sense on its own. You can nest ``<article>`` elements within one another. In this case, its implied that the nested elements are related to the outer ``<article>`` element.
  688. ``<header>``
  689. Indicates the header section of a self-contained block of content (an ``<article>``).
  690. ``<section>``
  691. Is the snippet default tag and it specifies a subsection of a block of content. It can be used to split ``<article>`` content into several parts. Its advisable to use a heading element (``<h1>`` ``<h6>``) to define the sections topic.
  692. ``<hgroup>``
  693. Is used to wrap a section of headings (``<h1>`` - ``<h6>``). A great example would be an article with both a headline and sub-headline at the top:
  694. .. code-block:: html
  695. <hgroup>
  696. <h1>Main Title</h1>
  697. <h2>Subheading</h2>
  698. </hgroup>
  699. Describe your page
  700. ------------------
  701. Define keywords
  702. '''''''''''''''
  703. You should use appropriate, relevant keywords and synonyms for those keywords. You can define them for each page using the built-in Promote function found in the bar at the top.
  704. Define a title and a description
  705. ''''''''''''''''''''''''''''''''
  706. Define them using the Promote function. Keep your page titles short and include the main keyword phrase for the page.
  707. Good titles evoke an emotional response, ask a question or promise something.
  708. Descriptions, while not important to search engine rankings, are extremely important in gaining user click-through. These are an opportunity to advertise content and to let people searching know exactly whether the given page contains the information they're looking for. It is important that titles and descriptions on each page are unique.