[ADD] Multi Website Core Support 🎉
This commit is contained in:
parent
7bdf18df35
commit
d00801d466
@ -10,7 +10,8 @@
|
||||
'website': 'https://flectrahq.com/page/website-builder',
|
||||
'version': '1.0',
|
||||
'description': "",
|
||||
'depends': ['web', 'web_editor', 'web_planner', 'http_routing', 'portal'],
|
||||
'depends': ['web', 'web_editor', 'web_planner',
|
||||
'http_routing', 'portal', 'base_automation'],
|
||||
'installable': True,
|
||||
'data': [
|
||||
'data/website_data.xml',
|
||||
@ -23,6 +24,7 @@
|
||||
'views/res_config_settings_views.xml',
|
||||
'views/ir_actions_views.xml',
|
||||
'views/res_company_views.xml',
|
||||
'views/module_view.xml',
|
||||
'wizard/base_language_install_views.xml',
|
||||
'data/web_planner_data.xml',
|
||||
],
|
||||
|
@ -7,8 +7,12 @@ from flectra.http import request
|
||||
|
||||
class WebsiteBackend(http.Controller):
|
||||
|
||||
@http.route('/website/fetch_dashboard_data', type="json", auth='user')
|
||||
def fetch_dashboard_data(self, date_from, date_to):
|
||||
@http.route('/website/fetch_dashboard_data', type="json", auth='user',
|
||||
website=True)
|
||||
def fetch_dashboard_data(self, date_from, date_to, website_id=None):
|
||||
if not website_id:
|
||||
website_id = request.website.id
|
||||
|
||||
has_group_system = request.env.user.has_group('base.group_system')
|
||||
has_group_designer = request.env.user.has_group('website.group_website_designer')
|
||||
if has_group_system:
|
||||
@ -36,6 +40,10 @@ class WebsiteBackend(http.Controller):
|
||||
ga_client_id=config.google_management_client_id or '', # void string instead of stringified False
|
||||
ga_analytics_key=config.google_analytics_key or '', # void string instead of stringified False
|
||||
)
|
||||
dashboard_data['website_ids'] = request.env['website'].search_read()
|
||||
dashboard_data['website'] = request.env['website'].browse(
|
||||
website_id).domain
|
||||
dashboard_data['current_website'] = request.website.domain
|
||||
return dashboard_data
|
||||
|
||||
@http.route('/website/dashboard/set_ga_data', type='json', auth='user')
|
||||
|
@ -272,7 +272,10 @@ class Website(Home):
|
||||
|
||||
@http.route("/website/get_switchable_related_views", type="json", auth="user", website=True)
|
||||
def get_switchable_related_views(self, key):
|
||||
views = request.env["ir.ui.view"].get_related_views(key, bundles=False).filtered(lambda v: v.customize_show)
|
||||
views = request.env["ir.ui.view"].get_related_views(
|
||||
key, bundles=False).filtered(
|
||||
lambda v: v.customize_show and (
|
||||
v.website_id if v.website_id == request.website else None))
|
||||
return views.read(['name', 'id', 'key', 'xml_id', 'arch', 'active', 'inherit_id'])
|
||||
|
||||
@http.route('/website/reset_templates', type='http', auth='user', methods=['POST'], website=True)
|
||||
|
@ -6,6 +6,7 @@
|
||||
<field name="domain">localhost</field>
|
||||
<field name="company_id" ref="base.main_company"/>
|
||||
<field name="user_id" ref="base.public_user"/>
|
||||
<field name="is_default_website">True</field>
|
||||
<field name="favicon" type="base64" file="web/static/src/img/favicon.ico"/>
|
||||
</record>
|
||||
|
||||
@ -35,6 +36,7 @@
|
||||
<field name="name">Home</field>
|
||||
<field name="type">qweb</field>
|
||||
<field name="key">website.homepage</field>
|
||||
<field name="website_id" ref="default_website"/>
|
||||
<field name="arch" type="xml">
|
||||
<t name="Home" priority="29" t-name="website.homepage">
|
||||
<t t-call="website.layout">
|
||||
@ -48,6 +50,7 @@
|
||||
<field name="website_published">True</field>
|
||||
<field name="url">/</field>
|
||||
<field name="view_id" ref="homepage" />
|
||||
<field name="website_ids" eval="[(4, ref('default_website'))]" />
|
||||
</record>
|
||||
<record id="default_website" model="website">
|
||||
<field name="homepage_id" ref="homepage_page" />
|
||||
@ -91,6 +94,7 @@
|
||||
<field name="url">/contactus</field>
|
||||
<field name="website_published">True</field>
|
||||
<field name="view_id" ref="contactus" />
|
||||
<field name="website_ids" eval="[(4, ref('default_website'))]" />
|
||||
</record>
|
||||
|
||||
<record id="aboutus" model="ir.ui.view">
|
||||
@ -147,6 +151,7 @@
|
||||
<field name="website_published">True</field>
|
||||
<field name="url">/aboutus</field>
|
||||
<field name="view_id" ref="aboutus" />
|
||||
<field name="website_ids" eval="[(4, ref('default_website'))]" />
|
||||
</record>
|
||||
|
||||
<record id="menu_homepage" model="website.menu">
|
||||
@ -555,5 +560,15 @@
|
||||
<field name="url">/website/static/src/img/snippets_demo/s_team_member_4.png</field>
|
||||
</record>
|
||||
|
||||
<!-- Multi Website Automated Action Rule -->
|
||||
<record id="multi_website_views_on_create" model="base.automation">
|
||||
<field name="name">Multi Website: multi website rule on create</field>
|
||||
<field name="model_id" ref="base.model_ir_ui_view"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">model.multi_website_view_rule()</field>
|
||||
<field name="trigger">on_create</field>
|
||||
<field name="active" eval="True"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</flectra>
|
||||
|
@ -126,122 +126,6 @@ response = request.render("website.template_partner_comment", {
|
||||
<record id="default_website" model="website">
|
||||
<field name="name">Website localhost</field>
|
||||
</record>
|
||||
<record id="website2" model="website">
|
||||
<field name="name">Website 0.0.0.0</field>
|
||||
<field name="domain">0.0.0.0</field>
|
||||
</record>
|
||||
|
||||
<record id="website2_homepage" model="ir.ui.view">
|
||||
<field name="name">Home</field>
|
||||
<field name="type">qweb</field>
|
||||
<field name="key">website2.homepage</field>
|
||||
<field name="arch" type="xml">
|
||||
<t name="Home" priority="29" t-name="website2.homepage">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap" class="oe_structure oe_empty">
|
||||
<div class="carousel slide mb32" id="myCarousel0" style="height: 320px;">
|
||||
<ol class="carousel-indicators hidden">
|
||||
<li class="active" data-slide-to="0" data-target="#myCarousel0"/>
|
||||
</ol>
|
||||
<div class="carousel-inner">
|
||||
<div class="item image_text oe_img_bg active" style="background-image: url(http://0.0.0.0:8069/web/image/website.s_background_image_11);">
|
||||
<div class="container">
|
||||
<div class="row content">
|
||||
<div class="carousel-content col-md-6 col-sm-12">
|
||||
<h2>Homepage 0.0.0.0</h2>
|
||||
<h3>Click to customize this text</h3>
|
||||
<p>
|
||||
<a class="btn btn-success btn-large" href="/contactus">Contact us</a>
|
||||
</p>
|
||||
</div>
|
||||
<span class="carousel-img col-md-6 hidden-sm hidden-xs"> </span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="carousel-control left hidden" data-slide="prev" data-target="#myCarousel0" href="#myCarousel0" style="width: 10%">
|
||||
<i class="fa fa-chevron-left"/>
|
||||
</div>
|
||||
<div class="carousel-control right hidden" data-slide="next" data-target="#myCarousel0" href="#myCarousel0" style="width: 10%">
|
||||
<i class="fa fa-chevron-right"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</field>
|
||||
</record>
|
||||
<record id="website2_homepage_page" model="website.page">
|
||||
<field name="website_published">True</field>
|
||||
<field name="url">/</field>
|
||||
<field name="view_id" ref="website2_homepage" />
|
||||
<field name="website_ids" eval="[(4, ref('website2'))]" />
|
||||
</record>
|
||||
|
||||
<record id="website2_contactus" model="ir.ui.view">
|
||||
<field name="name">Contact Us</field>
|
||||
<field name="type">qweb</field>
|
||||
<field name="key">website2.contactus</field>
|
||||
<field name="arch" type="xml">
|
||||
<t name="Contact Us" t-name="website2.contactus">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
<div class="container">
|
||||
<h1>Contact us</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="oe_structure">
|
||||
<div>
|
||||
<p>Contact us about anything related to our company or services.</p>
|
||||
<p>We'll do our best to get back to you as soon as possible.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mt64" name="mail_button">
|
||||
<a t-attf-href="mailto:{{ res_company.email }}" class="btn btn-primary" id="o_contact_mail">Send us an email</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb32">
|
||||
<t t-call="website.company_description"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</field>
|
||||
</record>
|
||||
<record id="website2_contactus_page" model="website.page">
|
||||
<field name="website_published">True</field>
|
||||
<field name="url">/contactus</field>
|
||||
<field name="view_id" ref="website2_contactus" />
|
||||
<field name="website_ids" eval="[(4, ref('website2'))]" />
|
||||
</record>
|
||||
|
||||
<!-- Menu & Homepage -->
|
||||
<record id="website2" model="website">
|
||||
<field name="homepage_id" ref="website2_homepage_page" />
|
||||
</record>
|
||||
<record id="website2_main_menu" model="website.menu">
|
||||
<field name="name">Top Menu</field>
|
||||
<field name="website_id" ref="website2"/>
|
||||
</record>
|
||||
<record id="website2_menu_homepage" model="website.menu">
|
||||
<field name="name">Home</field>
|
||||
<field name="url">/</field>
|
||||
<field name="parent_id" ref="website.website2_main_menu"/>
|
||||
<field name="sequence" type="int">10</field>
|
||||
<field name="website_id" ref="website2"/>
|
||||
<field name="page_id" ref="website2_homepage_page" />
|
||||
</record>
|
||||
<record id="website2_menu_contactus" model="website.menu">
|
||||
<field name="name">Contact us</field>
|
||||
<field name="url">/contactus</field>
|
||||
<field name="parent_id" ref="website.website2_main_menu"/>
|
||||
<field name="sequence" type="int">60</field>
|
||||
<field name="website_id" ref="website2"/>
|
||||
<field name="page_id" ref="website2_contactus_page" />
|
||||
</record>
|
||||
</data>
|
||||
</flectra>
|
||||
|
@ -6,10 +6,12 @@ from . import ir_attachment
|
||||
from . import ir_http
|
||||
from . import ir_qweb
|
||||
from . import website
|
||||
from . import ir_model
|
||||
from . import ir_ui_view
|
||||
from . import res_company
|
||||
from . import res_partner
|
||||
from . import web_planner
|
||||
from . import module
|
||||
from . import res_config_settings
|
||||
from . import ir_model_fields
|
||||
from . import ir_model
|
||||
|
@ -1,7 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details
|
||||
|
||||
from flectra import api, models
|
||||
import logging
|
||||
from flectra import api, fields, models, _
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# This is a nasty hack, targeted for V11 only
|
||||
@ -13,3 +16,171 @@ class IrModel(models.Model):
|
||||
self.env.cr.execute(
|
||||
"DELETE FROM ir_model_fields WHERE name='website_id'")
|
||||
return super(IrModel, self).unlink()
|
||||
|
||||
|
||||
class IrModelData(models.Model):
|
||||
_inherit = 'ir.model.data'
|
||||
|
||||
# Overriding Method
|
||||
@api.model
|
||||
def _update(self, model, module, values, xml_id=False, store=True,
|
||||
noupdate=False, mode='init', res_id=False):
|
||||
# records created during module install should not display the messages of OpenChatter
|
||||
self = self.with_context(install_mode=True)
|
||||
current_module = module
|
||||
|
||||
if xml_id and ('.' in xml_id):
|
||||
assert len(xml_id.split('.')) == 2, _(
|
||||
"'%s' contains too many dots. XML ids should not contain dots ! These are used to refer to other modules data, as in module.reference_id") % xml_id
|
||||
module, xml_id = xml_id.split('.')
|
||||
|
||||
action = self.browse()
|
||||
record = self.env[model].browse(res_id)
|
||||
|
||||
if xml_id:
|
||||
self._cr.execute("""SELECT imd.id, imd.res_id, md.id, imd.model, imd.noupdate
|
||||
FROM ir_model_data imd LEFT JOIN %s md ON (imd.res_id = md.id)
|
||||
WHERE imd.module=%%s AND imd.name=%%s""" % record._table,
|
||||
(module, xml_id))
|
||||
results = self._cr.fetchall()
|
||||
for imd_id, imd_res_id, real_id, imd_model, imd_noupdate in results:
|
||||
# In update mode, do not update a record if it's ir.model.data is flagged as noupdate
|
||||
if mode == 'update' and imd_noupdate:
|
||||
return imd_res_id
|
||||
if not real_id:
|
||||
self.clear_caches()
|
||||
self._cr.execute('DELETE FROM ir_model_data WHERE id=%s',
|
||||
(imd_id,))
|
||||
record = record.browse()
|
||||
else:
|
||||
assert model == imd_model, "External ID conflict, %s already refers to a `%s` record," \
|
||||
" you can't define a `%s` record with this ID." % (
|
||||
xml_id, imd_model, model)
|
||||
action = self.browse(imd_id)
|
||||
record = record.browse(imd_res_id)
|
||||
|
||||
if action and record:
|
||||
# Set ``is_cloned`` to ``False``
|
||||
if values.get('type') == 'qweb' and not values.get('is_cloned'):
|
||||
values.update({'is_cloned': False})
|
||||
|
||||
record.write(values)
|
||||
action.sudo().write({'date_update': fields.Datetime.now()})
|
||||
|
||||
elif record:
|
||||
record.write(values)
|
||||
if xml_id:
|
||||
for parent_model, parent_field in record._inherits.items():
|
||||
self.sudo().create({
|
||||
'name': xml_id + '_' + parent_model.replace('.', '_'),
|
||||
'model': parent_model,
|
||||
'module': module,
|
||||
'res_id': record[parent_field].id,
|
||||
'noupdate': noupdate,
|
||||
})
|
||||
self.sudo().create({
|
||||
'name': xml_id,
|
||||
'model': model,
|
||||
'module': module,
|
||||
'res_id': record.id,
|
||||
'noupdate': noupdate,
|
||||
})
|
||||
|
||||
elif mode == 'init' or (mode == 'update' and xml_id):
|
||||
existing_parents = set() # {parent_model, ...}
|
||||
if xml_id:
|
||||
for parent_model, parent_field in record._inherits.items():
|
||||
xid = self.sudo().search([
|
||||
('module', '=', module),
|
||||
('name', '=',
|
||||
xml_id + '_' + parent_model.replace('.', '_')),
|
||||
])
|
||||
# XML ID found in the database, try to recover an existing record
|
||||
if xid:
|
||||
parent = self.env[xid.model].browse(xid.res_id)
|
||||
if parent.exists():
|
||||
existing_parents.add(xid.model)
|
||||
values[parent_field] = parent.id
|
||||
else:
|
||||
xid.unlink()
|
||||
|
||||
record = record.create(values)
|
||||
if xml_id:
|
||||
# To add an external identifiers to all inherits model
|
||||
inherit_models = [record]
|
||||
while inherit_models:
|
||||
current_model = inherit_models.pop()
|
||||
for parent_model_name, parent_field in current_model._inherits.items():
|
||||
inherit_models.append(self.env[parent_model_name])
|
||||
if parent_model_name in existing_parents:
|
||||
continue
|
||||
self.sudo().create({
|
||||
'name': xml_id + '_' + parent_model_name.replace(
|
||||
'.', '_'),
|
||||
'model': parent_model_name,
|
||||
'module': module,
|
||||
'res_id': record[parent_field].id,
|
||||
'noupdate': noupdate,
|
||||
})
|
||||
existing_parents.add(parent_model_name)
|
||||
self.sudo().create({
|
||||
'name': xml_id,
|
||||
'model': model,
|
||||
'module': module,
|
||||
'res_id': record.id,
|
||||
'noupdate': noupdate
|
||||
})
|
||||
if current_module and module != current_module:
|
||||
_logger.warning(
|
||||
"Creating the ir.model.data %s in module %s instead of %s.",
|
||||
xml_id, module, current_module)
|
||||
|
||||
if xml_id and record:
|
||||
self.loads[(module, xml_id)] = (model, record.id)
|
||||
for parent_model, parent_field in record._inherits.items():
|
||||
parent_xml_id = xml_id + '_' + parent_model.replace('.', '_')
|
||||
self.loads[(module, parent_xml_id)] = (
|
||||
parent_model, record[parent_field].id)
|
||||
|
||||
return record.id
|
||||
|
||||
@api.model
|
||||
def _process_end(self, modules):
|
||||
super(IrModelData, self)._process_end(modules)
|
||||
ir_ui_view = self.env['ir.ui.view']
|
||||
ir_model_data = self.env['ir.model.data']
|
||||
|
||||
default_website = self.env['website'].search([
|
||||
('is_default_website', '=', True)])
|
||||
for cus_view in ir_ui_view.search([('customize_show', '=', True),
|
||||
('website_id', '=', False),
|
||||
'|', ('active', '=', False),
|
||||
('active', '=', True)]):
|
||||
if default_website:
|
||||
cus_view.write({'website_id': default_website.id})
|
||||
|
||||
for website in self.env['website'].search(
|
||||
[('is_default_website', '=', False)]):
|
||||
for view in ir_ui_view.search(
|
||||
[('website_id', '=', default_website.id),
|
||||
('customize_show', '=', True), ('is_cloned', '=', False),
|
||||
'|', ('active', '=', False), ('active', '=', True)]):
|
||||
if not ir_ui_view.search(
|
||||
[('key', '=', view.key +
|
||||
'_' + website.website_code),
|
||||
'|', ('active', '=', False), ('active', '=', True)]):
|
||||
new_cus_view = view.copy({
|
||||
'is_cloned': True,
|
||||
'key': view.key + '_' + website.website_code,
|
||||
'website_id': website.id
|
||||
})
|
||||
model_data_id = ir_model_data.create({
|
||||
'model': view.model_data_id.model,
|
||||
'name': view.model_data_id.name +
|
||||
'_' + website.website_code,
|
||||
'res_id': new_cus_view.id,
|
||||
'module': view.model_data_id.module,
|
||||
})
|
||||
new_cus_view.write({
|
||||
'model_data_id': model_data_id
|
||||
})
|
||||
|
@ -4,7 +4,7 @@
|
||||
import logging
|
||||
from itertools import groupby
|
||||
|
||||
from flectra import api, fields, models
|
||||
from flectra import api, fields, models, _
|
||||
from flectra import tools
|
||||
from flectra.addons.http_routing.models.ir_http import url_for
|
||||
from flectra.http import request
|
||||
@ -21,6 +21,11 @@ class View(models.Model):
|
||||
customize_show = fields.Boolean("Show As Optional Inherit", default=False)
|
||||
website_id = fields.Many2one('website', ondelete='cascade', string="Website")
|
||||
page_ids = fields.One2many('website.page', compute='_compute_page_ids', store=False)
|
||||
is_cloned = fields.Boolean(string='Cloned', copy=False, default=False,
|
||||
help="This view is cloned"
|
||||
"(not present physically in file system) "
|
||||
"from default website's view for "
|
||||
"supporting multi-website feature.")
|
||||
|
||||
@api.one
|
||||
def _compute_page_ids(self):
|
||||
@ -157,3 +162,102 @@ class View(models.Model):
|
||||
'url': '/website/pages',
|
||||
'target': 'self',
|
||||
}
|
||||
|
||||
# Multi Website: Automated Action On Create Rule
|
||||
##################################################
|
||||
# If views are manually created for default website,
|
||||
# then it'll automatically cloned for other websites.
|
||||
#
|
||||
# As this method is also called when new website is created.
|
||||
# Because at the time of website creation ``Home`` page will be cloned,
|
||||
# So, this method will automatically triggered to
|
||||
# cloned all customize view(s).
|
||||
@api.model
|
||||
def multi_website_view_rule(self):
|
||||
default_website = self.env['website'].search([
|
||||
('is_default_website', '=', True)])
|
||||
ir_model_data = self.env['ir.model.data']
|
||||
for website in self.env['website'].search(
|
||||
[('is_default_website', '=', False)]):
|
||||
for cus_view in self.search(
|
||||
[('website_id', '=', default_website.id),
|
||||
('customize_show', '=', True),
|
||||
('is_cloned', '=', False),
|
||||
'|', ('active', '=', False), ('active', '=', True)]):
|
||||
if not self.search(
|
||||
[('key', '=', cus_view.key +
|
||||
'_' + website.website_code),
|
||||
'|', ('active', '=', False), ('active', '=', True)]):
|
||||
new_cus_view = cus_view.copy(
|
||||
{'is_cloned': True,
|
||||
'key': cus_view.key + '_' + website.website_code,
|
||||
'website_id': website.id
|
||||
})
|
||||
model_data_id = ir_model_data.create({
|
||||
'model': cus_view.model_data_id.model,
|
||||
'name': cus_view.model_data_id.name +
|
||||
'_' + website.website_code,
|
||||
'res_id': new_cus_view.id,
|
||||
'module': cus_view.model_data_id.module,
|
||||
})
|
||||
new_cus_view.write({
|
||||
'model_data_id': model_data_id
|
||||
})
|
||||
|
||||
# Add the website_id to each customize QWeb view(s) at the time
|
||||
# of creation of new customize QWeb view(s).
|
||||
@api.model
|
||||
def create(self, values):
|
||||
# For Theme's View(s)
|
||||
if values.get('key') and values.get('type') == 'qweb' and \
|
||||
self.env.context.get('install_mode_data'):
|
||||
module_name = self.env.context['install_mode_data']['module']
|
||||
module_obj = self.env['ir.module.module'].sudo().search(
|
||||
[('name', '=', module_name)])
|
||||
if module_obj and \
|
||||
(module_obj.category_id.name == 'Theme'
|
||||
or (module_obj.category_id.parent_id
|
||||
and module_obj.category_id.parent_id.name
|
||||
== 'Theme')):
|
||||
values.update({
|
||||
'website_id': module_obj.website_ids.id,
|
||||
})
|
||||
return super(View, self).create(self._compute_defaults(values))
|
||||
|
||||
# Keep other website's view as it is when run server using -i/-u
|
||||
# As other website's views are not present anywhere in FS(file system).
|
||||
# So, once those are created/cloned from default website,
|
||||
# they can be changed/updated via debug mode only(ir.ui.view)
|
||||
# Menu: Settings/Technical/User Interface/Views
|
||||
#
|
||||
# Scenario 1:
|
||||
# -----------
|
||||
# For Delete those views, Manually set ``is_cloned`` field to ``False``
|
||||
#
|
||||
# Scenario 2:
|
||||
# -----------
|
||||
# If you write the code for already cloned views in FS(file system)/Module
|
||||
# to upgrade/update those views, then at the time of module update
|
||||
# process that cloned views id are found in FS(file system)/Module,
|
||||
# So in those particular views ``is_cloned`` will automatically
|
||||
# set to ``False`` (Definitely it'll be done from another method!!),
|
||||
# because now those views are not anymore cloned,
|
||||
# now they are physically present!!
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
for view in self:
|
||||
if view.is_cloned:
|
||||
# Do not delete cloned view(s)
|
||||
# ----------------
|
||||
# 'View(s) that you want delete are '
|
||||
# 'cloned view(s).\n'
|
||||
# 'Cloned view(s) are automatically created '
|
||||
# 'for supporting multi website feature.\n'
|
||||
# 'If you still want to delete this view(s) '
|
||||
# 'then first Uncheck(set to False) the '
|
||||
# 'cloned field.\n'
|
||||
# 'By deleting cloned view(s) multi website '
|
||||
# 'will not work properly.\n'
|
||||
# 'So, Be sure before deleting view(s).'
|
||||
return True
|
||||
return super(View, self).unlink()
|
||||
|
24
addons/website/models/module.py
Normal file
24
addons/website/models/module.py
Normal file
@ -0,0 +1,24 @@
|
||||
from flectra import api, fields, models, _
|
||||
from flectra.exceptions import Warning
|
||||
|
||||
|
||||
class IrModuleModule(models.Model):
|
||||
_inherit = 'ir.module.module'
|
||||
|
||||
website_ids = fields.One2many('website', 'website_theme_id',
|
||||
string='Website', readonly=True)
|
||||
|
||||
@api.multi
|
||||
def button_immediate_install(self):
|
||||
for app in self:
|
||||
if app.category_id and (
|
||||
app.category_id.name == 'Theme'
|
||||
or app.category_id.parent_id.name == 'Theme') and \
|
||||
not app.website_ids:
|
||||
raise Warning(_('You are trying to install Theme module!\n'
|
||||
'As Flectra will support multi-website so, '
|
||||
'please install theme in specific website.\n'
|
||||
'Go to...\n'
|
||||
'- Menu: Website/Configuration/Settings\n'
|
||||
'- Select website & its theme & Save it.'))
|
||||
return super(IrModuleModule, self).button_immediate_install()
|
@ -1,10 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
from ast import literal_eval
|
||||
|
||||
from flectra import api, fields, models
|
||||
from flectra.exceptions import AccessDenied
|
||||
from flectra import api, fields, models, _
|
||||
from flectra.exceptions import AccessDenied, AccessError, Warning
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
@ -40,6 +43,25 @@ class ResConfigSettings(models.TransientModel):
|
||||
('b2b', 'On invitation (B2B)'),
|
||||
('b2c', 'Free sign up (B2C)'),
|
||||
], string='Customer Account')
|
||||
website_theme_id = fields.Many2one(
|
||||
'ir.module.module', string='Theme',
|
||||
related='website_id.website_theme_id',
|
||||
help='Choose theme for current website.')
|
||||
|
||||
# Unique theme per Website for now ;)
|
||||
# @todo Flectra:
|
||||
# Do enable support for same theme in multiple website
|
||||
@api.onchange('website_theme_id')
|
||||
def onchange_theme_id(self):
|
||||
if (self.website_id.id not in self.website_theme_id.website_ids.ids) \
|
||||
and (self.website_theme_id and
|
||||
self.website_theme_id.website_ids):
|
||||
warning = {
|
||||
'title': 'Warning',
|
||||
'message': _('Selected theme is already active in '
|
||||
'different website.')}
|
||||
self.website_theme_id = False
|
||||
return {'warning': warning}
|
||||
|
||||
@api.onchange('has_google_analytics')
|
||||
def onchange_has_google_analytics(self):
|
||||
@ -96,3 +118,121 @@ class ResConfigSettings(models.TransientModel):
|
||||
action['res_id'] = literal_eval(self.env['ir.config_parameter'].sudo().get_param('auth_signup.template_user_id', 'False'))
|
||||
action['views'] = [[self.env.ref('base.view_users_form').id, 'form']]
|
||||
return action
|
||||
|
||||
@api.model
|
||||
def _get_classified_fields(self):
|
||||
res = super(ResConfigSettings, self)._get_classified_fields()
|
||||
if 'website_theme_id' in dir(self):
|
||||
ir_module = self.env['ir.module.module']
|
||||
install_theme_lst = []
|
||||
uninstall_theme_lst = []
|
||||
install_theme_lst.append(self.website_theme_id)
|
||||
theme_un = ir_module.sudo().search(
|
||||
['|', ('category_id.name', '=', 'Theme'),
|
||||
('category_id.parent_id.name', '=', 'Theme')]
|
||||
)
|
||||
for theme in theme_un:
|
||||
if not theme.website_ids and len(theme.website_ids.ids) < 1:
|
||||
uninstall_theme_lst.append(theme)
|
||||
res.update({
|
||||
'install_theme': install_theme_lst,
|
||||
'uninstall_theme': uninstall_theme_lst
|
||||
})
|
||||
return res
|
||||
|
||||
# Overriding Method
|
||||
@api.multi
|
||||
def execute(self):
|
||||
self.ensure_one()
|
||||
|
||||
# Multi Website: Do not allow more than 1 website as default website
|
||||
if self.env['website'].search_count(
|
||||
[('is_default_website', '=', True)]) > 1:
|
||||
raise Warning(
|
||||
_('You can define only one website as default one.\n'
|
||||
'More than one websites are not allowed '
|
||||
'as default website.'))
|
||||
|
||||
if not self.env.user._is_superuser() and not \
|
||||
self.env.user.has_group('base.group_system'):
|
||||
raise AccessError(_("Only administrators can change the settings"))
|
||||
|
||||
self = self.with_context(active_test=False)
|
||||
classified = self._get_classified_fields()
|
||||
|
||||
# default values fields
|
||||
IrDefault = self.env['ir.default'].sudo()
|
||||
for name, model, field in classified['default']:
|
||||
if isinstance(self[name], models.BaseModel):
|
||||
if self._fields[name].type == 'many2one':
|
||||
value = self[name].id
|
||||
else:
|
||||
value = self[name].ids
|
||||
else:
|
||||
value = self[name]
|
||||
IrDefault.set(model, field, value)
|
||||
|
||||
# group fields: modify group / implied groups
|
||||
for name, groups, implied_group in classified['group']:
|
||||
if self[name]:
|
||||
groups.write({'implied_ids': [(4, implied_group.id)]})
|
||||
else:
|
||||
groups.write({'implied_ids': [(3, implied_group.id)]})
|
||||
implied_group.write({'users': [(3, user.id) for user in
|
||||
groups.mapped('users')]})
|
||||
|
||||
# other fields: execute method 'set_values'
|
||||
# Methods that start with `set_` are now deprecated
|
||||
for method in dir(self):
|
||||
if method.startswith('set_') and method is not 'set_values':
|
||||
_logger.warning(_('Methods that start with `set_` '
|
||||
'are deprecated. Override `set_values` '
|
||||
'instead (Method %s)') % method)
|
||||
self.set_values()
|
||||
|
||||
# module fields: install/uninstall the selected modules
|
||||
to_install = []
|
||||
to_upgrade = self.env['ir.module.module']
|
||||
to_uninstall_modules = self.env['ir.module.module']
|
||||
lm = len('module_')
|
||||
for name, module in classified['module']:
|
||||
if self[name]:
|
||||
to_install.append((name[lm:], module))
|
||||
else:
|
||||
if module and module.state in ('installed', 'to upgrade'):
|
||||
to_uninstall_modules += module
|
||||
|
||||
if 'install_theme' in classified and 'uninstall_theme' in classified:
|
||||
for theme in classified['install_theme']:
|
||||
if theme:
|
||||
to_install.append((theme.name, theme))
|
||||
if theme.state == 'installed':
|
||||
to_upgrade += theme
|
||||
for theme in classified['uninstall_theme']:
|
||||
if theme and theme.state in ('installed', 'to upgrade'):
|
||||
to_uninstall_modules += theme
|
||||
|
||||
if to_uninstall_modules:
|
||||
to_uninstall_modules.button_immediate_uninstall()
|
||||
|
||||
if to_upgrade:
|
||||
to_upgrade.button_immediate_upgrade()
|
||||
|
||||
self._install_modules(to_install)
|
||||
|
||||
if to_install or to_uninstall_modules:
|
||||
# After the uninstall/install calls, the registry and environments
|
||||
# are no longer valid. So we reset the environment.
|
||||
self.env.reset()
|
||||
self = self.env()[self._name]
|
||||
|
||||
# pylint: disable=next-method-called
|
||||
config = self.env['res.config'].next() or {}
|
||||
if config.get('type') not in ('ir.actions.act_window_close',):
|
||||
return config
|
||||
|
||||
# force client-side reload (update user menu and current view)
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ import inspect
|
||||
import logging
|
||||
import hashlib
|
||||
import re
|
||||
from uuid import uuid4
|
||||
|
||||
from werkzeug import urls
|
||||
from werkzeug.exceptions import NotFound
|
||||
@ -17,6 +18,7 @@ from flectra.tools import pycompat
|
||||
from flectra.http import request
|
||||
from flectra.osv.expression import FALSE_DOMAIN
|
||||
from flectra.tools.translate import _
|
||||
from flectra.exceptions import Warning
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -71,6 +73,17 @@ class Website(models.Model):
|
||||
menu_id = fields.Many2one('website.menu', compute='_compute_menu', string='Main Menu')
|
||||
homepage_id = fields.Many2one('website.page', string='Homepage')
|
||||
favicon = fields.Binary(string="Website Favicon", help="This field holds the image used to display a favicon on the website.")
|
||||
is_default_website = fields.Boolean(string='Default Website', readonly=1)
|
||||
website_code = fields.Char(string='Website Code', readonly=1,
|
||||
default=lambda self: uuid4().hex[:8],
|
||||
help='Unique code per website.')
|
||||
website_theme_id = fields.Many2one('ir.module.module', string='Theme',
|
||||
help='Choose theme for current '
|
||||
'website.')
|
||||
|
||||
_sql_constraints = [
|
||||
('domain_uniq', 'unique(domain)', 'Domain name already exists !'),
|
||||
]
|
||||
|
||||
@api.multi
|
||||
def _compute_menu(self):
|
||||
@ -82,14 +95,95 @@ class Website(models.Model):
|
||||
def noop(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Multi Website
|
||||
# ----------------------------------------------------------
|
||||
@api.multi
|
||||
def write(self, values):
|
||||
self._get_languages.clear_cache(self)
|
||||
if values.get('website_code') or values.get('is_default_website'):
|
||||
raise Warning(_('Unexpected bad things will happen!\n'
|
||||
'Changing website code or default website '
|
||||
'can have unintended side effects.\n'
|
||||
'- We will not updated your old views.\n'
|
||||
'- If above action is not properly done '
|
||||
'then it will break your current '
|
||||
'multi website feature.'))
|
||||
return super(Website, self).write(values)
|
||||
|
||||
#----------------------------------------------------------
|
||||
@api.model
|
||||
def create(self, values):
|
||||
res = super(Website, self).create(values)
|
||||
|
||||
default_website = self.env['website'].search([(
|
||||
'is_default_website', '=', True)])
|
||||
if not len(default_website) or len(default_website) > 1:
|
||||
raise Warning(_('Either default website is not defined '
|
||||
'or multiple default website is defined!!\n'
|
||||
'You can define only one website as '
|
||||
'default website.'))
|
||||
|
||||
website_menu = self.env['website.menu']
|
||||
ir_model_data = self.env['ir.model.data']
|
||||
|
||||
# Menu Entries:
|
||||
# Clone top menu & home menu of default website for new website
|
||||
top_menu = self.env.ref('website.main_menu', False)
|
||||
home_menu = self.env.ref('website.menu_homepage', False)
|
||||
new_home_menu = False
|
||||
if top_menu and home_menu:
|
||||
top_menu = website_menu.search([
|
||||
('id', '=', self.env.ref('website.main_menu').id),
|
||||
('website_id', '=', default_website.id)])
|
||||
home_menu = website_menu.search([
|
||||
('id', '=', self.env.ref('website.menu_homepage').id),
|
||||
('website_id', '=', default_website.id)])
|
||||
new_top_menu = top_menu.copy()
|
||||
new_top_menu.write({
|
||||
'website_id': res.id,
|
||||
})
|
||||
new_home_menu = home_menu.copy()
|
||||
new_home_menu.write({
|
||||
'website_id': res.id,
|
||||
'parent_id': new_top_menu.id,
|
||||
})
|
||||
|
||||
# Home Page & View Entry:
|
||||
# Clone home page & view of default website for new website
|
||||
home_page = self.env.ref('website.homepage_page', False)
|
||||
if home_page and new_home_menu:
|
||||
new_home_page = home_page.copy()
|
||||
|
||||
new_home_page.view_id.write({
|
||||
'name': home_page.view_id.name,
|
||||
'website_id': res.id,
|
||||
'key': home_page.view_id.key + '_' + res.website_code,
|
||||
'is_cloned': True,
|
||||
})
|
||||
|
||||
home_model_data_id = ir_model_data.create({
|
||||
'model': home_page.view_id.model_data_id.model,
|
||||
'name': home_page.view_id.model_data_id.name +
|
||||
'_' + res.website_code,
|
||||
'res_id': new_home_page.view_id.id,
|
||||
'module': home_page.view_id.model_data_id.module,
|
||||
})
|
||||
new_home_page.view_id.write({
|
||||
'model_data_id': home_model_data_id
|
||||
})
|
||||
|
||||
new_home_page.write({
|
||||
'url': home_page.url,
|
||||
'view_id': new_home_page.view_id.id,
|
||||
'website_published': True,
|
||||
'website_ids': [(6, 0, [res.id])],
|
||||
'menu_ids': [(6, 0, [new_home_menu.id])],
|
||||
})
|
||||
return res
|
||||
|
||||
# ----------------------------------------------------------
|
||||
# Page Management
|
||||
#----------------------------------------------------------
|
||||
# ----------------------------------------------------------
|
||||
@api.model
|
||||
def new_page(self, name=False, add_menu=False, template='website.default_page', ispage=True, namespace=None):
|
||||
""" Create a new website page, and assign it a xmlid based on the given one
|
||||
@ -315,9 +409,9 @@ class Website(models.Model):
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
#----------------------------------------------------------
|
||||
# ----------------------------------------------------------
|
||||
# Languages
|
||||
#----------------------------------------------------------
|
||||
# ----------------------------------------------------------
|
||||
|
||||
@api.multi
|
||||
def get_languages(self):
|
||||
@ -362,9 +456,9 @@ class Website(models.Model):
|
||||
lang['hreflang'] = lang['short']
|
||||
return langs
|
||||
|
||||
#----------------------------------------------------------
|
||||
# ----------------------------------------------------------
|
||||
# Utilities
|
||||
#----------------------------------------------------------
|
||||
# ----------------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def get_current_website(self):
|
||||
|
@ -30,6 +30,7 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
'click .o_dashboard_action': 'on_dashboard_action',
|
||||
'click .o_dashboard_action_form': 'on_dashboard_action_form',
|
||||
'click .o_dashboard_hide_panel': 'on_dashboard_hide_panel',
|
||||
'click li.js_website_deshboard': 'js_website_deshboard',
|
||||
},
|
||||
|
||||
init: function(parent, context) {
|
||||
@ -42,6 +43,8 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
|
||||
this.dashboards_templates = ['website.dashboard_visits'];
|
||||
this.graphs = [];
|
||||
this.is_bound = $.Deferred();
|
||||
this.dashboards_header = ['website.dashboard_header'];
|
||||
},
|
||||
|
||||
willStart: function() {
|
||||
@ -58,21 +61,26 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
self.render_dashboards();
|
||||
self.render_graphs();
|
||||
self.$el.parent().addClass('oe_background_grey');
|
||||
self.bind_menu();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Fetches dashboard data
|
||||
*/
|
||||
fetch_data: function() {
|
||||
fetch_data: function(website_id=null) {
|
||||
var self = this;
|
||||
return this._rpc({
|
||||
route: '/website/fetch_dashboard_data',
|
||||
params: {
|
||||
date_from: this.date_from.year()+'-'+(this.date_from.month()+1)+'-'+this.date_from.date(),
|
||||
date_to: this.date_to.year()+'-'+(this.date_to.month()+1)+'-'+this.date_to.date(),
|
||||
'website_id': website_id,
|
||||
},
|
||||
}).done(function(result) {
|
||||
self.website_ids = result.website_ids;
|
||||
self.website = result.website;
|
||||
self.current_website = result.current_website;
|
||||
self.data = result;
|
||||
self.dashboards_data = result.dashboards;
|
||||
self.currency_id = result.currency_id;
|
||||
@ -80,6 +88,80 @@ var Dashboard = Widget.extend(ControlPanelMixin, {
|
||||
});
|
||||
},
|
||||
|
||||
js_website_deshboard: function(ev){
|
||||
ev.preventDefault();
|
||||
var self = this;
|
||||
$.when(this.fetch_data($(ev.target).data('website_id'))).then(function() {
|
||||
self.$('.o_website_dashboard_content').empty();
|
||||
self.$('.o_dashboard_common').remove();
|
||||
self.render_dashboards();
|
||||
self.render_dashboards_header();
|
||||
self.render_graphs();
|
||||
});
|
||||
},
|
||||
|
||||
render_dashboards_header: function() {
|
||||
var self = this;
|
||||
_.each(this.dashboards_header, function(template) {
|
||||
self.$('.o_website_dashboard_content').prepend(QWeb.render(template, {widget: self}));
|
||||
});
|
||||
},
|
||||
|
||||
bind_menu: function() {
|
||||
var self = this;
|
||||
var lazyreflow = _.debounce(this.reflow.bind(this), 200);
|
||||
core.bus.on('resize', this, function() {
|
||||
if ($(window).width() < 768 ) {
|
||||
lazyreflow('all_outside');
|
||||
} else {
|
||||
lazyreflow();
|
||||
}
|
||||
});
|
||||
core.bus.trigger('resize');
|
||||
|
||||
this.is_bound.resolve();
|
||||
},
|
||||
|
||||
reflow: function(behavior) {
|
||||
var self = this;
|
||||
var $more_container = this.$('#website_more_container').hide();
|
||||
var $more = this.$('#website_more');
|
||||
|
||||
$more.children('li').insertBefore($more_container);
|
||||
|
||||
if (behavior === 'all_outside') {
|
||||
// Show list of menu items
|
||||
self.$el.show();
|
||||
this.$el.find('li').show();
|
||||
$more_container.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
var $toplevel_items = this.$el.find('li').not($more_container).hide();
|
||||
|
||||
self.$el.show();
|
||||
$toplevel_items.each(function() {
|
||||
var remaining_space = self.$el.find('div.navbar-collapse.collapse').width() - $more_container.outerWidth();
|
||||
self.$el.find('div.navbar-collapse.collapse ul.website_tab :visible').each
|
||||
(function() {
|
||||
if($(this).parent("ul").length){
|
||||
remaining_space -= $(this).width();
|
||||
}
|
||||
});
|
||||
if ($(this).width() >= remaining_space) {
|
||||
return false;
|
||||
}
|
||||
$(this).show();
|
||||
});
|
||||
$more.append($toplevel_items.filter(':hidden').show());
|
||||
$more_container.toggle(!!$more.children().length);
|
||||
|
||||
var $toplevel = self.$el.children("ul.website_tab li:visible");
|
||||
if ($toplevel.length === 1) {
|
||||
$toplevel.hide();
|
||||
}
|
||||
},
|
||||
|
||||
on_link_analytics_settings: function(ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
|
@ -7,9 +7,26 @@
|
||||
|
||||
<t t-name="website.WebsiteDashboardMain">
|
||||
<div class="o_dashboards">
|
||||
<div class="container-fluid o_website_dashboard">
|
||||
<t t-call="website.dashboard_header"/>
|
||||
<div class="o_website_dashboard_content"/>
|
||||
<div class="o_dashboards">
|
||||
<div class="navbar-collapse collapse" style="padding:0;">
|
||||
<ul class="nav nav-tabs website_tab">
|
||||
<li t-foreach="widget.website_ids" t-as="website"
|
||||
t-attf-class="js_website_deshboard #{widget.current_website == website.domain and 'active' or ''}">
|
||||
<a data-toggle="tab" t-att-data-website_id="website.id">
|
||||
<t t-esc="website.name"/>
|
||||
</a>
|
||||
</li>
|
||||
<li id="website_more_container" class="dropdown" style="display: none;">
|
||||
<a href="#" class="dropdown-toggle"
|
||||
data-toggle="dropdown">More <b class="caret"/></a>
|
||||
<ul id="website_more" class="dropdown-menu"/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="container-fluid o_website_dashboard">
|
||||
<t t-call="website.dashboard_header"/>
|
||||
<div class="o_website_dashboard_content"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
@ -17,7 +34,9 @@
|
||||
<t t-name="website.dashboard_header">
|
||||
<div class="row o_dashboard_common">
|
||||
<div class="o_box">
|
||||
<a href="#" class="o_box_item o_dashboard_action" name="website.action_website" title="Go to Website">
|
||||
<a t-attf-href="#{widget.website}"
|
||||
class="o_box_item o_dashboard_action"
|
||||
name="website.action_website" title="Go to Website">
|
||||
<div class="o_inner_box o_primary">
|
||||
<i class="fa fa-globe fa-3x"></i><br/>
|
||||
Go to Website
|
||||
|
14
addons/website/views/module_view.xml
Normal file
14
addons/website/views/module_view.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<flectra>
|
||||
<record id="base_module_form_inherit" model="ir.ui.view">
|
||||
<field name="name">ir.module.module.form.inherit</field>
|
||||
<field name="model">ir.module.module</field>
|
||||
<field name="inherit_id" ref="base.module_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='state']" position="after">
|
||||
<field name="website_ids" widget="many2many_tags"
|
||||
invisible="0"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</flectra>
|
@ -10,24 +10,30 @@
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//div[hasclass('settings')]" position="inside">
|
||||
<div class="app_settings_block" data-string="Website" string="Website" data-key="website" groups="website.group_website_designer">
|
||||
<field name="website_id" invisible="1"/>
|
||||
<h2>Website</h2>
|
||||
<div class="row mt16 o_settings_container" id="webmaster_settings">
|
||||
<div class="col-xs-12 col-md-6 o_setting_box" id="domain_setting">
|
||||
<div class="o_setting_right_pane">
|
||||
<label string="Website Title"/>
|
||||
<div class="text-muted">
|
||||
Name and favicon of your website
|
||||
Name, favicon & theme of your website
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="row mt16">
|
||||
<label class="col-md-3 o_light_label" string="Name"/>
|
||||
<field name="website_name"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row mt8">
|
||||
<label class="col-md-3 o_light_label" for="favicon" />
|
||||
<field name="favicon" widget="image" class="pull-left oe_avatar"/>
|
||||
</div>
|
||||
<div class="row">
|
||||
<label class="col-md-3 o_light_label" for="website_theme_id"/>
|
||||
<field name="website_theme_id"
|
||||
options="{'no_create': True, 'no_open': True}"
|
||||
domain="['|', ('category_id.name', '=', 'Theme'),
|
||||
('category_id.parent_id.name', '=', 'Theme')]"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -69,7 +75,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div attrs="{'invisible': [('has_google_analytics', '=', False)]}">
|
||||
<a href="https://www.flectra.com/documentation/user/11.0/website/optimize/google_analytics.html"
|
||||
<a href="https://www.flectrahq.com/documentation/user/11.0/website/optimize/google_analytics.html"
|
||||
class="oe_link fa fa-arrow-right" target="_blank">
|
||||
How to get my Tracking ID
|
||||
</a>
|
||||
@ -96,7 +102,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div attrs="{'invisible': [('has_google_analytics_dashboard', '=', False)]}">
|
||||
<a href="https://www.flectra.com/documentation/user/online/website/optimize/google_analytics_dashboard.html"
|
||||
<a href="https://www.flectrahq.com/documentation/user/online/website/optimize/google_analytics_dashboard.html"
|
||||
class="oe_link fa fa-arrow-right" target="_blank">
|
||||
How to get my Client ID
|
||||
</a>
|
||||
@ -195,6 +201,24 @@
|
||||
<field name="context">{'module' : 'website'}</field>
|
||||
</record>
|
||||
|
||||
<record id="action_ui_qweb_view" model="ir.actions.act_window">
|
||||
<field name="name">QWeb Views</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">ir.ui.view</field>
|
||||
<field name="view_id" ref="base.view_view_tree"/>
|
||||
<field name="context">{'search_default_type': 'qweb',
|
||||
'search_default_group_website_id': True}
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_website_website_list" model="ir.actions.act_window">
|
||||
<field name="name">Websites</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">website</field>
|
||||
<field name="view_id" ref="website.view_website_tree"/>
|
||||
<field name="context">{}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_website_global_configuration" parent="menu_website_configuration"
|
||||
sequence="100" name="Configuration" groups="base.group_system"/>
|
||||
<menuitem name="Settings"
|
||||
@ -217,4 +241,25 @@
|
||||
sequence="30"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
<menuitem name="Websites"
|
||||
id="menu_website_website_list"
|
||||
action="action_website_website_list"
|
||||
parent="menu_website_global_configuration"
|
||||
sequence="10"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
<menuitem name="Menus"
|
||||
id="menu_website_menus_list"
|
||||
action="action_website_menu"
|
||||
parent="menu_website_global_configuration"
|
||||
sequence="40"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
<menuitem name="QWeb Views"
|
||||
id="menu_website_qweb_views_list"
|
||||
action="action_ui_qweb_view"
|
||||
parent="menu_website_global_configuration"
|
||||
sequence="50"
|
||||
groups="base.group_no_one"/>
|
||||
|
||||
</flectra>
|
||||
|
@ -21,8 +21,10 @@
|
||||
<div name="domain">
|
||||
<separator name="domain" string="Domain"/>
|
||||
<group name="domain">
|
||||
<field name="name"/>
|
||||
<field name="domain"/>
|
||||
<field name="name" required="True"/>
|
||||
<field name="domain" required="True"/>
|
||||
<field name="website_code" invisible="True"/>
|
||||
<field name="is_default_website" invisible="True"/>
|
||||
<field name="google_analytics_key" placeholder="UA-XXXXXXXX-Y"/>
|
||||
</group>
|
||||
</div>
|
||||
@ -57,6 +59,8 @@
|
||||
<field name="name"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="default_lang_id"/>
|
||||
<field name="website_code"/>
|
||||
<field name="is_default_website"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@ -212,6 +216,7 @@
|
||||
<field name="name" position="after">
|
||||
<field name="website_id" readonly="1"/>
|
||||
<field name="key" readonly="1"/>
|
||||
<field name="is_cloned"/>
|
||||
<field name="page_ids" invisible="1" />
|
||||
</field>
|
||||
<sheet position="before">
|
||||
@ -223,6 +228,20 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ir.ui.view search -->
|
||||
<record model="ir.ui.view" id="view_view_search_extend">
|
||||
<field name="model">ir.ui.view</field>
|
||||
<field name="inherit_id" ref="base.view_view_search"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="type" position="after">
|
||||
<field name="website_id"/>
|
||||
</field>
|
||||
<xpath expr="//group" position="inside">
|
||||
<filter name="group_website_id" string="Website" domain="[]" context="{'group_by':'website_id'}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Dashboard -->
|
||||
<record id="backend_dashboard" model="ir.actions.client">
|
||||
<field name="name">Dashboard</field>
|
||||
|
@ -157,7 +157,6 @@
|
||||
|
||||
<record model="ir.module.category" id="module_category_theme">
|
||||
<field name="name">Theme</field>
|
||||
<field name="exclusive" eval="1"/>
|
||||
<field name="sequence">50</field>
|
||||
</record>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user