[MIG] website: migration to new api
Migration of website module to new API.
The tricky phase is ir_http.py : we need to use `request.env`
only when the authenfication phase is done. Normally by
calling `super` of `_dispatch` method, but website module
required it to be done before. This is important since
`env`is a lazy property of `request` object.
Some hack were kept since this commit is a migration ('RequestUID'
in ir.http, ...)
Some docstrings were added.
This commit is contained in:
@@ -1,2 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import backend
|
||||
import main
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp import http
|
||||
from openerp.http import request
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from datetime import datetime, date, timedelta
|
||||
from math import floor
|
||||
import time
|
||||
import operator
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class WebsiteBackend(http.Controller):
|
||||
|
||||
+109
-125
@@ -1,22 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import datetime
|
||||
from itertools import islice
|
||||
import json
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
import logging
|
||||
import re
|
||||
|
||||
import werkzeug.utils
|
||||
import urllib2
|
||||
import werkzeug.utils
|
||||
import werkzeug.wrappers
|
||||
|
||||
import openerp
|
||||
from openerp.addons.web.controllers.main import WebClient, Binary
|
||||
from openerp import http
|
||||
from openerp.http import request
|
||||
from openerp.osv.orm import browse_record
|
||||
from openerp.addons.website.models.website import slug
|
||||
import odoo
|
||||
from odoo import http
|
||||
from odoo import fields
|
||||
from odoo.http import request
|
||||
from odoo.osv.orm import browse_record
|
||||
|
||||
from odoo.addons.website.models.website import slug
|
||||
from odoo.addons.web.controllers.main import WebClient, Binary, Home
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -34,8 +36,8 @@ class QueryURL(object):
|
||||
|
||||
def __call__(self, path=None, path_args=None, **kw):
|
||||
path = path or self.path
|
||||
for k, v in self.args.items():
|
||||
kw.setdefault(k, v)
|
||||
for key, value in self.args.items():
|
||||
kw.setdefault(key, value)
|
||||
path_args = set(path_args or []).union(self.path_args)
|
||||
paths, fragments = [], []
|
||||
for key, value in kw.items():
|
||||
@@ -56,40 +58,40 @@ class QueryURL(object):
|
||||
return path
|
||||
|
||||
|
||||
class Website(openerp.addons.web.controllers.main.Home):
|
||||
#------------------------------------------------------
|
||||
# View
|
||||
#------------------------------------------------------
|
||||
class Website(Home):
|
||||
|
||||
@http.route('/', type='http', auth="public", website=True)
|
||||
def index(self, **kw):
|
||||
page = 'homepage'
|
||||
try:
|
||||
main_menu = request.env.ref('website.main_menu')
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
main_menu = request.env.ref('website.main_menu', raise_if_not_found=False)
|
||||
if main_menu:
|
||||
first_menu = main_menu.child_id and main_menu.child_id[0]
|
||||
if first_menu:
|
||||
if first_menu.url and (not (first_menu.url.startswith(('/page/', '/?', '/#')) or (first_menu.url == '/'))):
|
||||
return request.redirect(first_menu.url)
|
||||
if first_menu.url and first_menu.url.startswith('/page/'):
|
||||
return request.registry['ir.http'].reroute(first_menu.url)
|
||||
return request.env['ir.http'].reroute(first_menu.url)
|
||||
return self.page(page)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Login - overwrite of the web login so that regular users are redirected to the backend
|
||||
# Login - overwrite of the web login so that regular users are redirected to the backend
|
||||
# while portal users are redirected to the frontend by default
|
||||
#------------------------------------------------------
|
||||
|
||||
@http.route(website=True, auth="public")
|
||||
def web_login(self, redirect=None, *args, **kw):
|
||||
r = super(Website, self).web_login(redirect=redirect, *args, **kw)
|
||||
response = super(Website, self).web_login(redirect=redirect, *args, **kw)
|
||||
if not redirect and request.params['login_success']:
|
||||
if request.registry['res.users'].has_group(request.cr, request.uid, 'base.group_user'):
|
||||
if request.env.user.has_group('base.group_user'):
|
||||
redirect = '/web?' + request.httprequest.query_string
|
||||
else:
|
||||
redirect = '/'
|
||||
return http.redirect_with_hash(redirect)
|
||||
return r
|
||||
return response
|
||||
|
||||
#------------------------------------------------------
|
||||
# Business
|
||||
#------------------------------------------------------
|
||||
|
||||
@http.route('/website/lang/<lang>', type='http', auth="public", website=True, multilang=False)
|
||||
def change_lang(self, lang, r='/', **kwargs):
|
||||
@@ -104,7 +106,7 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
def page(self, page, **opt):
|
||||
values = {
|
||||
'path': page,
|
||||
'deletable': True, # used to add 'delete this page' in content menu
|
||||
'deletable': True, # used to add 'delete this page' in content menu
|
||||
}
|
||||
# /page/website.XXX --> /page/XXX
|
||||
if page.startswith('website.'):
|
||||
@@ -120,7 +122,7 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
values.pop('deletable')
|
||||
page = 'website.page_404'
|
||||
else:
|
||||
return request.registry['ir.http']._handle_exception(e, 404)
|
||||
return request.env['ir.http']._handle_exception(e, 404)
|
||||
|
||||
return request.render(page, values)
|
||||
|
||||
@@ -135,35 +137,32 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
|
||||
@http.route('/sitemap.xml', type='http', auth="public", website=True)
|
||||
def sitemap_xml_index(self):
|
||||
cr, uid, context = request.cr, openerp.SUPERUSER_ID, request.context
|
||||
ira = request.registry['ir.attachment']
|
||||
iuv = request.registry['ir.ui.view']
|
||||
mimetype ='application/xml;charset=utf-8'
|
||||
Attachment = request.env['ir.attachment'].sudo()
|
||||
View = request.env['ir.ui.view'].sudo()
|
||||
mimetype = 'application/xml;charset=utf-8'
|
||||
content = None
|
||||
|
||||
def create_sitemap(url, content):
|
||||
return ira.create(cr, uid, dict(
|
||||
datas=content.encode('base64'),
|
||||
mimetype=mimetype,
|
||||
type='binary',
|
||||
name=url,
|
||||
url=url,
|
||||
), context=context)
|
||||
return Attachment.create({
|
||||
'datas': content.encode('base64'),
|
||||
'mimetype': mimetype,
|
||||
'type': 'binary',
|
||||
'name': url,
|
||||
'url': url,
|
||||
})
|
||||
|
||||
sitemap = ira.search_read(cr, uid, [('url', '=' , '/sitemap.xml'), ('type', '=', 'binary')], ('datas', 'create_date'), context=context)
|
||||
sitemap = Attachment.search([('url', '=', '/sitemap.xml'), ('type', '=', 'binary')], limit=1)
|
||||
if sitemap:
|
||||
# Check if stored version is still valid
|
||||
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
|
||||
create_date = datetime.datetime.strptime(sitemap[0]['create_date'], server_format)
|
||||
create_date = fields.Datetime.from_string(sitemap.create_date)
|
||||
delta = datetime.datetime.now() - create_date
|
||||
if delta < SITEMAP_CACHE_TIME:
|
||||
content = sitemap[0]['datas'].decode('base64')
|
||||
content = sitemap.datas.decode('base64')
|
||||
|
||||
if not content:
|
||||
# Remove all sitemaps in ir.attachments as we're going to regenerated them
|
||||
sitemap_ids = ira.search(cr, uid, [('url', '=like' , '/sitemap%.xml'), ('type', '=', 'binary')], context=context)
|
||||
if sitemap_ids:
|
||||
ira.unlink(cr, uid, sitemap_ids, context=context)
|
||||
sitemaps = Attachment.search([('url', '=like', '/sitemap%.xml'), ('type', '=', 'binary')])
|
||||
sitemaps.unlink()
|
||||
|
||||
pages = 0
|
||||
locs = request.website.sudo(user=request.website.user_id.id).enumerate_pages()
|
||||
@@ -172,23 +171,27 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
'locs': islice(locs, 0, LOC_PER_SITEMAP),
|
||||
'url_root': request.httprequest.url_root[:-1],
|
||||
}
|
||||
urls = iuv.render_template(cr, uid, 'website.sitemap_locs', values, context=context)
|
||||
urls = View.render_template('website.sitemap_locs', values)
|
||||
if urls.strip():
|
||||
content = iuv.render_template(cr, uid, 'website.sitemap_xml', dict(content=urls), context=context)
|
||||
content = View.render_template('website.sitemap_xml', {'content': urls})
|
||||
pages += 1
|
||||
last = create_sitemap('/sitemap-%d.xml' % pages, content)
|
||||
last_sitemap = create_sitemap('/sitemap-%d.xml' % pages, content)
|
||||
else:
|
||||
break
|
||||
|
||||
if not pages:
|
||||
return request.not_found()
|
||||
elif pages == 1:
|
||||
ira.write(cr, uid, last, dict(url="/sitemap.xml", name="/sitemap.xml"), context=context)
|
||||
last_sitemap.write({
|
||||
'url': "/sitemap.xml",
|
||||
'name': "/sitemap.xml"
|
||||
})
|
||||
else:
|
||||
# Sitemaps must be split in several smaller files with a sitemap index
|
||||
content = iuv.render_template(cr, uid, 'website.sitemap_index_xml', dict(
|
||||
pages=range(1, pages + 1),
|
||||
url_root=request.httprequest.url_root,
|
||||
), context=context)
|
||||
content = View.render_template('website.sitemap_index_xml', {
|
||||
'pages': range(1, pages + 1),
|
||||
'url_root': request.httprequest.url_root,
|
||||
})
|
||||
create_sitemap('/sitemap.xml', content)
|
||||
|
||||
return request.make_response(content, [('Content-Type', mimetype)])
|
||||
@@ -198,34 +201,34 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
try:
|
||||
request.website.get_template('website.info').name
|
||||
except Exception, e:
|
||||
return request.registry['ir.http']._handle_exception(e, 404)
|
||||
irm = request.env['ir.module.module'].sudo()
|
||||
apps = irm.search([('state','=','installed'),('application','=',True)])
|
||||
modules = irm.search([('state','=','installed'),('application','=',False)])
|
||||
return request.env['ir.http']._handle_exception(e, 404)
|
||||
Module = request.env['ir.module.module'].sudo()
|
||||
apps = Module.search([('state', '=', 'installed'), ('application', '=', True)])
|
||||
modules = Module.search([('state', '=', 'installed'), ('application', '=', False)])
|
||||
values = {
|
||||
'apps': apps,
|
||||
'modules': modules,
|
||||
'version': openerp.service.common.exp_version()
|
||||
'version': odoo.service.common.exp_version()
|
||||
}
|
||||
return request.render('website.info', values)
|
||||
|
||||
#------------------------------------------------------
|
||||
# Edit
|
||||
#------------------------------------------------------
|
||||
|
||||
@http.route('/website/add/<path:path>', type='http', auth="user", website=True)
|
||||
def pagenew(self, path, noredirect=False, add_menu=None, template=False):
|
||||
if template:
|
||||
xml_id = request.registry['website'].new_page(request.cr, request.uid, path, template=template, context=request.context)
|
||||
xml_id = request.env['website'].new_page(path, template=template)
|
||||
else:
|
||||
xml_id = request.registry['website'].new_page(request.cr, request.uid, path, context=request.context)
|
||||
xml_id = request.env['website'].new_page(path)
|
||||
if add_menu:
|
||||
request.registry['website.menu'].create(
|
||||
request.cr, request.uid, {
|
||||
'name': path,
|
||||
'url': "/page/" + xml_id[8:],
|
||||
'parent_id': request.website.menu_id.id,
|
||||
'website_id': request.website.id,
|
||||
}, context=request.context)
|
||||
request.env['website.menu'].create({
|
||||
'name': path,
|
||||
'url': "/page/" + xml_id[8:],
|
||||
'parent_id': request.website.menu_id.id,
|
||||
'website_id': request.website.id,
|
||||
})
|
||||
# Reverse action in order to allow shortcut for /page/<website_xml_id>
|
||||
url = "/page/" + re.sub(r"^website\.", '', xml_id)
|
||||
|
||||
@@ -242,7 +245,7 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
templates = request.httprequest.form.getlist('templates')
|
||||
modules_to_update = []
|
||||
for temp_id in templates:
|
||||
view = request.registry['ir.ui.view'].browse(request.cr, request.uid, int(temp_id), context=request.context)
|
||||
view = request.env['ir.ui.view'].browse(int(temp_id))
|
||||
if view.page:
|
||||
continue
|
||||
view.model_data_id.write({
|
||||
@@ -252,8 +255,7 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
modules_to_update.append(view.model_data_id.module)
|
||||
|
||||
if modules_to_update:
|
||||
module_obj = request.env['ir.module.module'].sudo()
|
||||
modules = module_obj.search([('name', 'in', modules_to_update)])
|
||||
modules = request.env['ir.module.module'].sudo().search([('name', 'in', modules_to_update)])
|
||||
if modules:
|
||||
modules.button_immediate_upgrade()
|
||||
return request.redirect(redirect)
|
||||
@@ -261,13 +263,11 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
@http.route('/website/customize_template_get', type='json', auth='user', website=True)
|
||||
def customize_template_get(self, key, full=False, bundles=False):
|
||||
""" Get inherit view's informations of the template ``key``. By default, only
|
||||
returns ``customize_show`` templates (which can be active or not), if
|
||||
``full=True`` returns inherit view's informations of the template ``key``.
|
||||
``bundles=True`` returns also the asset bundles
|
||||
:returns ``customize_show`` templates (which can be active or not), if
|
||||
``full=True`` returns inherit view's informations of the template ``key``.
|
||||
``bundles=True`` returns also the asset bundles
|
||||
"""
|
||||
return request.registry["ir.ui.view"].customize_template_get(
|
||||
request.cr, request.uid, key, full=full, bundles=bundles,
|
||||
context=request.context)
|
||||
return request.env["ir.ui.view"].customize_template_get(key, full=full, bundles=bundles)
|
||||
|
||||
@http.route('/website/translations', type='json', auth="public", website=True)
|
||||
def get_website_translations(self, lang, mods=None):
|
||||
@@ -282,18 +282,14 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
|
||||
@http.route(['/website/publish'], type='json', auth="public", website=True)
|
||||
def publish(self, id, object):
|
||||
_id = int(id)
|
||||
_object = request.registry[object]
|
||||
obj = _object.browse(request.cr, request.uid, _id)
|
||||
Model = request.env[object]
|
||||
record = Model.browse(int(id))
|
||||
|
||||
values = {}
|
||||
if 'website_published' in _object._fields:
|
||||
values['website_published'] = not obj.website_published
|
||||
_object.write(request.cr, request.uid, [_id],
|
||||
values, context=request.context)
|
||||
|
||||
obj = _object.browse(request.cr, request.uid, _id)
|
||||
return bool(obj.website_published)
|
||||
if 'website_published' in Model._fields:
|
||||
values['website_published'] = not record.website_published
|
||||
record.write(values)
|
||||
return bool(record.website_published)
|
||||
|
||||
@http.route(['/website/seo_suggest'], type='json', auth="user", website=True)
|
||||
def seo_suggest(self, keywords=None, lang=None):
|
||||
@@ -302,10 +298,10 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
try:
|
||||
req = urllib2.Request("%s?%s" % (url, werkzeug.url_encode({
|
||||
'ie': 'utf8', 'oe': 'utf8', 'output': 'toolbar', 'q': keywords, 'hl': language[0], 'gl': language[1]})))
|
||||
request = urllib2.urlopen(req)
|
||||
response = urllib2.urlopen(req)
|
||||
except (urllib2.HTTPError, urllib2.URLError):
|
||||
return []
|
||||
xmlroot = ET.fromstring(request.read())
|
||||
xmlroot = ET.fromstring(response.read())
|
||||
return json.dumps([sugg[0].attrib['data'] for sugg in xmlroot if len(sugg) and sugg[0].attrib['data']])
|
||||
|
||||
#------------------------------------------------------
|
||||
@@ -314,54 +310,46 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
|
||||
def get_view_ids(self, xml_ids):
|
||||
ids = []
|
||||
imd = request.registry['ir.model.data']
|
||||
for xml_id in xml_ids:
|
||||
if "." in xml_id:
|
||||
xml = xml_id.split(".")
|
||||
view_model, id = imd.get_object_reference(request.cr, request.uid, xml[0], xml[1])
|
||||
record_id = request.env.ref(xml_id).id
|
||||
else:
|
||||
id = int(xml_id)
|
||||
ids.append(id)
|
||||
record_id = int(xml_id)
|
||||
ids.append(record_id)
|
||||
return ids
|
||||
|
||||
@http.route(['/website/theme_customize_get'], type='json', auth="public", website=True)
|
||||
def theme_customize_get(self, xml_ids):
|
||||
view = request.registry["ir.ui.view"]
|
||||
enable = []
|
||||
disable = []
|
||||
ids = self.get_view_ids(xml_ids)
|
||||
context = dict(request.context or {}, active_test=True)
|
||||
for v in view.browse(request.cr, request.uid, ids, context=context):
|
||||
if v.active:
|
||||
enable.append(v.xml_id)
|
||||
for view in request.env['ir.ui.view'].with_context(active_test=True).browse(ids):
|
||||
if view.active:
|
||||
enable.append(view.xml_id)
|
||||
else:
|
||||
disable.append(v.xml_id)
|
||||
disable.append(view.xml_id)
|
||||
return [enable, disable]
|
||||
|
||||
@http.route(['/website/theme_customize'], type='json', auth="public", website=True)
|
||||
def theme_customize(self, enable, disable, get_bundle=False):
|
||||
""" enable or Disable lists of ``xml_id`` of the inherit templates
|
||||
"""
|
||||
cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
|
||||
view = pool["ir.ui.view"]
|
||||
|
||||
context = dict(request.context or {}, active_test=True)
|
||||
|
||||
""" enable or Disable lists of ``xml_id`` of the inherit templates """
|
||||
def set_active(ids, active):
|
||||
if ids:
|
||||
view.write(cr, uid, self.get_view_ids(ids), {'active': active}, context=context)
|
||||
real_ids = self.get_view_ids(ids)
|
||||
request.env['ir.ui.view'].with_context(active_test=True).browse(real_ids).write({'active': active})
|
||||
|
||||
set_active(disable, False)
|
||||
set_active(enable, True)
|
||||
|
||||
if get_bundle:
|
||||
context = dict(request.context, active_test=True)
|
||||
return request.env["ir.qweb"]._get_asset('web.assets_frontend', options=context)
|
||||
|
||||
return True
|
||||
|
||||
@http.route(['/website/theme_customize_reload'], type='http', auth="public", website=True)
|
||||
def theme_customize_reload(self, href, enable, disable):
|
||||
self.theme_customize(enable and enable.split(",") or [],disable and disable.split(",") or [])
|
||||
self.theme_customize(enable and enable.split(",") or [], disable and disable.split(",") or [])
|
||||
return request.redirect(href + ("&theme=true" if "#" in href else "#theme=true"))
|
||||
|
||||
@http.route(['/website/multi_render'], type='json', auth="public", website=True)
|
||||
@@ -375,22 +363,20 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
#------------------------------------------------------
|
||||
# Server actions
|
||||
#------------------------------------------------------
|
||||
|
||||
@http.route([
|
||||
'/website/action/<path_or_xml_id_or_id>',
|
||||
'/website/action/<path_or_xml_id_or_id>/<path:path>',
|
||||
], type='http', auth="public", website=True)
|
||||
def actions_server(self, path_or_xml_id_or_id, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
res, action_id, action = None, None, None
|
||||
ServerActions = request.registry['ir.actions.server']
|
||||
ServerActions = request.env['ir.actions.server']
|
||||
|
||||
# find the action_id: either an xml_id, the path, or an ID
|
||||
if isinstance(path_or_xml_id_or_id, basestring) and '.' in path_or_xml_id_or_id:
|
||||
action_id = request.registry['ir.model.data'].xmlid_to_res_id(request.cr, request.uid, path_or_xml_id_or_id, raise_if_not_found=False)
|
||||
if not action_id:
|
||||
action_ids = ServerActions.search(cr, uid, [('website_path', '=', path_or_xml_id_or_id), ('website_published', '=', True)], context=context)
|
||||
action_id = action_ids and action_ids[0] or None
|
||||
if not action_id:
|
||||
action = request.env.ref(path_or_xml_id_or_id, raise_if_not_found=False)
|
||||
if not action:
|
||||
action = ServerActions.search([('website_path', '=', path_or_xml_id_or_id), ('website_published', '=', True)], limit=1)
|
||||
if not action:
|
||||
try:
|
||||
action_id = int(path_or_xml_id_or_id)
|
||||
except ValueError:
|
||||
@@ -398,24 +384,22 @@ class Website(openerp.addons.web.controllers.main.Home):
|
||||
|
||||
# check it effectively exists
|
||||
if action_id:
|
||||
action_ids = ServerActions.exists(cr, uid, [action_id], context=context)
|
||||
action_id = action_ids and action_ids[0] or None
|
||||
action = ServerActions.browse(action_id).exists()
|
||||
# run it, return only if we got a Response object
|
||||
if action_id:
|
||||
action = ServerActions.browse(cr, uid, action_id, context=context)
|
||||
if action:
|
||||
if action.state == 'code' and action.website_published:
|
||||
action_res = ServerActions.run(cr, uid, [action_id], context=context)
|
||||
action_res = action.run()
|
||||
if isinstance(action_res, werkzeug.wrappers.Response):
|
||||
res = action_res
|
||||
if res:
|
||||
return res
|
||||
return action_res
|
||||
|
||||
return request.redirect('/')
|
||||
|
||||
|
||||
#------------------------------------------------------
|
||||
# Retrocompatibility routes
|
||||
#------------------------------------------------------
|
||||
class WebsiteBinary(openerp.http.Controller):
|
||||
class WebsiteBinary(http.Controller):
|
||||
|
||||
@http.route([
|
||||
'/website/image',
|
||||
'/website/image/<xmlid>',
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="default_website" model="website">
|
||||
<field name="name">Website localhost</field>
|
||||
@@ -368,4 +368,4 @@
|
||||
<field name="url">/web/image/website.s_background_image_01</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="default_website" model="website">
|
||||
@@ -15,28 +15,26 @@
|
||||
</data>
|
||||
|
||||
<data noupdate="1">
|
||||
|
||||
<!--
|
||||
Some demo data for front-end server action generating templates
|
||||
-->
|
||||
|
||||
-->
|
||||
<template id="template_partner_post" name="Partner Post and Thanks (Website Server Action Demo)">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="additional_title">Partner Detail</t>
|
||||
<div id="wrap">
|
||||
<div class="oe_structure">
|
||||
<section>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>Thank you for posting a message !</h2>
|
||||
</div>
|
||||
<t t-call="website.layout">
|
||||
<t t-set="additional_title">Partner Detail</t>
|
||||
<div id="wrap">
|
||||
<div class="oe_structure">
|
||||
<section>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h2>Thank you for posting a message !</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<record id="action_partner_post" model="ir.actions.server">
|
||||
@@ -207,4 +205,4 @@ response = request.render("website.template_partner_comment", values)
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import ir_actions
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import urlparse
|
||||
|
||||
@@ -6,16 +7,16 @@ from odoo import api, fields, models
|
||||
from odoo.http import request
|
||||
|
||||
|
||||
class actions_server(models.Model):
|
||||
class ServerAction(models.Model):
|
||||
""" Add website option in server actions. """
|
||||
|
||||
_name = 'ir.actions.server'
|
||||
_inherit = 'ir.actions.server'
|
||||
|
||||
xml_id = fields.Char(compute='_compute_xml_id', string="External ID",
|
||||
help="ID of the action if defined in a XML file")
|
||||
website_path = fields.Char()
|
||||
website_url = fields.Char(compute='_get_website_url', help='The full URL to access the server action through the website.')
|
||||
website_published = fields.Boolean(string='Available on the Website', copy=False,
|
||||
xml_id = fields.Char('External ID', compute='_compute_xml_id', help="ID of the action if defined in a XML file")
|
||||
website_path = fields.Char('Website Path')
|
||||
website_url = fields.Char('Website Url', compute='_get_website_url', help='The full URL to access the server action through the website.')
|
||||
website_published = fields.Boolean('Available on the Website', copy=False,
|
||||
help='A code server action can be executed from the website, using a dedicated '
|
||||
'controller. The address is <base>/website/action/<website_path>. '
|
||||
'Set this field as True to allow users to run this action. If it '
|
||||
@@ -43,7 +44,7 @@ class actions_server(models.Model):
|
||||
@api.model
|
||||
def _get_eval_context(self, action):
|
||||
""" Override to add the request object in eval_context. """
|
||||
eval_context = super(actions_server, self)._get_eval_context(action)
|
||||
eval_context = super(ServerAction, self)._get_eval_context(action)
|
||||
if action.state == 'code':
|
||||
eval_context['request'] = request
|
||||
return eval_context
|
||||
@@ -51,7 +52,8 @@ class actions_server(models.Model):
|
||||
@api.model
|
||||
def run_action_code_multi(self, action, eval_context=None):
|
||||
""" Override to allow returning response the same way action is already
|
||||
returned by the basic server action behavior. Note that response has
|
||||
priority over action, avoid using both. """
|
||||
res = super(actions_server, self).run_action_code_multi(action, eval_context)
|
||||
returned by the basic server action behavior. Note that response has
|
||||
priority over action, avoid using both.
|
||||
"""
|
||||
res = super(ServerAction, self).run_action_code_multi(action, eval_context)
|
||||
return eval_context.get('response', res)
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import openerp
|
||||
from openerp.osv import osv, fields
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class ir_attachment(osv.osv):
|
||||
class Attachment(models.Model):
|
||||
|
||||
_inherit = "ir.attachment"
|
||||
|
||||
_columns = {
|
||||
'website_url': fields.related("local_url", string="Attachment URL", type='char', deprecated=True), # related for backward compatibility with saas-6
|
||||
}
|
||||
# related for backward compatibility with saas-6
|
||||
website_url = fields.Char(string="Attachment URL", related='local_url', deprecated=True)
|
||||
|
||||
@@ -1,67 +1,84 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import traceback
|
||||
|
||||
import werkzeug
|
||||
import werkzeug.routing
|
||||
import werkzeug.utils
|
||||
|
||||
import openerp
|
||||
from openerp.addons.base import ir
|
||||
import odoo
|
||||
from odoo import api, models
|
||||
from odoo import SUPERUSER_ID
|
||||
from odoo.http import request
|
||||
from odoo.tools import config
|
||||
from odoo.exceptions import QWebException
|
||||
from openerp.addons.website.models.website import slug, url_for, _UNSLUG_RE
|
||||
from openerp.http import request
|
||||
from openerp.tools import config
|
||||
from openerp.osv import orm
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
from odoo.tools.safe_eval import safe_eval
|
||||
|
||||
from odoo.addons.base import ir
|
||||
from odoo.addons.website.models.website import slug, url_for, _UNSLUG_RE
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class RequestUID(object):
|
||||
def __init__(self, **kw):
|
||||
self.__dict__.update(kw)
|
||||
|
||||
class ir_http(orm.AbstractModel):
|
||||
|
||||
class Http(models.AbstractModel):
|
||||
|
||||
_inherit = 'ir.http'
|
||||
|
||||
rerouting_limit = 10
|
||||
_geoip_resolver = None
|
||||
|
||||
def _get_converters(self):
|
||||
""" Get the converters list for custom url pattern werkzeug need to
|
||||
match Rule. This override adds the website ones.
|
||||
"""
|
||||
return dict(
|
||||
super(ir_http, self)._get_converters(),
|
||||
super(Http, self)._get_converters(),
|
||||
model=ModelConverter,
|
||||
page=PageConverter,
|
||||
)
|
||||
|
||||
def _auth_method_public(self):
|
||||
""" If no user logged, set the public user of current website, or default
|
||||
public user as request uid.
|
||||
After this method `request.env` can be called, since the `request.uid` is
|
||||
set. The `env` lazy property of `request` will be correct.
|
||||
"""
|
||||
if not request.session.uid:
|
||||
website = self.pool['website'].get_current_website(request.cr, openerp.SUPERUSER_ID, context=request.context)
|
||||
env = api.Environment(request.cr, SUPERUSER_ID, request.context)
|
||||
website = env['website'].get_current_website()
|
||||
if website:
|
||||
request.uid = website.user_id.id
|
||||
else:
|
||||
request.uid = self.pool['ir.model.data'].xmlid_to_res_id(request.cr, openerp.SUPERUSER_ID, 'base', 'public_user')
|
||||
request.uid = env.ref('base.public_user').id
|
||||
else:
|
||||
request.uid = request.session.uid
|
||||
|
||||
bots = "bot|crawl|slurp|spider|curl|wget|facebookexternalhit".split("|")
|
||||
|
||||
def is_a_bot(self):
|
||||
# We don't use regexp and ustr voluntarily
|
||||
# timeit has been done to check the optimum method
|
||||
ua = request.httprequest.environ.get('HTTP_USER_AGENT', '').lower()
|
||||
user_agent = request.httprequest.environ.get('HTTP_USER_AGENT', '').lower()
|
||||
try:
|
||||
return any(bot in ua for bot in self.bots)
|
||||
return any(bot in user_agent for bot in self.bots)
|
||||
except UnicodeDecodeError:
|
||||
return any(bot in ua.encode('ascii', 'ignore') for bot in self.bots)
|
||||
return any(bot in user_agent.encode('ascii', 'ignore') for bot in self.bots)
|
||||
|
||||
def get_nearest_lang(self, lang):
|
||||
# Try to find a similar lang. Eg: fr_BE and fr_FR
|
||||
short = lang.partition('_')[0]
|
||||
short_match = False
|
||||
for code, name in request.website.get_languages():
|
||||
for code, dummy in request.website.get_languages():
|
||||
if code == lang:
|
||||
return lang
|
||||
if not short_match and code.startswith(short):
|
||||
@@ -94,6 +111,14 @@ class ir_http(orm.AbstractModel):
|
||||
return (self._name, "cache", request.uid, request.lang, request.httprequest.full_path)
|
||||
|
||||
def _dispatch(self):
|
||||
""" Before executing the endpoint method, add website params on request, such as
|
||||
- current website (record)
|
||||
- multilang support (set on cookies)
|
||||
- geoip dict data are added in the session
|
||||
Then follow the parent dispatching.
|
||||
Reminder : Do not use `request.env` before authentication phase, otherwise the env
|
||||
set on request will be created with uid=None (and it is a lazy property)
|
||||
"""
|
||||
first_pass = not hasattr(request, 'website')
|
||||
request.website = None
|
||||
func = None
|
||||
@@ -116,6 +141,7 @@ class ir_http(orm.AbstractModel):
|
||||
self._geoip_setup_resolver()
|
||||
self._geoip_resolve()
|
||||
|
||||
# For website routes (only), add website params on `request`
|
||||
cook_lang = request.httprequest.cookies.get('website_lang')
|
||||
if request.website_enabled:
|
||||
try:
|
||||
@@ -127,7 +153,7 @@ class ir_http(orm.AbstractModel):
|
||||
return self._handle_exception(e)
|
||||
|
||||
request.redirect = lambda url, code=302: werkzeug.utils.redirect(url_for(url), code)
|
||||
request.website = request.registry['website'].get_current_website(request.cr, request.uid, context=request.context)
|
||||
request.website = request.env['website'].get_current_website() # can use `request.env` since auth methods are called
|
||||
request.context['website_id'] = request.website.id
|
||||
langs = [lg[0] for lg in request.website.get_languages()]
|
||||
path = request.httprequest.path.split('/')
|
||||
@@ -170,7 +196,7 @@ class ir_http(orm.AbstractModel):
|
||||
|
||||
# removed cache for auth public
|
||||
request.cache_save = False
|
||||
resp = super(ir_http, self)._dispatch()
|
||||
resp = super(Http, self)._dispatch()
|
||||
|
||||
if request.website_enabled and cook_lang != request.lang and hasattr(resp, 'set_cookie'):
|
||||
resp.set_cookie('website_lang', request.lang)
|
||||
@@ -192,11 +218,11 @@ class ir_http(orm.AbstractModel):
|
||||
return self._dispatch()
|
||||
|
||||
def _postprocess_args(self, arguments, rule):
|
||||
super(ir_http, self)._postprocess_args(arguments, rule)
|
||||
super(Http, self)._postprocess_args(arguments, rule)
|
||||
|
||||
for key, val in arguments.items():
|
||||
# Replace uid placeholder by the current request.uid
|
||||
if isinstance(val, orm.BaseModel) and isinstance(val._uid, RequestUID):
|
||||
if isinstance(val, models.BaseModel) and isinstance(val._uid, RequestUID):
|
||||
arguments[key] = val.sudo(request.uid)
|
||||
|
||||
try:
|
||||
@@ -219,10 +245,10 @@ class ir_http(orm.AbstractModel):
|
||||
is_website_request = bool(getattr(request, 'website_enabled', False) and request.website)
|
||||
if not is_website_request:
|
||||
# Don't touch non website requests exception handling
|
||||
return super(ir_http, self)._handle_exception(exception)
|
||||
return super(Http, self)._handle_exception(exception)
|
||||
else:
|
||||
try:
|
||||
response = super(ir_http, self)._handle_exception(exception)
|
||||
response = super(Http, self)._handle_exception(exception)
|
||||
if isinstance(response, Exception):
|
||||
exception = response
|
||||
else:
|
||||
@@ -246,21 +272,20 @@ class ir_http(orm.AbstractModel):
|
||||
else:
|
||||
code = exception.code
|
||||
|
||||
if isinstance(exception, openerp.exceptions.AccessError):
|
||||
if isinstance(exception, odoo.exceptions.AccessError):
|
||||
code = 403
|
||||
|
||||
if isinstance(exception, QWebException):
|
||||
values.update(qweb_exception=exception)
|
||||
if isinstance(exception.qweb.get('cause'), openerp.exceptions.AccessError):
|
||||
if isinstance(exception.qweb.get('cause'), odoo.exceptions.AccessError):
|
||||
code = 403
|
||||
|
||||
if code == 500:
|
||||
logger.error("500 Internal Server Error:\n\n%s", values['traceback'])
|
||||
if 'qweb_exception' in values:
|
||||
view = request.registry.get("ir.ui.view")
|
||||
views = view._views_get(request.cr, request.uid, exception.qweb['template'],
|
||||
context=request.context)
|
||||
to_reset = [v for v in views if v.model_data_id.noupdate is True and not v.page]
|
||||
view = request.env["ir.ui.view"]
|
||||
views = view._views_get(exception.qweb['template'])
|
||||
to_reset = views.filtered(lambda view: view.model_data_id.noupdate is True and not view.page)
|
||||
values['views'] = to_reset
|
||||
elif code == 403:
|
||||
logger.warn("403 Forbidden:\n\n%s", values['traceback'])
|
||||
@@ -288,10 +313,12 @@ class ir_http(orm.AbstractModel):
|
||||
obj = env[model].browse(int(id))
|
||||
if obj and 'website_published' in obj._fields:
|
||||
if env[obj._name].sudo().search([('id', '=', obj.id), ('website_published', '=', True)]):
|
||||
env = env(user=openerp.SUPERUSER_ID)
|
||||
return super(ir_http, self).binary_content(xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename, filename_field=filename_field, download=download, mimetype=mimetype, default_mimetype=default_mimetype, env=env)
|
||||
env = env(user=SUPERUSER_ID)
|
||||
return super(Http, self).binary_content(xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename, filename_field=filename_field, download=download, mimetype=mimetype, default_mimetype=default_mimetype, env=env)
|
||||
|
||||
|
||||
class ModelConverter(ir.ir_http.ModelConverter):
|
||||
|
||||
def __init__(self, url_map, model=False, domain='[]'):
|
||||
super(ModelConverter, self).__init__(url_map, model)
|
||||
self.domain = domain
|
||||
@@ -301,43 +328,46 @@ class ModelConverter(ir.ir_http.ModelConverter):
|
||||
return slug(value)
|
||||
|
||||
def to_python(self, value):
|
||||
m = re.match(self.regex, value)
|
||||
_uid = RequestUID(value=value, match=m, converter=self)
|
||||
record_id = int(m.group(2))
|
||||
matching = re.match(self.regex, value)
|
||||
_uid = RequestUID(value=value, match=matching, converter=self)
|
||||
record_id = int(matching.group(2))
|
||||
env = api.Environment(request.cr, _uid, request.context)
|
||||
if record_id < 0:
|
||||
# limited support for negative IDs due to our slug pattern, assume abs() if not found
|
||||
if not request.registry[self.model].exists(request.cr, _uid, [record_id]):
|
||||
if not env[self.model].browse(record_id).exists():
|
||||
record_id = abs(record_id)
|
||||
return request.registry[self.model].browse(
|
||||
request.cr, _uid, record_id, context=request.context)
|
||||
return env[self.model].browse(record_id)
|
||||
|
||||
def generate(self, cr, uid, query=None, args=None, context=None):
|
||||
obj = request.registry[self.model]
|
||||
domain = eval( self.domain, (args or {}).copy())
|
||||
def generate(self, query=None, args=None):
|
||||
Model = request.env[self.model]
|
||||
domain = safe_eval(self.domain, (args or {}).copy())
|
||||
if query:
|
||||
domain.append((obj._rec_name, 'ilike', '%'+query+'%'))
|
||||
for record in obj.search_read(cr, uid, domain=domain, fields=['write_date',obj._rec_name], context=context):
|
||||
if record.get(obj._rec_name, False):
|
||||
yield {'loc': (record['id'], record[obj._rec_name])}
|
||||
domain.append((Model._rec_name, 'ilike', '%' + query + '%'))
|
||||
for record in Model.search_read(domain=domain, fields=['write_date', Model._rec_name]):
|
||||
if record.get(Model._rec_name, False):
|
||||
yield {'loc': (record['id'], record[Model._rec_name])}
|
||||
|
||||
|
||||
class PageConverter(werkzeug.routing.PathConverter):
|
||||
""" Only point of this converter is to bundle pages enumeration logic """
|
||||
def generate(self, cr, uid, query=None, args={}, context=None):
|
||||
View = request.registry['ir.ui.view']
|
||||
|
||||
def generate(self, query=None, args={}):
|
||||
View = request.env['ir.ui.view']
|
||||
domain = [('page', '=', True)]
|
||||
query = query and query.startswith('website.') and query[8:] or query
|
||||
if query:
|
||||
domain += [('key', 'like', query)]
|
||||
|
||||
views = View.search_read(cr, uid, domain, fields=['key', 'priority', 'write_date'], order='name', context=context)
|
||||
views = View.search_read(domain, fields=['key', 'priority', 'write_date'], order='name')
|
||||
for view in views:
|
||||
xid = view['key'].startswith('website.') and view['key'][8:] or view['key']
|
||||
# the 'page/homepage' url is indexed as '/', avoid aving the same page referenced twice
|
||||
# when we will have an url mapping mechanism, replace this by a rule: page/homepage --> /
|
||||
if xid=='homepage': continue
|
||||
if xid == 'homepage':
|
||||
continue
|
||||
record = {'loc': xid}
|
||||
if view['priority'] <> 16:
|
||||
record['__priority'] = min(round(view['priority'] / 32.0,1), 1)
|
||||
if view['priority'] != 16:
|
||||
record['__priority'] = min(round(view['priority'] / 32.0, 1), 1)
|
||||
if view['write_date']:
|
||||
record['__lastmod'] = view['write_date'][:10]
|
||||
yield record
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp.http import request
|
||||
from openerp.osv import orm
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import ast
|
||||
|
||||
from odoo import models
|
||||
from odoo.http import request
|
||||
|
||||
class QWeb(orm.AbstractModel):
|
||||
""" QWeb object for rendering stuff in the website context
|
||||
"""
|
||||
|
||||
class QWeb(models.AbstractModel):
|
||||
""" QWeb object for rendering stuff in the website context """
|
||||
|
||||
_inherit = 'ir.qweb'
|
||||
|
||||
@@ -36,6 +38,7 @@ class QWeb(orm.AbstractModel):
|
||||
|
||||
url_att = self.URL_ATTRS.get(el.tag)
|
||||
cdn_att = self.CDN_TRIGGERS.get(el.tag)
|
||||
|
||||
def process(item):
|
||||
if isinstance(item, tuple) and (item[0] in (url_att, cdn_att)):
|
||||
return (item[0], ast.Call(
|
||||
|
||||
+114
-124
@@ -1,100 +1,96 @@
|
||||
# -*- coding: ascii -*-
|
||||
import copy
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
|
||||
from itertools import groupby
|
||||
from lxml import etree, html
|
||||
from lxml import etree
|
||||
|
||||
from openerp import SUPERUSER_ID, api, tools
|
||||
from openerp.addons.website.models import website
|
||||
from openerp.http import request
|
||||
from openerp.osv import osv, fields
|
||||
from odoo import api, fields, models
|
||||
from odoo import tools
|
||||
|
||||
from odoo.addons.website.models import website
|
||||
from odoo.http import request
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class view(osv.osv):
|
||||
class View(models.Model):
|
||||
|
||||
_name = "ir.ui.view"
|
||||
_inherit = ["ir.ui.view", "website.seo.metadata"]
|
||||
_columns = {
|
||||
'page': fields.boolean("Whether this view is a web page template (complete)"),
|
||||
'customize_show': fields.boolean("Show As Optional Inherit"),
|
||||
'website_id': fields.many2one('website', ondelete='cascade', string="Website"),
|
||||
}
|
||||
|
||||
_defaults = {
|
||||
'page': False,
|
||||
'customize_show': False,
|
||||
}
|
||||
page = fields.Boolean("Whether this view is a web page template (complete)", default=False)
|
||||
customize_show = fields.Boolean("Show As Optional Inherit", default=False)
|
||||
website_id = fields.Many2one('website', ondelete='cascade', string="Website")
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
res = super(view, self).unlink(cr, uid, ids, context=context)
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
result = super(View, self).unlink()
|
||||
self.clear_caches()
|
||||
return res
|
||||
return result
|
||||
|
||||
@api.multi
|
||||
def _sort_suitability_key(self):
|
||||
""" Key function to sort views by descending suitability
|
||||
Suitability of a view is defined as follow:
|
||||
* if the view and request website_id are matched
|
||||
* then if the view has no set website
|
||||
"""
|
||||
Key function to sort views by descending suitability
|
||||
Suitability of a view is defined as follow:
|
||||
|
||||
* if the view and request website_id are matched
|
||||
* then if the view has no set website
|
||||
"""
|
||||
self.ensure_one()
|
||||
context_website_id = self.env.context.get('website_id', 1)
|
||||
website_id = self.website_id.id or 0
|
||||
different_website = context_website_id != website_id
|
||||
|
||||
return (different_website, website_id)
|
||||
|
||||
def filter_duplicate(self):
|
||||
"""
|
||||
Filter current recordset only keeping the most suitable view per distinct key
|
||||
"""
|
||||
filtered = self.browse([])
|
||||
for _, group in groupby(self, key=lambda r:r.key):
|
||||
filtered += sorted(group, key=lambda r:r._sort_suitability_key())[0]
|
||||
""" Filter current recordset only keeping the most suitable view per distinct key """
|
||||
filtered = self.env['ir.ui.view']
|
||||
for dummy, group in groupby(self, key=lambda record: record.key):
|
||||
filtered += sorted(group, key=lambda record: record._sort_suitability_key())[0]
|
||||
return filtered
|
||||
|
||||
def _view_obj(self, cr, uid, view_id, context=None):
|
||||
@api.model
|
||||
def _view_obj(self, view_id):
|
||||
if isinstance(view_id, basestring):
|
||||
if 'website_id' in (context or {}):
|
||||
domain = [('key', '=', view_id), '|', ('website_id', '=', False), ('website_id', '=', context.get('website_id'))]
|
||||
rec_id = self.search(cr, uid, domain, order='website_id', context=context)
|
||||
if 'website_id' in self._context:
|
||||
domain = [('key', '=', view_id), '|', ('website_id', '=', False), ('website_id', '=', self._context.get('website_id'))]
|
||||
order = 'website_id'
|
||||
else:
|
||||
rec_id = self.search(cr, uid, [('key', '=', view_id)], context=context)
|
||||
if rec_id:
|
||||
return self.browse(cr, uid, rec_id, context=context).filter_duplicate()
|
||||
domain = [('key', '=', view_id)]
|
||||
order = self._order
|
||||
views = self.search(domain, order=order)
|
||||
if views:
|
||||
return views.filter_duplicate()
|
||||
else:
|
||||
return self.pool['ir.model.data'].xmlid_to_object(
|
||||
cr, uid, view_id, raise_if_not_found=True, context=context)
|
||||
return self.env.ref(view_id)
|
||||
elif isinstance(view_id, (int, long)):
|
||||
return self.browse(cr, uid, view_id, context=context)
|
||||
return self.browse(view_id)
|
||||
|
||||
# assume it's already a view object (WTF?)
|
||||
return view_id
|
||||
|
||||
# Returns all views (called and inherited) related to a view
|
||||
# Used by translation mechanism, SEO and optional templates
|
||||
def _views_get(self, cr, uid, view_id, options=True, bundles=False, context=None, root=True):
|
||||
|
||||
@api.model
|
||||
def _views_get(self, view_id, options=True, bundles=False, root=True):
|
||||
""" For a given view ``view_id``, should return:
|
||||
|
||||
* the view itself
|
||||
* all views inheriting from it, enabled or not
|
||||
- but not the optional children of a non-enabled child
|
||||
* all views called from it (via t-call)
|
||||
* the view itself
|
||||
* all views inheriting from it, enabled or not
|
||||
- but not the optional children of a non-enabled child
|
||||
* all views called from it (via t-call)
|
||||
:returns recordset of ir.ui.view
|
||||
"""
|
||||
|
||||
try:
|
||||
view = self._view_obj(cr, uid, view_id, context=context)
|
||||
view = self._view_obj(view_id)
|
||||
except ValueError:
|
||||
_logger.warning("Could not find view object with view_id '%s'" % (view_id))
|
||||
# Shall we log that ? Yes, you should !
|
||||
_logger.warning("Could not find view object with view_id '%s'", view_id)
|
||||
return []
|
||||
|
||||
while root and view.inherit_id:
|
||||
view = view.inherit_id
|
||||
|
||||
result = [view]
|
||||
views_to_return = view
|
||||
|
||||
node = etree.fromstring(view.arch)
|
||||
xpath = "//t[@t-call]"
|
||||
@@ -102,49 +98,44 @@ class view(osv.osv):
|
||||
xpath += "| //t[@t-call-assets]"
|
||||
for child in node.xpath(xpath):
|
||||
try:
|
||||
called_view = self._view_obj(cr, uid, child.get('t-call', child.get('t-call-assets')), context=context)
|
||||
called_view = self._view_obj(child.get('t-call', child.get('t-call-assets')))
|
||||
except ValueError:
|
||||
continue
|
||||
if called_view not in result:
|
||||
result += self._views_get(cr, uid, called_view, options=options, bundles=bundles, context=context)
|
||||
if called_view not in views_to_return:
|
||||
views_to_return += self._views_get(called_view, options=options, bundles=bundles)
|
||||
|
||||
extensions = view.inherit_children_ids
|
||||
if not options:
|
||||
# only active children
|
||||
extensions = (v for v in view.inherit_children_ids if v.active)
|
||||
extensions = view.inherit_children_ids.filtered(lambda view: view.active)
|
||||
|
||||
# Keep options in a deterministic order regardless of their applicability
|
||||
for extension in sorted(extensions, key=lambda v: v.id):
|
||||
for r in self._views_get(
|
||||
cr, uid, extension,
|
||||
# only return optional grandchildren if this child is enabled
|
||||
options=extension.active,
|
||||
context=context, root=False):
|
||||
if r not in result:
|
||||
result.append(r)
|
||||
return result
|
||||
for extension in extensions.sorted(key=lambda v: v.id):
|
||||
# only return optional grandchildren if this child is enabled
|
||||
for ext_view in self._views_get(extension, options=extension.active, root=False):
|
||||
if ext_view not in views_to_return:
|
||||
views_to_return += ext_view
|
||||
return views_to_return
|
||||
|
||||
@tools.ormcache_context('uid', 'xml_id', keys=('website_id',))
|
||||
def get_view_id(self, cr, uid, xml_id, context=None):
|
||||
if context and 'website_id' in context and not isinstance(xml_id, (int, long)):
|
||||
domain = [('key', '=', xml_id), '|', ('website_id', '=', context['website_id']), ('website_id', '=', False)]
|
||||
[view_id] = self.search(cr, uid, domain, order='website_id', limit=1, context=context) or [None]
|
||||
if not view_id:
|
||||
_logger.warning("Could not find view object with xml_id '%s'" % (xml_id))
|
||||
raise ValueError('View %r in website %r not found' % (xml_id, context['website_id']))
|
||||
else:
|
||||
view_id = super(view, self).get_view_id(cr, uid, xml_id, context=context)
|
||||
return view_id
|
||||
@api.model
|
||||
@tools.ormcache_context('self._uid', 'xml_id', keys=('website_id',))
|
||||
def get_view_id(self, xml_id):
|
||||
if 'website_id' in self._context and not isinstance(xml_id, (int, long)):
|
||||
domain = [('key', '=', xml_id), '|', ('website_id', '=', self._context['website_id']), ('website_id', '=', False)]
|
||||
view = self.search(domain, order='website_id', limit=1)
|
||||
if not view:
|
||||
_logger.warning("Could not find view object with xml_id '%s'", xml_id)
|
||||
raise ValueError('View %r in website %r not found' % (xml_id, self._context['website_id']))
|
||||
return view.id
|
||||
return super(View, self).get_view_id(xml_id)
|
||||
|
||||
@api.cr_uid_ids_context
|
||||
def render(self, cr, uid, id_or_xml_id, values=None, engine='ir.qweb', context=None):
|
||||
@api.multi
|
||||
def render(self, values=None, engine='ir.qweb'):
|
||||
""" Render the template. If website is enabled on request, then extend rendering context with website values. """
|
||||
new_context = dict(self._context)
|
||||
if request and getattr(request, 'website_enabled', False):
|
||||
engine = 'ir.qweb'
|
||||
|
||||
if isinstance(id_or_xml_id, list):
|
||||
id_or_xml_id = id_or_xml_id[0]
|
||||
|
||||
qcontext = self._prepare_qcontext(cr, uid, context=context)
|
||||
qcontext = self._prepare_qcontext()
|
||||
|
||||
# add some values
|
||||
if values:
|
||||
@@ -153,78 +144,77 @@ class view(osv.osv):
|
||||
# in edit mode ir.ui.view will tag nodes
|
||||
if not qcontext.get('translatable') and not qcontext.get('rendering_bundle'):
|
||||
if qcontext.get('editable'):
|
||||
context = dict(context, inherit_branding=True)
|
||||
elif request.registry['res.users'].has_group(cr, uid, 'base.group_website_publisher'):
|
||||
context = dict(context, inherit_branding_auto=True)
|
||||
new_context = dict(self._context, inherit_branding=True)
|
||||
elif request.env.user.has_group('base.group_website_publisher'):
|
||||
new_context = dict(self._context, inherit_branding_auto=True)
|
||||
|
||||
view_obj = request.website.get_template(id_or_xml_id)
|
||||
if 'main_object' not in qcontext:
|
||||
qcontext['main_object'] = view_obj
|
||||
qcontext['main_object'] = self
|
||||
|
||||
values = qcontext
|
||||
|
||||
return super(view, self).render(cr, uid, id_or_xml_id, values=values, engine=engine, context=context)
|
||||
return super(View, self.with_context(new_context)).render(values, engine=engine)
|
||||
|
||||
def _prepare_qcontext(self, cr, uid, context=None):
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
company = self.pool['res.company'].browse(cr, SUPERUSER_ID, request.website.company_id.id, context=context)
|
||||
@api.model
|
||||
def _prepare_qcontext(self):
|
||||
""" Returns the qcontext : rendering context with website specific value (required
|
||||
to render website layout template)
|
||||
"""
|
||||
company = request.website.company_id.sudo()
|
||||
|
||||
editable = request.website.is_publisher()
|
||||
translatable = editable and context.get('lang') != request.website.default_lang_code
|
||||
translatable = editable and self._context.get('lang') != request.website.default_lang_code
|
||||
editable = not translatable and editable
|
||||
|
||||
qcontext = dict(
|
||||
context.copy(),
|
||||
self._context.copy(),
|
||||
website=request.website,
|
||||
url_for=website.url_for,
|
||||
slug=website.slug,
|
||||
res_company=company,
|
||||
user_id=self.pool.get("res.users").browse(cr, uid, uid),
|
||||
user_id=self.env["res.users"].browse(self.env.user.id),
|
||||
default_lang_code=request.website.default_lang_code,
|
||||
languages=request.website.get_languages(),
|
||||
translatable=translatable,
|
||||
editable=editable,
|
||||
menu_data=self.pool['ir.ui.menu'].load_menus_root(cr, uid, context=context) if request.website.is_user() else None,
|
||||
menu_data=self.env['ir.ui.menu'].load_menus_root() if request.website.is_user() else None,
|
||||
)
|
||||
return qcontext
|
||||
|
||||
def customize_template_get(self, cr, uid, key, full=False, bundles=False, context=None):
|
||||
@api.model
|
||||
def customize_template_get(self, key, full=False, bundles=False):
|
||||
""" Get inherit view's informations of the template ``key``. By default, only
|
||||
returns ``customize_show`` templates (which can be active or not), if
|
||||
``full=True`` returns inherit view's informations of the template ``key``.
|
||||
``bundles=True`` returns also the asset bundles
|
||||
returns ``customize_show`` templates (which can be active or not), if
|
||||
``full=True`` returns inherit view's informations of the template ``key``.
|
||||
``bundles=True`` returns also the asset bundles
|
||||
"""
|
||||
imd = self.pool['ir.model.data']
|
||||
theme_view_id = imd.xmlid_to_res_id(cr, uid, 'website.theme')
|
||||
user = self.pool['res.users'].browse(cr, uid, context=context)
|
||||
imd = self.env['ir.model.data']
|
||||
view_theme_id = imd.xmlid_to_res_id('website.theme')
|
||||
user = self.env.user
|
||||
user_groups = set(user.groups_id)
|
||||
views = self._views_get(
|
||||
cr, uid, key, bundles=bundles,
|
||||
context=dict(context or {}, active_test=False))
|
||||
views = self.with_context(active_test=False)._views_get(key, bundles=bundles)
|
||||
done = set()
|
||||
result = []
|
||||
for v in views:
|
||||
if not user_groups.issuperset(v.groups_id):
|
||||
for view in views:
|
||||
if not user_groups.issuperset(view.groups_id):
|
||||
continue
|
||||
if full or (v.customize_show and v.inherit_id.id != theme_view_id):
|
||||
if v.inherit_id not in done:
|
||||
if full or (view.customize_show and view.inherit_id.id != view_theme_id):
|
||||
if view.inherit_id not in done:
|
||||
result.append({
|
||||
'name': v.inherit_id.name,
|
||||
'id': v.id,
|
||||
'key': v.key,
|
||||
'inherit_id': v.inherit_id.id,
|
||||
'name': view.inherit_id.name,
|
||||
'id': view.id,
|
||||
'key': view.key,
|
||||
'inherit_id': view.inherit_id.id,
|
||||
'header': True,
|
||||
'active': False
|
||||
})
|
||||
done.add(v.inherit_id)
|
||||
done.add(view.inherit_id)
|
||||
result.append({
|
||||
'name': v.name,
|
||||
'id': v.id,
|
||||
'key': v.key,
|
||||
'inherit_id': v.inherit_id.id,
|
||||
'name': view.name,
|
||||
'id': view.id,
|
||||
'key': view.key,
|
||||
'inherit_id': view.inherit_id.id,
|
||||
'header': False,
|
||||
'active': v.active,
|
||||
'active': view.active,
|
||||
})
|
||||
return result
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import openerp
|
||||
from openerp.osv import osv, fields
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class res_company(osv.osv):
|
||||
class Company(models.Model):
|
||||
|
||||
_inherit = "res.company"
|
||||
|
||||
def google_map_img(self, cr, uid, ids, zoom=8, width=298, height=298, context=None):
|
||||
partner = self.browse(cr, openerp.SUPERUSER_ID, ids[0], context=context).partner_id
|
||||
return partner and partner.google_map_img(zoom, width, height, context=context) or None
|
||||
@api.multi
|
||||
def google_map_img(self, zoom=8, width=298, height=298):
|
||||
partner = self.sudo().partner_id
|
||||
return partner and partner.google_map_img(zoom, width, height) or None
|
||||
|
||||
def google_map_link(self, cr, uid, ids, zoom=8, context=None):
|
||||
partner = self.browse(cr, openerp.SUPERUSER_ID, ids[0], context=context).partner_id
|
||||
return partner and partner.google_map_link(zoom, context=context) or None
|
||||
@api.multi
|
||||
def google_map_link(self, zoom=8):
|
||||
partner = self.sudo().partner_id
|
||||
return partner and partner.google_map_link(zoom) or None
|
||||
|
||||
@@ -1,52 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp.osv import fields, osv
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class WebsiteConfigSettings(models.TransientModel):
|
||||
|
||||
class website_config_settings(osv.osv_memory):
|
||||
_name = 'website.config.settings'
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
_columns = {
|
||||
'website_id': fields.many2one('website', string="website", required=True),
|
||||
'website_name': fields.related('website_id', 'name', type="char", string="Website Name"),
|
||||
def _default_website(self):
|
||||
return self.env['website'].search([], limit=1)
|
||||
|
||||
'language_ids': fields.related('website_id', 'language_ids', type='many2many', relation='res.lang', string='Languages'),
|
||||
'default_lang_id': fields.related('website_id', 'default_lang_id', type='many2one', relation='res.lang', string='Default language'),
|
||||
'default_lang_code': fields.related('website_id', 'default_lang_code', type="char", string="Default language code"),
|
||||
'google_analytics_key': fields.related('website_id', 'google_analytics_key', type="char", string='Google Analytics Key'),
|
||||
|
||||
'social_twitter': fields.related('website_id', 'social_twitter', type="char", string='Twitter Account'),
|
||||
'social_facebook': fields.related('website_id', 'social_facebook', type="char", string='Facebook Account'),
|
||||
'social_github': fields.related('website_id', 'social_github', type="char", string='GitHub Account'),
|
||||
'social_linkedin': fields.related('website_id', 'social_linkedin', type="char", string='LinkedIn Account'),
|
||||
'social_youtube': fields.related('website_id', 'social_youtube', type="char", string='Youtube Account'),
|
||||
'social_googleplus': fields.related('website_id', 'social_googleplus', type="char", string='Google+ Account'),
|
||||
'compress_html': fields.related('website_id', 'compress_html', type="boolean", string='Compress rendered HTML for a better Google PageSpeed result'),
|
||||
'cdn_activated': fields.related('website_id', 'cdn_activated', type="boolean", string='Use a Content Delivery Network (CDN)'),
|
||||
'cdn_url': fields.related('website_id', 'cdn_url', type="char", string='CDN Base URL'),
|
||||
'cdn_filters': fields.related('website_id', 'cdn_filters', type="text", string='CDN Filters'),
|
||||
'module_website_form_editor': fields.boolean("Form builder: create and customize forms"),
|
||||
'module_website_version': fields.boolean("A/B testing and versioning"),
|
||||
'favicon': fields.related('website_id', 'favicon', type="binary", string="Favicon", help="This field holds the image used to display a favicon on the website."),
|
||||
}
|
||||
website_id = fields.Many2one('website', string="website", default=_default_website, required=True)
|
||||
website_name = fields.Char('Website Name', related='website_id.name')
|
||||
|
||||
def on_change_website_id(self, cr, uid, ids, website_id, context=None):
|
||||
if not website_id:
|
||||
return {'value': {}}
|
||||
website_data = self.pool.get('website').read(cr, uid, [website_id], [], context=context)[0]
|
||||
values = {'website_name': website_data['name']}
|
||||
for fname, v in website_data.items():
|
||||
if fname in self._columns:
|
||||
values[fname] = v[0] if v and self._columns[fname]._type == 'many2one' else v
|
||||
return {'value' : values}
|
||||
language_ids = fields.Many2many(related='website_id.language_ids', relation='res.lang')
|
||||
default_lang_id = fields.Many2one(related='website_id.default_lang_id', relation='res.lang')
|
||||
default_lang_code = fields.Char('Default language code', related='website_id.default_lang_code')
|
||||
google_analytics_key = fields.Char('Google Analytics Key', related='website_id.google_analytics_key')
|
||||
|
||||
# FIXME in trunk for god sake. Change the fields above to fields.char instead of fields.related,
|
||||
# and create the function set_website who will set the value on the website_id
|
||||
# create does not forward the values to the related many2one. Write does.
|
||||
def create(self, cr, uid, vals, context=None):
|
||||
config_id = super(website_config_settings, self).create(cr, uid, vals, context=context)
|
||||
self.write(cr, uid, config_id, vals, context=context)
|
||||
return config_id
|
||||
|
||||
_defaults = {
|
||||
'website_id': lambda self,cr,uid,c: self.pool.get('website').search(cr, uid, [], context=c)[0],
|
||||
}
|
||||
social_twitter = fields.Char(related='website_id.social_twitter')
|
||||
social_facebook = fields.Char(related='website_id.social_facebook')
|
||||
social_github = fields.Char(related='website_id.social_github')
|
||||
social_linkedin = fields.Char(related='website_id.social_linkedin')
|
||||
social_youtube = fields.Char(related='website_id.social_youtube')
|
||||
social_googleplus = fields.Char(related='website_id.social_googleplus')
|
||||
compress_html = fields.Boolean('Compress rendered HTML for a better Google PageSpeed result', related='website_id.compress_html')
|
||||
cdn_activated = fields.Boolean('Use a Content Delivery Network (CDN)', related='website_id.cdn_activated')
|
||||
cdn_url = fields.Char(related='website_id.cdn_url')
|
||||
cdn_filters = fields.Text(related='website_id.cdn_filters')
|
||||
module_website_form_editor = fields.Boolean("Form builde = create and customize forms")
|
||||
module_website_version = fields.Boolean("A/B testing and versioning")
|
||||
favicon = fields.Binary('Favicon', related='website_id.favicon')
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import werkzeug
|
||||
|
||||
import openerp
|
||||
from openerp.osv import orm, osv, fields
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
def urlplus(url, params):
|
||||
return werkzeug.Href(url)(params or None)
|
||||
|
||||
|
||||
class res_partner(osv.osv):
|
||||
class Partner(models.Model):
|
||||
|
||||
_inherit = "res.partner"
|
||||
|
||||
def google_map_img(self, cr, uid, ids, zoom=8, width=298, height=298, context=None):
|
||||
partner = self.browse(cr, uid, ids[0], context=context)
|
||||
@api.multi
|
||||
def google_map_img(self, zoom=8, width=298, height=298):
|
||||
params = {
|
||||
'center': '%s, %s %s, %s' % (partner.street or '', partner.city or '', partner.zip or '', partner.country_id and partner.country_id.name_get()[0][1] or ''),
|
||||
'center': '%s, %s %s, %s' % (self.street or '', self.city or '', self.zip or '', self.country_id and self.country_id.name_get()[0][1] or ''),
|
||||
'size': "%sx%s" % (height, width),
|
||||
'zoom': zoom,
|
||||
'sensor': 'false',
|
||||
}
|
||||
return urlplus('//maps.googleapis.com/maps/api/staticmap' , params)
|
||||
return urlplus('//maps.googleapis.com/maps/api/staticmap', params)
|
||||
|
||||
def google_map_link(self, cr, uid, ids, zoom=10, context=None):
|
||||
partner = self.browse(cr, uid, ids[0], context=context)
|
||||
@api.multi
|
||||
def google_map_link(self, zoom=10):
|
||||
params = {
|
||||
'q': '%s, %s %s, %s' % (partner.street or '', partner.city or '', partner.zip or '', partner.country_id and partner.country_id.name_get()[0][1] or ''),
|
||||
'q': '%s, %s %s, %s' % (self.street or '', self.city or '', self.zip or '', self.country_id and self.country_id.name_get()[0][1] or ''),
|
||||
'z': zoom,
|
||||
}
|
||||
return urlplus('https://maps.google.com/maps' , params)
|
||||
return urlplus('https://maps.google.com/maps', params)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import json
|
||||
|
||||
from openerp import api, models
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class WebsitePlanner(models.Model):
|
||||
|
||||
+318
-293
@@ -1,4 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import inspect
|
||||
import logging
|
||||
import math
|
||||
@@ -6,26 +8,34 @@ import unicodedata
|
||||
import re
|
||||
import urlparse
|
||||
import hashlib
|
||||
|
||||
from sys import maxint
|
||||
|
||||
import werkzeug
|
||||
from werkzeug.exceptions import NotFound
|
||||
|
||||
# optional python-slugify import (https://github.com/un33k/python-slugify)
|
||||
try:
|
||||
import slugify as slugify_lib
|
||||
except ImportError:
|
||||
slugify_lib = None
|
||||
|
||||
import openerp
|
||||
from openerp.tools.translate import _
|
||||
from openerp.osv import orm, osv, fields
|
||||
from openerp.tools import html_escape as escape, ustr, image_resize_and_sharpen, image_save_for_web
|
||||
from openerp.tools.safe_eval import safe_eval
|
||||
from openerp.http import request
|
||||
from werkzeug.exceptions import NotFound
|
||||
from odoo import api, fields, models
|
||||
from odoo import tools
|
||||
from odoo.tools import ustr
|
||||
from odoo.http import request
|
||||
from odoo.tools.translate import _
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
DEFAULT_CDN_FILTERS = [
|
||||
"^/[^/]+/static/",
|
||||
"^/web/(css|js)/",
|
||||
"^/web/image",
|
||||
"^/web/content",
|
||||
# retrocompatibility
|
||||
"^/website/image/",
|
||||
]
|
||||
|
||||
|
||||
def url_for(path_or_uri, lang=None):
|
||||
if isinstance(path_or_uri, unicode):
|
||||
path_or_uri = path_or_uri.encode('utf-8')
|
||||
@@ -58,6 +68,7 @@ def url_for(path_or_uri, lang=None):
|
||||
|
||||
return location.decode('utf-8')
|
||||
|
||||
|
||||
def is_multilang_url(local_url, langs=None):
|
||||
if not langs:
|
||||
langs = [lg[0] for lg in request.website.get_languages()]
|
||||
@@ -79,17 +90,20 @@ def is_multilang_url(local_url, langs=None):
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
####################################################
|
||||
# Slug API
|
||||
####################################################
|
||||
|
||||
def slugify(s, max_length=None):
|
||||
""" Transform a string to a slug that can be used in a url path.
|
||||
|
||||
This method will first try to do the job with python-slugify if present.
|
||||
Otherwise it will process string by stripping leading and ending spaces,
|
||||
converting unicode chars to ascii, lowering all chars and replacing spaces
|
||||
and underscore with hyphen "-".
|
||||
|
||||
:param s: str
|
||||
:param max_length: int
|
||||
:rtype: str
|
||||
This method will first try to do the job with python-slugify if present.
|
||||
Otherwise it will process string by stripping leading and ending spaces,
|
||||
converting unicode chars to ascii, lowering all chars and replacing spaces
|
||||
and underscore with hyphen "-".
|
||||
:param s: str
|
||||
:param max_length: int
|
||||
:rtype: str
|
||||
"""
|
||||
s = ustr(s)
|
||||
if slugify_lib:
|
||||
@@ -99,35 +113,27 @@ def slugify(s, max_length=None):
|
||||
except TypeError:
|
||||
pass
|
||||
uni = unicodedata.normalize('NFKD', s).encode('ascii', 'ignore').decode('ascii')
|
||||
slug = re.sub('[\W_]', ' ', uni).strip().lower()
|
||||
slug = re.sub('[-\s]+', '-', slug)
|
||||
slug_str = re.sub('[\W_]', ' ', uni).strip().lower()
|
||||
slug_str = re.sub('[-\s]+', '-', slug_str)
|
||||
|
||||
return slug_str[:max_length]
|
||||
|
||||
return slug[:max_length]
|
||||
|
||||
def slug(value):
|
||||
if isinstance(value, orm.browse_record):
|
||||
if isinstance(value, models.BaseModel):
|
||||
# [(id, name)] = value.name_get()
|
||||
id, name = value.id, value.display_name
|
||||
identifier, name = value.id, value.display_name
|
||||
else:
|
||||
# assume name_search result tuple
|
||||
id, name = value
|
||||
identifier, name = value
|
||||
slugname = slugify(name or '').strip().strip('-')
|
||||
if not slugname:
|
||||
return str(id)
|
||||
return "%s-%d" % (slugname, id)
|
||||
|
||||
return str(identifier)
|
||||
return "%s-%d" % (slugname, identifier)
|
||||
|
||||
# NOTE: as the pattern is used as it for the ModelConverter (ir_http.py), do not use any flags
|
||||
_UNSLUG_RE = re.compile(r'(?:(\w{1,2}|\w[A-Za-z0-9-_]+?\w)-)?(-?\d+)(?=$|/)')
|
||||
|
||||
DEFAULT_CDN_FILTERS = [
|
||||
"^/[^/]+/static/",
|
||||
"^/web/(css|js)/",
|
||||
"^/web/image",
|
||||
"^/web/content",
|
||||
# retrocompatibility
|
||||
"^/website/image/",
|
||||
]
|
||||
|
||||
def unslug(s):
|
||||
"""Extract slug and id from a string.
|
||||
@@ -139,72 +145,70 @@ def unslug(s):
|
||||
return m.group(1), int(m.group(2))
|
||||
|
||||
|
||||
class website(osv.osv):
|
||||
def _get_menu(self, cr, uid, ids, name, arg, context=None):
|
||||
res = {}
|
||||
menu_obj = self.pool.get('website.menu')
|
||||
for id in ids:
|
||||
menu_ids = menu_obj.search(cr, uid, [('parent_id', '=', False), ('website_id', '=', id)], order='id', context=context)
|
||||
res[id] = menu_ids and menu_ids[0] or False
|
||||
return res
|
||||
class Website(models.Model):
|
||||
|
||||
def _active_languages(self, cr, uid, context=None):
|
||||
return self.pool['res.lang'].search(cr, uid, [], context=context)
|
||||
|
||||
def _default_language(self, cr, uid, context=None):
|
||||
lang_code = self.pool['ir.values'].get_default(cr, uid, 'res.partner', 'lang')
|
||||
def_langs = self.pool['res.lang'].search(cr, uid, [('code', '=', lang_code)], context=context)
|
||||
return def_langs[0] if def_langs else self._active_languages(cr, uid, context=context)[0]
|
||||
|
||||
_name = "website" # Avoid website.website convention for conciseness (for new api). Got a special authorization from xmo and rco
|
||||
_name = "website" # Avoid website.website convention for conciseness (for new api). Got a special authorization from xmo and rco
|
||||
_description = "Website"
|
||||
_columns = {
|
||||
'name': fields.char('Website Name'),
|
||||
'domain': fields.char('Website Domain'),
|
||||
'company_id': fields.many2one('res.company', string="Company"),
|
||||
'language_ids': fields.many2many('res.lang', 'website_lang_rel', 'website_id', 'lang_id', 'Languages'),
|
||||
'default_lang_id': fields.many2one('res.lang', string="Default language"),
|
||||
'default_lang_code': fields.related('default_lang_id', 'code', type="char", string="Default language code", store=True),
|
||||
'social_twitter': fields.char('Twitter Account'),
|
||||
'social_facebook': fields.char('Facebook Account'),
|
||||
'social_github': fields.char('GitHub Account'),
|
||||
'social_linkedin': fields.char('LinkedIn Account'),
|
||||
'social_youtube': fields.char('Youtube Account'),
|
||||
'social_googleplus': fields.char('Google+ Account'),
|
||||
'google_analytics_key': fields.char('Google Analytics Key'),
|
||||
'user_id': fields.many2one('res.users', string='Public User'),
|
||||
'compress_html': fields.boolean('Compress HTML'),
|
||||
'cdn_activated': fields.boolean('Activate CDN for assets'),
|
||||
'cdn_url': fields.char('CDN Base URL'),
|
||||
'cdn_filters': fields.text('CDN Filters', help="URL matching those filters will be rewritten using the CDN Base URL"),
|
||||
'partner_id': fields.related('user_id','partner_id', type='many2one', relation='res.partner', string='Public Partner'),
|
||||
'menu_id': fields.function(_get_menu, relation='website.menu', type='many2one', string='Main Menu'),
|
||||
'favicon': fields.binary(string="Website Favicon", help="This field holds the image used to display a favicon on the website."),
|
||||
}
|
||||
_defaults = {
|
||||
'user_id': lambda self,cr,uid,c: self.pool['ir.model.data'].xmlid_to_res_id(cr, openerp.SUPERUSER_ID, 'base.public_user'),
|
||||
'company_id': lambda self,cr,uid,c: self.pool['ir.model.data'].xmlid_to_res_id(cr, openerp.SUPERUSER_ID,'base.main_company'),
|
||||
'compress_html': False,
|
||||
'cdn_activated': False,
|
||||
'cdn_url': '',
|
||||
'cdn_filters': '\n'.join(DEFAULT_CDN_FILTERS),
|
||||
'language_ids': _active_languages,
|
||||
'default_lang_id': _default_language,
|
||||
}
|
||||
|
||||
def _active_languages(self):
|
||||
return self.env['res.lang'].search([]).ids
|
||||
|
||||
def _default_language(self):
|
||||
lang_code = self.env['ir.values'].get_default('res.partner', 'lang')
|
||||
def_lang = self.env['res.lang'].search([('code', '=', lang_code)], limit=1)
|
||||
return def_lang.id if def_lang else self._active_languages()[0]
|
||||
|
||||
name = fields.Char('Website Name')
|
||||
domain = fields.Char('Website Domain')
|
||||
company_id = fields.Many2one('res.company', string="Company", default=lambda self: self.env.ref('base.main_company').id)
|
||||
language_ids = fields.Many2many('res.lang', 'website_lang_rel', 'website_id', 'lang_id', 'Languages', default=_active_languages)
|
||||
default_lang_id = fields.Many2one('res.lang', string="Default language", default=_default_language)
|
||||
default_lang_code = fields.Char(related='default_lang_id.code', string="Default language code", store=True)
|
||||
|
||||
social_twitter = fields.Char('Twitter Account')
|
||||
social_facebook = fields.Char('Facebook Account')
|
||||
social_github = fields.Char('GitHub Account')
|
||||
social_linkedin = fields.Char('LinkedIn Account')
|
||||
social_youtube = fields.Char('Youtube Account')
|
||||
social_googleplus = fields.Char('Google+ Account')
|
||||
google_analytics_key = fields.Char('Google Analytics Key')
|
||||
|
||||
user_id = fields.Many2one('res.users', string='Public User', default=lambda self: self.env.ref('base.public_user').id)
|
||||
compress_html = fields.Boolean('Compress HTML')
|
||||
cdn_activated = fields.Boolean('Activate CDN for assets')
|
||||
cdn_url = fields.Char('CDN Base URL', default='')
|
||||
cdn_filters = fields.Text('CDN Filters', default=lambda s: '\n'.join(DEFAULT_CDN_FILTERS), help="URL matching those filters will be rewritten using the CDN Base URL")
|
||||
partner_id = fields.Many2one(related='user_id.partner_id', relation='res.partner', string='Public Partner')
|
||||
menu_id = fields.Many2one('website.menu', compute='_compute_menu', string='Main Menu')
|
||||
favicon = fields.Binary(string="Website Favicon", help="This field holds the image used to display a favicon on the website.")
|
||||
|
||||
@api.multi
|
||||
def _compute_menu(self):
|
||||
Menu = self.env['website.menu']
|
||||
for website in self:
|
||||
website.menu_id = Menu.search([('parent_id', '=', False), ('website_id', '=', website.id)], order='id', limit=1).id
|
||||
|
||||
# cf. Wizard hack in website_views.xml
|
||||
def noop(self, *args, **kwargs):
|
||||
pass
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
@api.multi
|
||||
def write(self, values):
|
||||
self._get_languages.clear_cache(self)
|
||||
return super(website, self).write(cr, uid, ids, vals, context)
|
||||
return super(Website, self).write(values)
|
||||
|
||||
def new_page(self, cr, uid, name, template='website.default_page', ispage=True, context=None):
|
||||
context = context or {}
|
||||
imd = self.pool.get('ir.model.data')
|
||||
view = self.pool.get('ir.ui.view')
|
||||
template_module, template_name = template.split('.')
|
||||
#----------------------------------------------------------
|
||||
# Page Management
|
||||
#----------------------------------------------------------
|
||||
|
||||
@api.model
|
||||
def new_page(self, name, template='website.default_page', ispage=True):
|
||||
""" Create a new website page, and assign it a xmlid based on the given one
|
||||
:param name : the name of the page
|
||||
:param template : potential xml_id of the page to create
|
||||
"""
|
||||
template_module, dummy = template.split('.')
|
||||
website_id = self._context.get('website_id')
|
||||
|
||||
# completely arbitrary max_length
|
||||
page_name = slugify(name, max_length=50)
|
||||
@@ -212,77 +216,76 @@ class website(osv.osv):
|
||||
|
||||
# find a free xmlid
|
||||
inc = 0
|
||||
dom = [('website_id', '=', False), ('website_id', '=', context.get('website_id'))]
|
||||
while view.search(cr, openerp.SUPERUSER_ID, [('key', '=', page_xmlid), '|'] + dom, context=dict(context or {}, active_test=False)):
|
||||
domain_static = [('website_id', '=', False), ('website_id', '=', website_id)]
|
||||
while self.env['ir.ui.view'].with_context(active_test=False).sudo().search([('key', '=', page_xmlid), '|'] + domain_static):
|
||||
inc += 1
|
||||
page_xmlid = "%s.%s" % (template_module, page_name + (inc and "-%s" % inc or ""))
|
||||
page_xmlid = "%s.%s" % (template_module, page_name + ("-%s" % inc if inc else ""))
|
||||
page_name += (inc and "-%s" % inc or "")
|
||||
|
||||
# new page
|
||||
_, template_id = imd.get_object_reference(cr, uid, template_module, template_name)
|
||||
website_id = context.get('website_id')
|
||||
key = template_module+'.'+page_name
|
||||
page_id = view.copy(cr, uid, template_id, {'website_id': website_id, 'key': key}, context=context)
|
||||
page = view.browse(cr, uid, page_id, context=dict(context, lang=None))
|
||||
page.write({
|
||||
template_record = self.env.ref(template)
|
||||
key = '%s.%s' % (template_module, page_name)
|
||||
page = template_record.copy({'website_id': website_id, 'key': key})
|
||||
page.with_context(lang=None).write({
|
||||
'arch': page.arch.replace(template, page_xmlid),
|
||||
'name': page_name,
|
||||
'page': ispage,
|
||||
})
|
||||
return page_xmlid
|
||||
|
||||
def key_to_view_id(self, cr, uid, view_id, context=None):
|
||||
View = self.pool.get('ir.ui.view')
|
||||
return View.search(cr, uid, [
|
||||
def key_to_view_id(self, view_id):
|
||||
return self.env['ir.ui.view'].search([
|
||||
('id', '=', view_id),
|
||||
"|", ('website_id', '=', context.get('website_id')), ('website_id', '=', False),
|
||||
'|', ('website_id', '=', self._context.get('website_id')), ('website_id', '=', False),
|
||||
('page', '=', True),
|
||||
('type', '=', 'qweb')
|
||||
], context=context)
|
||||
])
|
||||
|
||||
def delete_page(self, cr, uid, view_id, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
View = self.pool.get('ir.ui.view')
|
||||
view_find = self.key_to_view_id(cr, uid, view_id, context=context)
|
||||
if view_find:
|
||||
View.unlink(cr, uid, view_find, context=context)
|
||||
|
||||
def rename_page(self, cr, uid, view_id, new_name, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
View = self.pool.get('ir.ui.view')
|
||||
view_find = self.key_to_view_id(cr, uid, view_id, context=context)
|
||||
if view_find:
|
||||
v = View.browse(cr, uid, view_find, context=context)
|
||||
@api.model
|
||||
def delete_page(self, view_id):
|
||||
""" Delete a page, given its identifier
|
||||
:param view_id : ir.ui.view identifier
|
||||
"""
|
||||
view = self.key_to_view_id(view_id)
|
||||
if view:
|
||||
view.unlink()
|
||||
|
||||
@api.model
|
||||
def rename_page(self, view_id, new_name):
|
||||
""" Change the name of the given page
|
||||
:param view_id : id of the view to rename
|
||||
:param new_name : name to use
|
||||
"""
|
||||
view = self.key_to_view_id(view_id)
|
||||
if view:
|
||||
# slugify the new name and prefix by module if
|
||||
# not already done by end user
|
||||
new_name = slugify(new_name, max_length=50)
|
||||
# Prefix by module if not already done by end user
|
||||
prefix = v.key.split('.')[0]
|
||||
prefix = view.key.split('.')[0]
|
||||
if not new_name.startswith(prefix):
|
||||
new_name = "%s.%s" % (prefix, new_name)
|
||||
|
||||
View.write(cr, uid, view_find, {
|
||||
view.write({
|
||||
'key': new_name,
|
||||
'arch_db': v.arch_db.replace(v.key, new_name, 1)
|
||||
'arch_db': view.arch_db.replace(view.key, new_name, 1)
|
||||
})
|
||||
return new_name
|
||||
return False
|
||||
|
||||
def page_search_dependencies(self, cr, uid, view_id=False, context=None):
|
||||
dep = {}
|
||||
@api.model
|
||||
def page_search_dependencies(self, view_id=False):
|
||||
""" Search dependencies just for information. It will not catch 100%
|
||||
of dependencies and False positive is more than possible
|
||||
Each module could add dependences in this dict
|
||||
:returns a dictionnary where key is the 'categorie' of object related to the given
|
||||
view, and the value is the list of text and link to the resource using given page
|
||||
"""
|
||||
dependencies = {}
|
||||
if not view_id:
|
||||
return dep
|
||||
return dependencies
|
||||
|
||||
# search dependencies just for information.
|
||||
# It will not catch 100% of dependencies and False positive is more than possible
|
||||
# Each module could add dependences in this dict
|
||||
if context is None:
|
||||
context = {}
|
||||
View = self.pool.get('ir.ui.view')
|
||||
Menu = self.pool.get('website.menu')
|
||||
|
||||
view = View.browse(cr, uid, view_id, context=context)
|
||||
website_id = context.get('website_id')
|
||||
view = self.env['ir.ui.view'].browse(view_id)
|
||||
website_id = self._context.get('website_id')
|
||||
name = view.key.replace("website.", "")
|
||||
fullname = "website.%s" % name
|
||||
|
||||
@@ -292,18 +295,18 @@ class website(osv.osv):
|
||||
'|', ('website_id', '=', website_id), ('website_id', '=', False),
|
||||
'|', ('arch_db', 'ilike', '/page/%s' % name), ('arch_db', 'ilike', '/page/%s' % fullname)
|
||||
]
|
||||
pages = View.search(cr, uid, page_search_dom, context=context)
|
||||
if pages:
|
||||
page_key = _('Page')
|
||||
dep[page_key] = []
|
||||
for page in View.browse(cr, uid, pages, context=context):
|
||||
|
||||
page_key = _('Page')
|
||||
pages = self.env['ir.ui.view'].search(page_search_dom)
|
||||
for page in pages:
|
||||
dependencies.setdefault(page_key, [])
|
||||
if page.page:
|
||||
dep[page_key].append({
|
||||
dependencies[page_key].append({
|
||||
'text': _('Page <b>%s</b> contains a link to this page') % page.key,
|
||||
'link': '/page/%s' % page.key
|
||||
})
|
||||
else:
|
||||
dep[page_key].append({
|
||||
dependencies[page_key].append({
|
||||
'text': _('Template <b>%s (id:%s)</b> contains a link to this page') % (page.key, page.id),
|
||||
'link': '#'
|
||||
})
|
||||
@@ -314,73 +317,65 @@ class website(osv.osv):
|
||||
'|', ('url', 'ilike', '/page/%s' % name), ('url', 'ilike', '/page/%s' % fullname)
|
||||
]
|
||||
|
||||
menus = Menu.search(cr, uid, menu_search_dom, context=context)
|
||||
if menus:
|
||||
menu_key = _('Menu')
|
||||
dep[menu_key] = []
|
||||
for menu in Menu.browse(cr, uid, menus, context=context):
|
||||
dep[menu_key].append({
|
||||
menu_key = _('Menu')
|
||||
menus = self.env['website.menu'].search(menu_search_dom)
|
||||
for menu in menus:
|
||||
dependencies.setdefault(menu_key, []).append({
|
||||
'text': _('This page is in the menu <b>%s</b>') % menu.name,
|
||||
'link': False
|
||||
})
|
||||
|
||||
return dep
|
||||
return dependencies
|
||||
|
||||
def page_for_name(self, cr, uid, ids, name, module='website', context=None):
|
||||
# whatever
|
||||
return '%s.%s' % (module, slugify(name, max_length=50))
|
||||
|
||||
def page_exists(self, cr, uid, ids, name, module='website', context=None):
|
||||
@api.model
|
||||
def page_exists(self, name, module='website'):
|
||||
try:
|
||||
name = (name or "").replace("/page/website.", "").replace("/page/", "")
|
||||
if not name:
|
||||
return False
|
||||
return self.pool["ir.model.data"].get_object_reference(cr, uid, module, name)
|
||||
return self.env.ref('%s.%s' % module, name)
|
||||
except:
|
||||
return False
|
||||
|
||||
@openerp.tools.ormcache('id')
|
||||
def _get_languages(self, cr, uid, id, context=None):
|
||||
website = self.browse(cr, uid, id)
|
||||
return [(lg.code, lg.name) for lg in website.language_ids]
|
||||
#----------------------------------------------------------
|
||||
# Languages
|
||||
#----------------------------------------------------------
|
||||
|
||||
def get_cdn_url(self, cr, uid, uri, context=None):
|
||||
# Currently only usable in a website_enable request context
|
||||
if request and request.website and not request.debug and request.website.user_id.id == request.uid:
|
||||
cdn_url = request.website.cdn_url
|
||||
cdn_filters = (request.website.cdn_filters or '').splitlines()
|
||||
for flt in cdn_filters:
|
||||
if flt and re.match(flt, uri):
|
||||
return urlparse.urljoin(cdn_url, uri)
|
||||
return uri
|
||||
@api.multi
|
||||
def get_languages(self):
|
||||
self.ensure_one()
|
||||
return self._get_languages()
|
||||
|
||||
def get_languages(self, cr, uid, ids, context=None):
|
||||
return self._get_languages(cr, uid, ids[0])
|
||||
@tools.cache('self.id')
|
||||
def _get_languages(self):
|
||||
return [(lg.code, lg.name) for lg in self.language_ids]
|
||||
|
||||
def get_alternate_languages(self, cr, uid, ids, req=None, context=None):
|
||||
@api.multi
|
||||
def get_alternate_languages(self, req=None):
|
||||
langs = []
|
||||
if req is None:
|
||||
req = request.httprequest
|
||||
default = self.get_current_website(cr, uid, context=context).default_lang_code
|
||||
default = self.get_current_website().default_lang_code
|
||||
shorts = []
|
||||
|
||||
def get_url_localized(router, lang):
|
||||
arguments = dict(request.endpoint_arguments)
|
||||
for k, v in arguments.items():
|
||||
if isinstance(v, orm.browse_record):
|
||||
arguments[k] = v.with_context(lang=lang)
|
||||
for key, val in arguments.items():
|
||||
if isinstance(val, models.BaseModel):
|
||||
arguments[key] = val.with_context(lang=lang)
|
||||
return router.build(request.endpoint, arguments)
|
||||
|
||||
router = request.httprequest.app.get_db_router(request.db).bind('')
|
||||
for code, name in self.get_languages(cr, uid, ids, context=context):
|
||||
for code, dummy in self.get_languages():
|
||||
lg_path = ('/' + code) if code != default else ''
|
||||
lg = code.split('_')
|
||||
shorts.append(lg[0])
|
||||
uri = request.endpoint and get_url_localized(router, code) or request.httprequest.path
|
||||
lg_codes = code.split('_')
|
||||
shorts.append(lg_codes[0])
|
||||
uri = get_url_localized(router, code) if request.endpoint else request.httprequest.path
|
||||
if req.query_string:
|
||||
uri += '?' + req.query_string
|
||||
lang = {
|
||||
'hreflang': ('-'.join(lg)).lower(),
|
||||
'short': lg[0],
|
||||
'hreflang': ('-'.join(lg_codes)).lower(),
|
||||
'short': lg_codes[0],
|
||||
'href': req.url_root[0:-1] + lg_path + uri,
|
||||
}
|
||||
langs.append(lang)
|
||||
@@ -389,47 +384,68 @@ class website(osv.osv):
|
||||
lang['hreflang'] = lang['short']
|
||||
return langs
|
||||
|
||||
@openerp.tools.ormcache('domain_name')
|
||||
def _get_current_website_id(self, cr, uid, domain_name, context=None):
|
||||
ids = self.search(cr, uid, [('domain', '=', domain_name)], limit=1, context=context)
|
||||
return ids and ids[0] or self.search(cr, uid, [], limit=1)[0]
|
||||
#----------------------------------------------------------
|
||||
# Utilities
|
||||
#----------------------------------------------------------
|
||||
|
||||
def get_current_website(self, cr, uid, context=None):
|
||||
@api.model
|
||||
def get_current_website(self):
|
||||
domain_name = request.httprequest.environ.get('HTTP_HOST', '').split(':')[0]
|
||||
website_id = self._get_current_website_id(cr, uid, domain_name)
|
||||
website_id = self._get_current_website_id(domain_name)
|
||||
request.context['website_id'] = website_id
|
||||
return self.browse(cr, uid, website_id, context=context)
|
||||
return self.browse(website_id)
|
||||
|
||||
def is_publisher(self, cr, uid, ids, context=None):
|
||||
Access = self.pool['ir.model.access']
|
||||
is_website_publisher = Access.check(cr, uid, 'ir.ui.view', 'write', False, context=context)
|
||||
return is_website_publisher
|
||||
@tools.cache('domain_name')
|
||||
def _get_current_website_id(self, domain_name):
|
||||
""" Reminder : cached method should be return record, since they will use a closed cursor. """
|
||||
website = self.search([('domain', '=', domain_name)], limit=1)
|
||||
if not website:
|
||||
website = self.search([], limit=1)
|
||||
return website.id
|
||||
|
||||
def is_user(self, cr, uid, ids, context=None):
|
||||
Access = self.pool['ir.model.access']
|
||||
return Access.check(cr, uid, 'ir.ui.menu', 'read', False, context=context)
|
||||
@api.model
|
||||
def is_publisher(self):
|
||||
return self.env['ir.model.access'].check('ir.ui.view', 'write', False)
|
||||
|
||||
def get_template(self, cr, uid, ids, template, context=None):
|
||||
View = self.pool['ir.ui.view']
|
||||
@api.model
|
||||
def is_user(self):
|
||||
return self.env['ir.model.access'].check('ir.ui.menu', 'read', False)
|
||||
|
||||
@api.model
|
||||
def get_template(self, template):
|
||||
View = self.env['ir.ui.view']
|
||||
if isinstance(template, (int, long)):
|
||||
view_id = template
|
||||
else:
|
||||
if '.' not in template:
|
||||
template = 'website.%s' % template
|
||||
view_id = View.get_view_id(cr, uid, template, context=context)
|
||||
view_id = View.get_view_id(template)
|
||||
if not view_id:
|
||||
raise NotFound
|
||||
return View.browse(cr, uid, view_id, context=context)
|
||||
return View.browse(view_id)
|
||||
|
||||
def _render(self, cr, uid, ids, template, values=None, context=None):
|
||||
@api.model
|
||||
def render(self, template, values=None, status_code=None):
|
||||
# TODO: remove this. (just kept for backward api compatibility for saas-3)
|
||||
return self.pool['ir.ui.view'].render_template(cr, uid, template, values=values, context=context)
|
||||
return request.render(template, values, uid=self._uid)
|
||||
|
||||
def render(self, cr, uid, ids, template, values=None, status_code=None, context=None):
|
||||
def _render(self, template, values=None):
|
||||
# TODO: remove this. (just kept for backward api compatibility for saas-3)
|
||||
return request.render(template, values, uid=uid)
|
||||
return self.env['ir.ui.view'].render_template(template, values=values)
|
||||
|
||||
def pager(self, cr, uid, ids, url, total, page=1, step=30, scope=5, url_args=None, context=None):
|
||||
@api.model
|
||||
def pager(self, url, total, page=1, step=30, scope=5, url_args=None):
|
||||
""" Generate a dict with required value to render `website.pager` template. This method compute
|
||||
url, page range to display, ... in the pager.
|
||||
:param url : base url of the page link
|
||||
:param total : number total of item to be splitted into pages
|
||||
:param page : current page
|
||||
:param step : item per page
|
||||
:param scope : number of page to display on pager
|
||||
:param url_args : additionnal parameters to add as query params to page url
|
||||
:type url_args : dict
|
||||
:returns dict
|
||||
"""
|
||||
# Compute Pager
|
||||
page_count = int(math.ceil(float(total) / step))
|
||||
|
||||
@@ -472,17 +488,15 @@ class website(osv.osv):
|
||||
'num': pmax
|
||||
},
|
||||
"pages": [
|
||||
{'url': get_url(page), 'num': page}
|
||||
for page in xrange(pmin, pmax+1)
|
||||
{'url': get_url(page), 'num': page} for page in xrange(pmin, pmax+1)
|
||||
]
|
||||
}
|
||||
|
||||
def rule_is_enumerable(self, rule):
|
||||
""" Checks that it is possible to generate sensible GET queries for
|
||||
a given rule (if the endpoint matches its own requirements)
|
||||
|
||||
:type rule: werkzeug.routing.Rule
|
||||
:rtype: bool
|
||||
a given rule (if the endpoint matches its own requirements)
|
||||
:type rule: werkzeug.routing.Rule
|
||||
:rtype: bool
|
||||
"""
|
||||
endpoint = rule.endpoint
|
||||
methods = endpoint.routing.get('methods') or ['GET']
|
||||
@@ -504,21 +518,20 @@ class website(osv.osv):
|
||||
args = spec.args[1:(-defaults_count or None)]
|
||||
|
||||
# check that all args have a converter
|
||||
return all( (arg in rule._converters) for arg in args)
|
||||
return all((arg in rule._converters) for arg in args)
|
||||
|
||||
def enumerate_pages(self, cr, uid, ids, query_string=None, context=None):
|
||||
@api.multi
|
||||
def enumerate_pages(self, query_string=None):
|
||||
""" Available pages in the website/CMS. This is mostly used for links
|
||||
generation and can be overridden by modules setting up new HTML
|
||||
controllers for dynamic pages (e.g. blog).
|
||||
|
||||
By default, returns template views marked as pages.
|
||||
|
||||
:param str query_string: a (user-provided) string, fetches pages
|
||||
matching the string
|
||||
:returns: a list of mappings with two keys: ``name`` is the displayable
|
||||
name of the resource (page), ``url`` is the absolute URL
|
||||
of the same.
|
||||
:rtype: list({name: str, url: str})
|
||||
generation and can be overridden by modules setting up new HTML
|
||||
controllers for dynamic pages (e.g. blog).
|
||||
By default, returns template views marked as pages.
|
||||
:param str query_string: a (user-provided) string, fetches pages
|
||||
matching the string
|
||||
:returns: a list of mappings with two keys: ``name`` is the displayable
|
||||
name of the resource (page), ``url`` is the absolute URL
|
||||
of the same.
|
||||
:rtype: list({name: str, url: str})
|
||||
"""
|
||||
router = request.httprequest.app.get_db_router(request.db)
|
||||
# Force enumeration to be performed as public user
|
||||
@@ -533,23 +546,23 @@ class website(osv.osv):
|
||||
values = [{}]
|
||||
convitems = converters.items()
|
||||
# converters with a domain are processed after the other ones
|
||||
gd = lambda x: hasattr(x[1], 'domain') and (x[1].domain <> '[]')
|
||||
gd = lambda x: hasattr(x[1], 'domain') and (x[1].domain != '[]')
|
||||
convitems.sort(lambda x, y: cmp(gd(x), gd(y)))
|
||||
for (i,(name, converter)) in enumerate(convitems):
|
||||
for (i, (name, converter)) in enumerate(convitems):
|
||||
newval = []
|
||||
for val in values:
|
||||
query = i==(len(convitems)-1) and query_string
|
||||
for v in converter.generate(request.cr, uid, query=query, args=val, context=context):
|
||||
newval.append( val.copy() )
|
||||
v[name] = v['loc']
|
||||
del v['loc']
|
||||
newval[-1].update(v)
|
||||
query = i == len(convitems)-1 and query_string
|
||||
for value_dict in converter.generate(query=query, args=val):
|
||||
newval.append(val.copy())
|
||||
value_dict[name] = value_dict['loc']
|
||||
del value_dict['loc']
|
||||
newval[-1].update(value_dict)
|
||||
values = newval
|
||||
|
||||
for value in values:
|
||||
domain_part, url = rule.build(value, append_unknown=False)
|
||||
page = {'loc': url}
|
||||
for key,val in value.items():
|
||||
for key, val in value.items():
|
||||
if key.startswith('__'):
|
||||
page[key[2:]] = val
|
||||
if url in ('/sitemap.xml',):
|
||||
@@ -560,54 +573,63 @@ class website(osv.osv):
|
||||
|
||||
yield page
|
||||
|
||||
def search_pages(self, cr, uid, ids, needle=None, limit=None, context=None):
|
||||
@api.multi
|
||||
def search_pages(self, needle=None, limit=None):
|
||||
name = re.sub(r"^/p(a(g(e(/(w(e(b(s(i(t(e(\.)?)?)?)?)?)?)?)?)?)?)?)?", "", needle or "")
|
||||
res = []
|
||||
for page in self.enumerate_pages(cr, uid, ids, query_string=name, context=context):
|
||||
for page in self.enumerate_pages(query_string=name):
|
||||
if needle in page['loc']:
|
||||
res.append(page)
|
||||
if len(res) == limit:
|
||||
break
|
||||
return res
|
||||
|
||||
def image_url(self, cr, uid, record, field, size=None, context=None):
|
||||
"""Returns a local url that points to the image field of a given browse record."""
|
||||
@api.model
|
||||
def image_url(self, record, field, size=None):
|
||||
""" Returns a local url that points to the image field of a given browse record. """
|
||||
sudo_record = record.sudo()
|
||||
sha = hashlib.sha1(getattr(sudo_record, '__last_update')).hexdigest()[0:7]
|
||||
size = '' if size is None else '/%s' % size
|
||||
return '/web/image/%s/%s/%s%s?unique=%s' % (record._name, record.id, field, size, sha)
|
||||
|
||||
class website_menu(osv.osv):
|
||||
@api.model
|
||||
def get_cdn_url(self, uri):
|
||||
# Currently only usable in a website_enable request context
|
||||
if request and request.website and not request.debug and request.website.user_id.id == request.uid:
|
||||
cdn_url = request.website.cdn_url
|
||||
cdn_filters = (request.website.cdn_filters or '').splitlines()
|
||||
for flt in cdn_filters:
|
||||
if flt and re.match(flt, uri):
|
||||
return urlparse.urljoin(cdn_url, uri)
|
||||
return uri
|
||||
|
||||
|
||||
class Menu(models.Model):
|
||||
|
||||
_name = "website.menu"
|
||||
_description = "Website Menu"
|
||||
_columns = {
|
||||
'name': fields.char('Menu', required=True, translate=True),
|
||||
'url': fields.char('Url'),
|
||||
'new_window': fields.boolean('New Window'),
|
||||
'sequence': fields.integer('Sequence'),
|
||||
# TODO: support multiwebsite once done for ir.ui.views
|
||||
'website_id': fields.many2one('website', 'Website'),
|
||||
'parent_id': fields.many2one('website.menu', 'Parent Menu', select=True, ondelete="cascade"),
|
||||
'child_id': fields.one2many('website.menu', 'parent_id', string='Child Menus'),
|
||||
'parent_left': fields.integer('Parent Left', select=True),
|
||||
'parent_right': fields.integer('Parent Right', select=True),
|
||||
}
|
||||
|
||||
def __defaults_sequence(self, cr, uid, context):
|
||||
menu = self.search_read(cr, uid, [(1,"=",1)], ["sequence"], limit=1, order="sequence DESC", context=context)
|
||||
return menu and menu[0]["sequence"] or 0
|
||||
|
||||
_defaults = {
|
||||
'url': '',
|
||||
'sequence': __defaults_sequence,
|
||||
'new_window': False,
|
||||
}
|
||||
_parent_store = True
|
||||
_parent_order = 'sequence'
|
||||
_order = "sequence"
|
||||
|
||||
def _default_sequence(self):
|
||||
menu = self.search(["sequence"], limit=1, order="sequence DESC")
|
||||
return menu.sequence or 0
|
||||
|
||||
name = fields.Char('Menu', required=True, translate=True)
|
||||
url = fields.Char('Url', default='')
|
||||
new_window = fields.Boolean('New Window')
|
||||
sequence = fields.Integer(defualt=_default_sequence)
|
||||
website_id = fields.Many2one('website', 'Website') # TODO: support multiwebsite once done for ir.ui.views
|
||||
parent_id = fields.Many2one('website.menu', 'Parent Menu', index=True, ondelete="cascade")
|
||||
child_id = fields.One2many('website.menu', 'parent_id', string='Child Menus')
|
||||
parent_left = fields.Integer('Parent Left', index=True)
|
||||
parent_right = fields.Integer('Parent Rigth', index=True)
|
||||
|
||||
# would be better to take a menu_id as argument
|
||||
def get_tree(self, cr, uid, website_id, menu_id=None, context=None):
|
||||
@api.model
|
||||
def get_tree(self, website_id, menu_id=None):
|
||||
def make_tree(node):
|
||||
menu_node = dict(
|
||||
id=node.id,
|
||||
@@ -622,12 +644,13 @@ class website_menu(osv.osv):
|
||||
menu_node['children'].append(make_tree(child))
|
||||
return menu_node
|
||||
if menu_id:
|
||||
menu = self.browse(cr, uid, menu_id, context=context)
|
||||
menu = self.browse(menu_id)
|
||||
else:
|
||||
menu = self.pool.get('website').browse(cr, uid, website_id, context=context).menu_id
|
||||
menu = self.env['website'].browse(website_id).menu_id
|
||||
return make_tree(menu)
|
||||
|
||||
def save(self, cr, uid, website_id, data, context=None):
|
||||
@api.model
|
||||
def save(self, website_id, data):
|
||||
def replace_id(old_id, new_id):
|
||||
for menu in data['data']:
|
||||
if menu['id'] == old_id:
|
||||
@@ -636,52 +659,54 @@ class website_menu(osv.osv):
|
||||
menu['parent_id'] = new_id
|
||||
to_delete = data['to_delete']
|
||||
if to_delete:
|
||||
self.unlink(cr, uid, to_delete, context=context)
|
||||
self.browse(to_delete).unlink()
|
||||
for menu in data['data']:
|
||||
mid = menu['id']
|
||||
if isinstance(mid, basestring):
|
||||
new_id = self.create(cr, uid, {'name': menu['name']}, context=context)
|
||||
replace_id(mid, new_id)
|
||||
new_menu = self.create({'name': menu['name']})
|
||||
replace_id(mid, new_menu.id)
|
||||
for menu in data['data']:
|
||||
self.write(cr, uid, [menu['id']], menu, context=context)
|
||||
self.browse(menu['id']).write(menu)
|
||||
return True
|
||||
|
||||
|
||||
class website_seo_metadata(osv.AbstractModel):
|
||||
class SeoMetadata(models.AbstractModel):
|
||||
|
||||
_name = 'website.seo.metadata'
|
||||
_description = 'SEO metadata'
|
||||
|
||||
_columns = {
|
||||
'website_meta_title': fields.char("Website meta title", translate=True),
|
||||
'website_meta_description': fields.text("Website meta description", translate=True),
|
||||
'website_meta_keywords': fields.char("Website meta keywords", translate=True),
|
||||
}
|
||||
website_meta_title = fields.Char("Website meta title", translate=True)
|
||||
website_meta_description = fields.Text("Website meta description", translate=True)
|
||||
website_meta_keywords = fields.Char("Website meta keywords", translate=True)
|
||||
|
||||
|
||||
class website_published_mixin(osv.AbstractModel):
|
||||
class WebsitePublishedMixin(models.AbstractModel):
|
||||
|
||||
_name = "website.published.mixin"
|
||||
|
||||
_website_url_proxy = lambda self, *a, **kw: self._website_url(*a, **kw)
|
||||
website_published = fields.Boolean('Visible in Website', copy=False)
|
||||
website_url = fields.Char('Website URL', compute='_website_url_proxy', help='The full URL to access the document through the website.')
|
||||
|
||||
_columns = {
|
||||
'website_published': fields.boolean('Visible in Website', copy=False),
|
||||
'website_url': fields.function(_website_url_proxy, type='char', string='Website URL',
|
||||
help='The full URL to access the document through the website.'),
|
||||
}
|
||||
# TODO remove proxy and _website_url unused arguments
|
||||
def _website_url_proxy(self):
|
||||
result = self._website_url(False, False)
|
||||
for website in self:
|
||||
website.website_url = result.get(website.id)
|
||||
|
||||
def _website_url(self, cr, uid, ids, field_name, arg, context=None):
|
||||
return dict.fromkeys(ids, '#')
|
||||
@api.multi
|
||||
def _website_url(self, field_name, arg):
|
||||
return dict.fromkeys(self.ids, '#')
|
||||
|
||||
def website_publish_button(self, cr, uid, ids, context=None):
|
||||
for i in self.browse(cr, uid, ids, context):
|
||||
if self.pool['res.users'].has_group(cr, uid, 'base.group_website_publisher') and i.website_url != '#':
|
||||
return self.open_website_url(cr, uid, ids, context)
|
||||
i.write({'website_published': not i.website_published})
|
||||
return True
|
||||
@api.multi
|
||||
def website_publish_button(self):
|
||||
self.ensure_one()
|
||||
if self.env.user.has_group('base.group_website_publisher') and self.website_url != '#':
|
||||
return self.open_website_url()
|
||||
return self.write({'website_published': not self.website_published})
|
||||
|
||||
def open_website_url(self, cr, uid, ids, context=None):
|
||||
def open_website_url(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': self.browse(cr, uid, ids[0]).website_url,
|
||||
'url': self.website_url,
|
||||
'target': 'self',
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<odoo>
|
||||
<record id="website_designer_edit_qweb" model="ir.rule">
|
||||
<field name="name">website_designer: Manage Website and qWeb view</field>
|
||||
<field name="model_id" ref="base.model_ir_ui_view"/>
|
||||
@@ -31,5 +30,4 @@
|
||||
<field name="perm_create" eval="True"/>
|
||||
<field name="perm_unlink" eval="True"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import unittest
|
||||
from openerp.addons.website.models.website import slugify, unslug
|
||||
from odoo.addons.website.models.website import slugify, unslug
|
||||
|
||||
class TestUnslug(unittest.TestCase):
|
||||
def test_unslug(self):
|
||||
|
||||
@@ -5,13 +5,13 @@ import time
|
||||
|
||||
import lxml.html
|
||||
|
||||
import openerp
|
||||
import odoo
|
||||
import re
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
class Crawler(openerp.tests.HttpCase):
|
||||
""" Test suite crawling an openerp CMS instance and checking that all
|
||||
class Crawler(odoo.tests.HttpCase):
|
||||
""" Test suite crawling an Odoo CMS instance and checking that all
|
||||
internal links lead to a 200 response.
|
||||
|
||||
If a username and a password are provided, authenticates the user before
|
||||
@@ -22,7 +22,7 @@ class Crawler(openerp.tests.HttpCase):
|
||||
post_install = True
|
||||
|
||||
def crawl(self, url, seen=None, msg=''):
|
||||
if seen == None:
|
||||
if seen is None:
|
||||
seen = set()
|
||||
|
||||
url_slug = re.sub(r"[/](([^/=?&]+-)?[0-9]+)([/]|$)", '/<slug>/', url)
|
||||
@@ -35,7 +35,7 @@ class Crawler(openerp.tests.HttpCase):
|
||||
_logger.info("%s %s", msg, url)
|
||||
r = self.url_open(url)
|
||||
code = r.getcode()
|
||||
self.assertIn( code, xrange(200, 300), "%s Fetching %s returned error response (%d)" % (msg, url, code))
|
||||
self.assertIn(code, xrange(200, 300), "%s Fetching %s returned error response (%d)" % (msg, url, code))
|
||||
|
||||
if r.info().gettype() == 'text/html':
|
||||
doc = lxml.html.fromstring(r.read())
|
||||
@@ -64,7 +64,6 @@ class Crawler(openerp.tests.HttpCase):
|
||||
self.crawl(href, seen, msg)
|
||||
return seen
|
||||
|
||||
|
||||
def test_10_crawl_public(self):
|
||||
t0 = time.time()
|
||||
t0_sql = self.registry.test_cr.sql_log_count
|
||||
@@ -72,7 +71,7 @@ class Crawler(openerp.tests.HttpCase):
|
||||
count = len(seen)
|
||||
duration = time.time() - t0
|
||||
sql = self.registry.test_cr.sql_log_count - t0_sql
|
||||
_logger.log(25, "public crawled %s urls in %.2fs %s queries, %.3fs %.2fq per request, ", count, duration, sql, duration/count, float(sql)/count)
|
||||
_logger.log(25, "public crawled %s urls in %.2fs %s queries, %.3fs %.2fq per request, ", count, duration, sql, duration / count, float(sql) / count)
|
||||
|
||||
def test_20_crawl_demo(self):
|
||||
t0 = time.time()
|
||||
@@ -82,7 +81,7 @@ class Crawler(openerp.tests.HttpCase):
|
||||
count = len(seen)
|
||||
duration = time.time() - t0
|
||||
sql = self.registry.test_cr.sql_log_count - t0_sql
|
||||
_logger.log(25, "demo crawled %s urls in %.2fs %s queries, %.3fs %.2fq per request", count, duration, sql, duration/count, float(sql)/count)
|
||||
_logger.log(25, "demo crawled %s urls in %.2fs %s queries, %.3fs %.2fq per request", count, duration, sql, duration / count, float(sql) / count)
|
||||
|
||||
def test_30_crawl_admin(self):
|
||||
t0 = time.time()
|
||||
@@ -92,4 +91,4 @@ class Crawler(openerp.tests.HttpCase):
|
||||
count = len(seen)
|
||||
duration = time.time() - t0
|
||||
sql = self.registry.test_cr.sql_log_count - t0_sql
|
||||
_logger.log(25, "admin crawled %s urls in %.2fs %s queries, %.3fs %.2fq per request", count, duration, sql, duration/count, float(sql)/count)
|
||||
_logger.log(25, "admin crawled %s urls in %.2fs %s queries, %.3fs %.2fq per request", count, duration, sql, duration / count, float(sql) / count)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import openerp.tests
|
||||
import odoo.tests
|
||||
|
||||
|
||||
class TestUi(openerp.tests.HttpCase):
|
||||
class TestUi(odoo.tests.HttpCase):
|
||||
def test_01_public_homepage(self):
|
||||
self.phantom_js("/", "console.log('ok')", "'website.snippets.animation' in odoo.__DEBUG__.services")
|
||||
|
||||
|
||||
@@ -208,18 +208,16 @@ class TestViewSaving(common.TransactionCase):
|
||||
def test_save_only_embedded(self):
|
||||
Company = self.registry('res.company')
|
||||
company_id = 1
|
||||
Company.write(self.cr, self.uid, company_id, {'name': "Foo Corporation"})
|
||||
company = Company.browse(company_id)
|
||||
company.write({'name': "Foo Corporation"})
|
||||
|
||||
node = html.tostring(h.SPAN(
|
||||
"Acme Corporation",
|
||||
attrs(model='res.company', id=company_id, field="name", expression='bob', type='char')))
|
||||
View = self.env['ir.ui.view']
|
||||
View.browse(company_id).save(value=node)
|
||||
|
||||
company = Company.browse(self.cr, self.uid, company_id)
|
||||
self.assertEqual(company.name, "Acme Corporation")
|
||||
|
||||
|
||||
def test_field_tail(self):
|
||||
View = self.env['ir.ui.view']
|
||||
replacement = ET.tostring(
|
||||
@@ -231,7 +229,7 @@ class TestViewSaving(common.TransactionCase):
|
||||
View.browse(self.view_id).save(value=replacement, xpath='/div/div[2]/ul/li[3]')
|
||||
|
||||
self.eq(
|
||||
ET.fromstring(View.browse(self.view_id).arch.encode('utf-8')),
|
||||
ET.fromstring(self.view_id.arch.encode('utf-8')),
|
||||
h.DIV(
|
||||
h.DIV(
|
||||
h.H3("Column 1"),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<odoo>
|
||||
|
||||
<record id="view_website_config_settings" model="ir.ui.view">
|
||||
<field name="name">Website settings</field>
|
||||
@@ -12,7 +11,7 @@
|
||||
<button string="Cancel" type="object" name="cancel" class="oe_link"/>
|
||||
</header>
|
||||
<div>
|
||||
<field name="website_id" on_change="on_change_website_id(website_id)" invisible="1"/>
|
||||
<field name="website_id" invisible="1"/>
|
||||
<group string="Domain">
|
||||
<field name="website_name" />
|
||||
<label for="google_analytics_key"/>
|
||||
@@ -159,5 +158,4 @@
|
||||
sequence="100" name="Configuration" groups="base.group_system"/>
|
||||
<menuitem id="menu_website_website_settings" parent="menu_website_global_configuration" name="Settings" action="action_website_configuration"/>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
@@ -1,41 +1,35 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import openerp
|
||||
from openerp.osv import orm, osv, fields
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class base_language_install(osv.osv_memory):
|
||||
class BaseLanguageInstall(models.TransientModel):
|
||||
|
||||
_inherit = "base.language.install"
|
||||
_columns = {
|
||||
'website_ids': fields.many2many('website', string='Websites to translate'),
|
||||
}
|
||||
|
||||
def default_get(self, cr, uid, fields, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
defaults = super(base_language_install, self).default_get(cr, uid, fields, context)
|
||||
website_id = context.get('params', {}).get('website_id')
|
||||
website_ids = fields.Many2many('website', string='Websites to translate')
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
defaults = super(BaseLanguageInstall, self).default_get(fields)
|
||||
website_id = self._context.get('params', {}).get('website_id')
|
||||
if website_id:
|
||||
if 'website_ids' not in defaults:
|
||||
defaults['website_ids'] = []
|
||||
defaults['website_ids'].append(website_id)
|
||||
return defaults
|
||||
|
||||
def lang_install(self, cr, uid, ids, context=None):
|
||||
if context is None:
|
||||
context = {}
|
||||
action = super(base_language_install, self).lang_install(cr, uid, ids, context)
|
||||
language_obj = self.browse(cr, uid, ids)[0]
|
||||
website_ids = [website.id for website in language_obj['website_ids']]
|
||||
lang_id = self.pool['res.lang'].search(cr, uid, [('code', '=', language_obj['lang'])])
|
||||
if website_ids and lang_id:
|
||||
data = {'language_ids': [(4, lang_id[0])]}
|
||||
self.pool['website'].write(cr, uid, website_ids, data)
|
||||
params = context.get('params', {})
|
||||
@api.multi
|
||||
def lang_install(self):
|
||||
action = super(BaseLanguageInstall, self).lang_install()
|
||||
lang = self.env['res.lang'].search([('code', '=', self.lang)], limit=1)
|
||||
if self.website_ids and lang:
|
||||
self.website_ids.write({'language_ids': [(4, lang.id)]})
|
||||
params = self._context.get('params', {})
|
||||
if 'url_return' in params:
|
||||
return {
|
||||
'url': params['url_return'].replace('[lang]', language_obj['lang']),
|
||||
'url': params['url_return'].replace('[lang]', self.lang),
|
||||
'type': 'ir.actions.act_url',
|
||||
'target': 'self'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user