diff --git a/conf.py b/conf.py index c815aacaf3..d627ee42ff 100644 --- a/conf.py +++ b/conf.py @@ -82,6 +82,7 @@ 'CURRENT_MAJOR_VERSION': current_major_version, 'GITHUB_PATH': f'https://github.com/odoo/odoo/blob/{version}', 'GITHUB_ENT_PATH': f'https://github.com/odoo/enterprise/blob/{version}', + 'GITHUB_TUTO_PATH': f'https://github.com/odoo/tutorials/blob/{current_major_branch}', 'OWL_PATH': f'https://github.com/odoo/owl/blob/master', } diff --git a/content/developer/tutorials.rst b/content/developer/tutorials.rst index 68c685828b..9a2ea5ec16 100644 --- a/content/developer/tutorials.rst +++ b/content/developer/tutorials.rst @@ -16,6 +16,7 @@ Tutorials tutorials/importable_modules tutorials/mixins tutorials/pdf_reports + tutorials/website_theme .. tip:: If you are new to Odoo development, we recommend starting with the :doc:`setup guide @@ -89,3 +90,9 @@ Expand your knowledge on the server framework :target: tutorials/pdf_reports Use QWeb, Odoo's powerful templating engine, to create custom PDF reports for your documents. + + .. card:: Build a website theme + :target: tutorials/website_theme + + Create a tailored website from scratch fully integrated with Odoo and editable via the Website + Builder. diff --git a/content/developer/tutorials/web.rst b/content/developer/tutorials/web.rst index 3688475425..ffc5cc4e33 100644 --- a/content/developer/tutorials/web.rst +++ b/content/developer/tutorials/web.rst @@ -13,7 +13,7 @@ Customizing the web client This guide is about creating modules for Odoo's web client. -To create websites with Odoo, see :doc:`website`; to add business capabilities +To create websites with Odoo, see :doc:`website_theme`; to add business capabilities or extend existing business systems of Odoo, see :doc:`backend`. .. warning:: diff --git a/content/developer/tutorials/website.rst b/content/developer/tutorials/website.rst deleted file mode 100644 index eaca1d6b04..0000000000 --- a/content/developer/tutorials/website.rst +++ /dev/null @@ -1,1012 +0,0 @@ -:orphan: - -================== -Building a Website -================== - -.. danger:: - This tutorial is outdated. We recommend reading :doc:`server_framework_101` instead. - -.. warning:: - - * This guide assumes `basic knowledge of Python - `_ - * This guide assumes :doc:`an installed Odoo ` - -Creating a basic module -======================= - -In Odoo, tasks are performed by creating modules. - -Modules customize the behavior of an Odoo installation, either by adding new -behaviors or by altering existing ones (including behaviors added by other -modules). - -:ref:`Odoo's scaffolding ` can setup a basic -module. To quickly get started simply invoke: - -.. code-block:: console - - $ ./odoo-bin scaffold Academy my-modules - -This will automatically create a ``my-modules`` *module directory* with an -``academy`` module inside. The directory can be an existing module directory -if you want, but the module name must be unique within the directory. - -A demonstration module -====================== - -We have a "complete" module ready for installation. - -Although it does absolutely nothing we can install it: - -* start the Odoo server - - .. code-block:: console - - $ ./odoo-bin --addons-path addons,my-modules - -* go to http://localhost:8069 -* create a new database including demonstration data -* to go :menuselection:`Settings --> Modules --> Modules` -* in the top-right corner remove the *Installed* filter and search for - *academy* -* click the :guilabel:`Install` button for the *Academy* module - -To the browser -============== - -:ref:`Controllers ` interpret browser requests and -send data back. - -Add a simple controller and ensure it is imported by ``__init__.py`` (so -Odoo can find it): - -.. code-block:: python - :caption: ``academy/controllers.py`` - - # -*- coding: utf-8 -*- - from odoo import http - - class Academy(http.Controller): - - @http.route('/academy/academy/', auth='public') - def index(self, **kw): - return "Hello, world" - -Shut down your server (:kbd:`^C`) then restart it: - -.. code-block:: console - - $ ./odoo-bin --addons-path addons,my-modules - -and open a page to http://localhost:8069/academy/academy/, you should see your -"page" appear: - -.. figure:: website/helloworld.png - -Templates -========= - -Generating HTML in Python isn't very pleasant. - -The usual solution is templates_, pseudo-documents with placeholders and -display logic. Odoo allows any Python templating system, but provides its -own :ref:`QWeb ` templating system which integrates with other -features. - -Create a template and ensure the template file is registered in the -``__manifest__.py`` manifest, and alter the controller to use our template: - -.. code-block:: python - :caption: ``academy/controllers.py`` - - class Academy(http.Controller): - - @http.route('/academy/academy/', auth='public') - def index(self, **kw): - return http.request.render('academy.index', { - 'teachers': ["Diana Padilla", "Jody Caroll", "Lester Vaughn"], - }) - -.. code-block:: xml - :caption: ``academy/templates.xml`` - - - - - - - -The templates iterates (``t-foreach``) on all the teachers (passed through the -*template context*), and prints each teacher in its own paragraph. - -Finally restart Odoo and update the module's data (to install the template) -by going to :menuselection:`Settings --> Modules --> Modules --> -Academy` and clicking :guilabel:`Upgrade`. - -.. tip:: - - Alternatively, Odoo can be restarted :option:`and update modules at - the same time`: - - .. code-block:: console - - $ odoo-bin --addons-path addons,my-modules -d academy -u academy - -Going to http://localhost:8069/academy/academy/ should now result in: - -.. image:: website/basic-list.png - -Storing data in Odoo -==================== - -:ref:`Odoo models ` map to database tables. - -In the previous section we just displayed a list of string entered statically -in the Python code. This doesn't allow modifications or persistent storage -so we'll now move our data to the database. - -Defining the data model ------------------------ - -Define a teacher model, and ensure it is imported from ``__init__.py`` so it -is correctly loaded: - -.. code-block:: python - :caption: ``academy/models.py`` - - from odoo import models, fields, api - - class Teachers(models.Model): - _name = 'academy.teachers' - - name = fields.Char() - -Then setup :ref:`basic access control ` for the model -and add them to the manifest: - -.. code-block:: python - :caption: ``academy/__manifest__.py`` - - # always loaded - 'data': [ - 'security/ir.model.access.csv', - 'templates.xml', - ], - -.. code-block:: csv - :caption: ``academy/security/ir.model.access.csv`` - - id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink - access_academy_teachers,access_academy_teachers,model_academy_teachers,,1,0,0,0 - -this simply gives read access (``perm_read``) to all users (``group_id:id`` -left empty). - -.. note:: - - :ref:`Data files ` (XML or CSV) must be added to the - module manifest, Python files (models or controllers) don't but have to - be imported from ``__init__.py`` (directly or indirectly) - -.. warning:: - - the administrator user bypasses access control, they have access to all - models even if not given access - -Demonstration data ------------------- - -The second step is to add some demonstration data to the system so it's -possible to test it easily. This is done by adding a ``demo`` -:ref:`data file `, which must be linked from the manifest: - -.. code-block:: xml - :caption: ``academy/demo.xml`` - - - - - Diana Padilla - - - Jody Carroll - - - Lester Vaughn - - - - -.. tip:: - - :ref:`Data files ` can be used for demo and non-demo data. - Demo data are only loaded in "demonstration mode" and can be used for flow - testing and demonstration, non-demo data are always loaded and used as - initial system setup. - - In this case we're using demonstration data because an actual user of the - system would want to input or import their own teachers list, this list - is only useful for testing. - -Accessing the data ------------------- - -The last step is to alter model and template to use our demonstration data: - -#. fetch the records from the database instead of having a static list -#. Because :meth:`~odoo.models.Model.search` returns a set of records - matching the filter ("all records" here), alter the template to print each - teacher's ``name`` - -.. code-block:: python - :caption: ``academy/controllers.py`` - - class Academy(http.Controller): - - @http.route('/academy/academy/', auth='public') - def index(self, **kw): - Teachers = http.request.env['academy.teachers'] - return http.request.render('academy.index', { - 'teachers': Teachers.search([]) - }) - -.. code-block:: xml - :caption: ``academy/templates.xml`` - - - - - - - -Restart the server and update the module (in order to update the manifest -and templates and load the demo file) then navigate to -http://localhost:8069/academy/academy/. The page should look slightly -different: names should simply be prefixed by a number (the database -identifier for the teacher). - -Website support -=============== - -Odoo bundles a module dedicated to building websites. - -So far we've used controllers fairly directly, but Odoo 8 added deeper -integration and a few other services (e.g. default styling, theming) via the -``website`` module. - -#. first, add ``website`` as a dependency to ``academy`` -#. then add the ``website=True`` flag on the controller, this sets up a few - new variables on :ref:`the request object ` and - allows using the website layout in our template -#. use the website layout in the template - -.. code-block:: python - :caption: ``academy/__manifest__.py`` - - 'version': '0.1', - - # any module necessary for this one to work correctly - 'depends': ['website'], - - # always loaded - 'data': [ - -.. code-block:: python - :caption: ``academy/controllers.py`` - - class Academy(http.Controller): - - @http.route('/academy/academy/', auth='public', website=True) - def index(self, **kw): - Teachers = http.request.env['academy.teachers'] - return http.request.render('academy.index', { - 'teachers': Teachers.search([]) - }) - -.. code-block:: xml - :caption: ``academy/templates.xml`` - - - - - - - -After restarting the server while updating the module (in order to update the -manifest and template) access http://localhost:8069/academy/academy/ should -yield a nicer looking page with branding and a number of built-in page -elements (top-level menu, footer, …) - -.. image:: website/layout.png - -The website layout also provides support for editing tools: click -:guilabel:`Sign In` (in the top-right), fill the credentials in (``admin`` / -``admin`` by default) then click :guilabel:`Log In`. - -You're now in Odoo "proper": the administrative interface. For now click on -the :guilabel:`Website` menu item (top-left corner. - -We're back in the website but as an administrator, with access to advanced -editing features provided by the *website* support: - -* a template code editor (:menuselection:`Customize --> HTML Editor`) where - you can see and edit all templates used for the current page -* the :guilabel:`Edit` button in the top-left switches to "editing mode" where - blocks (snippets) and rich text editing are available -* a number of other features such as mobile preview or :abbr:`SEO (Search - Engine Optimization)` - -URLs and routing -================ - -Controller methods are associated with *routes* via the -:func:`~odoo.http.route` decorator which takes a routing string and a -number of attributes to customise its behavior or security. - -We've seen a "literal" routing string, which matches a URL section exactly, -but routing strings can also use `converter patterns`_ which match bits -of URLs and make those available as local variables. For instance we can -create a new controller method which takes a bit of URL and prints it out: - -.. code-block:: python - :caption: ``academy/controllers.py`` - - # New route - @http.route('/academy//', auth='public', website=True) - def teacher(self, name): - return '

{}

'.format(name) - -restart Odoo, access http://localhost:8069/academy/Alice/ and -http://localhost:8069/academy/Bob/ and see the difference. - -As the name indicates, `converter patterns`_ don't just do extraction, they -also do *validation* and *conversion*, so we can change the new controller -to only accept integers: - -.. code-block:: python - :caption: ``academy/controllers.py`` - - @http.route('/academy//', auth='public', website=True) - def teacher(self, id): - return '

{} ({})

'.format(id, type(id).__name__) - -Restart Odoo, access http://localhost:8069/academy/2, note how the old value -was a string, but the new one was converted to an integers. Try accessing -http://localhost:8069/academy/Carol/ and note that the page was not found: -since "Carol" is not an integer, the route was ignored and no route could be -found. - -Odoo provides an additional converter called ``model`` which provides records -directly when given their id. Let's use this to create a generic page for -teacher biographies: - -.. code-block:: python - :caption: ``academy/controllers.py`` - - @http.route('/academy//', auth='public', website=True) - def teacher(self, teacher): - return http.request.render('academy.biography', { - 'person': teacher - }) - -.. code-block:: xml - :caption: ``academy/templates.xml`` - - - -then change the list of model to link to our new controller: - - -.. code-block:: xml - :caption: ``academy/templates.xml`` - - - -Restart Odoo and upgrade the module, then you can visit each teacher's page. -As an exercise, try adding blocks to a teacher's page to write a biography, -then go to another teacher's page and so forth. You will discover, that your -biography is shared between all teachers, because blocks are added to the -*template*, and the *biography* template is shared between all teachers, when -one page is edited they're all edited at the same time. - -Field editing -============= - -Data which is specific to a record should be saved on that record, so let us -add a new biography field to our teachers: - -.. code-block:: python - :caption: ``academy/models.py`` - - class Teachers(models.Model): - _name = 'academy.teachers' - - name = fields.Char() - biography = fields.Html() - -.. code-block:: xml - :caption: ``academy/templates.xml`` - - - -Restart Odoo and update the views, reload the teacher's page and… the field -is invisible since it contains nothing. - -.. todo:: the view has been set to noupdate because modified previously, - force via ``-i`` or do something else? - -For record fields, templates can use a special ``t-field`` directive which -allows editing the field content from the website using field-specific -interfaces. Change the *person* template to use ``t-field``: - -.. code-block:: xml - :caption: ``academy/templates.xml`` - -
-
-

-
-
-

- -Restart Odoo and upgrade the module, there is now a placeholder under the -teacher's name and a new zone for blocks in :guilabel:`Edit` mode. Content -dropped there is stored in the corresponding teacher's ``biography`` field, and -thus specific to that teacher. - -The teacher's name is also editable, and when saved the change is visible on -the index page. - -``t-field`` can also take formatting options which depend on the exact field. -For instance if we display the modification date for a teacher's record: - -.. code-block:: xml - :caption: ``academy/templates.xml`` - -
-
-

-

Last modified:

-
-
-

- -it is displayed in a very "computery" manner and hard to read, but we could -ask for a human-readable version: - -.. code-block:: xml - :caption: ``academy/templates.xml`` - -
-
-

-

Last modified:

-
-
-

- -or a relative display: - -.. code-block:: xml - :caption: ``academy/templates.xml`` - -
-
-

-

Last modified:

-
-
-

- -Administration and ERP integration -================================== - -A brief and incomplete introduction to the Odoo administration --------------------------------------------------------------- - -The Odoo administration was briefly seen during the `website support`_ section. -We can go back to it using :menuselection:`Administrator --> Administrator` in -the menu (or :guilabel:`Sign In` if you're signed out). - -The conceptual structure of the Odoo backend is simple: - -#. first are menus, a tree (menus can have sub-menus) of records. Menus - without children map to… -#. actions. Actions have various types: links, reports, code which Odoo should - execute or data display. Data display actions are called *window actions*, - and tell Odoo to display a given *model* according to a set of views… -#. a view has a type, a broad category to which it corresponds (a list, - a graph, a calendar) and an *architecture* which customises the way the - model is displayed inside the view. - -Editing in the Odoo administration ----------------------------------- - -By default, an Odoo model is essentially invisible to a user. To make it -visible it must be available through an action, which itself needs to be -reachable, generally through a menu. - -Let's create a menu for our model: - -.. code-block:: python - :caption: ``academy/__manifest__.py`` - - # always loaded - 'data': [ - 'security/ir.model.access.csv', - 'templates.xml', - 'views.xml', - ], - -.. code-block:: xml - :caption: ``academy/views.xml`` - - - - Academy teachers - academy.teachers - - - - - - - -then accessing http://localhost:8069/web/ in the top left should be a menu -:guilabel:`Academy`, which is selected by default, as it is the first menu, -and having opened a listing of teachers. From the listing it is possible to -:guilabel:`Create` new teacher records, and to switch to the "form" by-record -view. - -If there is no definition of how to present records (a -:doc:`view <../reference/user_interface/view_records>`) Odoo will automatically create a basic one -on-the-fly. In our case it works for the "list" view for now (only displays -the teacher's name) but in the "form" view the HTML ``biography`` field is -displayed side-by-side with the ``name`` field and not given enough space. -Let's define a custom form view to make viewing and editing teacher records -a better experience: - -.. code-block:: xml - :caption: ``academy/views.xml`` - - - Academy teachers: form - academy.teachers - -
- - - - -
-
-
- -Relations between models ------------------------- - -We have seen a pair of "basic" fields stored directly in the record. There are -:ref:`a number of basic fields `. The second -broad categories of fields are :ref:`relational -` and used to link records to one another -(within a model or across models). - -For demonstration, let's create a *courses* model. Each course should have a -``teacher`` field, linking to a single teacher record, but each teacher can -teach many courses: - -.. code-block:: python - :caption: ``academy/models.py`` - - class Courses(models.Model): - _name = 'academy.courses' - - name = fields.Char() - teacher_id = fields.Many2one('academy.teachers', string="Teacher") - -.. code-block:: csv - :caption: ``academy/security/ir.model.access.csv`` - - id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink - access_academy_teachers,access_academy_teachers,model_academy_teachers,,1,0,0,0 - access_academy_courses,access_academy_courses,model_academy_courses,,1,0,0,0 - -let's also add views so we can see and edit a course's teacher: - - -.. code-block:: xml - :caption: ``academy/views.xml`` - - - Academy courses - academy.courses - - - Academy courses: search - academy.courses - - - - - - - - - Academy courses: list - academy.courses - - - - - - - - - Academy courses: form - academy.courses - -
- - - - -
-
-
- - - - - - -It should also be possible to create new courses directly from a teacher's -page, or to see all the courses they teach, so add -:class:`the inverse relationship ` to the *teachers* -model: - -.. code-block:: python - :caption: ``academy/models.py`` - - class Teachers(models.Model): - _name = 'academy.teachers' - - name = fields.Char() - biography = fields.Html() - - course_ids = fields.One2many('academy.courses', 'teacher_id', string="Courses") - - class Courses(models.Model): - _name = 'academy.courses' - - name = fields.Char() - teacher_id = fields.Many2one('academy.teachers', string="Teacher") - -.. code-block:: xml - :caption: ``academy/views.xml`` - - - Academy teachers: form - academy.teachers - -
- - - - - - - - - -
-
-
- -Discussions and notifications ------------------------------ - -Odoo provides technical models, which don't directly fulfill business needs -but which add capabilities to business objects without having to build -them by hand. - -One of these is the *Chatter* system, part of Odoo's email and messaging -system, which can add notifications and discussion threads to any model. -The model simply has to :attr:`~odoo.models.Model._inherit` -``mail.thread``, and add the ``message_ids`` field to its form view to display -the discussion thread. Discussion threads are per-record. - -For our academy, it makes sense to allow discussing courses to handle e.g. -scheduling changes or discussions between teachers and assistants: - - -.. code-block:: python - :caption: ``academy/__manifest__.py`` - - 'version': '0.1', - - # any module necessary for this one to work correctly - 'depends': ['website', 'mail'], - - # always loaded - 'data': [ - -.. code-block:: python - :caption: ``academy/models.py`` - - class AcademyCourses(models.Model): - _inherit = ['mail.thread'] - - name = fields.Char() - teacher_id = fields.Many2one('academy.teachers', string="Teacher") - -.. code-block:: xml - :caption: ``academy/views.xml`` - - - Academy courses: form - academy.courses - -
- - - - -
- - -
-
-
-
- -At the bottom of each course form, there is now a discussion thread and the -possibility for users of the system to leave messages and follow or unfollow -discussions linked to specific courses. - -Selling courses ---------------- - -Odoo also provides business models which allow using or opting in business -needs more directly. For instance the ``website_sale`` module sets up an -e-commerce site based on the products in the Odoo system. We can easily make -course subscriptions sellable by making our courses specific kinds of -products. - -Rather than the previous classical inheritance, this means replacing our -*course* model by the *product* model, and extending products in-place (to -add anything we need to it). - -First of all we need to add a dependency on ``website_sale`` so we get both -products (via ``sale``) and the ecommerce interface: - -.. code-block:: python - :caption: ``academy/__manifest__.py`` - - 'version': '0.1', - - # any module necessary for this one to work correctly - 'depends': ['mail', 'website_sale'], - - # always loaded - 'data': [ - -restart Odoo, update your module, there is now a :guilabel:`Shop` section in -the website, listing a number of pre-filled (via demonstration data) products. - -The second step is to replace the *courses* model by ``product.template``, -and add a new category of product for courses: - -.. code-block:: python - :caption: ``academy/__manifest__.py`` - - 'security/ir.model.access.csv', - 'templates.xml', - 'views.xml', - 'data.xml', - ], - # only loaded in demonstration mode - 'demo': [ - -.. code-block:: xml - :caption: ``academy/data.xml`` - - - - Courses - - - - -.. code-block:: xml - :caption: ``academy/demo.xml`` - - - Course 0 - - - True - 0 - service - - - Course 1 - - - True - 0 - service - - - Course 2 - - - True - 0 - service - - - -.. code-block:: python - :caption: ``academy/models.py`` - - class Courses(models.Model): - _name = 'academy.courses' - _inherit = ['mail.thread', 'product.template'] - - name = fields.Char() - teacher_id = fields.Many2one('academy.teachers', string="Teacher") - -With this installed, a few courses are now available in the :guilabel:`Shop`, -though they may have to be looked for. - -.. note:: - - * to extend a model in-place, it's :attr:`inherited - ` without giving it a new - :attr:`~odoo.models.Model._name` - * ``product.template`` already uses the discussions system, so we can - remove it from our extension model - * we're creating our courses as *published* by default so they can be - seen without having to log in - -Altering existing views ------------------------ - -So far, we have briefly seen: - -* the creation of new models -* the creation of new views -* the creation of new records -* the alteration of existing models - -We're left with the alteration of existing records and the alteration of -existing views. We'll do both on the :guilabel:`Shop` pages. - -View alteration is done by creating *extension* views, which are applied on -top of the original view and alter it. These alteration views can be added or -removed without modifying the original, making it easier to try things out and -roll changes back. - -Since our courses are free, there is no reason to display their price on the -shop page, so we're going to alter the view and hide the price if it's 0. The -first task is finding out which view displays the price, this can be done via -:menuselection:`Customize --> HTML Editor` which lets us read the various -templates involved in rendering a page. Going through a few of them, "Product -item" looks a likely culprit. - -Altering view architectures is done in 3 steps: - -#. Create a new view -#. Extend the view to modify by setting the new view's ``inherit_id`` to the - modified view's external id -#. In the architecture, use the ``xpath`` tag to select and alter elements - from the modified view - -.. code-block:: xml - :caption: ``academy/templates.xml`` - - - -The second thing we will change is making the product categories sidebar -visible by default: :menuselection:`Customize --> Product Categories` lets -you toggle a tree of product categories (used to filter the main display) on -and off. - -This is done via the ``customize_show`` and ``active`` fields of extension -templates: an extension template (such as the one we've just created) can be -*customize_show=True*. This choice will display the view in the :guilabel:`Customize` -menu with a check box, allowing administrators to activate or disable them -(and easily customize their website pages). - -We simply need to modify the *Product Categories* record and set its default -to *active="True"*: - -.. code-block:: xml - :caption: ``academy/templates.xml`` - - - - - -With this, the *Product Categories* sidebar will automatically be enabled when -the *Academy* module is installed. - -.. _templates: https://en.wikipedia.org/wiki/Web_template -.. _postgres: -.. _postgresql: - https://www.postgresql.org -.. _converter pattern: -.. _converter patterns: - https://werkzeug.pocoo.org/docs/routing/#rule-format diff --git a/content/developer/tutorials/website_theme.rst b/content/developer/tutorials/website_theme.rst new file mode 100644 index 0000000000..02e79d0cfe --- /dev/null +++ b/content/developer/tutorials/website_theme.rst @@ -0,0 +1,54 @@ +:show-content: +:show-toc: +:hide-page-toc: + +===================== +Build a website theme +===================== + +| For this project, we'll collaborate on creating a custom website theme fully integrated with Odoo. +| Our client, Airproof, has provided their latest design for their waterproof drone e-commerce, + which we'll replicate. + +To start, you must have :doc:`installed Odoo locally `. +You will also need some knowledge in: + +- XML +- JavaScript (not mandatory) +- Bootstrap 5.1.3 +- SCSS +- QWeb (Odoo's own templating system) +- OWL (JavaScript framework, not mandatory) + +| **Goal** +| Replicate the Airproof design. + +.. image:: website_theme/airproof-home.png + :alt: Airproof homepage. + :align: center + +| In the :file:`README.md` of the `Airproof module <{GITHUB_TUTO_PATH}/website_airproof>`_, you can + find the various Airproof designs that you will replicate throughout the different exercises in + this tutorial. +| You can also find all the code necessary for creating the Airproof website there. You should also + obtain this by the end of the tutorial. It is recommended to try solving the exercices first + without looking at the solution! + +| **Don't go too fast!** +| Follow the exercises step by step and you will reach the final design at the end of the tutorial. + +Throughout this tutorial, you will find "See also" sections leading to parts of the :doc:`How-to +guide: Website themes <../howtos/website_themes>` documentation. Be sure to read this documentation +thoroughly each time! With it, you will find the solution to every exercise. + +Ready? Let's get started! + +.. toctree:: + :maxdepth: 2 + + website_theme/01_theming + website_theme/02_build_website + website_theme/03_customisation_part1 + website_theme/04_customisation_part2 + website_theme/05_dynamic_templates + website_theme/06_going_live diff --git a/content/developer/tutorials/website_theme/01_theming.rst b/content/developer/tutorials/website_theme/01_theming.rst new file mode 100644 index 0000000000..528569f73b --- /dev/null +++ b/content/developer/tutorials/website_theme/01_theming.rst @@ -0,0 +1,207 @@ +=================== +Chapter 1 - Theming +=================== + +Now that you have Odoo installed and your server is running locally, it's time to create your own +theme module for your website. + +.. _tutorials/website_theme/theming/setup: + +Setup +===== + +| The first step is to ensure that Odoo is running correctly locally. To do this, use a Shell script + to run the server. +| In this script, define the database name and install only the `website` module. + +.. seealso:: + See reference documentation on how to :ref:`run Odoo `. + +.. _tutorials/website_theme/theming/module: + +Build your module structure +=========================== + +Now that we know everything is working properly, let's start building our module. + +Based on the following structure, start creating your module that will be used as a theme. This is +where you are going to add your XML pages, SCSS, JS, … + +.. seealso:: + See reference documentation on how to structure your :ref:`theming/module`. + +| Start with the basics : :file:`/data`, :file:`/img`, :file:`/scss`, :file:`/js`. +| Don't forget to add the :file:`__init__.py` and :file:`__manifest__.py` files. + +In your :file:`__manifest__.py` file, you can declare your module with the following information: + +- name (required) +- description +- category +- version +- author +- license +- depends + +.. _tutorials/website_theme/theming/odoo_variables: + +Declare Odoo variables +====================== + +In the :file:`primary_variables.scss` file, you can override the default Odoo SCSS variables to +match your design. + +Based on the Airproof design, create your :file:`primary_variables.scss` file and define the +following elements: + +- Headings font family : Space Grotesk +- Content font family : Lato +- The color palette name and the 5 main colors that compose it: `#000000`, `#BBE1FA`, `#CEF8A1`, + `#FFFFFF`, `#0B8EE6` +- Header & Footer : Use one of the default templates for the moment, we will create a custom header + later. + +.. seealso:: + See reference documentation on how to use :ref:`primary variables `, as + well as a list of all `primary variables + <{GITHUB_PATH}/addons/website/static/src/scss/primary_variables.scss>`_ available. + +| Restart your script to immediately see the application of your changes. +| Don't forget to add the path to your manifest in the script and set your module as the app + to install. + +To ensure your changes are applied correctly, log in to your website and check that your +color palette includes your specified colors. + +.. tip:: + You will need to override more variables to replicate the Airproof design. Remember to add them + throughout the creation of your website. + +.. note:: + The font families are from `Google fonts `_. + +.. spoiler:: Solutions + + To complete this exercise, you need to: + + #. Create your :file:`primary_variables.scss` file. You can find all the necessary information in + the `primary_variables.scss + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/primary_variables.scss>`_ file from our + example module. + #. Declare your file in the :file:`__manifest__.py` as indicated in the documentation. + #. Install your module via your script. In our example, it looks like this: + + .. code-block:: xml + + ./odoo-bin --addons-path=../enterprise,addons,../myprojects --db-filter=theming -d theming + --without-demo=all -i website_airproof --dev=xml + +.. _tutorials/website_theme/theming/bootstrap_variables: + +Declare Bootstrap variables +=========================== + +On top of the default Odoo variables, you can also redefine the Bootstrap variables. Bootstrap is a +front-end framework which is included by default in Odoo. + +Based on the Airproof design, define the following elements: + +- Headings font sizes : + + - h1 : 3.125rem + - h2 : 2.5rem + - h3 : 2rem + - h4 : 1.75rem + - h5 : 1.5rem + - h6 : 1.25rem + +- Inputs border radius : 10px +- Inputs border color : black +- Inputs border width : 1px +- Large buttons border radius : 0px 10px 10px 10px + +.. seealso:: + - See reference documentation on how to use :ref:`theming/module/bootstrap`. + - A list of all `Bootstrap variables + <{GITHUB_PATH}/addons/web/static/lib/bootstrap/scss/_variables.scss>`_ used by Odoo. + - And `Bootstrap framework `_ + official documentation. + +.. tip:: + - You will need to override more variables to replicate the Airproof design. Remember to add them + throughout the creation of your website. + - Make it a habit to regularly check locally that your changes have been successfully applied + and have not caused any errors. + +.. spoiler:: Solutions + + To complete this exercise, you need to: + + #. Create your :file:`bootstrap_overridden.scss` file. You can find all the necessary information + in the `bootstrap_overridden.scss + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/bootstrap_overridden.scss>`_ file from + our example module. + #. Declare your file in the :file:`__manifest__.py` as indicated in the documentation. + +.. _tutorials/website_theme/theming/presets: + +Define presets +============== + +In addition to the variables we have just covered, you can also activate specific views to modify +the design. + +Add your :file:`presets.xml` file and based on the Airproof design, activate the appropriate views +to meet the following client requests: + +- Deactivate the Call-to-action in the header. +- Deactivate the wishlist feature in the shop but activate it on the product page. +- On the shop page, activate the filtering by categories only on the left side. + +.. seealso:: + | See how you can define your :ref:`presets `. + | To start writing your file, follow the instructions for any Odoo XML page described in + :doc:`/developer/howtos/website_themes/layout`. + +.. tip:: + - To complete the exercise, you need to install the **eCommerce** (`website_sale`) and + **wishlist** (`website_sale_whishlist`) applications. **Be careful!** Referencing an + application in your code that hasn't been installed will result in an error. + - | In order to find the templates to activate or not, go to the source code: + `odoo/addons/website/views/**`. + | For example, you can find all the templates for the header in + `website_templates.xml <{GITHUB_PATH}/addons/website/views/website_templates.xml>`_. + - To see the effect of your presets, add some **products** (*Airproof Mini*, *Airproof Robin*, + *Warranty*, *Charger cable*) and create **eCommerce categories** (*Warranties*, *Accessories*, + and *Drones* with *Camera drones* and *Waterproof drones*) in the database. You will find the + `product images here <{GITHUB_TUTO_PATH}/website_airproof/static/src/img/content>`_. + - You will need to activate more views to replicate the Airproof design. Remember to add them + throughout the creation of your website. + +.. spoiler:: Solutions + + To deactivate the Call-to-action: + + #. The view you have to find is in :file:`odoo/addons/website/views/website_templates.xml l:2113` + #. Create your :file:`presets.xml` file with the right records + + .. code-block:: xml + :caption: ``/website_airproof/data/presets.xml`` + + + + + + + + + #. In the manifest, add the 2 apps and declare your file. + + .. code-block:: python + :caption: ``/website_airproof/__manifest__.py`` + + 'depends': ['website_sale', 'website_sale_wishlist'], + 'data': [ + # Options + 'data/presets.xml', + ] diff --git a/content/developer/tutorials/website_theme/02_build_website.rst b/content/developer/tutorials/website_theme/02_build_website.rst new file mode 100644 index 0000000000..32412d4bea --- /dev/null +++ b/content/developer/tutorials/website_theme/02_build_website.rst @@ -0,0 +1,172 @@ +============================== +Chapter 2 - Build your website +============================== + +.. _tutorials/website_theme/build_website/page: + +Create a page +============= + +Now that the theme has been set up, let's move on to creating the content. + +First of all, start by creating your first theme page: the home page. For now, only indicate “Hello” +as content in the page. + +.. tip:: + You will need to deactivate the default homepage. + +.. seealso:: + See reference documentation on how to :ref:`desactivate a default page + ` and how to :ref:`start a new page + `. + +.. spoiler:: Solutions + + .. code-block:: python + :caption: ``/website_airproof/__manifest__.py`` + + 'data': [ + # Pages + 'data/pages/home.xml', + ] + + .. code-block:: xml + :caption: ``/website_airproof/data/pages/home.xml`` + + + + + + + + + + + Home + + website_airproof.page_home + / + qweb + + + + + One step beyond the horizon | Airproof + +
+

Hello

+
+
+
+
+
+
+
+ +.. _tutorials/website_theme/build_website/media: + +Add a media +=========== + +If you want the client to be able to reuse certain pictures that you are going to add on the +website, they must be added to the image library. + +To do the test, declare the drone image to add it to the library. You will find the `drone picture +here <{GITHUB_TUTO_PATH}/website_airproof/static/src/img/content/drone-robin.png>`_. + +.. seealso:: + See reference documentation on how to :ref:`add a media `. + +Go to the :guilabel:`Website Builder`, double-click on the :guilabel:`logo`, and you will see the +drone image in the library. + +.. spoiler:: Solutions + + To complete this exercise, you need to: + + #. Put your PNG in the right image folder. + #. Create your :file:`images.xml` file. You can find all the necessary information + in the `images.xml + <{GITHUB_TUTO_PATH}/website_airproof/data/images.xml>`_ + file from our example module. + #. Declare your file in the :file:`__manifest__.py`. + +.. _tutorials/website_theme/build_website/building_blocks: + +Add building blocks +=================== + +Now, let's get into the real work. Start adding content to the pages. + +In an Odoo website, we create the content of a page using building blocks. These can be compared to +snippets editable by the user in the Website Builder. The standard main container for any snippet +is a `section`. + +Based on the Airproof design, add the following elements to the homepage : + +- Create a section with the 3 boxes using the :guilabel:`Big boxes` building block. + + - For this section, you don't want the future user to be able to edit it via the Website Builder. + - Put an opacity filter on the background image of the 3 boxes. + +- Create another section containing the title and icons. + +You can use these `images <{GITHUB_TUTO_PATH}/website_airproof/static/src/img/content>`_ and `icons +<{GITHUB_TUTO_PATH}/website_airproof/static/src/img/content/icons>`_. + +.. seealso:: + See reference documentation on how to :ref:`write standard snippets + `. + +.. image:: 02_build_website/building-blocks.png + :alt: Airproof building blocks. + :scale: 75% + +.. tip:: + To determine the code needed to create your building blocks : + + - | Create a test page via the website builder. + | Drag & drop the building block that interests you and apply the right design. + | Use the code generated via :guilabel:`Editor HTML/SCSS` in the menu. + - You can also find the original building block code in Odoo : + :file:`odoo/addons/website/views/snippets/**.xml`. + +.. spoiler:: Solutions + + Find the solution in our Airproof example on `home.xml + <{GITHUB_TUTO_PATH}/website_airproof/data/pages/home.xml>`_. + +.. _tutorials/website_theme/build_website/navigation: + +Navigation +========== + +For now, the client is fine with the default header but has requested some navigation adjustments. + +The client has requested the following changes: + +- Remove the link to the homepage and the shop. +- Add a link to the future “About us” page. +- Replace the default blog item with a dropdown to display the different blogs: “Our latest news” + and “Tutorials”. +- Add a mega-menu “Waterproof drones” to display the different products. + +.. seealso:: + - You can find the original mega-menu templates code in Odoo : + `odoo/addons/website/views/snippets/s_mega_menu_**.xml + <{GITHUB_PATH}/addons/website/views/snippets>`_ + - See reference documentation on how to modifiy the + :doc:`/developer/howtos/website_themes/navigation`. + +.. image:: 02_build_website/mega-menu.png + :alt: Aiproof mega-menu. + +.. tip:: + - Make sure the Blog app is installed and create the two different blogs in the backend. + - Create the different products via the backend. You can use these `product pictures + <{GITHUB_TUTO_PATH}/website_airproof/static/src/img/content>`_. + +.. spoiler:: Solutions + + Find the solution in our Airproof example on `menu.xml + <{GITHUB_TUTO_PATH}/website_airproof/data/menu.xml>`_. diff --git a/content/developer/tutorials/website_theme/02_build_website/building-blocks.png b/content/developer/tutorials/website_theme/02_build_website/building-blocks.png new file mode 100644 index 0000000000..93076dab99 Binary files /dev/null and b/content/developer/tutorials/website_theme/02_build_website/building-blocks.png differ diff --git a/content/developer/tutorials/website_theme/02_build_website/mega-menu.png b/content/developer/tutorials/website_theme/02_build_website/mega-menu.png new file mode 100644 index 0000000000..8f2aebae84 Binary files /dev/null and b/content/developer/tutorials/website_theme/02_build_website/mega-menu.png differ diff --git a/content/developer/tutorials/website_theme/03_customisation_part1.rst b/content/developer/tutorials/website_theme/03_customisation_part1.rst new file mode 100644 index 0000000000..cebbf52ca4 --- /dev/null +++ b/content/developer/tutorials/website_theme/03_customisation_part1.rst @@ -0,0 +1,356 @@ +================================= +Chapter 3 - Customisation, Part I +================================= + +.. _tutorials/website_theme/customisation_part1/custom_scss: + +Add custom SCSS +=============== + +You've adjusted Odoo and Bootstrap variables and set presets, yet you still notice disparities +between your website and the client's design. The only solution is to incorporate custom SCSS. + +In :file:`theme.scss`, reproduce the following design elements: + +- Add a **green underline** on active nav items. +- Modify the **arrow** for collapsible nav items. +- Modify the **slider's arrows** by adding a green background and changing their design. + +You will find the various `media here +<{GITHUB_TUTO_PATH}/website_airproof/static/src/img/content/icons>`_. + +.. seealso:: + See reference documentation on how to :ref:`add your SCSS rules `. + +.. image:: 03_customisation_part1/menu.png + :scale: 50% + +.. image:: 03_customisation_part1/slider.png + +.. note:: + | It's always preferable to include all your SCSS rules in `#wrapwrap`. This ID is applied to the + div that groups the :guilabel:`header`, :guilabel:`footer`, and :guilabel:`main` content of all + your pages. + | So you will be sure that your rules will only have an impact on the website parts. + +.. spoiler:: Solutions + + Find the solution in our Airproof example on `header.scss + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/layout/header.scss>`_ + and `caroussel.scss + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/snippets/caroussel.scss>`_. + +.. _tutorials/website_theme/customisation_part1/custom_js: + +Add custom JS +============= + +Now, let's add a mouse follower to the website. This interactive element will enhance the browsing +experience, making it more engaging and visually appealing. + +.. image:: 03_customisation_part1/mouse-follower.gif + +Use your JavaScript skills to implement this. + +.. seealso:: + See reference documentation on how to :ref:`add Javascript code `. + +.. spoiler:: Solutions + + Find the solution in our Airproof example on `mouse_follower.js + <{GITHUB_TUTO_PATH}/website_airproof/static/src/js/mouse_follower.js>`_ and `mouse_follower.scss + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/components/mouse_follower.scss>`_. + +.. _tutorials/website_theme/customisation_part1/custom_header: + +Create a custom header +====================== + +With variables, presets, and custom SCSS in place, it's time to refine the layout and add key +cross-page elements, starting with the header. + +Based on the Airproof design, create a custom header with the following elements: + +- A centered logo. Ensure to declare the logo so that it appears automatically in the header. +- A custom shopping cart icon. +- A login/user as a button. +- Navigation text to 14px. + +You can find the `logo +<{GITHUB_TUTO_PATH}/website_airproof/static/src/img/content/branding/airproof-logo.svg>`_, +`cart icon <{GITHUB_TUTO_PATH}/website_airproof/static/src/img/content/icons/shopping.svg>`_ and +`template illustration +<{GITHUB_TUTO_PATH}/website_airproof/static/src/img/wbuilder/template-header-opt.svg>`_. + +.. seealso:: + See reference documentation on how to: + + - create :ref:`custom headers `, + - do a :ref:`website_themes/layout/xpath`, + - declare a :ref:`website_themes/media/images/use/logo`. + +.. image:: 03_customisation_part1/header.png + +.. tip:: + - Base yourself on the code of existing header templates that you can find in + `odoo/addons/website/views/website_templates.xml + <{GITHUB_PATH}/addons/website/views/website_templates.xml>`_. + - A good practise should be to create different files to manage your custom views and templates. + For example, everything concerning the general layout (header, footer...) in + :file:`website_templates.xml`, everything related to blog in :file:`website_blog_templates.xml`, + to event in :file:`website_event_templates.xml`, etc. + - | To modify the cart icon, you can use an `XPath`. + | Since this is linked to eCommerce, place it in a new file called + :file:`website_sale_templates.xml`. + - Don't forget to continue making as many modifications as you can through the :file:`Bootstrap + variables` and :file:`primary variables` (font, colors, size...). You can use them to help you + with this exercise. + +.. spoiler:: Solutions + + Find the solution in our Airproof example for: + + - the xml structure and to add the template to the options list on + `website_template.xml <{GITHUB_TUTO_PATH}/website_airproof/views/website_templates.xml>`_. + - disable the default header: + + .. code-block:: xml + :caption: ``/website_airproof/data/presets.xml`` + + + + + + + - record the logo: + + .. code-block:: xml + :caption: ``/website_airproof/data/images.xml`` + + + + + + + - declare your :file:`website_templates.xml` file along with all the new ones in your + :file:`manifest`. + - make the use of `primaries + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/primary_variables.scss>`_ like + `header-template`, `navbar-font`, `header-font-size`... + - use `bootstrap_overridden + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/bootstrap_overridden.scss>`_ like + `$navbar-light-color`, `$navbar-light-hover-color`, `$navbar-padding-y`... + - add some `scss <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/layout/header.scss>`_ + rules. + +.. _tutorials/website_theme/customisation_part1/custom_footer: + +Create a custom footer +====================== + +The client is delighted with the new header, as it aligns perfectly with the provided design. Now, +he wants a matching custom footer. + +Based on the Airproof design, create a custom footer with the following elements: + +- A section for newsletter subscription. +- A section for the copyright and social media. + +You will find the `icons here <{GITHUB_TUTO_PATH}/website_airproof/static/src/img/content/icons>`_. + +.. seealso:: + See reference documentation on how to create a :ref:`custom footer + ` and adapt the :ref:`website_themes/layout/copyright`. + +.. image:: 03_customisation_part1/footer.png + +.. tip:: + - You can enable or disable the copyright section via the presets. + - For the newsletter section to work, you need to install the `website_mass_mailing` application. + +.. spoiler:: Solutions + + To complete this exercise, you need to: + + - add `mass mailing` to your depends: + + .. code-block:: python + :caption: ``/website_airproof/__manifest__.py`` + + 'depends': ['website_sale', 'website_sale_wishlist', 'website_blog', + 'website_mass_mailing'], + + - find the xml structure and add the template to the options list on + `website_template.xml <{GITHUB_TUTO_PATH}/website_airproof/views/website_templates.xml>`_. + - disable the default footer and enable the copyright: + + .. code-block:: xml + :caption: ``/website_airproof/data/presets.xml`` + + + + + + + + + + + - make the use of `primaries + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/primary_variables.scss>`_ like + `footer-template`, `footer`, `o-cc4-link`... + - add a little scss rule for the `newsletter + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/snippets/newsletter.scss>`_ section. + +.. _tutorials/website_theme/customisation_part1/custom_building_blocks: + +Create your custom building blocks +================================== + +To allow your client to further customize his website, create tailor-made building blocks that he +can freely drag & drop onto different pages. + +Based on the Airproof design, create a custom carousel snippet to showcase drones. Then, add it as +cover section on your homepage. + +#. Create the snippet template and add it to the list of building blocks available in the website + builder. Here you will find the `images + <{GITHUB_TUTO_PATH}/website_airproof/static/src/img/snippets/s_airproof_caroussel>`_ and + `snippet illustration + <{GITHUB_TUTO_PATH}/website_airproof/static/src/img/wbuilder/s-airproof-snippet.svg>`_. + + .. seealso:: + See reference documentation on how to create a :ref:`custom building blocks + `. + + .. image:: 03_customisation_part1/custom-building-block.png + +#. Add an option in the Website Builder to allow users to choose between a blue or green bubble + shadow. + + .. seealso:: + See reference documentation on how to add :ref:`snippet options + `. + + .. image:: 03_customisation_part1/custom-building-block-option.png + :scale: 75% + +#. Add the snippet on your homepage. + +.. tip:: + Don't forget to always properly declare your new files in your :file:`__manifest__.py` and follow + the good :ref:`folder structure ` seen previously. + +.. spoiler:: Solutions + + To complete this exercise, you need to: + + #. Create your template. + + - You can find all the necessary information in `s_airproof_carousel.xml + <{GITHUB_TUTO_PATH}/website_airproof/views/snippets/s_airproof_carousel.xml>`_ file and + `s_airproof_carousel/000.scss + <{GITHUB_TUTO_PATH}/website_airproof/static/src/snippets/s_airproof_carousel/000.scss>`_ + file from our example module. + - Record your images in `images.xml <{GITHUB_TUTO_PATH}/website_airproof/data/images.xml>`_. + - Declare your files in the `__manifest__.py + <{GITHUB_TUTO_PATH}/website_airproof/__manifest__.py>`_. + - Add it to the list of building blocks. In our example, it looks like this: + + .. code-block:: xml + :caption: ``/website_airproof/views/snippets/options.xml`` + + + + + #. Add the option to the Website Builder. In our example, it looks like this: + + .. code-block:: xml + :caption: ``/website_airproof/views/snippets/s_airproof_carousel.xml`` + + + + + Additionally, the SCSS related to the bubbles in the `s_airproof_carousel/000.scss + <{GITHUB_TUTO_PATH}/website_airproof/static/src/snippets/s_airproof_carousel/000.scss>`_ file. + + #. Add your snippet to the homepage. You can find all the necessary information in the `home.xml + <{GITHUB_TUTO_PATH}/website_airproof/data/pages/home.xml>`_ file from our example module. + +.. _tutorials/website_theme/customisation_part1/custom_dynamic_template: + +Create a new dynamic snippets template +====================================== + +| Dynamic snippets are useful building blocks. These allow you to fetch information from the backend + and display it on the website according to certain filters. +| There are already several layout templates for displaying dynamic snippets. However, none of the + existing templates fully match your client's needs. + +Based on the Airproof design, create a custom template that you will apply to a product dynamic +snippet on the homepage. + +#. First, create a custom template that will be added to the list of dynamic products templates. It + has to include the following elements: + + - Add a :guilabel:`Discover more` link. + - Add a hover effect on cards. + - Move the navigation arrows. + + You will find the `icons here <{GITHUB_TUTO_PATH}/website_airproof/static/src/img/content/icons>`_. + + .. seealso:: + See reference documentation on how to :ref:`create a template for dynamic snippets + `. + + .. image:: 03_customisation_part1/custom-template.png + + .. tip:: + You can verify in the Website Builder that your template appears in the list of available + templates for the product dynamic snippet. + +#. Then, add a product dynamic snippet with the template you just created to the homepage. + + .. seealso:: + See reference documentation on how to :ref:`call a template + `. + +.. spoiler:: Solutions + + To complete this exercise, you need to: + + #. Create your snippet template. You can find all the necessary information in the + `options.xml <{GITHUB_TUTO_PATH}/website_airproof/views/snippets/options.xml>`_ + file and `caroussel.scss + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/snippets/caroussel.scss>`_ file from our + example module. + + #. Apply the template to a product dynamic snippet on the homepage. You can find all the + necessary information in the `home.xml + <{GITHUB_TUTO_PATH}/website_airproof/data/pages/home.xml>`_ file from our example module. diff --git a/content/developer/tutorials/website_theme/03_customisation_part1/custom-building-block-option.png b/content/developer/tutorials/website_theme/03_customisation_part1/custom-building-block-option.png new file mode 100644 index 0000000000..512ae796cf Binary files /dev/null and b/content/developer/tutorials/website_theme/03_customisation_part1/custom-building-block-option.png differ diff --git a/content/developer/tutorials/website_theme/03_customisation_part1/custom-building-block.png b/content/developer/tutorials/website_theme/03_customisation_part1/custom-building-block.png new file mode 100644 index 0000000000..bcc5db33e1 Binary files /dev/null and b/content/developer/tutorials/website_theme/03_customisation_part1/custom-building-block.png differ diff --git a/content/developer/tutorials/website_theme/03_customisation_part1/custom-template.png b/content/developer/tutorials/website_theme/03_customisation_part1/custom-template.png new file mode 100644 index 0000000000..1b2579de9d Binary files /dev/null and b/content/developer/tutorials/website_theme/03_customisation_part1/custom-template.png differ diff --git a/content/developer/tutorials/website_theme/03_customisation_part1/footer.png b/content/developer/tutorials/website_theme/03_customisation_part1/footer.png new file mode 100644 index 0000000000..7f31a7aaec Binary files /dev/null and b/content/developer/tutorials/website_theme/03_customisation_part1/footer.png differ diff --git a/content/developer/tutorials/website_theme/03_customisation_part1/header.png b/content/developer/tutorials/website_theme/03_customisation_part1/header.png new file mode 100644 index 0000000000..bf91e9ac31 Binary files /dev/null and b/content/developer/tutorials/website_theme/03_customisation_part1/header.png differ diff --git a/content/developer/tutorials/website_theme/03_customisation_part1/menu.png b/content/developer/tutorials/website_theme/03_customisation_part1/menu.png new file mode 100644 index 0000000000..ed4a3ff115 Binary files /dev/null and b/content/developer/tutorials/website_theme/03_customisation_part1/menu.png differ diff --git a/content/developer/tutorials/website_theme/03_customisation_part1/mouse-follower.gif b/content/developer/tutorials/website_theme/03_customisation_part1/mouse-follower.gif new file mode 100644 index 0000000000..11d2b01886 Binary files /dev/null and b/content/developer/tutorials/website_theme/03_customisation_part1/mouse-follower.gif differ diff --git a/content/developer/tutorials/website_theme/03_customisation_part1/slider.png b/content/developer/tutorials/website_theme/03_customisation_part1/slider.png new file mode 100644 index 0000000000..6a9a969723 Binary files /dev/null and b/content/developer/tutorials/website_theme/03_customisation_part1/slider.png differ diff --git a/content/developer/tutorials/website_theme/04_customisation_part2.rst b/content/developer/tutorials/website_theme/04_customisation_part2.rst new file mode 100644 index 0000000000..674f15e2ca --- /dev/null +++ b/content/developer/tutorials/website_theme/04_customisation_part2.rst @@ -0,0 +1,192 @@ +================================== +Chapter 4 - Customisation, Part II +================================== + +.. _tutorials/website_theme/customisation_part2/background_shape: + +Create a custom background shape +================================ + +Shapes are decorative elements that can be applied to backgrounds or images. They are SVG files +that can be animated and customized with different colors. + +#. To better align with the website's desired atmosphere, create a custom background shape that the + client can reuse on different blocks. + + Create your custom shape using the following setup: + + - Declare your shape. You can find the original `SVG shape here + <{GITHUB_TUTO_PATH}/website_airproof/shape-waves.svg>`_. + - Set the base color of the shape to the theme's green, and add it to the list of available + shapes. + +.. seealso:: + See reference documentation on how to add a :ref:`custom background shapes + `. + +.. image:: 04_customisation_part2/shape.png + +.. tip:: + | **Be careful,** there is a trick! + | In your shape SVG file, you have to use the colors from the default Odoo palette. + | Here, I want it to match my primary color 3 (`#CEF8A1`). Therefore, in the SVG file, you must + use color 3 from Odoo's default palette (`#F6F6F6`). + +.. spoiler:: Solutions + + Find the solution in our Airproof example for: + + - the shape declaration on `shapes.xml <{GITHUB_TUTO_PATH}/website_airproof/data/shapes.xml>`_. + - adding the shape to the list thanks to + `primary_variable.scss + <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/primary_variables.scss>`_ and `option.xml + <{GITHUB_TUTO_PATH}/website_airproof/views/snippets/options.xml>`_. + +2. Based on the Airproof design, apply the shape you just added to a `Text-Image` building block on + the homepage: + + - Ensure the shape is in the right position. + - Set its color to the theme's light blue. + +.. seealso:: + See reference documentation on how to use :ref:`background shapes + `. + +.. image:: 04_customisation_part2/shape-section.png + +.. tip:: + Unlike a standard Odoo shapes, when applying a custom shape to a section, replace `web_editor` + with `illustration` in the shape class. + +.. spoiler:: Solutions + + .. code-block:: xml + :caption: ``/website_airproof/data/pages/home.xml`` + + +
+
+ [...] +
+ +.. _tutorials/website_theme/customisation_part2/background_gradient: + +Add a background gradient +========================= + +Apply a custom background gradient to your ”*Latest products*” block, transitioning from blue +`rgb(11, 142, 230)` to dark blue `rgb(41, 128, 187)`. + +.. seealso:: + See reference documentation on how to use :doc:`/developer/howtos/website_themes/gradients`. + +.. spoiler:: Solutions + + .. code-block:: xml + :caption: ``/website_airproof/data/pages/home.xml`` + + +
+ [...] +
+ +.. _tutorials/website_theme/customisation_part2/animations: + +Animations +========== + +The client loves the overall design but finds the page a bit static. Enhance page interactivity with +animations such as `fade-in`, `rotate`, `bounce`, etc. These can be applied to columns, images, +texts, buttons… + +Based on the airproof design, animate the following elements: + +- the text of the first slide of the carousel. +- the sticker and the photo of the drone from the first slide. +- the 4 columns with icons. + +Adjust animation delays for smoother transitions. + +.. seealso:: + See reference documentation on how to apply :doc:`/developer/howtos/website_themes/animations`. + +.. image:: 04_customisation_part2/animations.gif + +.. spoiler:: Solutions + + Find the solution in our Airproof example on `home.xml + <{GITHUB_TUTO_PATH}/website_airproof/data/pages/home.xml>`_. + + .. code-block:: xml + :caption: Image animation + + + + Drone + + .. code-block:: xml + :caption: Text animation + + One + step + + .. code-block:: xml + :caption: Columns animation + +
+
+ +.. _tutorials/website_theme/customisation_part2/forms: + +Forms +===== + +The forms in Odoo are very powerful. They can send emails directly to a personal inbox or integrate +directly with other Odoo applications. This is great, as one of your client's main priorities is +after-sales service. Therefore, the contact form must be properly configured. + +Based on the airproof design, create a contact page. Remember to disable the default one and add the +new page link to the menu. The client has the following requests for their contact form: + +- *Name* and *email address* field. +- *Company name* field. +- *Conditional VAT* field displayed only if *Company name* is filled in. +- All fields should be mandatory, except for *Company name*. +- Form submission must trigger an email. +- After form submission, the `thank-you message` should remain visible on the contact page. + +.. seealso:: + See reference documentation on how to: + + - :ref:`deactivate default pages `, + - :ref:`create a new page `, + - :ref:`add a menu item `, + - :doc:`create a form `. + +.. tip:: + To determine the correct code for your form: + + - | Create a test page via the Website Builder. + | Drag & drop the building block that interests you and apply the right design. + | Use the code generated through :guilabel:`Editor HTML/SCSS`. + - You can also find the original building block code in Odoo: + `odoo/addons/website/views/snippets/s_website_form.xml + <{GITHUB_PATH}/addons/website/views/snippets/s_website_form.xml>`_. + +.. spoiler:: Solutions + + Find the solution in our Airproof example on `contact.xml + <{GITHUB_TUTO_PATH}/website_airproof/data/pages/contact.xml>`_. diff --git a/content/developer/tutorials/website_theme/04_customisation_part2/animations.gif b/content/developer/tutorials/website_theme/04_customisation_part2/animations.gif new file mode 100644 index 0000000000..9d08e5f357 Binary files /dev/null and b/content/developer/tutorials/website_theme/04_customisation_part2/animations.gif differ diff --git a/content/developer/tutorials/website_theme/04_customisation_part2/shape-section.png b/content/developer/tutorials/website_theme/04_customisation_part2/shape-section.png new file mode 100644 index 0000000000..d5cf0f3feb Binary files /dev/null and b/content/developer/tutorials/website_theme/04_customisation_part2/shape-section.png differ diff --git a/content/developer/tutorials/website_theme/04_customisation_part2/shape.png b/content/developer/tutorials/website_theme/04_customisation_part2/shape.png new file mode 100644 index 0000000000..591fe0e238 Binary files /dev/null and b/content/developer/tutorials/website_theme/04_customisation_part2/shape.png differ diff --git a/content/developer/tutorials/website_theme/05_dynamic_templates.rst b/content/developer/tutorials/website_theme/05_dynamic_templates.rst new file mode 100644 index 0000000000..29d44592bd --- /dev/null +++ b/content/developer/tutorials/website_theme/05_dynamic_templates.rst @@ -0,0 +1,86 @@ +============================= +Chapter 5 - Dynamic templates +============================= + +.. _tutorials/website_theme/dynamic_templates/shop: + +Adapt the shop template +======================= + +Now, let's adapt the dynamic sections of the website. As you may know, some pages such as those for +eCommerce are automatically generated. Pages like the shop, product, and checkout are automatically +generated when the `website_sale` application is installed. These template pages pull their +displayed information from the backend. + +To modify these pages, we need to edit the standard Odoo template. This can be done using SCSS, +presets, and especially XPath. Locate the standard Odoo template you want to modify and extend it +using `XPath`. Following the Airproof design, let's begin by modifying the shop view. + +#. First, locate the standard template in Odoo : :menuselection:`website_sale --> templates.xml --> + id="products"`. +#. Apply all changes in your :file:`website_sale_templates.xml` file. Start by: + + - Add a banner. + - Adapt the layout of the e-commerce category filtering on the left. + - Remove the search bar (you can remove it from both the shop and the product pages at the same + time). + - Move the breadcrumb. + - Hide the list or grid view option. + - Create the appropriate design and information for the product cards. + +.. image:: 05_dynamic_templates/airproof-shop-page.png + :align: center + +.. tip:: + - Apply your modifications using presets, XPath and SCSS. + - To enable attribute/variant filtering, activate the + :doc:`/applications/sales/sales/products_prices/products/variants` option in the + website backend settings and :ref:`configure attributes and variants + ` for the products. + +.. spoiler:: Solutions + + Find the solution in our Airproof example on `presets.xml + <{GITHUB_TUTO_PATH}/website_airproof/data/presets.xml>`_, `website_sale_templates.xml + <{GITHUB_TUTO_PATH}/website_airproof/views/website_sale_templates.xml>`_ part *shop page*, and + `shop.scss <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/pages/shop.scss>`_. + +.. _tutorials/website_theme/dynamic_templates/product: + +Adapt the product page template +=============================== + +The client is thrilled with the shop modifications. Next, let's apply our design to the product +pages. Based on the Airproof design below, adapt a few elements including: + +- Remove the search bar (if not done with the previous exercise). +- Remove the quantity selector, Terms and Conditions, and share icons. +- Update the :guilabel:`Add to cart` button icon. +- Insert a title above the product specifications (this section appears only when the product + has one variant per attribute). +- Design the appropriate layout for the carousel. +- Add a title and apply the previously created product template to the `Alternative products` + section (ensure alternative products are assigned on the product in the backend for this section + to appear). +- Implement a new drop zone below product details, visible on all products. As a use case, add an + `Text-Image` building block using the Website Builder (e.g.: See Airproof product page screenshot + with “*Six reasons to buy…*”). + +.. seealso:: + See reference documentation on how to create a :ref:`website_themes/layout/dropzone`. + +.. image:: 05_dynamic_templates/airproof-product-page.png + :align: center + +.. tip:: + - Make your modifications using presets, XPath, and SCSS. Be sure to comment your code properly + so you can find your way around. + - The drop zone will be visible on all products. To create a specific dropzone per product, you + need to add a new field to the product model. + +.. spoiler:: Solutions + + Find the solution in our Airproof example on `presets.xml + <{GITHUB_TUTO_PATH}/website_airproof/data/presets.xml>`_, `website_sale_templates.xml + <{GITHUB_TUTO_PATH}/website_airproof/views/website_sale_templates.xml>`_ part *product page*, and + `product_page.scss <{GITHUB_TUTO_PATH}/website_airproof/static/src/scss/pages/product_page.scss>`_. diff --git a/content/developer/tutorials/website_theme/05_dynamic_templates/airproof-product-page.png b/content/developer/tutorials/website_theme/05_dynamic_templates/airproof-product-page.png new file mode 100644 index 0000000000..39ef5d0983 Binary files /dev/null and b/content/developer/tutorials/website_theme/05_dynamic_templates/airproof-product-page.png differ diff --git a/content/developer/tutorials/website_theme/05_dynamic_templates/airproof-shop-page.png b/content/developer/tutorials/website_theme/05_dynamic_templates/airproof-shop-page.png new file mode 100644 index 0000000000..eb3a8654e3 Binary files /dev/null and b/content/developer/tutorials/website_theme/05_dynamic_templates/airproof-shop-page.png differ diff --git a/content/developer/tutorials/website_theme/06_going_live.rst b/content/developer/tutorials/website_theme/06_going_live.rst new file mode 100644 index 0000000000..e9e4bdfad3 --- /dev/null +++ b/content/developer/tutorials/website_theme/06_going_live.rst @@ -0,0 +1,84 @@ +====================== +Chapter 6 - Going live +====================== + +.. _tutorials/website_theme/going_live/translations: + +Translations +============ + +Congratulations! Your client has a beautifully designed homepage and contact page, and the eCommerce +is fully adapted to the Airproof design. Amazing! + +Now, the client wants the website translated into French. To do so: + +#. Add French to the website in the settings and enable the language switcher in the header via + presets. +#. Then for the translation itself, you have two options. We shall therefore test both: + + - Translate the content of the homepage carousel through the backend. + - But for the menu, make the translations through the frontend. + +#. Export the French :file:`.po` file for your Airproof module and place it in the :file:`/i18n` + translations folder. +#. If you would like, you can add more translations directly by editing the :file:`.po` + file. (Using Poedit software, your code editor, or another translation tool.) + +.. seealso:: + See reference documentation on :ref:`website_themes/translations/backend` and + :ref:`website_themes/translations/frontend` translations, and how to + :ref:`website_themes/translations/export` them. + +.. note:: + - Be careful when using Poedit, as it doesn't handle tags with styles well and generates an + :file:`.mo` file. + - To see the changes made directly via the :file:`.po` file, you will need to manually import the + file. + +.. spoiler:: Solutions + + Take a look at what the file `i18n/fr_BE.po <{GITHUB_TUTO_PATH}/website_airproof/i18n/fr_BE.po>`_ + of our Airproof example looks like. + +.. _tutorials/website_theme/going_live/module_import: + +Module import +============= + +Great job! The website is now completely finished and your module is ready for installation in the +client's SaaS database. + +Just before that, test the import process on a new database. + +.. seealso:: + See reference documentation on how to :doc:`deploy a module + ` on an Odoo SaaS database. + +.. tip:: + - Ensure the `base_import_module` is installed on the database before the module installation. + - Verify all required applications are installed. + - Skip the theme installation steps and start from scratch. + - Manually import translations after module installation, as they won't apply automatically. + +Conclusion +========== + +Congratulations on completing the **Build a module for a website theme** tutorial! +You've successfully navigated through every stage, from setting up your development environment to +launching a fully customized Odoo website theme. + +Throughout this journey, you've mastered: + +| ✅ **Theme module creation** - setting up the structure, declaring Odoo and Bootstrap variables. +| ✅ **Website building** - creating pages, adding media, and constructing dynamic building blocks. +| ✅ **Advanced customization** - implementing custom SCSS, JavaScript, headers, footers, and unique + design elements. +| ✅ **Visual enhancements** - designing background shapes, gradients, and animations for an + engaging user experience. +| ✅ **eCommerce optimization** - adapting shop and product templates for a seamless shopping + experience. +| ✅ **Final preparations** - managing translations and ensuring a smooth module import. + +| With these skills, you're now ready to design and develop professional, fully customized website + themes. Well done! +| We can't wait to see the amazing themes you'll create in the future. diff --git a/content/developer/tutorials/website_theme/airproof-home.png b/content/developer/tutorials/website_theme/airproof-home.png new file mode 100644 index 0000000000..60cfbdc6dd Binary files /dev/null and b/content/developer/tutorials/website_theme/airproof-home.png differ diff --git a/content/developer/tutorials/website/basic-list.png b/content/developer/tutorials/website_theme/basic-list.png similarity index 100% rename from content/developer/tutorials/website/basic-list.png rename to content/developer/tutorials/website_theme/basic-list.png diff --git a/content/developer/tutorials/website/helloworld.png b/content/developer/tutorials/website_theme/helloworld.png similarity index 100% rename from content/developer/tutorials/website/helloworld.png rename to content/developer/tutorials/website_theme/helloworld.png diff --git a/content/developer/tutorials/website/layout.png b/content/developer/tutorials/website_theme/layout.png similarity index 100% rename from content/developer/tutorials/website/layout.png rename to content/developer/tutorials/website_theme/layout.png