summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/portal/__init__.py1
-rw-r--r--addons/portal/__manifest__.py2
-rw-r--r--addons/portal/controllers/__init__.py1
-rw-r--r--addons/portal/controllers/portal.py122
-rw-r--r--addons/portal/controllers/web.py20
-rw-r--r--addons/portal/data/portal_data.xml (renamed from addons/website_portal/data/website_portal_data.xml)2
-rw-r--r--addons/portal/static/src/js/portal.js (renamed from addons/website_portal/static/src/js/website_portal.js)8
-rw-r--r--addons/portal/static/src/js/portal_tour.js (renamed from addons/website_portal/static/src/js/website_portal_tour.js)4
-rw-r--r--addons/portal/tests/__init__.py (renamed from addons/website_portal/tests/__init__.py)0
-rw-r--r--addons/portal/tests/test_load_process.py (renamed from addons/website_portal/tests/test_load_process.py)0
-rw-r--r--addons/portal/views/assets.xml7
-rw-r--r--addons/portal/views/portal_templates.xml253
-rw-r--r--addons/portal/wizard/__init__.py (renamed from addons/website_portal/wizard/__init__.py)0
-rw-r--r--addons/portal/wizard/portal_wizard.py (renamed from addons/website_portal/wizard/portal_wizard.py)2
-rw-r--r--addons/portal/wizard/portal_wizard_views.xml (renamed from addons/website_portal/wizard/portal_wizard_views.xml)0
-rw-r--r--addons/web/controllers/main.py7
-rw-r--r--addons/web/static/src/less/portal.less (renamed from addons/website_portal/static/src/less/website_portal.less)0
-rw-r--r--addons/web/views/webclient_templates.xml2
-rw-r--r--addons/website/views/website_templates.xml11
-rw-r--r--addons/website_account/views/account_templates.xml8
-rw-r--r--addons/website_crm_partner_assign/__manifest__.py2
-rw-r--r--addons/website_crm_partner_assign/controllers/main.py5
-rw-r--r--addons/website_crm_partner_assign/views/website_crm_partner_assign_templates.xml18
-rw-r--r--addons/website_payment/__manifest__.py2
-rw-r--r--addons/website_payment/views/website_payment_templates.xml4
-rw-r--r--addons/website_portal/__init__.py3
-rw-r--r--addons/website_portal/__manifest__.py3
-rw-r--r--addons/website_portal/controllers/__init__.py4
-rw-r--r--addons/website_portal/controllers/main.py145
-rw-r--r--addons/website_portal/views/website_portal_templates.xml264
-rw-r--r--addons/website_portal_purchase/__manifest__.py3
-rw-r--r--addons/website_portal_purchase/controllers/website_portal.py5
-rw-r--r--addons/website_portal_purchase/views/website_portal_purchase_templates.xml12
-rw-r--r--addons/website_portal_sale/controllers/main.py3
-rw-r--r--addons/website_portal_sale/views/website_portal_sale_templates.xml16
-rw-r--r--addons/website_project/__manifest__.py2
-rw-r--r--addons/website_project/controllers/main.py5
-rw-r--r--addons/website_project/views/project_templates.xml20
-rw-r--r--addons/website_quote/controllers/main.py2
-rw-r--r--addons/website_quote/views/website_quote_templates.xml4
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']}&amp;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']}&amp;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&amp;model=project.task&amp;id=%s&amp;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>