From dfa39f94b939ca2bb0db6b0153597af805c7cd4b Mon Sep 17 00:00:00 2001 From: Brandon Mercier <113701731+bram-odoo@users.noreply.github.com> Date: Tue, 6 May 2025 11:09:29 +0000 Subject: [PATCH] [REM] tutorials/website: Remove outdated tutorial MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-original-commit: 08c173d7da0c8d52c7e549e23aba9baae9e8ed00 Part-of: odoo/documentation#13224 Signed-off-by: Antoine Vandevenne (anv) Signed-off-by: Céline de Lannoy (cede) --- content/developer/tutorials/website.rst | 1012 ----------------- .../tutorials/website/basic-list.png | Bin 1434 -> 0 bytes .../tutorials/website/helloworld.png | Bin 823 -> 0 bytes .../developer/tutorials/website/layout.png | Bin 4826 -> 0 bytes 4 files changed, 1012 deletions(-) delete mode 100644 content/developer/tutorials/website.rst delete mode 100644 content/developer/tutorials/website/basic-list.png delete mode 100644 content/developer/tutorials/website/helloworld.png delete mode 100644 content/developer/tutorials/website/layout.png diff --git a/content/developer/tutorials/website.rst b/content/developer/tutorials/website.rst deleted file mode 100644 index eaca1d6b0..000000000 --- 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/basic-list.png b/content/developer/tutorials/website/basic-list.png deleted file mode 100644 index 3eb76ddf7c209b167bdad460147953ce5b884469..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1434 zcmV;L1!ek)P)`w??I`u zZm@i(%~CCL-_spLLiXotiPol|_+6D9VD~TNz!7N?;We~iGoz{}T;_H%iKrj(6Y$RA zyB5y1nXwqD*#x_mDx09x=W1yiQ$KLoK_U9l%PlOmO*MyGCC#toT-z+V!jd{WXpzI{ z;retf0X0$CL_AHf9v-|n*K zgs3w(TMIo7y7dr^Ubt)F@L0K(WS{1OMakK7X&byz6WEJ5XyNcp3+H+af6`LfbI(}0 zzP=PRn*u|>wq{n?Bt4Ef47OJyf^#{Tq_d{b?^@z|06#hPbw5EV7EDoDNS`ob+J6Ep z90pvuXxy{qazNr4%f^|uJ~2uLFf+~g5=d-Ue;XcibeG~s^qT)|91e%W;cz${4u`|x za5x-NeMn(W|B9Q|sw*`91c2E4!Rr!lv=2b0(fhpL1Z~E9&L$^I>_@NbNVgg>RrA>? zA~%fpoKMGEe)77Q%9>=of7T*G{*L@ITH1$!#d~>Ok7+8zbMiIy3X52&J*={EAnJ$z zgo3wRP78Tlo!z~z3fJ2pdMFPgXq!?#0AlO&mdk0Oe&~U-dtGE-tn-i=E}0zFX60HT zw=GxGvOa!k@$$NGI3nBZmh$ZxjkWNW>uDLNF+4umtFPY7FUytUy_B`^mMdvd9ya)8 zc~wwuUKh2!jia)B$m)PJ@EO~-Tu4jXq^o<(58`g~y0ANwa7_+?iS}WFfefQKky)x= zTh^;>E}%sY06vYunv9xfuS?@<@2<-LSv>lyKfsbvz+2?%hGC|>!XeXNq7*KUURPfn zi}t??TI^e{PdDer6ZfD8D8x-@=S2K@91e%W;cz${4u`|xa41ilM>}l)8~nHFEWq^j z9oqP@=c<9|^xunppiMsHxYs`YMtpv{mgwX6ndQe`Q%#b->$tm&mfS*2*5hOs-GecS ze1P$_%BLAKJeUMK9@!x4rYWk(K@**|qdx~7_!*>LJ5R*`v3H)9eDvu1?l2Wysyn+V zIV@xrbr#HOt!{o_U$Q|)b672y$`*2f6tzI{gw;5s`)OL*CY@)1Bs7Vbb!(|peFHnRHHiL|`Y?`)$!oY&X%{ius}A*Ix2vep1PAY*N@5>rJ6W~RG1 z52Xa5_4@BOKj_!TpNw&Bo_b2DDG>j8_@PnVLJJ)=LkIB|)bp&wZE-lX(03AZ8#P+r zM~(gxb0aPH(4!HlZ!;Gj%}un(o18JllevK!#e=!ST$Ix4E)N)`x5?aLE}I&m@1o@v oXY3{BMq1=8&KPpBxwtd-3zV8)?XKAc1ONa407*qoM6N<$g05n{>i_@% diff --git a/content/developer/tutorials/website/helloworld.png b/content/developer/tutorials/website/helloworld.png deleted file mode 100644 index fe76ad88bf71e6535eef4711f0badfa5e0f0555c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 823 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 Ip==)VFGu%Q2mk;8