739 lines
43 KiB
XML
739 lines
43 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<odoo>
|
|
<template id="frontend_layout" name="Main Frontend Layout" inherit_id="web.frontend_layout">
|
|
<xpath expr="//div[@id='wrapwrap']" position="attributes">
|
|
<attribute name="t-attf-class" add="#{request.env['res.lang']._lang_get_direction(request.env.lang) == 'rtl' and 'o_rtl' or ''}" separator=" "/>
|
|
<attribute name="t-attf-class" add="#{'o_portal' if is_portal else ''}" separator=" "/>
|
|
</xpath>
|
|
<xpath expr="//div[@id='wrapwrap']/header/img" position="replace">
|
|
<t t-cache="res_company">
|
|
<nav class="navbar navbar-expand navbar-light bg-light">
|
|
<div class="container">
|
|
<a href="/" class="navbar-brand logo">
|
|
<img t-att-src="'/logo.png?company=%s' % res_company.id" t-att-alt="'Logo of %s' % res_company.name" t-att-title="res_company.name"/>
|
|
</a>
|
|
<ul id="top_menu" class="nav navbar-nav ms-auto">
|
|
<t t-call="portal.placeholder_user_sign_in">
|
|
<t t-set="_item_class" t-value="'nav-item'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
</t>
|
|
<t t-call="portal.user_dropdown">
|
|
<t t-set="_user_name" t-value="true"/>
|
|
<t t-set="_item_class" t-value="'nav-item dropdown'"/>
|
|
<t t-set="_link_class" t-value="'nav-link'"/>
|
|
<t t-set="_dropdown_menu_class" t-value="'dropdown-menu-end'"/>
|
|
</t>
|
|
</ul>
|
|
</div>
|
|
</nav>
|
|
</t>
|
|
</xpath>
|
|
<xpath expr="//div[@id='wrapwrap']/main/t[@t-out='0']" position="before">
|
|
<div t-if="o_portal_fullwidth_alert" class="alert alert-info alert-dismissible rounded-0 fade show d-print-none css_editable_mode_hidden">
|
|
<div class="container">
|
|
<t t-out="o_portal_fullwidth_alert"/>
|
|
</div>
|
|
</div>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!-- Added by another template so that it can be disabled if needed -->
|
|
<template id="footer_language_selector" inherit_id="portal.frontend_layout" name="Footer Language Selector">
|
|
<xpath expr="//*[hasclass('o_footer_copyright_name')]" position="after">
|
|
<t id="language_selector_call" t-call="portal.language_selector">
|
|
<t t-set="_div_classes" t-value="(_div_classes or '') + ' dropup'"/>
|
|
</t>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="language_selector" name="Language Selector">
|
|
<t t-nocache="The query strings can change for the same page and the same rendering."
|
|
t-nocache-no_text="no_text"
|
|
t-nocache-_div_classes="_div_classes"
|
|
t-nocache-_btn_class="_btn_class"
|
|
t-nocache-_dropdown_menu_class="_dropdown_menu_class">
|
|
<t t-set="active_lang" t-value="list(filter(lambda lg : lg[0] == lang, languages))[0]"/>
|
|
<t t-set="language_selector_visible" t-value="len(languages) > 1"/>
|
|
<div t-attf-class="js_language_selector #{_div_classes} d-print-none" t-if="language_selector_visible">
|
|
<button t-attf-class="btn btn-sm btn-outline-secondary border-0 dropdown-toggle #{_btn_class}" type="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
|
|
<span t-if="not no_text"
|
|
class="align-middle"
|
|
t-esc="active_lang[2].split('/').pop()"/>
|
|
</button>
|
|
<div t-attf-class="dropdown-menu #{_dropdown_menu_class}" role="menu">
|
|
<t t-foreach="languages" t-as="lg">
|
|
<a t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang_code=lg[0])"
|
|
t-attf-class="dropdown-item js_change_lang #{active_lang == lg and 'active'}"
|
|
t-att-data-url_code="lg[1]">
|
|
<span t-if="not no_text" t-esc="lg[2].split('/').pop()"/>
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="user_dropdown" name="Portal User Dropdown">
|
|
<t t-nocache="Each user is different regardless of the page visited."
|
|
t-nocache-_avatar="_avatar"
|
|
t-nocache-_icon="_icon"
|
|
t-nocache-_icon_class="_icon_class"
|
|
t-nocache-_user_name="_user_name"
|
|
t-nocache-_user_name_class="_user_name_class"
|
|
t-nocache-_item_class="_item_class"
|
|
t-nocache-_link_class="_link_class"
|
|
t-nocache-_dropdown_menu_class="_dropdown_menu_class">
|
|
<t t-set="is_connected" t-value="not user_id._is_public()"/>
|
|
<li t-if="is_connected" t-attf-class="#{_item_class} o_no_autohide_item">
|
|
<a href="#" role="button" data-bs-toggle="dropdown" t-attf-class="dropdown-toggle #{_link_class}">
|
|
<t t-if="_avatar">
|
|
<t t-set="avatar_source" t-value="image_data_uri(user_id.avatar_256)"/>
|
|
<img t-att-src="avatar_source" t-attf-class="rounded-circle o_object_fit_cover #{_avatar_class}" width="24" height="24" alt="" loading="eager"/>
|
|
</t>
|
|
<i t-if="_icon" t-attf-class="fa fa-1x fa-fw fa-user-circle-o #{_icon_class}"/>
|
|
<span t-if="_user_name" t-attf-class="#{_user_name_class}" t-esc="user_id.name[:23] + '...' if user_id.name and len(user_id.name) > 25 else user_id.name"/>
|
|
</a>
|
|
<div t-attf-class="dropdown-menu js_usermenu #{_dropdown_menu_class}" role="menu">
|
|
<a groups="base.group_user" href="/web" role="menuitem" class="dropdown-item ps-3" id="o_backend_user_dropdown_link">
|
|
<i class="fa fa-fw fa-th me-1 small text-muted"/> Apps
|
|
</a>
|
|
<div id="o_logout_divider" class="dropdown-divider"/>
|
|
<a t-attf-href="/web/session/logout?redirect=/" role="menuitem" id="o_logout" class="dropdown-item ps-3">
|
|
<i class="fa fa-fw fa-sign-out me-1 small text-muted"/> Logout
|
|
</a>
|
|
</div>
|
|
</li>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="portal_breadcrumbs" name="Portal Breadcrumbs">
|
|
<ol t-if="page_name != 'home'" class="o_portal_submenu breadcrumb mb-0 py-2 flex-grow-1">
|
|
<li class="breadcrumb-item ms-1"><a href="/my/home" aria-label="Home" title="Home"><i class="fa fa-home"/></a></li>
|
|
<li t-if="page_name == 'my_details'" class="breadcrumb-item">Details</li>
|
|
</ol>
|
|
</template>
|
|
|
|
<template id="portal_back_in_edit_mode" name="Back to edit mode">
|
|
<div t-ignore="true" class="text-center">
|
|
<t t-if="custom_html" t-out="custom_html"/>
|
|
<t t-else="">This is a preview of the customer portal.</t>
|
|
<a t-att-href="backend_url"><i class="fa fa-arrow-right me-1"/>Back to edit mode</a>
|
|
</div>
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</template>
|
|
|
|
<template id="portal_layout" name="Portal Layout">
|
|
<t t-call="portal.frontend_layout">
|
|
<t t-set="is_portal" t-value="True"/>
|
|
|
|
<div t-if="not no_breadcrumbs and not my_details and not breadcrumbs_searchbar" class="o_portal container mt-3">
|
|
<div class="row align-items-center bg-white g-0 border rounded">
|
|
<div class="col-10">
|
|
<t t-call="portal.portal_breadcrumbs"></t>
|
|
</div>
|
|
<div t-if="prev_record or next_record" class="col-2 flex-grow-0 text-center">
|
|
<t t-call='portal.record_pager'/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="wrap" class='o_portal_wrap'>
|
|
<div class="container mb64">
|
|
<t t-if="my_details">
|
|
<div class="row justify-content-between mt-4">
|
|
<div t-attf-class="col-12 col-md col-lg-6">
|
|
<t t-out="0"/>
|
|
</div>
|
|
<div id="o_my_sidebar" class="pt-3 pt-lg-0 col-12 col-md col-lg-4 col-xl-3 o_my_sidebar">
|
|
<div class="o_my_contact" t-if="sales_user">
|
|
<t t-call="portal.portal_contact"/>
|
|
</div>
|
|
<div class="o_portal_my_details">
|
|
<h4>Details <a role="button" href="/my/account" class="btn btn-sm btn-link"><i class="fa fa-pencil"/> Edit</a></h4>
|
|
<hr class="mt-1 mb-0"/>
|
|
<div t-field="user_id.partner_id" t-options='{"widget": "contact", "fields": ["email", "phone", "address", "name"]}'/>
|
|
</div>
|
|
<div class="o_portal_my_security mt-3">
|
|
<h4>Account Security </h4>
|
|
<hr class="mt-1 mb-1"/>
|
|
<a href="/my/security"><i class="fa fa-pencil mx-1"/>Edit Security Settings</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
<t t-else="">
|
|
<t t-out="0"/>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="placeholder_user_sign_in" name="User Sign In Placeholder"/>
|
|
|
|
<template id="user_sign_in" name="User Sign In" inherit_id="portal.placeholder_user_sign_in">
|
|
<xpath expr="." position="inside">
|
|
<li t-nocache="Profile session and user group can change unrelated to parent caches."
|
|
t-nocache-_item_class="_item_class"
|
|
t-nocache-_link_class="_link_class"
|
|
groups="base.group_public" t-attf-class="#{_item_class} o_no_autohide_item">
|
|
<a t-attf-href="/web/login" t-attf-class="#{_link_class}">Sign in<span t-if="request.session.profile_session" class="text-danger fa fa-circle"/></a>
|
|
</li>
|
|
</xpath>
|
|
</template>
|
|
|
|
<template id="portal_my_home" name="My Portal">
|
|
<t t-call="portal.portal_layout">
|
|
<t t-set="my_details" t-value="True"/>
|
|
<div class="o_portal_my_home">
|
|
<div class="oe_structure" id="oe_structure_portal_my_home_1"/>
|
|
<h3>Documents</h3>
|
|
<div class="o_portal_docs list-group">
|
|
<div class="o_portal_doc_spinner spinner-border text-o-color-2 align-self-center mt-5"/>
|
|
<p class="o_portal_no_doc_message d-none">No Documents to display</p>
|
|
</div>
|
|
</div>
|
|
<div class="oe_structure" id="oe_structure_portal_my_home_2"/>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="portal_docs_entry" name="My Portal Docs Entry">
|
|
<a t-att-href="url" t-att-title="title" class="list-group-item list-group-item-action d-flex align-items-center justify-content-between d-none">
|
|
<t t-esc="title"/>
|
|
<t t-if='count'>
|
|
<span class="badge text-bg-secondary rounded-pill" t-esc="count"/>
|
|
</t>
|
|
<t t-elif="placeholder_count">
|
|
<span class="badge text-bg-secondary rounded-pill" t-att-data-placeholder_count="placeholder_count">
|
|
<i class="fa fa-spin fa-circle-o-notch"></i>
|
|
</span>
|
|
</t>
|
|
</a>
|
|
</template>
|
|
|
|
<template id="portal_table" name="My Portal Table">
|
|
<div t-attf-class="table-responsive border rounded border-top-0 #{classes if classes else ''}">
|
|
<table class="table rounded mb-0 bg-white o_portal_my_doc_table">
|
|
<t t-out="0"/>
|
|
</table>
|
|
</div>
|
|
<div t-if="pager" class="o_portal_pager d-flex justify-content-center">
|
|
<t t-call="portal.pager"/>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="portal_record_sidebar" name="My Portal Record Sidebar">
|
|
<div t-attf-class="#{classes}">
|
|
<div class="card bg-white mb-4 sticky-top" id="sidebar_content">
|
|
<div t-if="title" class="card-body text-center pb-2 pt-3">
|
|
<t t-out="title"/>
|
|
</div>
|
|
<t t-if="entries" t-out="entries"/>
|
|
<div class="card-footer small text-center text-muted border-top-0 pt-1 pb-1 d-none d-lg-block">
|
|
Powered by <a target="_blank" href="http://www.odoo.com?utm_source=db&utm_medium=portal" title="odoo"><img src="/web/static/img/logo.png" alt="Odoo Logo" height="15"/></a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!--
|
|
The search bar is composed of 2 buttons : a "filter by" and a "sort by". Changing the 'sortby'
|
|
criteria will keep the number of page, query params, ... Changing the 'filterby' param will
|
|
redirect the user to the beginning of document list, keeping query parameters.
|
|
|
|
These 2 buttons can be prepended by a advanced search input, to activate it, search_input need
|
|
to be initialized at 'True' and the content of the t-call is the list of li elements searchable.
|
|
|
|
:param dict searchbar_sortings : containing the sort criteria like
|
|
{'date': {'label': _('Newest'), 'order': 'create_date desc'}}
|
|
:param string sortby : name of the sort criteria
|
|
:param dict searchbar_filters : containing the filter criteria like
|
|
{'open': {'label': _('In Progress'), 'domain': [('state', '=', 'open')]}}
|
|
:param string filterby : name of the filter criteria
|
|
:param default_url : the base url of the pages (like '/my/orders')
|
|
:param boolean breadcrumbs_searchbar : set to True to show breadcrumbs rather than the title
|
|
:param boolean o_portal_search_panel : set to True to active the input search
|
|
:param html $0 : content of the t-call
|
|
:param title : bavbar title
|
|
:param classes : navbar classes
|
|
-->
|
|
<template id="portal_searchbar" name="Portal Search Bar">
|
|
<nav t-attf-class="navbar navbar-light navbar-expand-lg border py-0 mb-2 o_portal_navbar {{classes if classes else ''}} {{'mt-3 rounded' if breadcrumbs_searchbar else 'border-top-0' }}">
|
|
<!-- Navbar breadcrumb or title -->
|
|
<t t-if="breadcrumbs_searchbar">
|
|
<t t-call="portal.portal_breadcrumbs"/>
|
|
</t>
|
|
<span t-else="" class="navbar-brand mb-0 h1 me-auto" t-esc="title or 'No title'"/>
|
|
|
|
<!-- Collapse button -->
|
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#o_portal_navbar_content" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle filters">
|
|
<span class="navbar-toggler-icon small"/>
|
|
</button>
|
|
|
|
<!-- Collapsable content -->
|
|
<div class="collapse navbar-collapse" id="o_portal_navbar_content">
|
|
<div class="nav flex-column flex-lg-row ms-auto p-0 mb-3 mb-lg-0 mt-1 mt-lg-0">
|
|
<div t-if="searchbar_sortings">
|
|
<span class="small me-1 navbar-text">Sort By:</span>
|
|
<div class="btn-group">
|
|
<button id="portal_searchbar_sortby" data-bs-toggle="dropdown" class="btn btn-secondary btn-sm dropdown-toggle">
|
|
<t t-esc="searchbar_sortings[sortby].get('label', 'Newest')"/>
|
|
</button>
|
|
<div class="dropdown-menu" aria-labelledby="portal_searchbar_sortby">
|
|
<t t-foreach="searchbar_sortings" t-as="option">
|
|
<a t-att-href="request.httprequest.path + '?' + keep_query('*', sortby=option)"
|
|
t-attf-class="dropdown-item#{sortby == option and ' active' or ''}">
|
|
<span t-esc="searchbar_sortings[option].get('label')"/>
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div t-if="searchbar_filters" class="ms-lg-2">
|
|
<span class="small me-1 navbar-text">Filter By:</span>
|
|
<div class="btn-group">
|
|
<button id="portal_searchbar_filters" data-bs-toggle="dropdown" class="btn btn-secondary btn-sm dropdown-toggle">
|
|
<t t-esc="searchbar_filters.get(filterby,searchbar_filters.get('all')).get('label', 'All')"/>
|
|
</button>
|
|
<div class="dropdown-menu" aria-labelledby="portal_searchbar_filters">
|
|
<t t-foreach="searchbar_filters" t-as="option">
|
|
<a t-att-href="default_url + '?' + keep_query('*', filterby=option)"
|
|
t-attf-class="dropdown-item#{filterby == option and ' active' or ''}">
|
|
<span t-esc="searchbar_filters[option].get('label')"/>
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div t-if="searchbar_groupby" class="ms-lg-2">
|
|
<span class="small me-1 navbar-text">Group By:</span>
|
|
<div class="btn-group">
|
|
<button id="portal_searchbar_groupby" data-bs-toggle="dropdown" class="btn btn-secondary btn-sm dropdown-toggle">
|
|
<t t-esc="searchbar_groupby[groupby].get('label', 'None')"/>
|
|
</button>
|
|
<div class="dropdown-menu" aria-labelledby="portal_searchbar_groupby">
|
|
<t t-foreach="searchbar_groupby" t-as="option">
|
|
<a t-att-href="default_url + '?' + keep_query('*', groupby=option)"
|
|
t-attf-class="dropdown-item#{groupby == option and ' active' or ''}">
|
|
<span t-esc="searchbar_groupby[option].get('label')"/>
|
|
</a>
|
|
</t>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<t t-out="0"/>
|
|
</div>
|
|
<form t-if="searchbar_inputs" class="o_portal_search_panel ms-lg-4 col-xl-4 col-md-5">
|
|
<div class="input-group input-group-sm w-100">
|
|
<button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown"/>
|
|
<div class="dropdown-menu" role="menu">
|
|
<t t-foreach='searchbar_inputs' t-as='input'>
|
|
<a t-att-href="'#' + input_value['input']"
|
|
t-attf-class="dropdown-item#{search_in == input_value['input'] and ' active' or ''}">
|
|
<span t-out="input_value['label']"/>
|
|
</a>
|
|
</t>
|
|
</div>
|
|
<input type="text" class="form-control form-control-sm" placeholder="Search" t-att-value='search' name="search"/>
|
|
<button class="btn btn-secondary o_wait_lazy_js" type="submit">
|
|
<span class="fa fa-search"/>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</nav>
|
|
</template>
|
|
|
|
<template id="portal_record_layout" name="Portal single record layout">
|
|
<div t-attf-class="card mt-0 border-top-0 rounded-0 rounded-bottom #{classes if classes else ''}">
|
|
<div t-if="card_header" t-attf-class="card-header #{header_classes if header_classes else ''}">
|
|
<t t-out="card_header"/>
|
|
</div>
|
|
<div t-if="card_body" t-attf-class="card-body #{body_classes if body_classes else ''}">
|
|
<t t-out="card_body"/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<!--
|
|
TODO adapt in master: remove the use of the "title" variable in this
|
|
template (or rename the variable). Using it at controller level actually
|
|
also controls the browser tab title, which makes no sense.
|
|
-->
|
|
<template id="portal_contact" name="Contact">
|
|
<div class="o_portal_contact_details mb-5">
|
|
<h4><t t-if="title" t-esc="title"/><t t-else="">Your contact</t></h4>
|
|
<hr class="mt-1 mb0"/>
|
|
<h6 class="mb-1"><b t-esc="sales_user.name"/></h6>
|
|
<div class="d-flex align-items-center mb-1">
|
|
<div class="fa fa-envelope fa-fw me-1"></div>
|
|
<a t-att-href="'mailto:'+sales_user.email" t-esc="sales_user.email"/>
|
|
</div>
|
|
<div class="d-flex flex-nowrap align-items-center mb-1">
|
|
<div class="fa fa-phone fa-fw me-1"></div>
|
|
<span t-esc="sales_user.phone"/>
|
|
</div>
|
|
<div class="d-flex flex-nowrap align-items-center mb-1">
|
|
<div class="fa fa-map-marker fa-fw me-1"></div>
|
|
<span t-esc="sales_user.city"/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="portal_my_details_fields">
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
|
<div t-if="error_message" class="alert alert-danger" role="alert">
|
|
<div class="col-lg-12">
|
|
<t t-foreach="error_message" t-as="err"><t t-esc="err"/><br /></t>
|
|
</div>
|
|
</div>
|
|
<div t-attf-class="mb-3 #{error.get('name') and 'o_has_error' or ''} col-xl-6">
|
|
<label class="col-form-label" for="name">Name</label>
|
|
<input type="text" name="name" t-attf-class="form-control #{error.get('name') and 'is-invalid' or ''}" t-att-value="name or partner.name" />
|
|
</div>
|
|
<div t-attf-class="mb-3 #{error.get('email') and 'o_has_error' or ''} col-xl-6">
|
|
<label class="col-form-label" for="email">Email</label>
|
|
<input type="email" name="email" t-attf-class="form-control #{error.get('email') and 'is-invalid' or ''}" t-att-value="email or partner.email" />
|
|
</div>
|
|
|
|
<div class="clearfix" />
|
|
<div t-attf-class="mb-1 #{error.get('company_name') and 'o_has_error' or ''} col-xl-6">
|
|
<label class="col-form-label label-optional" for="company_name">Company Name</label>
|
|
<!-- The <input> use "disabled" attribute to avoid sending an unauthorized value on form submit.
|
|
The user might not have rights to change company_name but should still be able to see it.
|
|
-->
|
|
<input type="text" name="company_name" t-attf-class="form-control #{error.get('company_name') and 'is-invalid' or ''}" t-att-value="company_name or partner.commercial_company_name" t-att-disabled="None if partner_can_edit_vat else '1'" />
|
|
<small t-if="not partner_can_edit_vat" class="form-text text-muted d-block d-xl-none">
|
|
Changing company name is not allowed once document(s) have been issued for your account. Please contact us directly for this operation.
|
|
</small>
|
|
</div>
|
|
<div t-attf-class="mb-1 #{error.get('vat') and 'o_has_error' or ''} col-xl-6">
|
|
<label class="col-form-label label-optional" for="vat">VAT Number</label>
|
|
<!-- The <input> use "disabled" attribute to avoid sending an unauthorized value on form submit.
|
|
The user might not have rights to change company_name but should still be able to see it.
|
|
-->
|
|
<input type="text" name="vat" t-attf-class="form-control #{error.get('vat') and 'is-invalid' or ''}" t-att-value="vat or partner.vat" t-att-disabled="None if partner_can_edit_vat else '1'" />
|
|
<small t-if="not partner_can_edit_vat" class="form-text text-muted d-block d-xl-none">Changing VAT number is not allowed once document(s) have been issued for your account. Please contact us directly for this operation.</small>
|
|
</div>
|
|
<div t-if="not partner_can_edit_vat" class="col-12 d-none d-xl-block">
|
|
<small class="form-text text-muted">Changing company name or VAT number is not allowed once document(s) have been issued for your account. <br/>Please contact us directly for this operation.</small>
|
|
</div>
|
|
<div t-attf-class="mb-3 #{error.get('phone') and 'o_has_error' or ''} col-xl-6">
|
|
<label class="col-form-label" for="phone">Phone</label>
|
|
<input type="tel" name="phone" t-attf-class="form-control #{error.get('phone') and 'is-invalid' or ''}" t-att-value="phone or partner.phone" />
|
|
</div>
|
|
|
|
<div class="clearfix" />
|
|
<div t-attf-class="mb-3 #{error.get('street') and 'o_has_error' or ''} col-xl-6">
|
|
<label class="col-form-label" for="street">Street</label>
|
|
<input type="text" name="street" t-attf-class="form-control #{error.get('street') and 'is-invalid' or ''}" t-att-value="street or partner.street"/>
|
|
</div>
|
|
<div t-attf-class="mb-3 #{error.get('city') and 'o_has_error' or ''} col-xl-6">
|
|
<label class="col-form-label" for="city">City</label>
|
|
<input type="text" name="city" t-attf-class="form-control #{error.get('city') and 'is-invalid' or ''}" t-att-value="city or partner.city" />
|
|
</div>
|
|
<div t-attf-class="mb-3 #{error.get('zip') and 'o_has_error' or ''} col-xl-6">
|
|
<label class="col-form-label label-optional" for="zipcode">Zip / Postal Code</label>
|
|
<input type="text" name="zipcode" t-attf-class="form-control #{error.get('zip') and 'is-invalid' or ''}" t-att-value="zipcode or partner.zip" />
|
|
</div>
|
|
<div t-attf-class="mb-3 #{error.get('country_id') and 'o_has_error' or ''} col-xl-6">
|
|
<label class="col-form-label" for="country_id">Country</label>
|
|
<select name="country_id" t-attf-class="form-select #{error.get('country_id') and 'is-invalid' or ''}">
|
|
<option value="">Country...</option>
|
|
<t t-foreach="countries or []" t-as="country">
|
|
<option t-att-value="country.id" t-att-selected="country.id == int(country_id) if country_id else country.id == partner.country_id.id">
|
|
<t t-esc="country.name" />
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
<div t-attf-class="mb-3 #{error.get('state_id') and 'o_has_error' or ''} col-xl-6">
|
|
<label class="col-form-label label-optional" for="state_id">State / Province</label>
|
|
<select name="state_id" t-attf-class="form-select #{error.get('state_id') and 'is-invalid' or ''}">
|
|
<option value="">select...</option>
|
|
<t t-foreach="states or []" t-as="state">
|
|
<option t-att-value="state.id" style="display:none;" t-att-data-country_id="state.country_id.id" t-att-selected="state.id == int(state_id) if state_id else state.id == partner.state_id.id">
|
|
<t t-esc="state.name" />
|
|
</option>
|
|
</t>
|
|
</select>
|
|
</div>
|
|
</template>
|
|
|
|
<template id="portal_my_details">
|
|
<t t-call="portal.portal_layout">
|
|
<t t-set="additional_title">Contact Details</t>
|
|
<form action="/my/account" method="post">
|
|
<div class="row o_portal_details">
|
|
<div class="col-lg-8">
|
|
<div class="row">
|
|
<t t-call="portal.portal_my_details_fields"/>
|
|
<input type="hidden" name="redirect" t-att-value="redirect"/>
|
|
</div>
|
|
<div class="clearfix">
|
|
<button type="submit" class="btn btn-primary float-end mb32 ">
|
|
Confirm
|
|
<span class="fa fa-long-arrow-right" />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="portal_my_security">
|
|
<t t-call="portal.portal_layout"><div class="o_portal_security_body">
|
|
<t t-set="additional_title">Security</t>
|
|
<t t-set="no_breadcrumbs" t-value="1"/>
|
|
<div class="alert alert-danger" role="alert" t-if="get_error(errors)">
|
|
<t t-esc="errors"/>
|
|
</div>
|
|
<section name="portal_change_password">
|
|
<h3>Change Password</h3>
|
|
<t t-set="path">password</t>
|
|
<div class="alert alert-success" role="alert" t-if="success and success.get('password')">
|
|
Password Updated!
|
|
</div>
|
|
<div class="alert alert-danger" role="alert" t-if="get_error(errors, 'password')">
|
|
<t t-esc="errors['password']"/>
|
|
</div>
|
|
<form action="/my/security" method="post" class="oe_reset_password_form">
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
|
<input type="hidden" name="op" value="password"/>
|
|
<div class="mb-3">
|
|
<label for="current">Password:</label>
|
|
<input type="password" t-attf-class="form-control form-control-sm {{ 'is-invalid' if get_error(errors, 'password.old') else '' }}"
|
|
id="current" name="old"
|
|
autocomplete="current-password" required="required"/>
|
|
<div class="invalid-feedback">
|
|
<t t-esc="get_error(errors, 'password.old')"/>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="new">New Password:</label>
|
|
<input type="password" t-attf-class="form-control form-control-sm {{ 'is-invalid' if get_error(errors, 'password.new1') else '' }}"
|
|
id="new" name="new1"
|
|
autocomplete="new-password" required="required"/>
|
|
<div class="invalid-feedback">
|
|
<t t-esc="get_error(errors, 'password.new1')"/>
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="new2">Verify New Password:</label>
|
|
<input type="password" t-attf-class="form-control form-control-sm {{ 'is-invalid' if get_error(errors, 'password.new2') else '' }}"
|
|
id="new2" name="new2"
|
|
autocomplete="new-password" required="required"/>
|
|
<div class="invalid-feedback">
|
|
<t t-esc="get_error(errors, 'password.new2')"/>
|
|
</div>
|
|
</div>
|
|
<button type="submit" class="btn btn-secondary">Change Password</button>
|
|
</form>
|
|
</section>
|
|
<section t-if="debug and allow_api_keys">
|
|
<h3>
|
|
Developer API Keys
|
|
<a href="https://www.odoo.com/documentation/16.0/developer/misc/api/external_api.html#api-keys" target="_blank">
|
|
<i title="Documentation" class="fa fa-fw o_button_icon fa-info-circle"></i>
|
|
</a>
|
|
</h3>
|
|
<div>
|
|
<table class="table o_main_table">
|
|
<thead>
|
|
<tr>
|
|
<th>Description</th>
|
|
<th>Scope</th>
|
|
<th>Added On</th>
|
|
<th/>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<t t-foreach="request.env.user.api_key_ids" t-as="key">
|
|
<tr>
|
|
<td><span t-field="key.name"/></td>
|
|
<td><span t-field="key.scope"/></td>
|
|
<td><span t-field="key.create_date"/></td>
|
|
<td>
|
|
<i class="fa fa-trash text-danger o_portal_remove_api_key" type="button" t-att-id="key.id"/>
|
|
</td>
|
|
</tr>
|
|
</t>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
<div>
|
|
<button type="submit" class="btn btn-secondary o_portal_new_api_key">New API Key</button>
|
|
</div>
|
|
</section>
|
|
<section name="portal_deactivate_account" groups="base.group_portal">
|
|
<h3>Delete Account</h3>
|
|
<t t-set="deactivate_error" t-value="get_error(errors, 'deactivate')"/>
|
|
<button class="btn btn-secondary" data-bs-toggle="modal"
|
|
data-bs-target="#portal_deactivate_account_modal">
|
|
Delete Account
|
|
</button>
|
|
<div t-attf-class="modal #{'show d-block' if open_deactivate_modal else ''}"
|
|
id="portal_deactivate_account_modal" tabindex="-1" role="dialog">
|
|
<div class="modal-dialog" role="document">
|
|
<div class="modal-content">
|
|
<div class="modal-header bg-danger">
|
|
<h5 class="modal-title">Are you sure you want to do this?</h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<form action="/my/deactivate_account" method="post" class="modal-body"
|
|
id="portal_deactivate_account_form">
|
|
<div>
|
|
<div class="alert alert-danger"
|
|
t-esc="get_error(errors, 'deactivate.other')"/>
|
|
<p class="text-muted">
|
|
Disable your account, preventing any further login.<br/>
|
|
<b>
|
|
<i class="fa fa-exclamation-triangle text-danger"></i>
|
|
This action cannot be undone.
|
|
</b>
|
|
</p>
|
|
<hr/>
|
|
<p>1. Enter your password to confirm you own this account</p>
|
|
<input name="password" type="password" required="1"
|
|
t-attf-class="form-control #{'is-invalid' if deactivate_error == 'password' else ''}"
|
|
placeholder="Password"/>
|
|
<div t-if="deactivate_error == 'password'" class="invalid-feedback">
|
|
Wrong password.
|
|
</div>
|
|
<hr/>
|
|
<p>
|
|
2. Confirm you want to delete your account by
|
|
copying down your login (<t t-esc="env.user.login"/>).
|
|
</p>
|
|
<input name="validation" type="text" required="1"
|
|
t-attf-class="form-control #{'is-invalid' if deactivate_error == 'validation' else ''}"/>
|
|
<div t-if="deactivate_error == 'validation'" class="invalid-feedback">
|
|
You should enter "<t t-esc="env.user.login"/>" to validate your action.
|
|
</div>
|
|
<div class="d-flex flex-row align-items-center">
|
|
<input type="checkbox" name="request_blacklist" id="request_blacklist" checked="1"/>
|
|
<label for="request_blacklist" class="ms-2 mw-100 fw-normal mt-3">
|
|
Put my email and phone in a block list to make sure I'm never contacted again
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
|
|
</form>
|
|
<div class="modal-footer justify-content-start">
|
|
<input type="submit" class="btn btn-danger" form="portal_deactivate_account_form"
|
|
value="Delete Account"/>
|
|
<button type="button" class="btn" data-bs-dismiss="modal">
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div></t>
|
|
</template>
|
|
|
|
<template id="record_pager" name="Portal Record Pager">
|
|
<t t-if='prev_record or next_record'>
|
|
<div class="record_pager btn-group" role="group">
|
|
<a role="button" t-att-class="'btn btn-link %s' % ('disabled' if not prev_record else '')" t-att-href="prev_record or '#'" ><i class="fa fa-chevron-left" role="img" aria-label="Previous" title="Previous"></i></a>
|
|
<a role="button" t-att-class="'btn btn-link %s' % ('disabled' if not next_record else '')" t-att-href="next_record or '#'" ><i class="fa fa-chevron-right" role="img" aria-label="Next" title="Next"></i></a>
|
|
</div>
|
|
</t>
|
|
</template>
|
|
|
|
<template id="pager" name="Pager">
|
|
<ul t-if="pager['page_count'] > 1" t-attf-class="#{ classname or '' } pagination m-0 #{_classes}" t-att-style="style or None">
|
|
<li t-attf-class="page-item #{'disabled' if pager['page']['num'] == 1 else ''}">
|
|
<a t-att-href=" pager['page_previous']['url'] if pager['page']['num'] != 1 else None" t-attf-class="page-link #{extraLinkClass}">
|
|
<span class="fa fa-chevron-left" role="img" aria-label="Previous" title="Previous"/>
|
|
</a>
|
|
</li>
|
|
<t t-foreach="pager['pages']" t-as="page">
|
|
<li t-attf-class="page-item #{'active' if page['num'] == pager['page']['num'] else ''}"> <a t-att-href="page['url']" t-attf-class="page-link #{extraLinkClass}" t-out="page['num']"/></li>
|
|
</t>
|
|
<li t-attf-class="page-item #{'disabled' if pager['page']['num'] == pager['page_count'] else ''}">
|
|
<a t-att-href="pager['page_next']['url'] if pager['page']['num'] != pager['page_count'] else None" t-attf-class="page-link #{extraLinkClass}">
|
|
<span class="fa fa-chevron-right" role="img" aria-label="Next" title="Next"/>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
</template>
|
|
|
|
<template id="my_account_link" name="Link to frontend portal" inherit_id="portal.user_dropdown">
|
|
<xpath expr="//*[@id='o_logout_divider']" position="before">
|
|
<a href="/my/home" role="menuitem" class="dropdown-item ps-3">
|
|
<i class="fa fa-fw fa-id-card-o me-1 small text-muted"/> My Account
|
|
</a>
|
|
</xpath>
|
|
</template>
|
|
|
|
<!--
|
|
Generic chatter template for the frontend
|
|
This template provide the container of the chatter. The rest is done in js.
|
|
To use this template, you need to call it after setting the following variable in your template or in your controller:
|
|
:object browserecord : the mail_thread object
|
|
:message_per_page int (optional): number of message per chatter page
|
|
:token string (optional): if you want your chatter to be available for non-logged user,
|
|
you can use a token to verify the identity of the user;
|
|
the message will be posted with the identity of the partner_id of the object
|
|
:hash : signed token with the partner_id using `_sign_token` method (on mail.thread)
|
|
:pid : identifier of the partner signing the token
|
|
|
|
NOTE: for standard portal flows, _get_page_view_values should be used to properly setup the template parameters
|
|
-->
|
|
<template id="message_thread">
|
|
<div id="discussion" data-anchor="true"
|
|
class="d-print-none o_portal_chatter o_not_editable p-0"
|
|
t-att-data-token="token"
|
|
t-att-data-res_model="object._name"
|
|
t-att-data-pid="pid"
|
|
t-att-data-hash="hash"
|
|
t-att-data-res_id="object.id"
|
|
t-att-data-pager_step="message_per_page or 10"
|
|
t-att-data-allow_composer="'0' if disable_composer else '1'"
|
|
t-att-data-two_columns="'true' if two_columns else 'false'">
|
|
</div>
|
|
</template>
|
|
|
|
<!--
|
|
Snippet to request user signature in the portal. The feature comes with
|
|
the JS file `portal_signature.js`.
|
|
|
|
The following variable has to be set:
|
|
- {string} call_url: url where to send the name and signature by RPC
|
|
The url should contain a query string if additional parameters
|
|
have to be sent, such as an access token.
|
|
|
|
The following variables are optional:
|
|
- {string} default_name: the default name to display
|
|
- {string} mode: 'draw', 'auto', or 'load'
|
|
- {string} send_label: label of the send button
|
|
- {number} signature_ratio: ratio of the signature area
|
|
- {string} signature_type: 'signature' or 'initial'
|
|
|
|
For the default values and more information, see init() of the widgets
|
|
SignatureForm and NameAndSignature.
|
|
-->
|
|
<template id="portal.signature_form" name="Ask Signature">
|
|
<div class="o_portal_signature_form"
|
|
t-att-data-call-url="call_url"
|
|
t-att-data-default-name="default_name"
|
|
t-att-data-mode="mode"
|
|
t-att-data-send-label="send_label"
|
|
t-att-data-signature-ratio="signature_ratio"
|
|
t-att-data-signature-type="signature_type"
|
|
t-att-data-font-color="font_color"
|
|
/>
|
|
</template>
|
|
|
|
<template id="portal_sidebar" name="Sidebar">
|
|
<t t-call="portal.portal_layout">
|
|
<body data-bs-spy="scroll" data-target=".navspy" data-offset="50">
|
|
<div class="container o_portal_sidebar"></div>
|
|
<div class="oe_structure mb32" id="oe_structure_portal_sidebar_1"/>
|
|
</body>
|
|
</t>
|
|
</template>
|
|
</odoo>
|