[ADD] extensions/cards: add an extension to implement cards
The extension adds two new directive:
- `cards` is the row container for one or more `card` directives.
- `card` is the implementation of a Bootstrap card that accepts a
`target` argument for the href of the card, a `large` option to render
the card on two columns, a `tag` option to display a single arbitrary
tag on the card, and arbitrary content that is shown in the card
body.
task-3141419
closes odoo/documentation#3689
X-original-commit: 34bc63d2bc
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
This commit is contained in:
parent
0fb94a128e
commit
009ae8ffde
3
conf.py
3
conf.py
@ -165,6 +165,9 @@ extensions = [
|
|||||||
# Content tabs
|
# Content tabs
|
||||||
'sphinx_tabs.tabs',
|
'sphinx_tabs.tabs',
|
||||||
|
|
||||||
|
# Cards
|
||||||
|
'cards',
|
||||||
|
|
||||||
# Spoilers
|
# Spoilers
|
||||||
'spoilers',
|
'spoilers',
|
||||||
|
|
||||||
|
@ -1003,6 +1003,54 @@ set, the label is used instead of the language for grouping tabs.
|
|||||||
|
|
||||||
console.log("Hello World");
|
console.log("Hello World");
|
||||||
|
|
||||||
|
.. _contributing/cards:
|
||||||
|
|
||||||
|
Cards
|
||||||
|
=====
|
||||||
|
|
||||||
|
.. list-table::
|
||||||
|
:class: o-showcase-table
|
||||||
|
|
||||||
|
* - .. cards::
|
||||||
|
|
||||||
|
.. card:: Documentation
|
||||||
|
:target: ../documentation
|
||||||
|
:tag: Step-by-step guide
|
||||||
|
:large:
|
||||||
|
|
||||||
|
Use this guide to acquire the tools and knowledge you need to write documentation.
|
||||||
|
|
||||||
|
.. card:: Content guidelines
|
||||||
|
:target: content_guidelines
|
||||||
|
|
||||||
|
List of guidelines and trips and tricks to make your content shine at its brightest!
|
||||||
|
|
||||||
|
.. card:: RST guidelines
|
||||||
|
:target: rst_guidelines
|
||||||
|
|
||||||
|
List of technical guidelines to observe when writing with reStructuredText.
|
||||||
|
|
||||||
|
* - .. code-block:: text
|
||||||
|
|
||||||
|
.. cards::
|
||||||
|
|
||||||
|
.. card:: Documentation
|
||||||
|
:target: ../documentation
|
||||||
|
:tag: Step-by-step guide
|
||||||
|
:large:
|
||||||
|
|
||||||
|
Use this guide to acquire the tools and knowledge you need to write documentation.
|
||||||
|
|
||||||
|
.. card:: Content guidelines
|
||||||
|
:target: content_guidelines
|
||||||
|
|
||||||
|
List of guidelines and trips and tricks to make your content shine at its brightest!
|
||||||
|
|
||||||
|
.. card:: RST guidelines
|
||||||
|
:target: rst_guidelines
|
||||||
|
|
||||||
|
List of technical guidelines to observe when writing with reStructuredText.
|
||||||
|
|
||||||
.. _contributing/document-metadata:
|
.. _contributing/document-metadata:
|
||||||
|
|
||||||
Document metadata
|
Document metadata
|
||||||
|
@ -16,95 +16,40 @@ How-to guides
|
|||||||
howtos/provide_iap_services
|
howtos/provide_iap_services
|
||||||
howtos/connect_device
|
howtos/connect_device
|
||||||
|
|
||||||
.. raw:: html
|
.. cards::
|
||||||
|
|
||||||
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-3 row-cols-xxl-4 g-4 mb-4">
|
.. card:: Write lean easy-to-maintain CSS
|
||||||
|
:target: howtos/scss_tips
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="howtos/scss_tips.html">
|
Follow this guide to keep the technical debt of your CSS code under control.
|
||||||
<div class="card h-100">
|
|
||||||
<div class="card-body pb-0">
|
|
||||||
<h4 class="card-title text-primary mb-1">Write lean easy-to-maintain CSS</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Follow this guide to keep the technical debt of your CSS code under control.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="howtos/web_services.html">
|
.. card:: Web services
|
||||||
<div class="card h-100 pb-0">
|
:target: howtos/web_services
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="card-title text-primary mb-1">Web services</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Learn more about Odoo's web services.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0"></div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="howtos/company.html">
|
Learn more about Odoo's web services.
|
||||||
<div class="card h-100 pb-0">
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="card-title text-primary mb-1">Multi-company guidelines</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Learn how to manage multiple companies and deal with the records-related
|
|
||||||
specificities of a multi-company environment.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0"></div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="howtos/accounting_localization.html">
|
.. card:: Multi-company guidelines
|
||||||
<div class="card h-100 pb-0">
|
:target: howtos/company
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="card-title text-primary mb-1">Accounting localization</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Learn how to build a localization module, create bank operation models and
|
|
||||||
dynamic reports.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0"></div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="howtos/translations.html">
|
Learn how to manage multiple companies and deal with the records-related specificities of a
|
||||||
<div class="card h-100 pb-0">
|
multi-company environment.
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="card-title text-primary mb-1">Translating modules</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Learn how to provide translation abilities to your module.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0"></div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="howtos/provide_iap_services.html">
|
.. card:: Accounting localization
|
||||||
<div class="card h-100 pb-0">
|
:target: howtos/accounting_localization
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="card-title text-primary mb-1">Provide IAP services</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Learn how to provide ongoing services with Odoo's In-App Purchase (IAP).
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0"></div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="howtos/connect_device.html">
|
Learn how to build a localization module, create bank operation models and dynamic reports.
|
||||||
<div class="card h-100 pb-0">
|
|
||||||
<div class="card-body">
|
|
||||||
<h4 class="card-title text-primary mb-1">Connect with a device</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Learn how to enable a module to detect and communicate with an IoT device.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0"></div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</div>
|
.. card:: Translating modules
|
||||||
|
:target: howtos/translations
|
||||||
|
|
||||||
|
Learn how to provide translation abilities to your module.
|
||||||
|
|
||||||
|
.. card:: Provide IAP services
|
||||||
|
:target: howtos/provide_iap_services
|
||||||
|
|
||||||
|
Learn how to provide ongoing services with Odoo's In-App Purchase (IAP).
|
||||||
|
|
||||||
|
.. card:: Connect with a device
|
||||||
|
:target: howtos/connect_device
|
||||||
|
|
||||||
|
Learn how to enable a module to detect and communicate with an IoT device.
|
||||||
|
@ -17,124 +17,58 @@ Tutorials
|
|||||||
tutorials/pdf_reports
|
tutorials/pdf_reports
|
||||||
tutorials/dashboards
|
tutorials/dashboards
|
||||||
|
|
||||||
.. raw:: html
|
.. cards::
|
||||||
|
|
||||||
<!-- 12 col on small screen, 6 on md, 3 on xl, 3 on xxl -->
|
.. card:: Getting started
|
||||||
<div class="row row-cols-1 row-cols-md-2 row-cols-xl-3 row-cols-xxl-4 g-4 mb-4">
|
:target: tutorials/getting_started
|
||||||
<!-- Big card with badge rounded-pill -->
|
:tag: Beginner
|
||||||
<a class="o_toctree_card col-md-12 col-xl-8 col-xxl-6" href="tutorials/getting_started.html">
|
:large:
|
||||||
<div class="card h-100">
|
|
||||||
<div class="card-body pb-0">
|
|
||||||
<h4 class="card-title text-primary mb-1">Getting started</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Develop your own module with the Odoo framework. This step-by-step tutorial
|
|
||||||
is crafted for newcomers and any other individual curious about Odoo
|
|
||||||
development.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0">
|
|
||||||
<span class="badge rounded-pill bg-dark mt-auto mb-2">Beginner</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col-md-12 col-xl-8 col-xxl-6" href="tutorials/discover_js_framework.html">
|
Develop your own module with the Odoo framework. This step-by-step tutorial is crafted for
|
||||||
<div class="card h-100">
|
newcomers and any other individual curious about Odoo development.
|
||||||
<div class="card-body pb-0">
|
|
||||||
<h4 class="card-title text-primary mb-1">Discover the JavaScript Framework</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Learn everything you need to know about the JavaScript framework of Odoo.
|
|
||||||
This tutorial will teach you how to build custom components and views, give
|
|
||||||
life to your application, and even re-introduce the kitten mode.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0">
|
|
||||||
<span class="badge rounded-pill bg-dark mt-auto mb-2">Beginner</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="tutorials/define_module_data.html">
|
.. card:: Discover the JavaScript Framework
|
||||||
<div class="card h-100">
|
:target: tutorials/discover_js_framework
|
||||||
<div class="card-body pb-0">
|
:tag: Beginner
|
||||||
<h4 class="card-title text-primary mb-1">Define module data</h4>
|
:large:
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Define master and demo data for an Odoo module, leveraging the strengths of
|
|
||||||
the CSV and XML file formats to accommodate specific data requirements.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0">
|
|
||||||
<span class="badge rounded-pill bg-dark mt-auto mb-2">Beginner</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="tutorials/restrict_data_access.html">
|
Learn everything you need to know about the JavaScript framework of Odoo. This tutorial will
|
||||||
<div class="card h-100">
|
teach you how to build custom components and views, give life to your application, and even
|
||||||
<div class="card-body pb-0">
|
re-introduce the kitten mode.
|
||||||
<h4 class="card-title text-primary mb-1">Restrict access to data</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Implement security measures to restrict access to sensitive data with the
|
|
||||||
help of groups, access rights, and record rules.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0">
|
|
||||||
<span class="badge rounded-pill bg-dark mt-auto mb-2">Beginner</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="tutorials/unit_tests.html">
|
.. card:: Define module data
|
||||||
<div class="card h-100">
|
:target: tutorials/define_module_data
|
||||||
<div class="card-body pb-0">
|
:tag: Beginner
|
||||||
<h4 class="card-title text-primary mb-1">Safeguard your code with unit tests</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Write effective unit tests in Python to ensure the resilience of your code
|
|
||||||
and safeguard it against unexpected behaviors and regressions.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0">
|
|
||||||
<span class="badge rounded-pill bg-dark mt-auto mb-2">Beginner</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="tutorials/mixins.html">
|
Define master and demo data for an Odoo module, leveraging the strengths of the CSV and XML
|
||||||
<div class="card h-100">
|
file formats to accommodate specific data requirements.
|
||||||
<div class="card-body pb-0">
|
|
||||||
<h4 class="card-title text-primary mb-1">Reuse code with mixins</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Create mixins to code features once and reuse them in multiple models.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0"></div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="tutorials/pdf_reports.html">
|
.. card:: Restrict access to data
|
||||||
<div class="card h-100">
|
:target: tutorials/restrict_data_access
|
||||||
<div class="card-body pb-0">
|
:tag: Beginner
|
||||||
<h4 class="card-title text-primary mb-1">Build PDF reports</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Use QWeb, Odoo's powerful templating engine, to create custom PDF reports for
|
|
||||||
your documents.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0"></div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a class="o_toctree_card col" href="tutorials/dashboards.html">
|
Implement security measures to restrict access to sensitive data with the help of groups,
|
||||||
<div class="card h-100">
|
access rights, and record rules.
|
||||||
<div class="card-body pb-0">
|
|
||||||
<h4 class="card-title text-primary mb-1">Visualize data in dashboards</h4>
|
|
||||||
<p class="card-text text-dark fw-normal">
|
|
||||||
Create data visualization dashboards using the enterprise edition "Dashboard"
|
|
||||||
view and so-called "SQL views".
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer border-0"></div>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
</div>
|
.. card:: Safeguard your code with unit tests
|
||||||
|
:target: tutorials/unit_tests
|
||||||
|
:tag: Beginner
|
||||||
|
|
||||||
|
Write effective unit tests in Python to ensure the resilience of your code and safeguard it
|
||||||
|
against unexpected behaviors and regressions.
|
||||||
|
|
||||||
|
.. card:: Reuse code with mixins
|
||||||
|
:target: tutorials/mixins
|
||||||
|
|
||||||
|
Create mixins to code features once and reuse them in multiple models.
|
||||||
|
|
||||||
|
.. card:: Build PDF reports
|
||||||
|
:target: tutorials/pdf_reports
|
||||||
|
|
||||||
|
Use QWeb, Odoo's powerful templating engine, to create custom PDF reports for your documents.
|
||||||
|
|
||||||
|
.. card:: Visualize data in dashboards
|
||||||
|
:target: tutorials/dashboards
|
||||||
|
|
||||||
|
Create data visualization dashboards using the enterprise edition "Dashboard" view and
|
||||||
|
so-called "SQL views".
|
||||||
|
125
extensions/cards/__init__.py
Normal file
125
extensions/cards/__init__.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
from docutils.parsers.rst import directives
|
||||||
|
from sphinx.util.docutils import SphinxDirective
|
||||||
|
|
||||||
|
|
||||||
|
class Cards(SphinxDirective):
|
||||||
|
""" Implement a `cards` directive as a Bootstrap `row`. """
|
||||||
|
has_content = True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
""" Process the content of the directive.
|
||||||
|
|
||||||
|
We use custom node classes to represent HTML elements (e.g., 'div') rather than the
|
||||||
|
corresponding sphinx.nodes.* class (e.g., sphinx.nodes.container) to prevent automatically
|
||||||
|
setting the name of the node as class (e.g., "container") on the element.
|
||||||
|
"""
|
||||||
|
self.assert_has_content()
|
||||||
|
|
||||||
|
div_row = Div(classes=[
|
||||||
|
'row', 'row-cols-1', 'row-cols-md-2', 'row-cols-xl-3', 'row-cols-xxl-4', 'g-4', 'mb-4'
|
||||||
|
])
|
||||||
|
self.state.nested_parse(self.content, self.content_offset, div_row)
|
||||||
|
return [div_row]
|
||||||
|
|
||||||
|
|
||||||
|
class Card(SphinxDirective):
|
||||||
|
""" Implement a `card` directive with Bootstrap's card component. """
|
||||||
|
required_arguments = 1
|
||||||
|
final_argument_whitespace = True
|
||||||
|
option_spec = {
|
||||||
|
'target': directives.unchanged_required,
|
||||||
|
'tag': directives.unchanged,
|
||||||
|
'large': directives.flag,
|
||||||
|
}
|
||||||
|
has_content = True
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
""" Process the content of the directive.
|
||||||
|
|
||||||
|
We use custom node classes to represent HTML elements (e.g., 'div') rather than the
|
||||||
|
corresponding sphinx.nodes.* class (e.g., sphinx.nodes.container) to prevent automatically
|
||||||
|
setting the name of the node as class (e.g., "container") on the element.
|
||||||
|
"""
|
||||||
|
self.assert_has_content()
|
||||||
|
|
||||||
|
current_document = f'{self.env.docname}.rst'
|
||||||
|
target_document = f'{self.options["target"]}.rst'
|
||||||
|
if target_document.startswith('/'):
|
||||||
|
raise self.warning(f"card directive's target starts with a '/'")
|
||||||
|
target_file = Path(self.env.srcdir) / Path(current_document).parent / target_document
|
||||||
|
if not target_file.exists():
|
||||||
|
raise self.warning(f"card directive targets nonexisting document '{target_document}'")
|
||||||
|
|
||||||
|
a_col_href = target_document.replace('.rst', '.html')
|
||||||
|
a_col_classes = ['o_toctree_card']
|
||||||
|
if 'large' in self.options:
|
||||||
|
a_col_classes += ['col-md-12', 'col-xl-8', 'col-xxl-6']
|
||||||
|
else:
|
||||||
|
a_col_classes += ['col']
|
||||||
|
a_col = A(href=a_col_href, classes=a_col_classes)
|
||||||
|
|
||||||
|
div_card = Div(classes=['card', 'h-100'])
|
||||||
|
a_col += div_card
|
||||||
|
|
||||||
|
div_card_body = Div(classes=['card-body', 'pb-0'])
|
||||||
|
div_card += div_card_body
|
||||||
|
|
||||||
|
h4_title = H4(classes=['card-title', 'text-primary', 'mb-1'])
|
||||||
|
h4_title += nodes.Text(self.arguments[0])
|
||||||
|
div_card_body += h4_title
|
||||||
|
|
||||||
|
p_card_text = nodes.paragraph(classes=['card-text', 'text-dark', 'fw-normal'])
|
||||||
|
p_card_text += nodes.Text('\n'.join(self.content))
|
||||||
|
div_card_body += p_card_text
|
||||||
|
|
||||||
|
div_card_footer = Div(classes=['card-footer', 'border-0'])
|
||||||
|
div_card += div_card_footer
|
||||||
|
|
||||||
|
if 'tag' in self.options:
|
||||||
|
span_badge = Span(classes=['badge', 'rounded-pill', 'bg-dark', 'mt-auto', 'mb-2'])
|
||||||
|
div_card_footer += span_badge
|
||||||
|
span_badge += nodes.Text(self.options['tag'])
|
||||||
|
|
||||||
|
return [a_col]
|
||||||
|
|
||||||
|
|
||||||
|
class Div(nodes.General, nodes.Element):
|
||||||
|
custom_tag_name = 'div'
|
||||||
|
|
||||||
|
|
||||||
|
class A(nodes.General, nodes.Element):
|
||||||
|
custom_tag_name = 'a'
|
||||||
|
|
||||||
|
|
||||||
|
class Span(nodes.General, nodes.Element):
|
||||||
|
custom_tag_name = 'span'
|
||||||
|
|
||||||
|
|
||||||
|
class H4(nodes.General, nodes.Element):
|
||||||
|
custom_tag_name = 'h4'
|
||||||
|
|
||||||
|
|
||||||
|
def visit_node(translator, node):
|
||||||
|
custom_attr = {k: v for k, v in node.attributes.items() if k not in node.known_attributes}
|
||||||
|
translator.body.append(translator.starttag(node, node.custom_tag_name, **custom_attr).rstrip())
|
||||||
|
|
||||||
|
|
||||||
|
def depart_node(translator, node):
|
||||||
|
translator.body.append(f'</{node.custom_tag_name}>')
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.add_directive('cards', Cards)
|
||||||
|
app.add_directive('card', Card)
|
||||||
|
app.add_node(Div, html=(visit_node, depart_node))
|
||||||
|
app.add_node(A, html=(visit_node, depart_node))
|
||||||
|
app.add_node(Span, html=(visit_node, depart_node))
|
||||||
|
app.add_node(H4, html=(visit_node, depart_node))
|
||||||
|
|
||||||
|
return {
|
||||||
|
'parallel_read_safe': True,
|
||||||
|
'parallel_write_safe': True,
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user