portal_templates.xml 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <odoo>
  3. <template id="frontend_layout" name="Main Frontend Layout" inherit_id="web.frontend_layout">
  4. <xpath expr="//div[@id='wrapwrap']" position="attributes">
  5. <attribute name="t-attf-class" add="#{request.env['res.lang']._lang_get_direction(request.env.lang) == 'rtl' and 'o_rtl' or ''}" separator=" "/>
  6. <attribute name="t-attf-class" add="#{'o_portal' if is_portal else ''}" separator=" "/>
  7. </xpath>
  8. <xpath expr="//div[@id='wrapwrap']/header/img" position="replace">
  9. <t t-cache="res_company">
  10. <nav class="navbar navbar-expand navbar-light bg-light">
  11. <div class="container">
  12. <a href="/" class="navbar-brand logo">
  13. <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"/>
  14. </a>
  15. <ul id="top_menu" class="nav navbar-nav ms-auto">
  16. <t t-call="portal.placeholder_user_sign_in">
  17. <t t-set="_item_class" t-value="'nav-item'"/>
  18. <t t-set="_link_class" t-value="'nav-link'"/>
  19. </t>
  20. <t t-call="portal.user_dropdown">
  21. <t t-set="_user_name" t-value="true"/>
  22. <t t-set="_item_class" t-value="'nav-item dropdown'"/>
  23. <t t-set="_link_class" t-value="'nav-link'"/>
  24. <t t-set="_dropdown_menu_class" t-value="'dropdown-menu-end'"/>
  25. </t>
  26. </ul>
  27. </div>
  28. </nav>
  29. </t>
  30. </xpath>
  31. <xpath expr="//div[@id='wrapwrap']/main/t[@t-out='0']" position="before">
  32. <div t-if="o_portal_fullwidth_alert" class="alert alert-info alert-dismissible rounded-0 fade show d-print-none css_editable_mode_hidden">
  33. <div class="container">
  34. <t t-out="o_portal_fullwidth_alert"/>
  35. </div>
  36. </div>
  37. </xpath>
  38. </template>
  39. <!-- Added by another template so that it can be disabled if needed -->
  40. <template id="footer_language_selector" inherit_id="portal.frontend_layout" name="Footer Language Selector">
  41. <xpath expr="//*[hasclass('o_footer_copyright_name')]" position="after">
  42. <t id="language_selector_call" t-call="portal.language_selector">
  43. <t t-set="_div_classes" t-value="(_div_classes or '') + ' dropup'"/>
  44. </t>
  45. </xpath>
  46. </template>
  47. <template id="language_selector" name="Language Selector">
  48. <t t-nocache="The query strings can change for the same page and the same rendering."
  49. t-nocache-no_text="no_text"
  50. t-nocache-_div_classes="_div_classes"
  51. t-nocache-_btn_class="_btn_class"
  52. t-nocache-_dropdown_menu_class="_dropdown_menu_class">
  53. <t t-set="active_lang" t-value="list(filter(lambda lg : lg[0] == lang, languages))[0]"/>
  54. <t t-set="language_selector_visible" t-value="len(languages) &gt; 1"/>
  55. <div t-attf-class="js_language_selector #{_div_classes} d-print-none" t-if="language_selector_visible">
  56. <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">
  57. <span t-if="not no_text"
  58. class="align-middle"
  59. t-esc="active_lang[2].split('/').pop()"/>
  60. </button>
  61. <div t-attf-class="dropdown-menu #{_dropdown_menu_class}" role="menu">
  62. <t t-foreach="languages" t-as="lg">
  63. <a t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang_code=lg[0])"
  64. t-attf-class="dropdown-item js_change_lang #{active_lang == lg and 'active'}"
  65. t-att-data-url_code="lg[1]">
  66. <span t-if="not no_text" t-esc="lg[2].split('/').pop()"/>
  67. </a>
  68. </t>
  69. </div>
  70. </div>
  71. </t>
  72. </template>
  73. <template id="user_dropdown" name="Portal User Dropdown">
  74. <t t-nocache="Each user is different regardless of the page visited."
  75. t-nocache-_avatar="_avatar"
  76. t-nocache-_icon="_icon"
  77. t-nocache-_icon_class="_icon_class"
  78. t-nocache-_user_name="_user_name"
  79. t-nocache-_user_name_class="_user_name_class"
  80. t-nocache-_item_class="_item_class"
  81. t-nocache-_link_class="_link_class"
  82. t-nocache-_dropdown_menu_class="_dropdown_menu_class">
  83. <t t-set="is_connected" t-value="not user_id._is_public()"/>
  84. <li t-if="is_connected" t-attf-class="#{_item_class} o_no_autohide_item">
  85. <a href="#" role="button" data-bs-toggle="dropdown" t-attf-class="dropdown-toggle #{_link_class}">
  86. <t t-if="_avatar">
  87. <t t-set="avatar_source" t-value="image_data_uri(user_id.avatar_256)"/>
  88. <img t-att-src="avatar_source" t-attf-class="rounded-circle o_object_fit_cover #{_avatar_class}" width="24" height="24" alt="" loading="eager"/>
  89. </t>
  90. <i t-if="_icon" t-attf-class="fa fa-1x fa-fw fa-user-circle-o #{_icon_class}"/>
  91. <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) &gt; 25 else user_id.name"/>
  92. </a>
  93. <div t-attf-class="dropdown-menu js_usermenu #{_dropdown_menu_class}" role="menu">
  94. <a groups="base.group_user" href="/web" role="menuitem" class="dropdown-item ps-3" id="o_backend_user_dropdown_link">
  95. <i class="fa fa-fw fa-th me-1 small text-muted"/> Apps
  96. </a>
  97. <div id="o_logout_divider" class="dropdown-divider"/>
  98. <a t-attf-href="/web/session/logout?redirect=/" role="menuitem" id="o_logout" class="dropdown-item ps-3">
  99. <i class="fa fa-fw fa-sign-out me-1 small text-muted"/> Logout
  100. </a>
  101. </div>
  102. </li>
  103. </t>
  104. </template>
  105. <template id="portal_breadcrumbs" name="Portal Breadcrumbs">
  106. <ol t-if="page_name != 'home'" class="o_portal_submenu breadcrumb mb-0 py-2 flex-grow-1">
  107. <li class="breadcrumb-item ms-1"><a href="/my/home" aria-label="Home" title="Home"><i class="fa fa-home"/></a></li>
  108. <li t-if="page_name == 'my_details'" class="breadcrumb-item">Details</li>
  109. </ol>
  110. </template>
  111. <template id="portal_back_in_edit_mode" name="Back to edit mode">
  112. <div t-ignore="true" class="text-center">
  113. <t t-if="custom_html" t-out="custom_html"/>
  114. <t t-else="">This is a preview of the customer portal.</t>
  115. <a t-att-href="backend_url"><i class="fa fa-arrow-right me-1"/>Back to edit mode</a>
  116. </div>
  117. <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
  118. </template>
  119. <template id="portal_layout" name="Portal Layout">
  120. <t t-call="portal.frontend_layout">
  121. <t t-set="is_portal" t-value="True"/>
  122. <div t-if="not no_breadcrumbs and not my_details and not breadcrumbs_searchbar" class="o_portal container mt-3">
  123. <div class="row align-items-center bg-white g-0 border rounded">
  124. <div class="col-10">
  125. <t t-call="portal.portal_breadcrumbs"></t>
  126. </div>
  127. <div t-if="prev_record or next_record" class="col-2 flex-grow-0 text-center">
  128. <t t-call='portal.record_pager'/>
  129. </div>
  130. </div>
  131. </div>
  132. <div id="wrap" class='o_portal_wrap'>
  133. <div class="container mb64">
  134. <t t-if="my_details">
  135. <div class="row justify-content-between mt-4">
  136. <div t-attf-class="col-12 col-md col-lg-6">
  137. <t t-out="0"/>
  138. </div>
  139. <div id="o_my_sidebar" class="pt-3 pt-lg-0 col-12 col-md col-lg-4 col-xl-3 o_my_sidebar">
  140. <div class="o_my_contact" t-if="sales_user">
  141. <t t-call="portal.portal_contact"/>
  142. </div>
  143. <div class="o_portal_my_details">
  144. <h4>Details <a role="button" href="/my/account" class="btn btn-sm btn-link"><i class="fa fa-pencil"/> Edit</a></h4>
  145. <hr class="mt-1 mb-0"/>
  146. <div t-field="user_id.partner_id" t-options='{"widget": "contact", "fields": ["email", "phone", "address", "name"]}'/>
  147. </div>
  148. <div class="o_portal_my_security mt-3">
  149. <h4>Account Security </h4>
  150. <hr class="mt-1 mb-1"/>
  151. <a href="/my/security"><i class="fa fa-pencil mx-1"/>Edit Security Settings</a>
  152. </div>
  153. </div>
  154. </div>
  155. </t>
  156. <t t-else="">
  157. <t t-out="0"/>
  158. </t>
  159. </div>
  160. </div>
  161. </t>
  162. </template>
  163. <template id="placeholder_user_sign_in" name="User Sign In Placeholder"/>
  164. <template id="user_sign_in" name="User Sign In" inherit_id="portal.placeholder_user_sign_in">
  165. <xpath expr="." position="inside">
  166. <li t-nocache="Profile session and user group can change unrelated to parent caches."
  167. t-nocache-_item_class="_item_class"
  168. t-nocache-_link_class="_link_class"
  169. groups="base.group_public" t-attf-class="#{_item_class} o_no_autohide_item">
  170. <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>
  171. </li>
  172. </xpath>
  173. </template>
  174. <template id="portal_my_home" name="My Portal">
  175. <t t-call="portal.portal_layout">
  176. <t t-set="my_details" t-value="True"/>
  177. <div class="o_portal_my_home">
  178. <div class="oe_structure" id="oe_structure_portal_my_home_1"/>
  179. <h3>Documents</h3>
  180. <div class="o_portal_docs list-group">
  181. <div class="o_portal_doc_spinner spinner-border text-o-color-2 align-self-center mt-5"/>
  182. <p class="o_portal_no_doc_message d-none">No Documents to display</p>
  183. </div>
  184. </div>
  185. <div class="oe_structure" id="oe_structure_portal_my_home_2"/>
  186. </t>
  187. </template>
  188. <template id="portal_docs_entry" name="My Portal Docs Entry">
  189. <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">
  190. <t t-esc="title"/>
  191. <t t-if='count'>
  192. <span class="badge text-bg-secondary rounded-pill" t-esc="count"/>
  193. </t>
  194. <t t-elif="placeholder_count">
  195. <span class="badge text-bg-secondary rounded-pill" t-att-data-placeholder_count="placeholder_count">
  196. <i class="fa fa-spin fa-circle-o-notch"></i>
  197. </span>
  198. </t>
  199. </a>
  200. </template>
  201. <template id="portal_table" name="My Portal Table">
  202. <div t-attf-class="table-responsive border rounded border-top-0 #{classes if classes else ''}">
  203. <table class="table rounded mb-0 bg-white o_portal_my_doc_table">
  204. <t t-out="0"/>
  205. </table>
  206. </div>
  207. <div t-if="pager" class="o_portal_pager d-flex justify-content-center">
  208. <t t-call="portal.pager"/>
  209. </div>
  210. </template>
  211. <template id="portal_record_sidebar" name="My Portal Record Sidebar">
  212. <div t-attf-class="#{classes}">
  213. <div class="card bg-white mb-4 sticky-top" id="sidebar_content">
  214. <div t-if="title" class="card-body text-center pb-2 pt-3">
  215. <t t-out="title"/>
  216. </div>
  217. <t t-if="entries" t-out="entries"/>
  218. <div class="card-footer small text-center text-muted border-top-0 pt-1 pb-1 d-none d-lg-block">
  219. Powered by <a target="_blank" href="http://www.odoo.com?utm_source=db&amp;utm_medium=portal" title="odoo"><img src="/web/static/img/logo.png" alt="Odoo Logo" height="15"/></a>
  220. </div>
  221. </div>
  222. </div>
  223. </template>
  224. <!--
  225. The search bar is composed of 2 buttons : a "filter by" and a "sort by". Changing the 'sortby'
  226. criteria will keep the number of page, query params, ... Changing the 'filterby' param will
  227. redirect the user to the beginning of document list, keeping query parameters.
  228. These 2 buttons can be prepended by a advanced search input, to activate it, search_input need
  229. to be initialized at 'True' and the content of the t-call is the list of li elements searchable.
  230. :param dict searchbar_sortings : containing the sort criteria like
  231. {'date': {'label': _('Newest'), 'order': 'create_date desc'}}
  232. :param string sortby : name of the sort criteria
  233. :param dict searchbar_filters : containing the filter criteria like
  234. {'open': {'label': _('In Progress'), 'domain': [('state', '=', 'open')]}}
  235. :param string filterby : name of the filter criteria
  236. :param default_url : the base url of the pages (like '/my/orders')
  237. :param boolean breadcrumbs_searchbar : set to True to show breadcrumbs rather than the title
  238. :param boolean o_portal_search_panel : set to True to active the input search
  239. :param html $0 : content of the t-call
  240. :param title : bavbar title
  241. :param classes : navbar classes
  242. -->
  243. <template id="portal_searchbar" name="Portal Search Bar">
  244. <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' }}">
  245. <!-- Navbar breadcrumb or title -->
  246. <t t-if="breadcrumbs_searchbar">
  247. <t t-call="portal.portal_breadcrumbs"/>
  248. </t>
  249. <span t-else="" class="navbar-brand mb-0 h1 me-auto" t-esc="title or 'No title'"/>
  250. <!-- Collapse button -->
  251. <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">
  252. <span class="navbar-toggler-icon small"/>
  253. </button>
  254. <!-- Collapsable content -->
  255. <div class="collapse navbar-collapse" id="o_portal_navbar_content">
  256. <div class="nav flex-column flex-lg-row ms-auto p-0 mb-3 mb-lg-0 mt-1 mt-lg-0">
  257. <div t-if="searchbar_sortings">
  258. <span class="small me-1 navbar-text">Sort By:</span>
  259. <div class="btn-group">
  260. <button id="portal_searchbar_sortby" data-bs-toggle="dropdown" class="btn btn-secondary btn-sm dropdown-toggle">
  261. <t t-esc="searchbar_sortings[sortby].get('label', 'Newest')"/>
  262. </button>
  263. <div class="dropdown-menu" aria-labelledby="portal_searchbar_sortby">
  264. <t t-foreach="searchbar_sortings" t-as="option">
  265. <a t-att-href="request.httprequest.path + '?' + keep_query('*', sortby=option)"
  266. t-attf-class="dropdown-item#{sortby == option and ' active' or ''}">
  267. <span t-esc="searchbar_sortings[option].get('label')"/>
  268. </a>
  269. </t>
  270. </div>
  271. </div>
  272. </div>
  273. <div t-if="searchbar_filters" class="ms-lg-2">
  274. <span class="small me-1 navbar-text">Filter By:</span>
  275. <div class="btn-group">
  276. <button id="portal_searchbar_filters" data-bs-toggle="dropdown" class="btn btn-secondary btn-sm dropdown-toggle">
  277. <t t-esc="searchbar_filters.get(filterby,searchbar_filters.get('all')).get('label', 'All')"/>
  278. </button>
  279. <div class="dropdown-menu" aria-labelledby="portal_searchbar_filters">
  280. <t t-foreach="searchbar_filters" t-as="option">
  281. <a t-att-href="default_url + '?' + keep_query('*', filterby=option)"
  282. t-attf-class="dropdown-item#{filterby == option and ' active' or ''}">
  283. <span t-esc="searchbar_filters[option].get('label')"/>
  284. </a>
  285. </t>
  286. </div>
  287. </div>
  288. </div>
  289. <div t-if="searchbar_groupby" class="ms-lg-2">
  290. <span class="small me-1 navbar-text">Group By:</span>
  291. <div class="btn-group">
  292. <button id="portal_searchbar_groupby" data-bs-toggle="dropdown" class="btn btn-secondary btn-sm dropdown-toggle">
  293. <t t-esc="searchbar_groupby[groupby].get('label', 'None')"/>
  294. </button>
  295. <div class="dropdown-menu" aria-labelledby="portal_searchbar_groupby">
  296. <t t-foreach="searchbar_groupby" t-as="option">
  297. <a t-att-href="default_url + '?' + keep_query('*', groupby=option)"
  298. t-attf-class="dropdown-item#{groupby == option and ' active' or ''}">
  299. <span t-esc="searchbar_groupby[option].get('label')"/>
  300. </a>
  301. </t>
  302. </div>
  303. </div>
  304. </div>
  305. <t t-out="0"/>
  306. </div>
  307. <form t-if="searchbar_inputs" class="o_portal_search_panel ms-lg-4 col-xl-4 col-md-5">
  308. <div class="input-group input-group-sm w-100">
  309. <button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown"/>
  310. <div class="dropdown-menu" role="menu">
  311. <t t-foreach='searchbar_inputs' t-as='input'>
  312. <a t-att-href="'#' + input_value['input']"
  313. t-attf-class="dropdown-item#{search_in == input_value['input'] and ' active' or ''}">
  314. <span t-out="input_value['label']"/>
  315. </a>
  316. </t>
  317. </div>
  318. <input type="text" class="form-control form-control-sm" placeholder="Search" t-att-value='search' name="search"/>
  319. <button class="btn btn-secondary o_wait_lazy_js" type="submit">
  320. <span class="fa fa-search"/>
  321. </button>
  322. </div>
  323. </form>
  324. </div>
  325. </nav>
  326. </template>
  327. <template id="portal_record_layout" name="Portal single record layout">
  328. <div t-attf-class="card mt-0 border-top-0 rounded-0 rounded-bottom #{classes if classes else ''}">
  329. <div t-if="card_header" t-attf-class="card-header #{header_classes if header_classes else ''}">
  330. <t t-out="card_header"/>
  331. </div>
  332. <div t-if="card_body" t-attf-class="card-body #{body_classes if body_classes else ''}">
  333. <t t-out="card_body"/>
  334. </div>
  335. </div>
  336. </template>
  337. <!--
  338. TODO adapt in master: remove the use of the "title" variable in this
  339. template (or rename the variable). Using it at controller level actually
  340. also controls the browser tab title, which makes no sense.
  341. -->
  342. <template id="portal_contact" name="Contact">
  343. <div class="o_portal_contact_details mb-5">
  344. <h4><t t-if="title" t-esc="title"/><t t-else="">Your contact</t></h4>
  345. <hr class="mt-1 mb0"/>
  346. <h6 class="mb-1"><b t-esc="sales_user.name"/></h6>
  347. <div class="d-flex align-items-center mb-1">
  348. <div class="fa fa-envelope fa-fw me-1"></div>
  349. <a t-att-href="'mailto:'+sales_user.email" t-esc="sales_user.email"/>
  350. </div>
  351. <div class="d-flex flex-nowrap align-items-center mb-1">
  352. <div class="fa fa-phone fa-fw me-1"></div>
  353. <span t-esc="sales_user.phone"/>
  354. </div>
  355. <div class="d-flex flex-nowrap align-items-center mb-1">
  356. <div class="fa fa-map-marker fa-fw me-1"></div>
  357. <span t-esc="sales_user.city"/>
  358. </div>
  359. </div>
  360. </template>
  361. <template id="portal_my_details_fields">
  362. <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
  363. <div t-if="error_message" class="alert alert-danger" role="alert">
  364. <div class="col-lg-12">
  365. <t t-foreach="error_message" t-as="err"><t t-esc="err"/><br /></t>
  366. </div>
  367. </div>
  368. <div t-attf-class="mb-3 #{error.get('name') and 'o_has_error' or ''} col-xl-6">
  369. <label class="col-form-label" for="name">Name</label>
  370. <input type="text" name="name" t-attf-class="form-control #{error.get('name') and 'is-invalid' or ''}" t-att-value="name or partner.name" />
  371. </div>
  372. <div t-attf-class="mb-3 #{error.get('email') and 'o_has_error' or ''} col-xl-6">
  373. <label class="col-form-label" for="email">Email</label>
  374. <input type="email" name="email" t-attf-class="form-control #{error.get('email') and 'is-invalid' or ''}" t-att-value="email or partner.email" />
  375. </div>
  376. <div class="clearfix" />
  377. <div t-attf-class="mb-1 #{error.get('company_name') and 'o_has_error' or ''} col-xl-6">
  378. <label class="col-form-label label-optional" for="company_name">Company Name</label>
  379. <!-- The <input> use "disabled" attribute to avoid sending an unauthorized value on form submit.
  380. The user might not have rights to change company_name but should still be able to see it.
  381. -->
  382. <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'" />
  383. <small t-if="not partner_can_edit_vat" class="form-text text-muted d-block d-xl-none">
  384. Changing company name is not allowed once document(s) have been issued for your account. Please contact us directly for this operation.
  385. </small>
  386. </div>
  387. <div t-attf-class="mb-1 #{error.get('vat') and 'o_has_error' or ''} col-xl-6">
  388. <label class="col-form-label label-optional" for="vat">VAT Number</label>
  389. <!-- The <input> use "disabled" attribute to avoid sending an unauthorized value on form submit.
  390. The user might not have rights to change company_name but should still be able to see it.
  391. -->
  392. <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'" />
  393. <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>
  394. </div>
  395. <div t-if="not partner_can_edit_vat" class="col-12 d-none d-xl-block">
  396. <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>
  397. </div>
  398. <div t-attf-class="mb-3 #{error.get('phone') and 'o_has_error' or ''} col-xl-6">
  399. <label class="col-form-label" for="phone">Phone</label>
  400. <input type="tel" name="phone" t-attf-class="form-control #{error.get('phone') and 'is-invalid' or ''}" t-att-value="phone or partner.phone" />
  401. </div>
  402. <div class="clearfix" />
  403. <div t-attf-class="mb-3 #{error.get('street') and 'o_has_error' or ''} col-xl-6">
  404. <label class="col-form-label" for="street">Street</label>
  405. <input type="text" name="street" t-attf-class="form-control #{error.get('street') and 'is-invalid' or ''}" t-att-value="street or partner.street"/>
  406. </div>
  407. <div t-attf-class="mb-3 #{error.get('city') and 'o_has_error' or ''} col-xl-6">
  408. <label class="col-form-label" for="city">City</label>
  409. <input type="text" name="city" t-attf-class="form-control #{error.get('city') and 'is-invalid' or ''}" t-att-value="city or partner.city" />
  410. </div>
  411. <div t-attf-class="mb-3 #{error.get('zip') and 'o_has_error' or ''} col-xl-6">
  412. <label class="col-form-label label-optional" for="zipcode">Zip / Postal Code</label>
  413. <input type="text" name="zipcode" t-attf-class="form-control #{error.get('zip') and 'is-invalid' or ''}" t-att-value="zipcode or partner.zip" />
  414. </div>
  415. <div t-attf-class="mb-3 #{error.get('country_id') and 'o_has_error' or ''} col-xl-6">
  416. <label class="col-form-label" for="country_id">Country</label>
  417. <select name="country_id" t-attf-class="form-select #{error.get('country_id') and 'is-invalid' or ''}">
  418. <option value="">Country...</option>
  419. <t t-foreach="countries or []" t-as="country">
  420. <option t-att-value="country.id" t-att-selected="country.id == int(country_id) if country_id else country.id == partner.country_id.id">
  421. <t t-esc="country.name" />
  422. </option>
  423. </t>
  424. </select>
  425. </div>
  426. <div t-attf-class="mb-3 #{error.get('state_id') and 'o_has_error' or ''} col-xl-6">
  427. <label class="col-form-label label-optional" for="state_id">State / Province</label>
  428. <select name="state_id" t-attf-class="form-select #{error.get('state_id') and 'is-invalid' or ''}">
  429. <option value="">select...</option>
  430. <t t-foreach="states or []" t-as="state">
  431. <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">
  432. <t t-esc="state.name" />
  433. </option>
  434. </t>
  435. </select>
  436. </div>
  437. </template>
  438. <template id="portal_my_details">
  439. <t t-call="portal.portal_layout">
  440. <t t-set="additional_title">Contact Details</t>
  441. <form action="/my/account" method="post">
  442. <div class="row o_portal_details">
  443. <div class="col-lg-8">
  444. <div class="row">
  445. <t t-call="portal.portal_my_details_fields"/>
  446. <input type="hidden" name="redirect" t-att-value="redirect"/>
  447. </div>
  448. <div class="clearfix">
  449. <button type="submit" class="btn btn-primary float-end mb32 ">
  450. Confirm
  451. <span class="fa fa-long-arrow-right" />
  452. </button>
  453. </div>
  454. </div>
  455. </div>
  456. </form>
  457. </t>
  458. </template>
  459. <template id="portal_my_security">
  460. <t t-call="portal.portal_layout"><div class="o_portal_security_body">
  461. <t t-set="additional_title">Security</t>
  462. <t t-set="no_breadcrumbs" t-value="1"/>
  463. <div class="alert alert-danger" role="alert" t-if="get_error(errors)">
  464. <t t-esc="errors"/>
  465. </div>
  466. <section name="portal_change_password">
  467. <h3>Change Password</h3>
  468. <t t-set="path">password</t>
  469. <div class="alert alert-success" role="alert" t-if="success and success.get('password')">
  470. Password Updated!
  471. </div>
  472. <div class="alert alert-danger" role="alert" t-if="get_error(errors, 'password')">
  473. <t t-esc="errors['password']"/>
  474. </div>
  475. <form action="/my/security" method="post" class="oe_reset_password_form">
  476. <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
  477. <input type="hidden" name="op" value="password"/>
  478. <div class="mb-3">
  479. <label for="current">Password:</label>
  480. <input type="password" t-attf-class="form-control form-control-sm {{ 'is-invalid' if get_error(errors, 'password.old') else '' }}"
  481. id="current" name="old"
  482. autocomplete="current-password" required="required"/>
  483. <div class="invalid-feedback">
  484. <t t-esc="get_error(errors, 'password.old')"/>
  485. </div>
  486. </div>
  487. <div class="mb-3">
  488. <label for="new">New Password:</label>
  489. <input type="password" t-attf-class="form-control form-control-sm {{ 'is-invalid' if get_error(errors, 'password.new1') else '' }}"
  490. id="new" name="new1"
  491. autocomplete="new-password" required="required"/>
  492. <div class="invalid-feedback">
  493. <t t-esc="get_error(errors, 'password.new1')"/>
  494. </div>
  495. </div>
  496. <div class="mb-3">
  497. <label for="new2">Verify New Password:</label>
  498. <input type="password" t-attf-class="form-control form-control-sm {{ 'is-invalid' if get_error(errors, 'password.new2') else '' }}"
  499. id="new2" name="new2"
  500. autocomplete="new-password" required="required"/>
  501. <div class="invalid-feedback">
  502. <t t-esc="get_error(errors, 'password.new2')"/>
  503. </div>
  504. </div>
  505. <button type="submit" class="btn btn-secondary">Change Password</button>
  506. </form>
  507. </section>
  508. <section t-if="debug and allow_api_keys">
  509. <h3>
  510. Developer API Keys
  511. <a href="https://www.odoo.com/documentation/16.0/developer/misc/api/external_api.html#api-keys" target="_blank">
  512. <i title="Documentation" class="fa fa-fw o_button_icon fa-info-circle"></i>
  513. </a>
  514. </h3>
  515. <div>
  516. <table class="table o_main_table">
  517. <thead>
  518. <tr>
  519. <th>Description</th>
  520. <th>Scope</th>
  521. <th>Added On</th>
  522. <th/>
  523. </tr>
  524. </thead>
  525. <tbody>
  526. <t t-foreach="request.env.user.api_key_ids" t-as="key">
  527. <tr>
  528. <td><span t-field="key.name"/></td>
  529. <td><span t-field="key.scope"/></td>
  530. <td><span t-field="key.create_date"/></td>
  531. <td>
  532. <i class="fa fa-trash text-danger o_portal_remove_api_key" type="button" t-att-id="key.id"/>
  533. </td>
  534. </tr>
  535. </t>
  536. </tbody>
  537. </table>
  538. </div>
  539. <div>
  540. <button type="submit" class="btn btn-secondary o_portal_new_api_key">New API Key</button>
  541. </div>
  542. </section>
  543. <section name="portal_deactivate_account" groups="base.group_portal">
  544. <h3>Delete Account</h3>
  545. <t t-set="deactivate_error" t-value="get_error(errors, 'deactivate')"/>
  546. <button class="btn btn-secondary" data-bs-toggle="modal"
  547. data-bs-target="#portal_deactivate_account_modal">
  548. Delete Account
  549. </button>
  550. <div t-attf-class="modal #{'show d-block' if open_deactivate_modal else ''}"
  551. id="portal_deactivate_account_modal" tabindex="-1" role="dialog">
  552. <div class="modal-dialog" role="document">
  553. <div class="modal-content">
  554. <div class="modal-header bg-danger">
  555. <h5 class="modal-title">Are you sure you want to do this?</h5>
  556. <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
  557. </div>
  558. <form action="/my/deactivate_account" method="post" class="modal-body"
  559. id="portal_deactivate_account_form">
  560. <div>
  561. <div class="alert alert-danger"
  562. t-esc="get_error(errors, 'deactivate.other')"/>
  563. <p class="text-muted">
  564. Disable your account, preventing any further login.<br/>
  565. <b>
  566. <i class="fa fa-exclamation-triangle text-danger"></i>
  567. This action cannot be undone.
  568. </b>
  569. </p>
  570. <hr/>
  571. <p>1. Enter your password to confirm you own this account</p>
  572. <input name="password" type="password" required="1"
  573. t-attf-class="form-control #{'is-invalid' if deactivate_error == 'password' else ''}"
  574. placeholder="Password"/>
  575. <div t-if="deactivate_error == 'password'" class="invalid-feedback">
  576. Wrong password.
  577. </div>
  578. <hr/>
  579. <p>
  580. 2. Confirm you want to delete your account by
  581. copying down your login (<t t-esc="env.user.login"/>).
  582. </p>
  583. <input name="validation" type="text" required="1"
  584. t-attf-class="form-control #{'is-invalid' if deactivate_error == 'validation' else ''}"/>
  585. <div t-if="deactivate_error == 'validation'" class="invalid-feedback">
  586. You should enter "<t t-esc="env.user.login"/>" to validate your action.
  587. </div>
  588. <div class="d-flex flex-row align-items-center">
  589. <input type="checkbox" name="request_blacklist" id="request_blacklist" checked="1"/>
  590. <label for="request_blacklist" class="ms-2 mw-100 fw-normal mt-3">
  591. Put my email and phone in a block list to make sure I'm never contacted again
  592. </label>
  593. </div>
  594. </div>
  595. <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
  596. </form>
  597. <div class="modal-footer justify-content-start">
  598. <input type="submit" class="btn btn-danger" form="portal_deactivate_account_form"
  599. value="Delete Account"/>
  600. <button type="button" class="btn" data-bs-dismiss="modal">
  601. Cancel
  602. </button>
  603. </div>
  604. </div>
  605. </div>
  606. </div>
  607. </section>
  608. </div></t>
  609. </template>
  610. <template id="record_pager" name="Portal Record Pager">
  611. <t t-if='prev_record or next_record'>
  612. <div class="record_pager btn-group" role="group">
  613. <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>
  614. <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>
  615. </div>
  616. </t>
  617. </template>
  618. <template id="pager" name="Pager">
  619. <ul t-if="pager['page_count'] > 1" t-attf-class="#{ classname or '' } pagination m-0 #{_classes}" t-att-style="style or None">
  620. <li t-attf-class="page-item #{'disabled' if pager['page']['num'] == 1 else ''}">
  621. <a t-att-href=" pager['page_previous']['url'] if pager['page']['num'] != 1 else None" t-attf-class="page-link #{extraLinkClass}">Prev</a>
  622. </li>
  623. <t t-foreach="pager['pages']" t-as="page">
  624. <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>
  625. </t>
  626. <li t-attf-class="page-item #{'disabled' if pager['page']['num'] == pager['page_count'] else ''}">
  627. <a t-att-href="pager['page_next']['url'] if pager['page']['num'] != pager['page_count'] else None" t-attf-class="page-link #{extraLinkClass}">Next</a>
  628. </li>
  629. </ul>
  630. </template>
  631. <template id="my_account_link" name="Link to frontend portal" inherit_id="portal.user_dropdown">
  632. <xpath expr="//*[@id='o_logout_divider']" position="before">
  633. <a href="/my/home" role="menuitem" class="dropdown-item ps-3">
  634. <i class="fa fa-fw fa-id-card-o me-1 small text-muted"/> My Account
  635. </a>
  636. </xpath>
  637. </template>
  638. <!--
  639. Generic chatter template for the frontend
  640. This template provide the container of the chatter. The rest is done in js.
  641. To use this template, you need to call it after setting the following variable in your template or in your controller:
  642. :object browserecord : the mail_thread object
  643. :message_per_page int (optional): number of message per chatter page
  644. :token string (optional): if you want your chatter to be available for non-logged user,
  645. you can use a token to verify the identity of the user;
  646. the message will be posted with the identity of the partner_id of the object
  647. :hash : signed token with the partner_id using `_sign_token` method (on mail.thread)
  648. :pid : identifier of the partner signing the token
  649. NOTE: for standard portal flows, _get_page_view_values should be used to properly setup the template parameters
  650. -->
  651. <template id="message_thread">
  652. <div id="discussion" data-anchor="true"
  653. class="d-print-none o_portal_chatter o_not_editable p-0"
  654. t-att-data-token="token"
  655. t-att-data-res_model="object._name"
  656. t-att-data-pid="pid"
  657. t-att-data-hash="hash"
  658. t-att-data-res_id="object.id"
  659. t-att-data-pager_step="message_per_page or 10"
  660. t-att-data-allow_composer="'0' if disable_composer else '1'"
  661. t-att-data-two_columns="'true' if two_columns else 'false'">
  662. </div>
  663. </template>
  664. <!--
  665. Snippet to request user signature in the portal. The feature comes with
  666. the JS file `portal_signature.js`.
  667. The following variable has to be set:
  668. - {string} call_url: url where to send the name and signature by RPC
  669. The url should contain a query string if additional parameters
  670. have to be sent, such as an access token.
  671. The following variables are optional:
  672. - {string} default_name: the default name to display
  673. - {string} mode: 'draw', 'auto', or 'load'
  674. - {string} send_label: label of the send button
  675. - {number} signature_ratio: ratio of the signature area
  676. - {string} signature_type: 'signature' or 'initial'
  677. For the default values and more information, see init() of the widgets
  678. SignatureForm and NameAndSignature.
  679. -->
  680. <template id="portal.signature_form" name="Ask Signature">
  681. <div class="o_portal_signature_form"
  682. t-att-data-call-url="call_url"
  683. t-att-data-default-name="default_name"
  684. t-att-data-mode="mode"
  685. t-att-data-send-label="send_label"
  686. t-att-data-signature-ratio="signature_ratio"
  687. t-att-data-signature-type="signature_type"
  688. t-att-data-font-color="font_color"
  689. />
  690. </template>
  691. <template id="portal_sidebar" name="Sidebar">
  692. <t t-call="portal.portal_layout">
  693. <body data-bs-spy="scroll" data-target=".navspy" data-offset="50">
  694. <div class="container o_portal_sidebar"></div>
  695. <div class="oe_structure mb32" id="oe_structure_portal_sidebar_1"/>
  696. </body>
  697. </t>
  698. </template>
  699. </odoo>