diff options
40 files changed, 472 insertions, 500 deletions
diff --git a/addons/portal/__init__.py b/addons/portal/__init__.py index dd15b5c32e5..c92af4efd3d 100644 --- a/addons/portal/__init__.py +++ b/addons/portal/__init__.py @@ -2,3 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import controllers +from . import wizard diff --git a/addons/portal/__manifest__.py b/addons/portal/__manifest__.py index 61e0818a929..c57c80433a0 100644 --- a/addons/portal/__manifest__.py +++ b/addons/portal/__manifest__.py @@ -16,8 +16,10 @@ This module contains most code coming from odoo v10 website_portal. Purpose of this module is to allow the display of a customer portal without having a dependency towards website edition and customization capabilities.""", 'data': [ + 'data/portal_data.xml', 'views/assets.xml', 'views/portal_templates.xml', + 'wizard/portal_wizard_views.xml', ], 'depends': ['http_routing', 'mail'], } diff --git a/addons/portal/controllers/__init__.py b/addons/portal/controllers/__init__.py index 903b755e71e..ac871b40c75 100644 --- a/addons/portal/controllers/__init__.py +++ b/addons/portal/controllers/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import web from . import portal diff --git a/addons/portal/controllers/portal.py b/addons/portal/controllers/portal.py index 1fd7c9ee759..8c5d632fdc2 100644 --- a/addons/portal/controllers/portal.py +++ b/addons/portal/controllers/portal.py @@ -5,9 +5,10 @@ import math from werkzeug import urls +from odoo import fields as odoo_fields, tools, _ +from odoo.exceptions import ValidationError from odoo.http import Controller, request, route - # -------------------------------------------------- # Misc tools # -------------------------------------------------- @@ -71,14 +72,131 @@ def pager(url, total, page=1, step=30, scope=5, url_args=None): } +def get_records_pager(ids, current): + if current.id in ids: + idx = ids.index(current.id) + return { + 'prev_record': idx != 0 and current.browse(ids[idx - 1]).website_url, + 'next_record': idx < len(ids) - 1 and current.browse(ids[idx + 1]).website_url + } + return {} + + class CustomerPortal(Controller): + MANDATORY_BILLING_FIELDS = ["name", "phone", "email", "street", "city", "country_id"] + OPTIONAL_BILLING_FIELDS = ["zipcode", "state_id", "vat", "company_name"] + + _items_per_page = 20 + + def _get_archive_groups(self, model, domain=None, fields=None, groupby="create_date", order="create_date desc"): + if not model: + return [] + if domain is None: + domain = [] + if fields is None: + fields = ['name', 'create_date'] + groups = [] + for group in request.env[model]._read_group_raw(domain, fields=fields, groupby=groupby, orderby=order): + dates, label = group[groupby] + date_begin, date_end = dates.split('/') + groups.append({ + 'date_begin': odoo_fields.Date.to_string(odoo_fields.Date.from_string(date_begin)), + 'date_end': odoo_fields.Date.to_string(odoo_fields.Date.from_string(date_end)), + 'name': label, + 'item_count': group[groupby + '_count'] + }) + return groups + def _prepare_portal_layout_values(self): + # get customer sales rep + sales_user = False + partner = request.env.user.partner_id + if partner.user_id and not partner.user_id._is_public(): + sales_user = partner.user_id + return { + 'sales_user': sales_user, 'page_name': 'home', + 'archive_groups': [], } @route(['/my', '/my/home'], type='http', auth="user", website=True) def home(self, **kw): values = self._prepare_portal_layout_values() - return request.render("portal.portal_layout", values) + return request.render("portal.portal_my_home", values) + + @route(['/my/account'], type='http', auth='user', website=True) + def account(self, redirect=None, **post): + partner = request.env.user.partner_id + values = { + 'error': {}, + 'error_message': [] + } + + if post: + error, error_message = self.details_form_validate(post) + values.update({'error': error, 'error_message': error_message}) + values.update(post) + if not error: + values = {key: post[key] for key in self.MANDATORY_BILLING_FIELDS} + values.update({key: post[key] for key in self.OPTIONAL_BILLING_FIELDS if key in post}) + values.update({'zip': values.pop('zipcode', '')}) + partner.sudo().write(values) + if redirect: + return request.redirect(redirect) + return request.redirect('/my/home') + + countries = request.env['res.country'].sudo().search([]) + states = request.env['res.country.state'].sudo().search([]) + + values.update({ + 'partner': partner, + 'countries': countries, + 'states': states, + 'has_check_vat': hasattr(request.env['res.partner'], 'check_vat'), + 'redirect': redirect, + 'page_name': 'my_details', + }) + + response = request.render("portal.portal_my_details", values) + response.headers['X-Frame-Options'] = 'DENY' + return response + + def details_form_validate(self, data): + error = dict() + error_message = [] + + # Validation + for field_name in self.MANDATORY_BILLING_FIELDS: + if not data.get(field_name): + error[field_name] = 'missing' + + # email validation + if data.get('email') and not tools.single_email_re.match(data.get('email')): + error["email"] = 'error' + error_message.append(_('Invalid Email! Please enter a valid email address.')) + + # vat validation + partner = request.env["res.partner"] + if data.get("vat") and hasattr(partner, "check_vat"): + partner_dummy = partner.new({ + 'vat': data['vat'], + 'country_id': (int(data['country_id']) + if data.get('country_id') else False), + }) + try: + partner_dummy.check_vat() + except ValidationError: + error["vat"] = 'error' + + # error message for empty required fields + if [err for err in tools.pycompat.values(error) if err == 'missing']: + error_message.append(_('Some required fields are empty.')) + + unknown = [k for k in data if k not in self.MANDATORY_BILLING_FIELDS + self.OPTIONAL_BILLING_FIELDS] + if unknown: + error['common'] = 'Unknown field' + error_message.append("Unknown field '%s'" % ','.join(unknown)) + + return error, error_message diff --git a/addons/portal/controllers/web.py b/addons/portal/controllers/web.py new file mode 100644 index 00000000000..d5db3a30b7b --- /dev/null +++ b/addons/portal/controllers/web.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import http +from odoo.addons.web.controllers.main import Home +from odoo.http import request + + +class Home(Home): + + @http.route() + def index(self, *args, **kw): + if request.session.uid and not request.env['res.users'].sudo().browse(request.session.uid).has_group('base.group_user'): + return http.local_redirect('/my', query=request.params, keep_hash=True) + return super(Home, self).index(*args, **kw) + + def _login_redirect(self, uid, redirect=None): + if not redirect and not request.env['res.users'].sudo().browse(uid).has_group('base.group_user'): + return '/my' + return super(Home, self)._login_redirect(uid, redirect=redirect) diff --git a/addons/website_portal/data/website_portal_data.xml b/addons/portal/data/portal_data.xml index 3ffcb7693ff..ffe12934227 100644 --- a/addons/website_portal/data/website_portal_data.xml +++ b/addons/portal/data/portal_data.xml @@ -4,7 +4,7 @@ <record id="mail_template_data_portal_welcome" model="mail.template"> <field name="name">Portal: new user</field> - <field name="model_id" ref="website_portal.model_portal_wizard_user"/> + <field name="model_id" ref="portal.model_portal_wizard_user"/> <field name="email_to">${object.partner_id.email}</field> <field name="subject">Your Odoo account at ${user.company_id.name}</field> <field name="lang">${object.partner_id.lang}</field> diff --git a/addons/website_portal/static/src/js/website_portal.js b/addons/portal/static/src/js/portal.js index c969b06f0b7..edc5ae4c2dd 100644 --- a/addons/website_portal/static/src/js/website_portal.js +++ b/addons/portal/static/src/js/portal.js @@ -1,7 +1,8 @@ -odoo.define('website_portal', function(require) { - 'use strict'; - require('website.website'); +odoo.define('portal.portal', function(require) { +'use strict'; + +$(document).ready(function () { if(!$('.o_portal').length) { return $.Deferred().reject("DOM doesn't contain '.o_portal'"); @@ -45,3 +46,4 @@ odoo.define('website_portal', function(require) { }); } }); +}); diff --git a/addons/website_portal/static/src/js/website_portal_tour.js b/addons/portal/static/src/js/portal_tour.js index a3a8beb66be..aa566f8d0d4 100644 --- a/addons/website_portal/static/src/js/website_portal_tour.js +++ b/addons/portal/static/src/js/portal_tour.js @@ -1,13 +1,11 @@ -odoo.define('website_portal.tour', function (require) { +odoo.define('portal.tour', function (require) { 'use strict'; var tour = require("web_tour.tour"); -var base = require("web_editor.base"); tour.register('portal_load_homepage', { test: true, url: '/my', - wait_for: base.ready() }, [ { diff --git a/addons/website_portal/tests/__init__.py b/addons/portal/tests/__init__.py index 279e43d2034..279e43d2034 100644 --- a/addons/website_portal/tests/__init__.py +++ b/addons/portal/tests/__init__.py diff --git a/addons/website_portal/tests/test_load_process.py b/addons/portal/tests/test_load_process.py index 344d8bec7a9..344d8bec7a9 100644 --- a/addons/website_portal/tests/test_load_process.py +++ b/addons/portal/tests/test_load_process.py diff --git a/addons/portal/views/assets.xml b/addons/portal/views/assets.xml index c90c7f3aacc..a40533c7876 100644 --- a/addons/portal/views/assets.xml +++ b/addons/portal/views/assets.xml @@ -1,10 +1,15 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> <template id="assets_frontend" inherit_id="web.assets_frontend" name="Portal Assets"> - <xpath expr="//script[last()]" position="after"> + <xpath expr="//link[last()]" position="after"> <link rel="stylesheet" type="text/less" href="/portal/static/src/less/website.ui.less"/> <link rel="stylesheet" type="text/less" href="/portal/static/src/less/website.wrapwrap.less"/> <link rel="stylesheet" type="text/less" href="/portal/static/src/less/bootstrap.extend.less"/> </xpath> + + <xpath expr="//link[last()]" position="after"> + <script type="text/javascript" src="/portal/static/src/js/portal.js"></script> + <script type="text/javascript" src="/portal/static/src/js/portal_tour.js"></script> + </xpath> </template> </odoo>
\ No newline at end of file diff --git a/addons/portal/views/portal_templates.xml b/addons/portal/views/portal_templates.xml index bd89945c81f..67e027aea48 100644 --- a/addons/portal/views/portal_templates.xml +++ b/addons/portal/views/portal_templates.xml @@ -62,11 +62,258 @@ <template id="portal_layout" name="Portal Layout"> <t t-call="portal.frontend_layout"> + <div class="container mt16 o_portal"> + <div class="oe_structure"> + <div t-if="not no_breadcrumbs" class="row"> + <div class="col-sm-12"> + <ol class="breadcrumb o_portal_submenu"> + <li><a href="/my/home"><i class="fa fa-home"/></a></li> + <li t-if="page_name == 'my_details'">Your Details</li> + </ol> + </div> + </div> + </div> + </div> + <div id="wrap" class='portal-wrap'> + <div class="container mb64"> + <t t-if="my_details"> + <div class="row"> + <div t-attf-class="col-md-9 col-lg-8"> + <t t-raw="0"/> + </div> + <div id="o_my_sidebar" class="col-md-3 col-lg-offset-1 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"> + <h3 class="page-header">Your Details <a href="/my/account" class="btn btn-default btn-xs">Change</a></h3> + <div t-field="user_id.partner_id" t-options='{"widget": "contact", "fields": ["email", "phone", "address", "name"]}'/> + </div> + <div class="o_my_archives" t-if="archive_groups"> + <t t-call="portal.portal_archive_groups"/> + </div> + </div> + </div> + </t> + <t t-else=""> + <t t-raw="0"/> + </t> + </div> + </div> </t> </template> + <template id="portal_show_sign_in" inherit_id="portal.frontend_layout" name="Show Sign In"> + <xpath expr="//ul[@id='top_menu']" position="inside"> + <li class="divider" groups="base.group_public"/> + <li groups="base.group_public"> + <a t-attf-href="/web/login"> + <b>Sign in</b> + </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_my_home_content"> + <div class="oe_structure"></div> + <h3 class="page-header">Your Documents</h3> + <ul class="o_portal_docs list-group"> + </ul> + </div> + <div class="col-md-12 oe_structure"/> + </t> + </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 o_portal_search_panel : set to True to active the input search + :param html $0 : content of the t-call, <li>a</li><li>b</li> + --> + <template id="portal_searchbar" name="Portal Search Bar"> + <t t-if='searchbar_inputs'> + <div class='hidden-xs pull-right o_portal_search_panel'> + <div class="input-group"> + <div class="input-group-btn"> + <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> + <span id="search_label">Search</span> <span class="caret"></span> + </button> + <ul class="dropdown-menu" role="menu"> + <li t-foreach='searchbar_inputs' t-as='input' t-att-class="search_in == searchbar_inputs[input]['input'] and 'active'"> + <a t-att-href="'#' + searchbar_inputs[input]['input']"><span t-raw="searchbar_inputs[input]['label']"/></a> + </li> + </ul> + </div> + <input type="text" class="form-control search_panel_fixed_width" t-att-value='search' name="search"/> + <span class="input-group-btn"> + <button class="btn btn-default search-submit" type="button"><span class="fa fa-search"></span></button> + </span> + </div> + </div> + </t> + <div t-if="searchbar_sortings" class="dropdown pull-right mr4"> + <button id="portal_searchbar_sortby" class="search_panel_fixed_width btn btn-default" type="button" data-toggle="dropdown"> + <span class="fa fa-sort fa-lg" /> + <span class='hidden-xs hidden-sm hidden-md' t-esc="searchbar_sortings[sortby].get('label', 'Newest')"/> + <span class="caret"></span> + </button> + <ul class="dropdown-menu" aria-labelledby="portal_searchbar_sortby"> + <li t-foreach="searchbar_sortings" t-as="option" t-attf-class="#{sortby == option and 'active'}"> + <a t-att-href="request.httprequest.path + '?' + keep_query('*', sortby=option)"> + <span t-esc="searchbar_sortings[option].get('label')"/> + </a> + </li> + </ul> + </div> + <div t-if="searchbar_filters" class="dropdown pull-right mr4"> + <button id="portal_searchbar_filters" class="search_panel_fixed_width btn btn-default" type="button" data-toggle="dropdown"> + <span class="fa fa-filter fa-lg" /> + <span class='hidden-xs hidden-sm hidden-md' t-esc="searchbar_filters[filterby].get('label', 'All')"/> + <span class="caret"></span> + </button> + <ul class="dropdown-menu" aria-labelledby="portal_searchbar_filters"> + <li t-foreach="searchbar_filters" t-as="option" t-att-class="filterby == option and 'active'"> + <a t-att-href="default_url + '?' + keep_query('*', filterby=option)"> + <span t-esc="searchbar_filters[option].get('label')"/> + </a> + </li> + </ul> + </div> + </template> + + <template id="portal_contact" name="Contact"> + <h3 class="page-header"><t t-esc="title"/></h3> + <h5 t-esc="sales_user.name"/> + <p><a t-att-href="'mailto:'+sales_user.email" t-esc="sales_user.email"/></p> + <p t-esc="sales_user.phone"/> + <p t-esc="sales_user.city"/> + </template> + + <template id="portal_archive_groups" name="Portal Archive Groups"> + <h3 class="page-header">Archives</h3> + <ul class="nav nav-pills nav-stacked"> + <t t-foreach="archive_groups" t-as="group"> + <t t-if="group['date_begin'] == date"> + <li class="active"> + <a t-att-href="default_url" t-ignore="True"><t t-esc="group['name']"/><span class="pull-right badge" t-esc="group['item_count']"/></a> + </li> + </t> + <t t-if="group['date_begin'] != date"> + <li> + <a t-ignore="True" t-attf-href="#{default_url}?date_begin=#{group['date_begin']}&date_end=#{group['date_end']}"><t t-esc="group['name']"/><span class="pull-right badge" t-esc="group['item_count']"/></a> + </li> + </t> + </t> + </ul> + </template> + + <template id="portal_my_details"> + <t t-call="portal.portal_layout"> + <t t-set="additional_title">Your Contact Details</t> + <h3>Your Details</h3> + <form action="/my/account" method="post"> + <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/> + <div class="row o_portal_details"> + <div class="col-md-8"> + <div class="row"> + <div class="col-md-12"> + <div t-if="error_message" class="alert alert-danger"> + <t t-foreach="error_message" t-as="err"><t t-esc="err"/><br /></t> + </div> + </div> + <div t-attf-class="form-group #{error.get('name') and 'has-error' or ''} col-lg-6"> + <label class="control-label" for="name">Your Name</label> + <input type="text" name="name" class="form-control" t-att-value="name or partner.name" /> + </div> + <div t-attf-class="form-group #{error.get('email') and 'has-error' or ''} col-lg-6"> + <label class="control-label" for="email">Email</label> + <input type="email" name="email" class="form-control" t-att-value="email or partner.email" /> + </div> + <div t-attf-class="form-group #{error.get('phone') and 'has-error' or ''} col-lg-6"> + <label class="control-label" for="phone">Phone</label> + <input type="tel" name="phone" class="form-control" t-att-value="phone or partner.phone" /> + </div> + + <div class="clearfix" /> + <div t-attf-class="form-group #{error.get('company_name') and 'has-error' or ''} col-lg-6"> + <label class="control-label label-optional" for="company_name">Company Name</label> + <input type="text" name="company_name" class="form-control" t-att-value="company_name or partner.company_name"/> + </div> + <div t-if="has_check_vat" t-attf-class="form-group #{error.get('vat') and 'has-error' or ''} col-lg-6"> + <label class="control-label label-optional" for="vat">VAT Number</label> + <input type="text" name="vat" class="form-control" t-att-value="vat or partner.vat" /> + </div> + + <div class="clearfix" /> + <div t-attf-class="form-group #{error.get('street') and 'has-error' or ''} col-lg-6"> + <label class="control-label label-optional" for="street">Street</label> + <input type="text" name="street" class="form-control" t-att-value="street or partner.street"/> + </div> + <div t-attf-class="form-group #{error.get('city') and 'has-error' or ''} col-lg-6"> + <label class="control-label" for="city">City</label> + <input type="text" name="city" class="form-control" t-att-value="city or partner.city" /> + </div> + <div t-attf-class="form-group #{error.get('zip') and 'has-error' or ''} col-lg-6"> + <label class="control-label" for="zipcode">Zip / Postal Code</label> + <input type="text" name="zipcode" class="form-control" t-att-value="zipcode or partner.zip" /> + </div> + <div t-attf-class="form-group #{error.get('country_id') and 'has-error' or ''} col-lg-6"> + <label class="control-label" for="country_id">Country</label> + <select name="country_id" class="form-control"> + <option value="">Country...</option> + <t t-foreach="countries or []" t-as="country"> + <option t-att-value="country.id" t-att-selected="country.id == partner.country_id.id"> + <t t-esc="country.name" /> + </option> + </t> + </select> + </div> + <div t-attf-class="form-group #{error.get('state_id') and 'has-error' or ''} col-lg-6"> + <label class="control-label label-optional" for="state_id">State / Province</label> + <select name="state_id" class="form-control"> + <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 == partner.state_id.id"> + <t t-esc="state.name" /> + </option> + </t> + </select> + </div> + <input type="hidden" name="redirect" t-att-value="redirect"/> + </div> + <div class="clearfix"> + <button type="submit" class="btn btn-primary pull-right mb32 "> + Confirm + <span class="fa fa-long-arrow-right" /> + </button> + </div> + </div> + </div> + </form> + </t> + </template> + + <template id="record_pager" name="Portal Record Pager"> + <t t-if='prev_record or next_record'> + <div class="ml8 record_pager btn-group btn-group-xs pull-right" role="group"> + <a t-att-class="'btn btn-default %s' % ('disabled' if not prev_record else '')" t-att-href="prev_record or '#'" ><i class="fa fa-arrow-left" aria-hidden="true"></i></a> + <a t-att-class="'btn btn-default %s' % ('disabled' if not next_record else '')" t-att-href="next_record or '#'" ><i class="fa fa-arrow-right" aria-hidden="true"></i></a> + </div> </t> </template> @@ -83,4 +330,10 @@ </li> </ul> </template> + + <template id="my_account_link" name="Link to frontend portal" inherit_id="portal.frontend_layout"> + <xpath expr="//li[@id='o_logout']" position="before"> + <li><a href="/my/home" role="menuitem">My Account</a></li> + </xpath> + </template> </odoo> diff --git a/addons/website_portal/wizard/__init__.py b/addons/portal/wizard/__init__.py index d92478011a5..d92478011a5 100644 --- a/addons/website_portal/wizard/__init__.py +++ b/addons/portal/wizard/__init__.py diff --git a/addons/website_portal/wizard/portal_wizard.py b/addons/portal/wizard/portal_wizard.py index 4bc731c3ff8..0c8206c86f6 100644 --- a/addons/website_portal/wizard/portal_wizard.py +++ b/addons/portal/wizard/portal_wizard.py @@ -182,7 +182,7 @@ class PortalWizardUser(models.TransientModel): raise UserError(_('You must have an email address in your User Preferences to send emails.')) # determine subject and body in the portal user's language - template = self.env.ref('website_portal.mail_template_data_portal_welcome') + template = self.env.ref('portal.mail_template_data_portal_welcome') for wizard_line in self: lang = wizard_line.user_id.lang partner = wizard_line.user_id.partner_id diff --git a/addons/website_portal/wizard/portal_wizard_views.xml b/addons/portal/wizard/portal_wizard_views.xml index b1cb31e63ba..b1cb31e63ba 100644 --- a/addons/website_portal/wizard/portal_wizard_views.xml +++ b/addons/portal/wizard/portal_wizard_views.xml diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index fd2d596a4b2..e2f2d7814a4 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -458,6 +458,9 @@ class Home(http.Controller): ensure_db() return werkzeug.utils.redirect(redirect, 303) + def _login_redirect(self, uid, redirect=None): + return redirect if redirect else '/web' + @http.route('/web/login', type='http', auth="none") def web_login(self, redirect=None, **kw): ensure_db() @@ -479,9 +482,7 @@ class Home(http.Controller): uid = request.session.authenticate(request.session.db, request.params['login'], request.params['password']) if uid is not False: request.params['login_success'] = True - if not redirect: - redirect = '/web' - return http.redirect_with_hash(redirect) + return http.redirect_with_hash(self._login_redirect(uid, redirect=redirect)) request.uid = old_uid values['error'] = _("Wrong login/password") else: diff --git a/addons/website_portal/static/src/less/website_portal.less b/addons/web/static/src/less/portal.less index afc5b17dd08..afc5b17dd08 100644 --- a/addons/website_portal/static/src/less/website_portal.less +++ b/addons/web/static/src/less/portal.less diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 730b4d883e1..c2c3d5a2a4c 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -241,6 +241,8 @@ <t t-call="web.less_helpers"/> <link rel="stylesheet" type="text/less" href="/web/static/src/less/import_bootstrap.less"/> + <link rel='stylesheet' type="text/less" href='/web/static/src/less/portal.less'/> + <script type="text/javascript" src="/web/static/src/js/services/session.js"></script> </template> diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index 1cc552a3208..b46402aba28 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -216,17 +216,6 @@ </xpath> </template> -<template id="show_sign_in" customize_show="True" inherit_id="website.layout" name="Show Sign In"> - <xpath expr="//ul[@id='top_menu']" position="inside"> - <li class="divider" groups="base.group_public"/> - <li groups="base.group_public"> - <a t-attf-href="/web/login"> - <b>Sign in</b> - </a> - </li> - </xpath> -</template> - <template id="footer_custom" inherit_id="website.layout" name="Footer"> <xpath expr="//div[@id='footer']" position="replace"> <div class="oe_structure" id="footer" t-if='not no_footer'> diff --git a/addons/website_account/views/account_templates.xml b/addons/website_account/views/account_templates.xml index dbce386184b..6fe2c7fd6ac 100644 --- a/addons/website_account/views/account_templates.xml +++ b/addons/website_account/views/account_templates.xml @@ -1,5 +1,5 @@ <odoo> - <template id="portal_my_home_menu_invoice" name="Portal layout : invoice menu entries" inherit_id="website_portal.portal_layout" priority="30"> + <template id="portal_my_home_menu_invoice" name="Portal layout : invoice menu entries" inherit_id="portal.portal_layout" priority="30"> <xpath expr="//ol[contains(@class,'o_portal_submenu')]" position="inside"> <li t-if="page_name == 'invoice'" class="active"> Invoices @@ -7,7 +7,7 @@ </xpath> </template> - <template id="portal_my_home_invoice" name="Portal My Home : invoice entries" inherit_id="website_portal.portal_my_home" priority="30"> + <template id="portal_my_home_invoice" name="Portal My Home : invoice entries" inherit_id="portal.portal_my_home" priority="30"> <xpath expr="//ul[contains(@class,'o_portal_docs')]" position="inside"> <li t-if="invoice_count" class="list-group-item"> <span class="badge" t-esc="invoice_count"/> @@ -17,9 +17,9 @@ </template> <template id="portal_my_invoices" name="My Invoices and Payments"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <h3>Invoices - <t t-call="website_portal.portal_searchbar"/> + <t t-call="portal.portal_searchbar"/> </h3> <t t-if="not invoices"> <p>There are currently no invoices and payments for your account.</p> diff --git a/addons/website_crm_partner_assign/__manifest__.py b/addons/website_crm_partner_assign/__manifest__.py index 3bf2b7d2a2b..2065bb14845 100644 --- a/addons/website_crm_partner_assign/__manifest__.py +++ b/addons/website_crm_partner_assign/__manifest__.py @@ -12,7 +12,7 @@ Publish and Assign Partner ========================== """, 'depends': ['base_geolocalize', 'crm', 'account', - 'website_partner', 'website_google_map', 'website_portal'], + 'website_partner', 'website_google_map', 'portal'], 'data': [ 'data/crm_partner_assign_data.xml', 'security/ir.model.access.csv', diff --git a/addons/website_crm_partner_assign/controllers/main.py b/addons/website_crm_partner_assign/controllers/main.py index 9eb27180be9..bb973843018 100644 --- a/addons/website_crm_partner_assign/controllers/main.py +++ b/addons/website_crm_partner_assign/controllers/main.py @@ -11,15 +11,14 @@ from odoo import fields from odoo import http from odoo.http import request from odoo.addons.http_routing.models.ir_http import slug, unslug +from odoo.addons.portal.controllers.portal import CustomerPortal from odoo.addons.website_partner.controllers.main import WebsitePartnerPage from odoo.tools import pycompat from odoo.tools.translate import _ -from odoo.addons.website_portal.controllers.main import website_account - -class WebsiteAccount(website_account): +class WebsiteAccount(CustomerPortal): def get_domain_my_lead(self, user): return [ diff --git a/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml b/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml index 8b92d401113..202f090f331 100644 --- a/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml +++ b/addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml @@ -192,7 +192,7 @@ <script type="text/javascript" src="/website_crm_partner_assign/static/src/js/crm_partner_assign.js" /> </xpath> </template> - <template id="portal_my_home_menu_lead" name="Portal layout : lead menu entry" inherit_id="website_portal.portal_layout" priority="15"> + <template id="portal_my_home_menu_lead" name="Portal layout : lead menu entry" inherit_id="portal.portal_layout" priority="15"> <xpath expr="//ol[contains(@class,'o_portal_submenu')]" position="inside"> <li t-if="page_name == 'lead' or lead"> <a t-if="lead" t-attf-href="/my/leads?{{ keep_query() }}"> @@ -219,9 +219,9 @@ </xpath> </template> - <template id="portal_my_home_lead" name="Portal My Home : lead entries" inherit_id="website_portal.portal_my_home" priority="15"> + <template id="portal_my_home_lead" name="Portal My Home : lead entries" inherit_id="portal.portal_my_home" priority="15"> <xpath expr="//div[contains(@class,'o_my_home_content')]" position="inside"> - <t t-if="user.commercial_partner_id.grade_id"> + <t t-if="user_id.commercial_partner_id.grade_id"> <h3 t-if="lead_count" class="page-header"> <a href="/my/leads">My Leads <span class="badge ml8" t-esc="lead_count"/> @@ -240,10 +240,10 @@ </template> <template id="portal_my_leads" name="My Leads"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <h3 class="page-header"> My Leads - <t t-call="website_portal.portal_searchbar"/> + <t t-call="portal.portal_searchbar"/> </h3> <div t-if="not leads" class="alert alert-warning mt8" role="alert"> There are no leads. @@ -275,10 +275,10 @@ </template> <template id="portal_my_opportunities" name="My Opportunities"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <h3 class="page-header"> My Opportunities - <t t-call="website_portal.portal_searchbar"/> + <t t-call="portal.portal_searchbar"/> <button name='new_opp' data-toggle="modal" data-target=".modal_new_opp" class="btn btn-default pull-right mr8"><i class="fa fa-plus fa-lg" /></button> </h3> <div class="modal fade modal_new_opp" role="form" aria-hidden="true"> @@ -350,7 +350,7 @@ </template> <template id="portal_my_lead" name="My Lead"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <div class="panel panel-default"> <div class="panel-heading"> <div class="row"> @@ -516,7 +516,7 @@ </template> <template id="portal_my_opportunity" name="My Opportunity"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <div class="panel panel-default"> <div class="panel-heading"> <div class="row"> diff --git a/addons/website_payment/__manifest__.py b/addons/website_payment/__manifest__.py index 481c0110906..fce4212ca02 100644 --- a/addons/website_payment/__manifest__.py +++ b/addons/website_payment/__manifest__.py @@ -9,7 +9,7 @@ 'depends': [ 'website', 'payment', - 'website_portal', + 'portal', ], 'data': [ 'views/website_payment_view.xml', diff --git a/addons/website_payment/views/website_payment_templates.xml b/addons/website_payment/views/website_payment_templates.xml index bdaff74d0c4..ba012478c79 100644 --- a/addons/website_payment/views/website_payment_templates.xml +++ b/addons/website_payment/views/website_payment_templates.xml @@ -70,8 +70,8 @@ </t> </template> - <template id="pay_meth_link" inherit_id="website_portal.portal_layout" customize_show="True"> - <xpath expr="//div[@class='o_my_details']" position="inside"> + <template id="pay_meth_link" inherit_id="portal.portal_layout" customize_show="True"> + <xpath expr="//div[@class='o_portal_my_details']" position="inside"> <t t-if="request.env['payment.acquirer'].search([('website_published', '=', True), ('registration_view_template_id', '!=', False)])"> <div class='manage_payment_method mt16'><a href="/my/payment_method">Manage your payment acquirers</a></div> </t> diff --git a/addons/website_portal/__init__.py b/addons/website_portal/__init__.py index c92af4efd3d..67dee8c60db 100644 --- a/addons/website_portal/__init__.py +++ b/addons/website_portal/__init__.py @@ -1,5 +1,2 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import controllers -from . import wizard diff --git a/addons/website_portal/__manifest__.py b/addons/website_portal/__manifest__.py index c9e243a5efd..6b9012d3474 100644 --- a/addons/website_portal/__manifest__.py +++ b/addons/website_portal/__manifest__.py @@ -13,8 +13,5 @@ Allows your customers to manage their account from a beautiful web interface. 'website', 'auth_signup' ], 'data': [ - 'views/website_portal_templates.xml', - 'wizard/portal_wizard_views.xml', - 'data/website_portal_data.xml', ], } diff --git a/addons/website_portal/controllers/__init__.py b/addons/website_portal/controllers/__init__.py deleted file mode 100644 index 5d4b25db9c0..00000000000 --- a/addons/website_portal/controllers/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import main diff --git a/addons/website_portal/controllers/main.py b/addons/website_portal/controllers/main.py deleted file mode 100644 index 3b2fbfc2a2d..00000000000 --- a/addons/website_portal/controllers/main.py +++ /dev/null @@ -1,145 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import http -from odoo.http import request -from odoo import tools -from odoo.tools import pycompat -from odoo.tools.translate import _ -from odoo.exceptions import ValidationError - -from odoo.fields import Date - - -def get_records_pager(ids, current): - if current.id in ids: - idx = ids.index(current.id) - return { - 'prev_record': idx != 0 and current.browse(ids[idx - 1]).website_url, - 'next_record': idx < len(ids) - 1 and current.browse(ids[idx + 1]).website_url - } - return {} - - -class website_account(http.Controller): - - MANDATORY_BILLING_FIELDS = ["name", "phone", "email", "street", "city", "country_id"] - OPTIONAL_BILLING_FIELDS = ["zipcode", "state_id", "vat", "company_name"] - - _items_per_page = 20 - - def _prepare_portal_layout_values(self): - """ prepare the values to render portal layout template. This returns the - data displayed on every portal pages. - """ - partner = request.env.user.partner_id - # get customer sales rep - if partner.user_id: - sales_rep = partner.user_id - else: - sales_rep = False - values = { - 'sales_rep': sales_rep, - 'company': request.website.company_id, - 'user': request.env.user - } - return values - - def _get_archive_groups(self, model, domain=None, fields=None, groupby="create_date", order="create_date desc"): - if not model: - return [] - if domain is None: - domain = [] - if fields is None: - fields = ['name', 'create_date'] - groups = [] - for group in request.env[model]._read_group_raw(domain, fields=fields, groupby=groupby, orderby=order): - dates, label = group[groupby] - date_begin, date_end = dates.split('/') - groups.append({ - 'date_begin': Date.to_string(Date.from_string(date_begin)), - 'date_end': Date.to_string(Date.from_string(date_end)), - 'name': label, - 'item_count': group[groupby + '_count'] - }) - return groups - - @http.route(['/my', '/my/home'], type='http', auth="user", website=True) - def account(self, **kw): - values = self._prepare_portal_layout_values() - return request.render("website_portal.portal_my_home", values) - - @http.route(['/my/account'], type='http', auth='user', website=True) - def details(self, redirect=None, **post): - partner = request.env.user.partner_id - values = { - 'error': {}, - 'error_message': [] - } - - if post: - error, error_message = self.details_form_validate(post) - values.update({'error': error, 'error_message': error_message}) - values.update(post) - if not error: - values = {key: post[key] for key in self.MANDATORY_BILLING_FIELDS} - values.update({key: post[key] for key in self.OPTIONAL_BILLING_FIELDS if key in post}) - values.update({'zip': values.pop('zipcode', '')}) - partner.sudo().write(values) - if redirect: - return request.redirect(redirect) - return request.redirect('/my/home') - - countries = request.env['res.country'].sudo().search([]) - states = request.env['res.country.state'].sudo().search([]) - - values.update({ - 'partner': partner, - 'countries': countries, - 'states': states, - 'has_check_vat': hasattr(request.env['res.partner'], 'check_vat'), - 'redirect': redirect, - 'page_name': 'my_details', - }) - - response = request.render("website_portal.details", values) - response.headers['X-Frame-Options'] = 'DENY' - return response - - def details_form_validate(self, data): - error = dict() - error_message = [] - - # Validation - for field_name in self.MANDATORY_BILLING_FIELDS: - if not data.get(field_name): - error[field_name] = 'missing' - - # email validation - if data.get('email') and not tools.single_email_re.match(data.get('email')): - error["email"] = 'error' - error_message.append(_('Invalid Email! Please enter a valid email address.')) - - # vat validation - partner = request.env["res.partner"] - if data.get("vat") and hasattr(partner, "check_vat"): - partner_dummy = partner.new({ - 'vat': data['vat'], - 'country_id': (int(data['country_id']) - if data.get('country_id') else False), - }) - try: - partner_dummy.check_vat() - except ValidationError: - error["vat"] = 'error' - - # error message for empty required fields - if [err for err in pycompat.values(error) if err == 'missing']: - error_message.append(_('Some required fields are empty.')) - - unknown = [k for k in data if k not in self.MANDATORY_BILLING_FIELDS + self.OPTIONAL_BILLING_FIELDS] - if unknown: - error['common'] = 'Unknown field' - error_message.append("Unknown field '%s'" % ','.join(unknown)) - - return error, error_message diff --git a/addons/website_portal/views/website_portal_templates.xml b/addons/website_portal/views/website_portal_templates.xml deleted file mode 100644 index d320689553e..00000000000 --- a/addons/website_portal/views/website_portal_templates.xml +++ /dev/null @@ -1,264 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<odoo> - - <template id="assets_frontend" inherit_id="website.assets_frontend" name="Account Manager"> - <xpath expr="." position="inside"> - <link rel='stylesheet' href='/website_portal/static/src/less/website_portal.less'/> - <script type="text/javascript" src="/website_portal/static/src/js/website_portal.js"></script> - <script type="text/javascript" src="/website_portal/static/src/js/website_portal_tour.js"></script> - </xpath> - </template> - - <template id="portal_archive_groups" name="Portal Archive Groups"> - <h3 class="page-header">Archives</h3> - <ul class="nav nav-pills nav-stacked"> - <t t-foreach="archive_groups" t-as="group"> - <t t-if="group['date_begin'] == date"> - <li class="active"> - <a t-att-href="default_url" t-ignore="True"><t t-esc="group['name']"/><span class="pull-right badge" t-esc="group['item_count']"/></a> - </li> - </t> - <t t-if="group['date_begin'] != date"> - <li> - <a t-ignore="True" t-attf-href="#{default_url}?date_begin=#{group['date_begin']}&date_end=#{group['date_end']}"><t t-esc="group['name']"/><span class="pull-right badge" t-esc="group['item_count']"/></a> - </li> - </t> - </t> - </ul> - </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 o_portal_search_panel : set to True to active the input search - :param html $0 : content of the t-call, <li>a</li><li>b</li> - --> - <template id="portal_searchbar" name="Portal Search Bar"> - <t t-if='searchbar_inputs'> - <div class='hidden-xs pull-right o_portal_search_panel'> - <div class="input-group"> - <div class="input-group-btn"> - <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> - <span id="search_label">Search</span> <span class="caret"></span> - </button> - <ul class="dropdown-menu" role="menu"> - <li t-foreach='searchbar_inputs' t-as='input' t-att-class="search_in == searchbar_inputs[input]['input'] and 'active'"> - <a t-att-href="'#' + searchbar_inputs[input]['input']"><span t-raw="searchbar_inputs[input]['label']"/></a> - </li> - </ul> - </div> - <input type="text" class="form-control search_panel_fixed_width" t-att-value='search' name="search"/> - <span class="input-group-btn"> - <button class="btn btn-default search-submit" type="button"><span class="fa fa-search"></span></button> - </span> - </div> - </div> - </t> - <div t-if="searchbar_sortings" class="dropdown pull-right mr4"> - <button id="portal_searchbar_sortby" class="search_panel_fixed_width btn btn-default" type="button" data-toggle="dropdown"> - <span class="fa fa-sort fa-lg" /> - <span class='hidden-xs hidden-sm hidden-md' t-esc="searchbar_sortings[sortby].get('label', 'Newest')"/> - <span class="caret"></span> - </button> - <ul class="dropdown-menu" aria-labelledby="portal_searchbar_sortby"> - <li t-foreach="searchbar_sortings" t-as="option" t-attf-class="#{sortby == option and 'active'}"> - <a t-att-href="request.httprequest.path + '?' + keep_query('*', sortby=option)"> - <span t-esc="searchbar_sortings[option].get('label')"/> - </a> - </li> - </ul> - </div> - <div t-if="searchbar_filters" class="dropdown pull-right mr4"> - <button id="portal_searchbar_filters" class="search_panel_fixed_width btn btn-default" type="button" data-toggle="dropdown"> - <span class="fa fa-filter fa-lg" /> - <span class='hidden-xs hidden-sm hidden-md' t-esc="searchbar_filters[filterby].get('label', 'All')"/> - <span class="caret"></span> - </button> - <ul class="dropdown-menu" aria-labelledby="portal_searchbar_filters"> - <li t-foreach="searchbar_filters" t-as="option" t-att-class="filterby == option and 'active'"> - <a t-att-href="default_url + '?' + keep_query('*', filterby=option)"> - <span t-esc="searchbar_filters[option].get('label')"/> - </a> - </li> - </ul> - </div> - </template> - - <template id="portal_layout" name="Portal Layout"> - <t t-call="website.layout"> - <div class="container mt16 o_portal"> - <div class="oe_structure"> - <div t-if="not no_breadcrumbs" class="row"> - <div class="col-sm-12"> - <ol class="breadcrumb o_portal_submenu"> - <li><a href="/my/home"><i class="fa fa-home"/></a></li> - <li t-if="page_name == 'my_details'">Your Details</li> - </ol> - </div> - </div> - </div> - </div> - <div id="wrap" class='portal-wrap'> - <div class="container mb64"> - <t t-if="my_details"> - <div class="row"> - <div t-attf-class="col-md-9 col-lg-8"> - <t t-raw="0"/> - </div> - <div id="o_my_sidebar" class="col-md-3 col-lg-offset-1 o_my_sidebar"> - <div class="o_my_contact" t-if="sales_rep"> - <t t-call="website_portal.contact"/> - </div> - <div class="o_my_details"> - <h3 class="page-header">Your Details <a href="/my/account" class="btn btn-default btn-xs">Change</a></h3> - <div t-field="user.partner_id" t-options='{"widget": "contact", "fields": ["email", "phone", "address", "name"]}'/> - </div> - <div class="o_my_archives" t-if="archive_groups"> - <t t-call="website_portal.portal_archive_groups"/> - </div> - </div> - </div> - </t> - <t t-else=""> - <t t-raw="0"/> - </t> - </div> - </div> - </t> - </template> - - <template id="portal_my_home" name="My Portal"> - <t t-call="website_portal.portal_layout"> - <t t-set="my_details" t-value="True"/> - <div class="o_my_home_content"> - <div class="oe_structure"></div> - <h3 class="page-header">Your Documents</h3> - <ul class="o_portal_docs list-group"> - </ul> - </div> - <div class="col-md-12 oe_structure"/> - </t> - </template> - - <template id="contact" name="Contact"> - <h3 class="page-header">Salesperson</h3> - <h5 t-esc="sales_rep.name"/> - <p><a t-att-href="'mailto:'+sales_rep.email" t-esc="sales_rep.email"/></p> - <p t-esc="sales_rep.phone"/> - <p t-esc="sales_rep.city"/> - </template> - - <template id="details"> - <t t-call="website_portal.portal_layout"> - <t t-set="additional_title">Your Contact Details</t> - <h3>Your Details</h3> - <form action="/my/account" method="post"> - <input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/> - <div class="row o_portal_details"> - <div class="col-md-8"> - <div class="row"> - <div class="col-md-12"> - <div t-if="error_message" class="alert alert-danger"> - <t t-foreach="error_message" t-as="err"><t t-esc="err"/><br /></t> - </div> - </div> - <div t-attf-class="form-group #{error.get('name') and 'has-error' or ''} col-lg-6"> - <label class="control-label" for="name">Your Name</label> - <input type="text" name="name" class="form-control" t-att-value="name or partner.name" /> - </div> - <div t-attf-class="form-group #{error.get('email') and 'has-error' or ''} col-lg-6"> - <label class="control-label" for="email">Email</label> - <input type="email" name="email" class="form-control" t-att-value="email or partner.email" /> - </div> - <div t-attf-class="form-group #{error.get('phone') and 'has-error' or ''} col-lg-6"> - <label class="control-label" for="phone">Phone</label> - <input type="tel" name="phone" class="form-control" t-att-value="phone or partner.phone" /> - </div> - - <div class="clearfix" /> - <div t-attf-class="form-group #{error.get('company_name') and 'has-error' or ''} col-lg-6"> - <label class="control-label label-optional" for="company_name">Company Name</label> - <input type="text" name="company_name" class="form-control" t-att-value="company_name or partner.company_name"/> - </div> - <div t-if="has_check_vat" t-attf-class="form-group #{error.get('vat') and 'has-error' or ''} col-lg-6"> - <label class="control-label label-optional" for="vat">VAT Number</label> - <input type="text" name="vat" class="form-control" t-att-value="vat or partner.vat" /> - </div> - - <div class="clearfix" /> - <div t-attf-class="form-group #{error.get('street') and 'has-error' or ''} col-lg-6"> - <label class="control-label label-optional" for="street">Street</label> - <input type="text" name="street" class="form-control" t-att-value="street or partner.street"/> - </div> - <div t-attf-class="form-group #{error.get('city') and 'has-error' or ''} col-lg-6"> - <label class="control-label" for="city">City</label> - <input type="text" name="city" class="form-control" t-att-value="city or partner.city" /> - </div> - <div t-attf-class="form-group #{error.get('zip') and 'has-error' or ''} col-lg-6"> - <label class="control-label" for="zipcode">Zip / Postal Code</label> - <input type="text" name="zipcode" class="form-control" t-att-value="zipcode or partner.zip" /> - </div> - <div t-attf-class="form-group #{error.get('country_id') and 'has-error' or ''} col-lg-6"> - <label class="control-label" for="country_id">Country</label> - <select name="country_id" class="form-control"> - <option value="">Country...</option> - <t t-foreach="countries or []" t-as="country"> - <option t-att-value="country.id" t-att-selected="country.id == partner.country_id.id"> - <t t-esc="country.name" /> - </option> - </t> - </select> - </div> - <div t-attf-class="form-group #{error.get('state_id') and 'has-error' or ''} col-lg-6"> - <label class="control-label label-optional" for="state_id">State / Province</label> - <select name="state_id" class="form-control"> - <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 == partner.state_id.id"> - <t t-esc="state.name" /> - </option> - </t> - </select> - </div> - <input type="hidden" name="redirect" t-att-value="redirect"/> - </div> - <div class="clearfix"> - <button type="submit" class="btn btn-primary pull-right mb32 "> - Confirm - <span class="fa fa-long-arrow-right" /> - </button> - </div> - </div> - </div> - </form> - </t> - </template> - - <template id="my_account_link" name="Link to frontend portal" inherit_id="website.layout"> - <xpath expr="//li[@id='o_logout']" position="before"> - <li><a href="/my/home" role="menuitem">My Account</a></li> - </xpath> - </template> - - <template id="record_pager" name="Portal Record Pager"> - <t t-if='prev_record or next_record'> - <div class="ml8 record_pager btn-group btn-group-xs pull-right" role="group"> - <a t-att-class="'btn btn-default %s' % ('disabled' if not prev_record else '')" t-att-href="prev_record or '#'" ><i class="fa fa-arrow-left" aria-hidden="true"></i></a> - <a t-att-class="'btn btn-default %s' % ('disabled' if not next_record else '')" t-att-href="next_record or '#'" ><i class="fa fa-arrow-right" aria-hidden="true"></i></a> - </div> - </t> - </template> - -</odoo> diff --git a/addons/website_portal_purchase/__manifest__.py b/addons/website_portal_purchase/__manifest__.py index 473ee58521f..5e99ea91d51 100644 --- a/addons/website_portal_purchase/__manifest__.py +++ b/addons/website_portal_purchase/__manifest__.py @@ -11,7 +11,8 @@ Display purchase orders on your website. Customers will be able to access their """, 'depends': [ 'purchase', - 'website_portal', + 'website', + 'portal', ], 'data': [ 'views/website_portal_purchase_templates.xml', diff --git a/addons/website_portal_purchase/controllers/website_portal.py b/addons/website_portal_purchase/controllers/website_portal.py index 874c311f92a..4694cc541c3 100644 --- a/addons/website_portal_purchase/controllers/website_portal.py +++ b/addons/website_portal_purchase/controllers/website_portal.py @@ -8,11 +8,10 @@ from odoo.exceptions import AccessError from odoo.http import request from odoo.tools import pycompat from odoo.tools.translate import _ +from odoo.addons.portal.controllers.portal import get_records_pager, CustomerPortal -from odoo.addons.website_portal.controllers.main import website_account, get_records_pager - -class WebsitePortal(website_account): +class WebsitePortal(CustomerPortal): def _prepare_portal_layout_values(self): values = super(WebsitePortal, self)._prepare_portal_layout_values() diff --git a/addons/website_portal_purchase/views/website_portal_purchase_templates.xml b/addons/website_portal_purchase/views/website_portal_purchase_templates.xml index 17fe2e81d31..118b317b802 100644 --- a/addons/website_portal_purchase/views/website_portal_purchase_templates.xml +++ b/addons/website_portal_purchase/views/website_portal_purchase_templates.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> - <template id="portal_my_home_menu_purchase" name="Portal layout : purchase menu entries" inherit_id="website_portal.portal_layout" priority="25"> + <template id="portal_my_home_menu_purchase" name="Portal layout : purchase menu entries" inherit_id="portal.portal_layout" priority="25"> <xpath expr="//ol[contains(@class,'o_portal_submenu')]" position="inside"> <li t-if="page_name == 'purchase' or purchase_order"> <a t-if="purchase_order" t-attf-href="/my/purchase?{{ keep_query() }}">Purchase Orders</a> @@ -13,7 +13,7 @@ </xpath> </template> - <template id="portal_my_home_purchase" name="Portal My Home : purchase entry" inherit_id="website_portal.portal_my_home" priority="25"> + <template id="portal_my_home_purchase" name="Portal My Home : purchase entry" inherit_id="portal.portal_my_home" priority="25"> <xpath expr="//ul[contains(@class,'o_portal_docs')]" position="inside"> <li t-if="purchase_count" class="list-group-item"> <span class="badge" t-esc="purchase_count"/> @@ -23,9 +23,9 @@ </template> <template id="portal_my_purchase_orders" name="Portal: My Purchase Orders"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <h3>Purchase Orders - <t t-call="website_portal.portal_searchbar"/> + <t t-call="portal.portal_searchbar"/> </h3> <div t-if="orders" class="panel panel-default"> <div class="table-responsive"><table class="table table-hover o_my_status_table"> @@ -61,7 +61,7 @@ </template> <template id="portal_my_purchase_order" name="Portal: My Purchase Order"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <t t-set="purchase_order" t-value="order"/> <div id="optional_placeholder"></div> <div class="container"> @@ -70,7 +70,7 @@ <div class="row"> <div class="col-md-12"> <h4> - <t t-call='website_portal.record_pager'/> + <t t-call='portal.record_pager'/> Purchase Order <span t-esc="order.name"/> </h4> diff --git a/addons/website_portal_sale/controllers/main.py b/addons/website_portal_sale/controllers/main.py index 3103b3037e3..2da2aa1ba06 100644 --- a/addons/website_portal_sale/controllers/main.py +++ b/addons/website_portal_sale/controllers/main.py @@ -6,7 +6,8 @@ from odoo.exceptions import AccessError from odoo.http import request from odoo.tools import consteq -from odoo.addons.website_portal.controllers.main import website_account, get_records_pager +from odoo.addons.portal.controllers.portal import get_records_pager +from odoo.addons.website_portal.controllers.main import website_account class website_account(website_account): diff --git a/addons/website_portal_sale/views/website_portal_sale_templates.xml b/addons/website_portal_sale/views/website_portal_sale_templates.xml index 01884a10859..1374d1a506d 100644 --- a/addons/website_portal_sale/views/website_portal_sale_templates.xml +++ b/addons/website_portal_sale/views/website_portal_sale_templates.xml @@ -6,7 +6,7 @@ </xpath> </template> - <template id="portal_my_home_menu_sale" name="Portal layout : sales menu entries" inherit_id="website_portal.portal_layout" priority="20"> + <template id="portal_my_home_menu_sale" name="Portal layout : sales menu entries" inherit_id="portal.portal_layout" priority="20"> <xpath expr="//ol[contains(@class,'o_portal_submenu')]" position="inside"> <li t-if="page_name == 'quote' or sales_order and sales_order.state == 'sent'"> <a t-if="sales_order" t-attf-href="/my/quotes?{{ keep_query() }}">Quotations</a> @@ -24,7 +24,7 @@ </xpath> </template> - <template id="portal_my_home_sale" name="Portal My Home : sales entries" inherit_id="website_portal.portal_my_home" priority="20"> + <template id="portal_my_home_sale" name="Portal My Home : sales entries" inherit_id="portal.portal_my_home" priority="20"> <xpath expr="//ul[contains(@class,'o_portal_docs')]" position="inside"> <li t-if="quotation_count" class="list-group-item"> <span class="badge" t-esc="quotation_count"/> @@ -38,9 +38,9 @@ </template> <template id="portal_my_quotations" name="My Quotations"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <h3>Quotations - <t t-call="website_portal.portal_searchbar"/> + <t t-call="portal.portal_searchbar"/> </h3> <t t-if="not quotations"> <p>There are currently no quotations for your account.</p> @@ -84,9 +84,9 @@ </template> <template id="portal_my_orders" name="My Sales Orders"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <h3>Sales Orders - <t t-call="website_portal.portal_searchbar"/> + <t t-call="portal.portal_searchbar"/> </h3> <t t-if="not orders"> <p>There are currently no orders for your account.</p> @@ -136,7 +136,7 @@ </template> <template id="orders_followup" name="Sales Order"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <t t-set="sales_order" t-value="order"/> <!-- rename to ensure breadcrumb compatibility (differentiate with purchase orders) --> <div id="optional_placeholder"></div> <div class="container"> @@ -146,7 +146,7 @@ <div class="row"> <div class="col-md-12"> <h4> - <t t-call='website_portal.record_pager'/> + <t t-call='portal.record_pager'/> <t t-if="order.state == 'sent'"> Quotation </t> diff --git a/addons/website_project/__manifest__.py b/addons/website_project/__manifest__.py index e105d8177a7..50212999215 100644 --- a/addons/website_project/__manifest__.py +++ b/addons/website_project/__manifest__.py @@ -7,7 +7,7 @@ Website portal for Project and Tasks ==================================== """, 'category': 'Website', - 'depends': ['project', 'website_portal'], + 'depends': ['project', 'website', 'portal'], 'data': [ 'security/project_security.xml', 'security/ir.model.access.csv', diff --git a/addons/website_project/controllers/main.py b/addons/website_project/controllers/main.py index d8716efd9ac..4e29fb1ccf4 100644 --- a/addons/website_project/controllers/main.py +++ b/addons/website_project/controllers/main.py @@ -5,13 +5,12 @@ from collections import OrderedDict from odoo import http, _ from odoo.http import request - -from odoo.addons.website_portal.controllers.main import website_account, get_records_pager +from odoo.addons.portal.controllers.portal import get_records_pager, CustomerPortal from odoo.osv.expression import OR from odoo.tools import pycompat -class WebsiteAccount(website_account): +class WebsiteAccount(CustomerPortal): def _prepare_portal_layout_values(self): values = super(WebsiteAccount, self)._prepare_portal_layout_values() diff --git a/addons/website_project/views/project_templates.xml b/addons/website_project/views/project_templates.xml index cd4a6e9dac5..1b0b0599c1a 100644 --- a/addons/website_project/views/project_templates.xml +++ b/addons/website_project/views/project_templates.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> - <template id="portal_layout" name="Portal layout: project menu entry" inherit_id="website_portal.portal_layout" priority="40"> + <template id="portal_layout" name="Portal layout: project menu entry" inherit_id="portal.portal_layout" priority="40"> <xpath expr="//ol[contains(@class,'o_portal_submenu')]" position="inside"> <li t-if="page_name == 'project' or project"> <a t-if="project" t-attf-href="/my/projects?{{ keep_query() }}">Projects</a> @@ -19,7 +19,7 @@ </xpath> </template> - <template id="portal_my_home" name="Portal My Home: project entries" inherit_id="website_portal.portal_my_home" priority="40"> + <template id="portal_my_home" name="Portal My Home: project entries" inherit_id="portal.portal_my_home" priority="40"> <xpath expr="//ul[contains(@class,'o_portal_docs')]" position="inside"> <li t-if="project_count" class="list-group-item"> <span class="badge" t-esc="project_count"/> @@ -33,9 +33,9 @@ </template> <template id="my_projects" name="My Projects"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <h3>Projects - <t t-call="website_portal.portal_searchbar"/> + <t t-call="portal.portal_searchbar"/> </h3> <t t-if="not projects"> <div class="alert alert-warning mt8" role="alert"> @@ -72,13 +72,13 @@ </template> <template id="my_project" name="My Project"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <div class="panel panel-default"> <div class="panel-heading"> <div class="row"> <div class="col-md-12"> <h4> - <t t-call='website_portal.record_pager'/> + <t t-call='portal.record_pager'/> Project - <span t-field="project.name"/> </h4> </div> @@ -126,9 +126,9 @@ </template> <template id="my_tasks" name="My Tasks"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <h3>Tasks - <t t-call="website_portal.portal_searchbar"/> + <t t-call="portal.portal_searchbar"/> </h3> <t t-if="not tasks"> <div class="alert alert-warning mt8" role="alert"> @@ -164,13 +164,13 @@ </template> <template id="my_task" name="My Task"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <div class="panel panel-default"> <div class="panel-heading"> <div class="row"> <div class="col-md-12"> <h4> - <t t-call='website_portal.record_pager'/> + <t t-call='portal.record_pager'/> Task - <span t-field="task.name"/> <span t-field="task.stage_id.name" class="pull-right label label-info" title="Current stage of this task"/> <a class="btn btn-info" t-att-href="'/web#return_label=Website&model=project.task&id=%s&view_type=form' % (task.id)" groups="project.group_project_user">Edit Task</a> diff --git a/addons/website_quote/controllers/main.py b/addons/website_quote/controllers/main.py index 8e4359ecbec..522d3561b7f 100644 --- a/addons/website_quote/controllers/main.py +++ b/addons/website_quote/controllers/main.py @@ -5,8 +5,8 @@ import werkzeug from odoo import fields, http, _ from odoo.http import request +from odoo.addons.portal.controllers.portal import get_records_pager from odoo.addons.website_mail.controllers.main import _message_post_helper -from odoo.addons.website_portal.controllers.main import get_records_pager class sale_quote(http.Controller): diff --git a/addons/website_quote/views/website_quote_templates.xml b/addons/website_quote/views/website_quote_templates.xml index 80f54b02f2f..e7e9c574ec0 100644 --- a/addons/website_quote/views/website_quote_templates.xml +++ b/addons/website_quote/views/website_quote_templates.xml @@ -166,7 +166,7 @@ <!-- Complete page of the quotation --> <template id="so_quotation" name="Product Quotation"> - <t t-call="website_portal.portal_layout"> + <t t-call="portal.portal_layout"> <t t-set="sales_order" t-value="quotation"/> <!-- rename to ensure breadcrumb compatibility with website_portal_sale's portal_layout inheritance --> <body data-spy="scroll" data-target=".navspy" data-offset="50"> <div class="container o_website_quote"> @@ -470,7 +470,7 @@ <a class="btn btn-info" target="_blank" t-att-href="'/quote/%s/%s' % (quotation.id,quotation.access_token)+'?pdf=True'">Print</a> </li> <li> - <t t-call='website_portal.record_pager'/> + <t t-call='portal.record_pager'/> </li> </ul> </div> |
