[MERGE]Merge lp:~openerp-dev/openobject-addons/trunk-website-al.
bzr revid: bth@tinyerp.com-20131108112213-3yhd0lg7j1zy0qut
This commit is contained in:
commit
723c47907a
|
@ -155,7 +155,6 @@
|
|||
<field name="type" ref="event_type_4"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="address_id" ref="base.res_partner_2"/>
|
||||
<field name="organizer_id" ref="base.res_partner_address_4"/>
|
||||
<field name="description"><![CDATA[
|
||||
<div class="oe_structure">
|
||||
<center><strong>5-days Technical Training</strong></center>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import base64
|
||||
import cStringIO
|
||||
import contextlib
|
||||
import hashlib
|
||||
|
@ -38,7 +37,7 @@ logger = logging.getLogger(__name__)
|
|||
def auth_method_public():
|
||||
registry = openerp.modules.registry.RegistryManager.get(request.db)
|
||||
if not request.session.uid:
|
||||
request.uid = registry['website'].get_public_user().id
|
||||
request.uid = registry['website'].get_public_user(request.cr, openerp.SUPERUSER_ID, request.context).id
|
||||
else:
|
||||
request.uid = request.session.uid
|
||||
http.auth_methods['public'] = auth_method_public
|
||||
|
@ -49,7 +48,19 @@ MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768)
|
|||
class Website(openerp.addons.web.controllers.main.Home):
|
||||
@website.route('/', type='http', auth="public", multilang=True)
|
||||
def index(self, **kw):
|
||||
return self.page("website.homepage")
|
||||
# TODO: check if plain SQL is needed
|
||||
menu = request.registry['website.menu']
|
||||
root_domain = [('parent_id', '=', False)] # TODO: multiwebsite ('website_id', '=', request.website.id),
|
||||
root_id = menu.search(request.cr, request.uid, root_domain, limit=1, context=request.context)[0]
|
||||
first_menu = menu.search_read(
|
||||
request.cr, request.uid, [('parent_id', '=', root_id)], ['url'],
|
||||
limit=1, order='sequence', context=request.context)
|
||||
if first_menu:
|
||||
first_menu = first_menu[0]['url']
|
||||
if first_menu and first_menu != '/':
|
||||
return request.redirect(first_menu)
|
||||
else:
|
||||
return self.page("website.homepage")
|
||||
|
||||
@website.route('/pagenew/<path:path>', type='http', auth="user")
|
||||
def pagenew(self, path, noredirect=NOPE):
|
||||
|
@ -124,11 +135,8 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
values = {
|
||||
'path': path,
|
||||
}
|
||||
try:
|
||||
html = request.website.render(path, values)
|
||||
except ValueError:
|
||||
html = request.website.render('website.404', values)
|
||||
return html
|
||||
|
||||
return request.website.render(path, values)
|
||||
|
||||
@website.route('/website/customize_template_toggle', type='json', auth='user')
|
||||
def customize_template_set(self, view_id):
|
||||
|
@ -269,7 +277,8 @@ class Website(openerp.addons.web.controllers.main.Home):
|
|||
|
||||
@website.route(['/robots.txt'], type='http', auth="public")
|
||||
def robots(self):
|
||||
return request.website.render('website.robots', {'url_root': request.httprequest.url_root})
|
||||
body = request.website.render('website.robots', {'url_root': request.httprequest.url_root})
|
||||
return request.make_response(body, headers=[('Content-Type', 'text/plain')])
|
||||
|
||||
@website.route('/sitemap', type='http', auth='public', multilang=True)
|
||||
def sitemap(self, **kwargs):
|
||||
|
|
|
@ -8,6 +8,24 @@
|
|||
<field name="default_lang_id" ref="base.lang_en"/>
|
||||
</record>
|
||||
|
||||
<record id="main_menu" model="website.menu">
|
||||
<field name="name">Main Menu</field>
|
||||
</record>
|
||||
|
||||
<record id="menu_homepage" model="website.menu">
|
||||
<field name="name">Homepage</field>
|
||||
<field name="url">/</field>
|
||||
<field name="parent_id" ref="website.main_menu"/>
|
||||
<field name="sequence" type="int">10</field>
|
||||
</record>
|
||||
|
||||
<record id="menu_contactus" model="website.menu">
|
||||
<field name="name">Contact us</field>
|
||||
<field name="url">/page/website.contactus</field>
|
||||
<field name="parent_id" ref="website.main_menu"/>
|
||||
<field name="sequence" type="int">20</field>
|
||||
</record>
|
||||
|
||||
<record id="public_user" model="res.users">
|
||||
<field name="name">public</field>
|
||||
<field name="login">public</field>
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import fnmatch
|
||||
import functools
|
||||
import logging
|
||||
import math
|
||||
import simplejson
|
||||
import traceback
|
||||
import urllib
|
||||
import urlparse
|
||||
|
||||
import werkzeug
|
||||
import werkzeug.exceptions
|
||||
import werkzeug.wrappers
|
||||
|
||||
import openerp
|
||||
from openerp.exceptions import AccessError, AccessDenied
|
||||
from openerp.osv import osv, fields
|
||||
from openerp.tools.safe_eval import safe_eval
|
||||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
import urllib
|
||||
from urlparse import urljoin
|
||||
import math
|
||||
import traceback
|
||||
from openerp.tools.safe_eval import safe_eval
|
||||
from openerp.exceptions import AccessError, AccessDenied
|
||||
import werkzeug
|
||||
from openerp.addons.base.ir.ir_qweb import QWebException
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def route(routes, *route_args, **route_kwargs):
|
||||
|
@ -33,47 +35,46 @@ def route(routes, *route_args, **route_kwargs):
|
|||
request.route_lang = kwargs.get('lang_code', None)
|
||||
if not hasattr(request, 'website'):
|
||||
request.multilang = f.multilang
|
||||
request.website = request.registry['website'].get_current()
|
||||
# TODO: Select website, currently hard coded
|
||||
request.website = request.registry['website'].browse(
|
||||
request.cr, request.uid, 1, context=request.context)
|
||||
|
||||
if request.route_lang:
|
||||
lang_ok = [lg.code for lg in request.website.language_ids if lg.code == request.route_lang]
|
||||
if not lang_ok:
|
||||
return request.not_found()
|
||||
request.website.preprocess_request(*args, **kwargs)
|
||||
request.website.preprocess_request(request)
|
||||
return f(*args, **kwargs)
|
||||
return wrap
|
||||
return decorator
|
||||
|
||||
def auth_method_public():
|
||||
registry = openerp.modules.registry.RegistryManager.get(request.db)
|
||||
if not request.session.uid:
|
||||
request.uid = registry['website'].get_public_user().id
|
||||
else:
|
||||
request.uid = request.session.uid
|
||||
http.auth_methods['public'] = auth_method_public
|
||||
|
||||
def url_for(path, lang=None, keep_query=None):
|
||||
if request:
|
||||
path = urljoin(request.httprequest.path, path)
|
||||
def url_for(path_or_uri, lang=None, keep_query=None):
|
||||
location = path_or_uri.strip()
|
||||
url = urlparse.urlparse(location)
|
||||
if request and not url.netloc and not url.scheme:
|
||||
location = urlparse.urljoin(request.httprequest.path, location)
|
||||
langs = request.context.get('langs')
|
||||
if path[0] == '/' and (len(langs) > 1 or lang):
|
||||
ps = path.split('/')
|
||||
if location[0] == '/' and (len(langs) > 1 or lang):
|
||||
ps = location.split('/')
|
||||
lang = lang or request.context.get('lang')
|
||||
if ps[1] in langs:
|
||||
ps[1] = lang
|
||||
else:
|
||||
ps.insert(1, lang)
|
||||
path = '/'.join(ps)
|
||||
location = '/'.join(ps)
|
||||
if keep_query:
|
||||
keep = []
|
||||
params = werkzeug.url_decode(request.httprequest.query_string)
|
||||
params_keys = tuple(params.keys())
|
||||
url = urlparse.urlparse(location)
|
||||
location = url.path
|
||||
params = werkzeug.url_decode(url.query)
|
||||
query_params = frozenset(werkzeug.url_decode(request.httprequest.query_string).keys())
|
||||
for kq in keep_query:
|
||||
keep += fnmatch.filter(params_keys, kq)
|
||||
if keep:
|
||||
params = dict([(k, params[k]) for k in keep])
|
||||
path += u'?%s' % werkzeug.urls.url_encode(params)
|
||||
for param in fnmatch.filter(query_params, kq):
|
||||
params[param] = request.params[param]
|
||||
params = werkzeug.urls.url_encode(params)
|
||||
if params:
|
||||
location += '?%s' % params
|
||||
|
||||
return path
|
||||
return location
|
||||
|
||||
def urlplus(url, params):
|
||||
if not params:
|
||||
|
@ -103,32 +104,28 @@ class website(osv.osv):
|
|||
|
||||
public_user = None
|
||||
|
||||
def get_public_user(self):
|
||||
def get_public_user(self, cr, uid, context=None):
|
||||
if not self.public_user:
|
||||
ref = request.registry['ir.model.data'].get_object_reference(request.cr, openerp.SUPERUSER_ID, 'website', 'public_user')
|
||||
self.public_user = request.registry[ref[0]].browse(request.cr, openerp.SUPERUSER_ID, ref[1])
|
||||
uid = openerp.SUPERUSER_ID
|
||||
ref = self.pool['ir.model.data'].get_object_reference(cr, uid, 'website', 'public_user')
|
||||
self.public_user = self.pool[ref[0]].browse(cr, uid, ref[1])
|
||||
return self.public_user
|
||||
|
||||
def get_lang(self):
|
||||
website = request.registry['website'].get_current()
|
||||
|
||||
if hasattr(request, 'route_lang'):
|
||||
lang = request.route_lang
|
||||
else:
|
||||
lang = request.params.get('lang', None) or request.httprequest.cookies.get('lang', None)
|
||||
|
||||
if lang not in [lg.code for lg in website.language_ids]:
|
||||
lang = website.default_lang_id.code
|
||||
|
||||
return lang
|
||||
|
||||
def preprocess_request(self, cr, uid, ids, *args, **kwargs):
|
||||
def preprocess_request(self, cr, uid, ids, request, context=None):
|
||||
def redirect(url):
|
||||
return werkzeug.utils.redirect(url_for(url))
|
||||
request.redirect = redirect
|
||||
|
||||
is_public_user = request.uid == self.get_public_user().id
|
||||
lang = self.get_lang()
|
||||
is_public_user = request.uid == self.get_public_user(cr, uid, context).id
|
||||
|
||||
# Select current language
|
||||
if hasattr(request, 'route_lang'):
|
||||
lang = request.route_lang
|
||||
else:
|
||||
lang = request.params.get('lang', None) or request.httprequest.cookies.get('lang', None)
|
||||
if lang not in [lg.code for lg in request.website.language_ids]:
|
||||
lang = request.website.default_lang_id.code
|
||||
|
||||
is_master_lang = lang == request.website.default_lang_id.code
|
||||
request.context.update({
|
||||
'lang': lang,
|
||||
|
@ -141,16 +138,15 @@ class website(osv.osv):
|
|||
'translatable': not is_public_user and not is_master_lang and request.multilang,
|
||||
})
|
||||
|
||||
def get_current(self):
|
||||
# WIP, currently hard coded
|
||||
return self.browse(request.cr, request.uid, 1)
|
||||
def render(self, cr, uid, ids, template, values=None, context=None):
|
||||
view = self.pool.get("ir.ui.view")
|
||||
IMD = self.pool.get("ir.model.data")
|
||||
user = self.pool.get("res.users")
|
||||
|
||||
def render(self, cr, uid, ids, template, values=None):
|
||||
view = request.registry.get("ir.ui.view")
|
||||
IMD = request.registry.get("ir.model.data")
|
||||
user = request.registry.get("res.users")
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
qweb_context = request.context.copy()
|
||||
qweb_context = context.copy()
|
||||
|
||||
if values:
|
||||
qweb_context.update(values)
|
||||
|
@ -164,7 +160,6 @@ class website(osv.osv):
|
|||
user_id=user.browse(cr, uid, uid),
|
||||
)
|
||||
|
||||
context = request.context.copy()
|
||||
context.update(
|
||||
inherit_branding=qweb_context.setdefault('editable', False),
|
||||
)
|
||||
|
@ -179,12 +174,11 @@ class website(osv.osv):
|
|||
try:
|
||||
view_ref = IMD.get_object_reference(cr, uid, module, xmlid)
|
||||
except ValueError:
|
||||
logger.error("Website Rendering Error.\n\n%s" % traceback.format_exc())
|
||||
return self.render(cr, uid, ids, 'website.404', qweb_context)
|
||||
return self.error(cr, uid, 404, qweb_context, context=context)
|
||||
|
||||
if 'main_object' not in qweb_context:
|
||||
try:
|
||||
main_object = request.registry[view_ref[0]].browse(cr, uid, view_ref[1])
|
||||
main_object = self.pool[view_ref[0]].browse(cr, uid, view_ref[1])
|
||||
qweb_context['main_object'] = main_object
|
||||
except Exception:
|
||||
pass
|
||||
|
@ -197,26 +191,25 @@ class website(osv.osv):
|
|||
logger.error(err)
|
||||
qweb_context['error'] = err[1]
|
||||
logger.warn("Website Rendering Error.\n\n%s" % traceback.format_exc())
|
||||
return self.render(cr, uid, ids, 'website.401', qweb_context)
|
||||
except (QWebException,), err:
|
||||
return self.error(cr, uid, 401, qweb_context, context=context)
|
||||
except Exception, e:
|
||||
qweb_context['template'] = getattr(e, 'qweb_template', '')
|
||||
node = getattr(e, 'qweb_node', None)
|
||||
qweb_context['node'] = node and node.toxml()
|
||||
qweb_context['expr'] = getattr(e, 'qweb_eval', '')
|
||||
qweb_context['traceback'] = traceback.format_exc()
|
||||
qweb_context['template'] = err.template
|
||||
qweb_context['message'] = err.message
|
||||
qweb_context['node'] = err.node and err.node.toxml()
|
||||
logger.error("Website Rendering Error.\n%(message)s\n%(node)s\n\n%(traceback)s" % qweb_context)
|
||||
return view.render(
|
||||
cr, uid,
|
||||
'website.500' if qweb_context['editable'] else 'website.404',
|
||||
qweb_context, context=context)
|
||||
except Exception:
|
||||
logger.exception("Website Rendering Error.")
|
||||
qweb_context['traceback'] = traceback.format_exc()
|
||||
return view.render(
|
||||
cr, uid,
|
||||
'website.500' if qweb_context['editable'] else 'website.404',
|
||||
qweb_context, context=context)
|
||||
logger.exception("Website Rendering Error.\n%(template)s\n%(expr)s\n%(node)s" % qweb_context)
|
||||
return self.error(cr, uid, 500 if qweb_context['editable'] else 404,
|
||||
qweb_context, context=context)
|
||||
|
||||
def pager(self, cr, uid, ids, url, total, page=1, step=30, scope=5, url_args=None):
|
||||
def error(self, cr, uid, code, qweb_context, context=None):
|
||||
View = request.registry['ir.ui.view']
|
||||
return werkzeug.wrappers.Response(
|
||||
View.render(cr, uid, 'website.%d' % code, qweb_context),
|
||||
status=code,
|
||||
content_type='text/html;charset=utf-8')
|
||||
|
||||
def pager(self, cr, uid, ids, url, total, page=1, step=30, scope=5, url_args=None, context=None):
|
||||
# Compute Pager
|
||||
page_count = int(math.ceil(float(total) / step))
|
||||
|
||||
|
@ -287,15 +280,15 @@ class website(osv.osv):
|
|||
if xids[view['id']]
|
||||
]
|
||||
|
||||
def kanban(self, cr, uid, ids, model, domain, column, template, step=None, scope=None, orderby=None):
|
||||
def kanban(self, cr, uid, ids, model, domain, column, template, step=None, scope=None, orderby=None, context=None):
|
||||
step = step and int(step) or 10
|
||||
scope = scope and int(scope) or 5
|
||||
orderby = orderby or "name"
|
||||
|
||||
get_args = dict(request.httprequest.args or {})
|
||||
model_obj = request.registry[model]
|
||||
model_obj = self.pool[model]
|
||||
relation = model_obj._columns.get(column)._obj
|
||||
relation_obj = request.registry[relation]
|
||||
relation_obj = self.pool[relation]
|
||||
|
||||
get_args.setdefault('kanban', "")
|
||||
kanban = get_args.pop('kanban')
|
||||
|
@ -349,9 +342,9 @@ class website(osv.osv):
|
|||
}
|
||||
return request.website.render("website.kanban_contain", values)
|
||||
|
||||
def kanban_col(self, cr, uid, ids, model, domain, page, template, step, orderby):
|
||||
def kanban_col(self, cr, uid, ids, model, domain, page, template, step, orderby, context=None):
|
||||
html = ""
|
||||
model_obj = request.registry[model]
|
||||
model_obj = self.pool[model]
|
||||
domain = safe_eval(domain)
|
||||
step = int(step)
|
||||
offset = (int(page)-1) * step
|
||||
|
@ -361,6 +354,74 @@ class website(osv.osv):
|
|||
html += request.website.render(template, {'object_id': object_id})
|
||||
return html
|
||||
|
||||
def get_menu(self, cr, uid, ids, context=None):
|
||||
return self.pool['website.menu'].get_menu(cr, uid, ids[0], context=context)
|
||||
|
||||
class website_menu(osv.osv):
|
||||
_name = "website.menu"
|
||||
_description = "Website Menu"
|
||||
_columns = {
|
||||
'name': fields.char('Menu', size=64, required=True, translate=True),
|
||||
'url': fields.char('Url', required=True, translate=True),
|
||||
'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),
|
||||
}
|
||||
_defaults = {
|
||||
'url': '',
|
||||
'sequence': 0,
|
||||
}
|
||||
_parent_store = True
|
||||
_parent_order = 'sequence, name'
|
||||
_order = "parent_left"
|
||||
|
||||
def get_menu(self, cr, uid, website_id, context=None):
|
||||
root_domain = [('parent_id', '=', False)] # ('website_id', '=', website_id),
|
||||
menu_ids = self.search(cr, uid, root_domain, context=context)
|
||||
menu = self.browse(cr, uid, menu_ids, context=context)
|
||||
return menu[0]
|
||||
|
||||
def get_tree(self, cr, uid, website_id, context=None):
|
||||
def make_tree(node):
|
||||
menu_node = dict(
|
||||
id=node.id,
|
||||
name=node.name,
|
||||
url=node.url,
|
||||
new_window=node.new_window,
|
||||
sequence=node.sequence,
|
||||
parent_id=node.parent_id.id,
|
||||
children=[],
|
||||
)
|
||||
for child in node.child_id:
|
||||
menu_node['children'].append(make_tree(child))
|
||||
return menu_node
|
||||
menu = self.get_menu(cr, uid, website_id, context=context)
|
||||
return make_tree(menu)
|
||||
|
||||
def save(self, cr, uid, website_id, data, context=None):
|
||||
def replace_id(old_id, new_id):
|
||||
for menu in data['data']:
|
||||
if menu['id'] == old_id:
|
||||
menu['id'] = new_id
|
||||
if menu['parent_id'] == old_id:
|
||||
menu['parent_id'] = new_id
|
||||
to_delete = data['to_delete']
|
||||
if to_delete:
|
||||
self.unlink(cr, uid, to_delete, context=context)
|
||||
for menu in data['data']:
|
||||
mid = menu['id']
|
||||
if isinstance(mid, str):
|
||||
new_id = self.create(cr, uid, {'name': menu['name']}, context=context)
|
||||
replace_id(mid, new_id)
|
||||
for menu in data['data']:
|
||||
self.write(cr, uid, [menu['id']], menu, context=context)
|
||||
return True
|
||||
|
||||
class ir_attachment(osv.osv):
|
||||
_inherit = "ir.attachment"
|
||||
def _website_url_get(self, cr, uid, ids, name, arg, context=None):
|
||||
|
@ -401,6 +462,15 @@ class res_partner(osv.osv):
|
|||
}
|
||||
return urlplus('https://maps.google.be/maps' , params)
|
||||
|
||||
class res_company(osv.osv):
|
||||
_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).parent_id
|
||||
return partner and partner.google_map_img(zoom, width, height, context=context) 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).parent_id
|
||||
return partner and partner.google_map_link(zoom, context=context) or None
|
||||
|
||||
class base_language_install(osv.osv):
|
||||
_inherit = "base.language.install"
|
||||
_columns = {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_website_public,website,website.model_website,base.group_public,1,0,0,0
|
||||
access_website,website,website.model_website,base.group_user,1,0,0,0
|
||||
access_website_menu,access_website_menu,model_website_menu,,1,0,0,0
|
||||
access_website_converter_test,access_website_converter_test,model_website_converter_test,,1,1,1,1
|
||||
access_website_converter_test_sub,access_website_converter_test_sub,model_website_converter_test_sub,,1,1,1,1
|
||||
access_website_qweb,access_website_qweb,model_website_qweb,,0,0,0,0
|
||||
|
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,429 @@
|
|||
/*
|
||||
* jQuery UI Nested Sortable
|
||||
* v 1.3.5 / 21 jun 2012
|
||||
* http://mjsarfatti.com/code/nestedSortable
|
||||
*
|
||||
* Depends on:
|
||||
* jquery.ui.sortable.js 1.8+
|
||||
*
|
||||
* Copyright (c) 2010-2012 Manuele J Sarfatti
|
||||
* Licensed under the MIT License
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
$.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, {
|
||||
|
||||
options: {
|
||||
tabSize: 20,
|
||||
disableNesting: 'mjs-nestedSortable-no-nesting',
|
||||
errorClass: 'mjs-nestedSortable-error',
|
||||
doNotClear: false,
|
||||
listType: 'ol',
|
||||
maxLevels: 0,
|
||||
protectRoot: false,
|
||||
rootID: null,
|
||||
rtl: false,
|
||||
isAllowed: function(item, parent) { return true; }
|
||||
},
|
||||
|
||||
_create: function() {
|
||||
this.element.data('sortable', this.element.data('nestedSortable'));
|
||||
|
||||
if (!this.element.is(this.options.listType))
|
||||
throw new Error('nestedSortable: Please check the listType option is set to your actual list type');
|
||||
|
||||
return $.ui.sortable.prototype._create.apply(this, arguments);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
this.element
|
||||
.removeData("nestedSortable")
|
||||
.unbind(".nestedSortable");
|
||||
return $.ui.sortable.prototype.destroy.apply(this, arguments);
|
||||
},
|
||||
|
||||
_mouseDrag: function(event) {
|
||||
|
||||
//Compute the helpers position
|
||||
this.position = this._generatePosition(event);
|
||||
this.positionAbs = this._convertPositionTo("absolute");
|
||||
|
||||
if (!this.lastPositionAbs) {
|
||||
this.lastPositionAbs = this.positionAbs;
|
||||
}
|
||||
|
||||
var o = this.options;
|
||||
|
||||
//Do scrolling
|
||||
if(this.options.scroll) {
|
||||
var scrolled = false;
|
||||
if(this.scrollParent[0] != document && this.scrollParent[0].tagName != 'HTML') {
|
||||
|
||||
if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity)
|
||||
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
|
||||
else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity)
|
||||
this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
|
||||
|
||||
if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity)
|
||||
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
|
||||
else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity)
|
||||
this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
|
||||
|
||||
} else {
|
||||
|
||||
if(event.pageY - $(document).scrollTop() < o.scrollSensitivity)
|
||||
scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
|
||||
else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity)
|
||||
scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
|
||||
|
||||
if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity)
|
||||
scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
|
||||
else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity)
|
||||
scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
|
||||
|
||||
}
|
||||
|
||||
if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour)
|
||||
$.ui.ddmanager.prepareOffsets(this, event);
|
||||
}
|
||||
|
||||
//Regenerate the absolute position used for position checks
|
||||
this.positionAbs = this._convertPositionTo("absolute");
|
||||
|
||||
// Find the top offset before rearrangement,
|
||||
var previousTopOffset = this.placeholder.offset().top;
|
||||
|
||||
//Set the helper position
|
||||
if(!this.options.axis || this.options.axis != "y") this.helper[0].style.left = this.position.left+'px';
|
||||
if(!this.options.axis || this.options.axis != "x") this.helper[0].style.top = this.position.top+'px';
|
||||
|
||||
//Rearrange
|
||||
for (var i = this.items.length - 1; i >= 0; i--) {
|
||||
|
||||
//Cache variables and intersection, continue if no intersection
|
||||
var item = this.items[i], itemElement = item.item[0], intersection = this._intersectsWithPointer(item);
|
||||
if (!intersection) continue;
|
||||
|
||||
if(itemElement != this.currentItem[0] //cannot intersect with itself
|
||||
&& this.placeholder[intersection == 1 ? "next" : "prev"]()[0] != itemElement //no useless actions that have been done before
|
||||
&& !$.contains(this.placeholder[0], itemElement) //no action if the item moved is the parent of the item checked
|
||||
&& (this.options.type == 'semi-dynamic' ? !$.contains(this.element[0], itemElement) : true)
|
||||
//&& itemElement.parentNode == this.placeholder[0].parentNode // only rearrange items within the same container
|
||||
) {
|
||||
|
||||
$(itemElement).mouseenter();
|
||||
|
||||
this.direction = intersection == 1 ? "down" : "up";
|
||||
|
||||
if (this.options.tolerance == "pointer" || this._intersectsWithSides(item)) {
|
||||
$(itemElement).mouseleave();
|
||||
this._rearrange(event, item);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
// Clear emtpy ul's/ol's
|
||||
this._clearEmpty(itemElement);
|
||||
|
||||
this._trigger("change", event, this._uiHash());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var parentItem = (this.placeholder[0].parentNode.parentNode &&
|
||||
$(this.placeholder[0].parentNode.parentNode).closest('.ui-sortable').length)
|
||||
? $(this.placeholder[0].parentNode.parentNode)
|
||||
: null,
|
||||
level = this._getLevel(this.placeholder),
|
||||
childLevels = this._getChildLevels(this.helper);
|
||||
|
||||
// To find the previous sibling in the list, keep backtracking until we hit a valid list item.
|
||||
var previousItem = this.placeholder[0].previousSibling ? $(this.placeholder[0].previousSibling) : null;
|
||||
if (previousItem != null) {
|
||||
while (previousItem[0].nodeName.toLowerCase() != 'li' || previousItem[0] == this.currentItem[0] || previousItem[0] == this.helper[0]) {
|
||||
if (previousItem[0].previousSibling) {
|
||||
previousItem = $(previousItem[0].previousSibling);
|
||||
} else {
|
||||
previousItem = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To find the next sibling in the list, keep stepping forward until we hit a valid list item.
|
||||
var nextItem = this.placeholder[0].nextSibling ? $(this.placeholder[0].nextSibling) : null;
|
||||
if (nextItem != null) {
|
||||
while (nextItem[0].nodeName.toLowerCase() != 'li' || nextItem[0] == this.currentItem[0] || nextItem[0] == this.helper[0]) {
|
||||
if (nextItem[0].nextSibling) {
|
||||
nextItem = $(nextItem[0].nextSibling);
|
||||
} else {
|
||||
nextItem = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var newList = document.createElement(o.listType);
|
||||
|
||||
this.beyondMaxLevels = 0;
|
||||
|
||||
// If the item is moved to the left, send it to its parent's level unless there are siblings below it.
|
||||
if (parentItem != null && nextItem == null &&
|
||||
(o.rtl && (this.positionAbs.left + this.helper.outerWidth() > parentItem.offset().left + parentItem.outerWidth()) ||
|
||||
!o.rtl && (this.positionAbs.left < parentItem.offset().left))) {
|
||||
parentItem.after(this.placeholder[0]);
|
||||
this._clearEmpty(parentItem[0]);
|
||||
this._trigger("change", event, this._uiHash());
|
||||
}
|
||||
// If the item is below a sibling and is moved to the right, make it a child of that sibling.
|
||||
else if (previousItem != null &&
|
||||
(o.rtl && (this.positionAbs.left + this.helper.outerWidth() < previousItem.offset().left + previousItem.outerWidth() - o.tabSize) ||
|
||||
!o.rtl && (this.positionAbs.left > previousItem.offset().left + o.tabSize))) {
|
||||
this._isAllowed(previousItem, level, level+childLevels+1);
|
||||
if (!previousItem.children(o.listType).length) {
|
||||
previousItem[0].appendChild(newList);
|
||||
}
|
||||
// If this item is being moved from the top, add it to the top of the list.
|
||||
if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) {
|
||||
previousItem.children(o.listType).prepend(this.placeholder);
|
||||
}
|
||||
// Otherwise, add it to the bottom of the list.
|
||||
else {
|
||||
previousItem.children(o.listType)[0].appendChild(this.placeholder[0]);
|
||||
}
|
||||
this._trigger("change", event, this._uiHash());
|
||||
}
|
||||
else {
|
||||
this._isAllowed(parentItem, level, level+childLevels);
|
||||
}
|
||||
|
||||
//Post events to containers
|
||||
this._contactContainers(event);
|
||||
|
||||
//Interconnect with droppables
|
||||
if($.ui.ddmanager) $.ui.ddmanager.drag(this, event);
|
||||
|
||||
//Call callbacks
|
||||
this._trigger('sort', event, this._uiHash());
|
||||
|
||||
this.lastPositionAbs = this.positionAbs;
|
||||
return false;
|
||||
|
||||
},
|
||||
|
||||
_mouseStop: function(event, noPropagation) {
|
||||
|
||||
// If the item is in a position not allowed, send it back
|
||||
if (this.beyondMaxLevels) {
|
||||
|
||||
this.placeholder.removeClass(this.options.errorClass);
|
||||
|
||||
if (this.domPosition.prev) {
|
||||
$(this.domPosition.prev).after(this.placeholder);
|
||||
} else {
|
||||
$(this.domPosition.parent).prepend(this.placeholder);
|
||||
}
|
||||
|
||||
this._trigger("revert", event, this._uiHash());
|
||||
|
||||
}
|
||||
|
||||
// Clean last empty ul/ol
|
||||
for (var i = this.items.length - 1; i >= 0; i--) {
|
||||
var item = this.items[i].item[0];
|
||||
this._clearEmpty(item);
|
||||
}
|
||||
|
||||
$.ui.sortable.prototype._mouseStop.apply(this, arguments);
|
||||
|
||||
},
|
||||
|
||||
serialize: function(options) {
|
||||
|
||||
var o = $.extend({}, this.options, options),
|
||||
items = this._getItemsAsjQuery(o && o.connected),
|
||||
str = [];
|
||||
|
||||
$(items).each(function() {
|
||||
var res = ($(o.item || this).attr(o.attribute || 'id') || '')
|
||||
.match(o.expression || (/(.+)[-=_](.+)/)),
|
||||
pid = ($(o.item || this).parent(o.listType)
|
||||
.parent(o.items)
|
||||
.attr(o.attribute || 'id') || '')
|
||||
.match(o.expression || (/(.+)[-=_](.+)/));
|
||||
|
||||
if (res) {
|
||||
str.push(((o.key || res[1]) + '[' + (o.key && o.expression ? res[1] : res[2]) + ']')
|
||||
+ '='
|
||||
+ (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID));
|
||||
}
|
||||
});
|
||||
|
||||
if(!str.length && o.key) {
|
||||
str.push(o.key + '=');
|
||||
}
|
||||
|
||||
return str.join('&');
|
||||
|
||||
},
|
||||
|
||||
toHierarchy: function(options) {
|
||||
|
||||
var o = $.extend({}, this.options, options),
|
||||
sDepth = o.startDepthCount || 0,
|
||||
ret = [];
|
||||
|
||||
$(this.element).children(o.items).each(function () {
|
||||
var level = _recursiveItems(this);
|
||||
ret.push(level);
|
||||
});
|
||||
|
||||
return ret;
|
||||
|
||||
function _recursiveItems(item) {
|
||||
var id = ($(item).attr(o.attribute || 'id') || '').match(o.expression || (/(.+)[-=_](.+)/));
|
||||
if (id) {
|
||||
var currentItem = {"id" : id[2]};
|
||||
if ($(item).children(o.listType).children(o.items).length > 0) {
|
||||
currentItem.children = [];
|
||||
$(item).children(o.listType).children(o.items).each(function() {
|
||||
var level = _recursiveItems(this);
|
||||
currentItem.children.push(level);
|
||||
});
|
||||
}
|
||||
return currentItem;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
toArray: function(options) {
|
||||
|
||||
var o = $.extend({}, this.options, options),
|
||||
sDepth = o.startDepthCount || 0,
|
||||
ret = [],
|
||||
left = 2;
|
||||
|
||||
ret.push({
|
||||
"item_id": o.rootID,
|
||||
"parent_id": 'none',
|
||||
"depth": sDepth,
|
||||
"left": '1',
|
||||
"right": ($(o.items, this.element).length + 1) * 2
|
||||
});
|
||||
|
||||
$(this.element).children(o.items).each(function () {
|
||||
left = _recursiveArray(this, sDepth + 1, left);
|
||||
});
|
||||
|
||||
ret = ret.sort(function(a,b){ return (a.left - b.left); });
|
||||
|
||||
return ret;
|
||||
|
||||
function _recursiveArray(item, depth, left) {
|
||||
|
||||
var right = left + 1,
|
||||
id,
|
||||
pid;
|
||||
|
||||
if ($(item).children(o.listType).children(o.items).length > 0) {
|
||||
depth ++;
|
||||
$(item).children(o.listType).children(o.items).each(function () {
|
||||
right = _recursiveArray($(this), depth, right);
|
||||
});
|
||||
depth --;
|
||||
}
|
||||
|
||||
id = ($(item).attr(o.attribute || 'id')).match(o.expression || (/(.+)[-=_](.+)/));
|
||||
|
||||
if (depth === sDepth + 1) {
|
||||
pid = o.rootID;
|
||||
} else {
|
||||
var parentItem = ($(item).parent(o.listType)
|
||||
.parent(o.items)
|
||||
.attr(o.attribute || 'id'))
|
||||
.match(o.expression || (/(.+)[-=_](.+)/));
|
||||
pid = parentItem[2];
|
||||
}
|
||||
|
||||
if (id) {
|
||||
ret.push({"item_id": id[2], "parent_id": pid, "depth": depth, "left": left, "right": right});
|
||||
}
|
||||
|
||||
left = right + 1;
|
||||
return left;
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_clearEmpty: function(item) {
|
||||
|
||||
var emptyList = $(item).children(this.options.listType);
|
||||
if (emptyList.length && !emptyList.children().length && !this.options.doNotClear) {
|
||||
emptyList.remove();
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_getLevel: function(item) {
|
||||
|
||||
var level = 1;
|
||||
|
||||
if (this.options.listType) {
|
||||
var list = item.closest(this.options.listType);
|
||||
while (list && list.length > 0 &&
|
||||
!list.is('.ui-sortable')) {
|
||||
level++;
|
||||
list = list.parent().closest(this.options.listType);
|
||||
}
|
||||
}
|
||||
|
||||
return level;
|
||||
},
|
||||
|
||||
_getChildLevels: function(parent, depth) {
|
||||
var self = this,
|
||||
o = this.options,
|
||||
result = 0;
|
||||
depth = depth || 0;
|
||||
|
||||
$(parent).children(o.listType).children(o.items).each(function (index, child) {
|
||||
result = Math.max(self._getChildLevels(child, depth + 1), result);
|
||||
});
|
||||
|
||||
return depth ? result + 1 : result;
|
||||
},
|
||||
|
||||
_isAllowed: function(parentItem, level, levels) {
|
||||
var o = this.options,
|
||||
isRoot = $(this.domPosition.parent).hasClass('ui-sortable') ? true : false,
|
||||
maxLevels = this.placeholder.closest('.ui-sortable').nestedSortable('option', 'maxLevels'); // this takes into account the maxLevels set to the recipient list
|
||||
|
||||
// Is the root protected?
|
||||
// Are we trying to nest under a no-nest?
|
||||
// Are we nesting too deep?
|
||||
if (!o.isAllowed(this.currentItem, parentItem) ||
|
||||
parentItem && parentItem.hasClass(o.disableNesting) ||
|
||||
o.protectRoot && (parentItem == null && !isRoot || isRoot && level > 1)) {
|
||||
this.placeholder.addClass(o.errorClass);
|
||||
if (maxLevels < levels && maxLevels != 0) {
|
||||
this.beyondMaxLevels = levels - maxLevels;
|
||||
} else {
|
||||
this.beyondMaxLevels = 1;
|
||||
}
|
||||
} else {
|
||||
if (maxLevels < levels && maxLevels != 0) {
|
||||
this.placeholder.addClass(o.errorClass);
|
||||
this.beyondMaxLevels = levels - maxLevels;
|
||||
} else {
|
||||
this.placeholder.removeClass(o.errorClass);
|
||||
this.beyondMaxLevels = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}));
|
||||
|
||||
$.mjs.nestedSortable.prototype.options = $.extend({}, $.ui.sortable.prototype.options, $.mjs.nestedSortable.prototype.options);
|
||||
})(jQuery);
|
|
@ -1,4 +1,3 @@
|
|||
@charset "utf-8";
|
||||
/* ---- CKEditor Minimal Reset ---- */
|
||||
.navbar.navbar-inverse .cke_chrome {
|
||||
border: none;
|
||||
|
@ -114,7 +113,7 @@ table.editorbar-panel td.selected {
|
|||
|
||||
.oe_translate_or {
|
||||
color: white;
|
||||
padding: 0 0.2em;
|
||||
padding: 0 0 0 1em;
|
||||
}
|
||||
|
||||
.oe_translate_examples li {
|
||||
|
@ -142,6 +141,84 @@ table.editorbar-panel td.selected {
|
|||
background: #ffffb6;
|
||||
}
|
||||
|
||||
div.oe_menu_buttons {
|
||||
top: -8px;
|
||||
right: -8px;
|
||||
}
|
||||
|
||||
ul.oe_menu_editor > li:first-child > div > i:before {
|
||||
content: "\f015";
|
||||
}
|
||||
ul.oe_menu_editor, ul.oe_menu_editor ul {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ul.oe_menu_editor ul {
|
||||
padding-left: 30px;
|
||||
}
|
||||
ul.oe_menu_editor button {
|
||||
margin-left: 4px;
|
||||
}
|
||||
ul.oe_menu_editor li {
|
||||
margin: 5px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
ul.oe_menu_editor .oe_menu_placeholder {
|
||||
outline: 1px dashed #4183c4;
|
||||
}
|
||||
ul.oe_menu_editor .mjs-nestedSortable-error {
|
||||
background: #fbe3e4;
|
||||
border-color: transparent;
|
||||
}
|
||||
ul.oe_menu_editor li div {
|
||||
border: 1px solid #d4d4d4;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
border-color: #d4d4d4 #d4d4d4 #bcbcbc;
|
||||
padding: 6px;
|
||||
margin: 0;
|
||||
cursor: move;
|
||||
background: #f6f6f6;
|
||||
background: -moz-linear-gradient(top, white 0%, #f6f6f6 47%, #ededed 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(47%, #f6f6f6), color-stop(100%, #ededed));
|
||||
background: -webkit-linear-gradient(top, white 0%, #f6f6f6 47%, #ededed 100%);
|
||||
background: -o-linear-gradient(top, white 0%, #f6f6f6 47%, #ededed 100%);
|
||||
background: -ms-linear-gradient(top, white 0%, #f6f6f6 47%, #ededed 100%);
|
||||
background: linear-gradient(to bottom, #ffffff 0%, #f6f6f6 47%, #ededed 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#ededed',GradientType=0 );
|
||||
}
|
||||
ul.oe_menu_editor li.mjs-nestedSortable-branch div {
|
||||
background: -moz-linear-gradient(top, white 0%, #f6f6f6 47%, #f0ece9 100%);
|
||||
background: -webkit-linear-gradient(top, white 0%, #f6f6f6 47%, #f0ece9 100%);
|
||||
}
|
||||
ul.oe_menu_editor li.mjs-nestedSortable-leaf div {
|
||||
background: -moz-linear-gradient(top, white 0%, #f6f6f6 47%, #bcccbc 100%);
|
||||
background: -webkit-linear-gradient(top, white 0%, #f6f6f6 47%, #bcccbc 100%);
|
||||
}
|
||||
ul.oe_menu_editor li.mjs-nestedSortable-collapsed.mjs-nestedSortable-hovering div {
|
||||
border-color: #999999;
|
||||
background: #fafafa;
|
||||
}
|
||||
ul.oe_menu_editor .disclose {
|
||||
cursor: pointer;
|
||||
width: 10px;
|
||||
display: none;
|
||||
}
|
||||
ul.oe_menu_editor li.mjs-nestedSortable-collapsed > ul {
|
||||
display: none;
|
||||
}
|
||||
ul.oe_menu_editor li.mjs-nestedSortable-branch > div > .disclose {
|
||||
display: inline-block;
|
||||
}
|
||||
ul.oe_menu_editor li.mjs-nestedSortable-collapsed > div > .disclose > span:before {
|
||||
content: "+ ";
|
||||
}
|
||||
ul.oe_menu_editor li.mjs-nestedSortable-expanded > div > .disclose > span:before {
|
||||
content: "- ";
|
||||
}
|
||||
|
||||
/* ---- RTE ---- */
|
||||
.oe_editable .btn {
|
||||
-webkit-user-select: auto;
|
||||
|
@ -161,6 +238,14 @@ table.editorbar-panel td.selected {
|
|||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.modal.nosave .modal-footer button.save {
|
||||
display: none;
|
||||
}
|
||||
.modal.nosave .modal-footer button.wait {
|
||||
display: inline-block !important;
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
.cke_widget_wrapper {
|
||||
position: static !important;
|
||||
}
|
||||
|
|
|
@ -108,7 +108,7 @@ table.editorbar-panel
|
|||
// ---- TRANSLATIONS ---- {{{
|
||||
.oe_translate_or
|
||||
color: white
|
||||
padding: 0 0.2em
|
||||
padding: 0 0 0 1em
|
||||
.oe_translate_examples li
|
||||
margin: 10px
|
||||
padding: 4px
|
||||
|
@ -124,6 +124,84 @@ table.editorbar-panel
|
|||
background: rgb(255, 255, 182)
|
||||
// }}}
|
||||
|
||||
// -------- MENU -------- {{{
|
||||
div.oe_menu_buttons
|
||||
top: -8px
|
||||
right: -8px
|
||||
ul.oe_menu_editor
|
||||
> li:first-child > div > i:before
|
||||
content: "\f015"
|
||||
&, & ul
|
||||
list-style-type: none
|
||||
margin: 0
|
||||
padding: 0
|
||||
& ul
|
||||
padding-left: 30px
|
||||
|
||||
button
|
||||
margin-left: 4px
|
||||
|
||||
li
|
||||
margin: 5px 0 0 0
|
||||
padding: 0
|
||||
|
||||
.oe_menu_placeholder
|
||||
outline: 1px dashed #4183C4
|
||||
|
||||
.mjs-nestedSortable-error
|
||||
background: #fbe3e4
|
||||
border-color: transparent
|
||||
|
||||
|
||||
// TODO: use compass mixins
|
||||
li div
|
||||
border: 1px solid #d4d4d4
|
||||
-webkit-border-radius: 3px
|
||||
-moz-border-radius: 3px
|
||||
border-radius: 3px
|
||||
border-color: #D4D4D4 #D4D4D4 #BCBCBC
|
||||
padding: 6px
|
||||
margin: 0
|
||||
cursor: move
|
||||
background: #f6f6f6
|
||||
background: -moz-linear-gradient(top, #ffffff 0%, #f6f6f6 47%, #ededed 100%)
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ffffff), color-stop(47%,#f6f6f6), color-stop(100%,#ededed))
|
||||
background: -webkit-linear-gradient(top, #ffffff 0%,#f6f6f6 47%,#ededed 100%)
|
||||
background: -o-linear-gradient(top, #ffffff 0%,#f6f6f6 47%,#ededed 100%)
|
||||
background: -ms-linear-gradient(top, #ffffff 0%,#f6f6f6 47%,#ededed 100%)
|
||||
background: linear-gradient(to bottom, #ffffff 0%,#f6f6f6 47%,#ededed 100%)
|
||||
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ffffff', endColorstr='#ededed',GradientType=0 )
|
||||
|
||||
li.mjs-nestedSortable-branch div
|
||||
background: -moz-linear-gradient(top, #ffffff 0%, #f6f6f6 47%, #f0ece9 100%)
|
||||
background: -webkit-linear-gradient(top, #ffffff 0%,#f6f6f6 47%,#f0ece9 100%)
|
||||
|
||||
li.mjs-nestedSortable-leaf div
|
||||
background: -moz-linear-gradient(top, #ffffff 0%, #f6f6f6 47%, #bcccbc 100%)
|
||||
background: -webkit-linear-gradient(top, #ffffff 0%,#f6f6f6 47%,#bcccbc 100%)
|
||||
|
||||
li.mjs-nestedSortable-collapsed.mjs-nestedSortable-hovering div
|
||||
border-color: #999
|
||||
background: #fafafa
|
||||
|
||||
.disclose
|
||||
cursor: pointer
|
||||
width: 10px
|
||||
display: none
|
||||
|
||||
li.mjs-nestedSortable-collapsed > ul
|
||||
display: none
|
||||
|
||||
li.mjs-nestedSortable-branch > div > .disclose
|
||||
display: inline-block
|
||||
|
||||
li.mjs-nestedSortable-collapsed > div > .disclose > span:before
|
||||
content: '+ '
|
||||
|
||||
li.mjs-nestedSortable-expanded > div > .disclose > span:before
|
||||
content: '- '
|
||||
// }}}
|
||||
|
||||
/* ---- RTE ---- */
|
||||
|
||||
// bootstrap makes .btn elements unselectable -> RTE double-click can't know
|
||||
|
@ -141,6 +219,13 @@ table.editorbar-panel
|
|||
.modal .image-preview
|
||||
margin-bottom: 0.5em
|
||||
|
||||
.modal.nosave .modal-footer
|
||||
button.save
|
||||
display: none
|
||||
button.wait
|
||||
display: inline-block !important
|
||||
visibility: visible !important
|
||||
|
||||
// wrapper positioned relatively for drag&drop widget which is disabled below.
|
||||
// Breaks completely horribly crazy products listing page, so take it out.
|
||||
.cke_widget_wrapper
|
||||
|
|
|
@ -239,7 +239,7 @@ footer {
|
|||
}
|
||||
|
||||
.oe_structure.oe_empty:empty:before, [data-oe-type=html]:empty:before, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before {
|
||||
content: "Click Edit To Create Content";
|
||||
content: "Press The Top-Left Edit Button";
|
||||
text-align: center;
|
||||
display: block;
|
||||
padding-top: 160px;
|
||||
|
|
|
@ -164,7 +164,7 @@ footer
|
|||
position: static
|
||||
|
||||
.oe_structure.oe_empty:empty:before, [data-oe-type=html]:empty:before, .oe_structure.oe_empty > .oe_drop_zone.oe_insert:only-child:before, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child:before
|
||||
content: 'Click Edit To Create Content'
|
||||
content: 'Press The Top-Left Edit Button'
|
||||
text-align: center
|
||||
display: block
|
||||
padding-top: 160px
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
|
@ -6,7 +6,7 @@
|
|||
var hash = "#advanced-view-editor";
|
||||
|
||||
var website = openerp.website;
|
||||
website.templates.push('/website/static/src/xml/website.ace.xml');
|
||||
website.add_template_file('/website/static/src/xml/website.ace.xml');
|
||||
|
||||
website.ready().then(function () {
|
||||
if (window.location.hash.indexOf(hash) >= 0) {
|
||||
|
|
|
@ -4,17 +4,26 @@
|
|||
var website = openerp.website;
|
||||
// $.fn.data automatically parses value, '0'|'1' -> 0|1
|
||||
|
||||
website.templates.push('/website/static/src/xml/website.editor.xml');
|
||||
website.add_template_file('/website/static/src/xml/website.editor.xml');
|
||||
website.dom_ready.done(function () {
|
||||
var is_smartphone = $(document.body)[0].clientWidth < 767;
|
||||
|
||||
if (!is_smartphone) {
|
||||
website.ready().then(website.init_editor);
|
||||
}
|
||||
|
||||
$(document).on('hide.bs.dropdown', '.dropdown', function (ev) {
|
||||
// Prevent dropdown closing when a contenteditable children is focused
|
||||
if (ev.originalEvent
|
||||
&& $(ev.target).has(ev.originalEvent.target).length
|
||||
&& $(ev.originalEvent.target).is('[contenteditable]')) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function link_dialog(editor) {
|
||||
return new website.editor.LinkDialog(editor).appendTo(document.body);
|
||||
return new website.editor.RTELinkDialog(editor).appendTo(document.body);
|
||||
}
|
||||
function image_dialog(editor) {
|
||||
return new website.editor.RTEImageDialog(editor).appendTo(document.body);
|
||||
|
@ -54,6 +63,39 @@
|
|||
link_dialog(editor);
|
||||
}, null, null, 500);
|
||||
|
||||
var previousSelection;
|
||||
editor.on('selectionChange', function (evt) {
|
||||
var selected = evt.data.path.lastElement;
|
||||
if (previousSelection) {
|
||||
// cleanup previous selection
|
||||
$(previousSelection).next().remove();
|
||||
previousSelection = null;
|
||||
}
|
||||
if (!selected.is('img')
|
||||
|| selected.data('cke-realelement')
|
||||
|| selected.isReadOnly()
|
||||
|| selected.data('oe-model') === 'ir.ui.view') {
|
||||
return;
|
||||
}
|
||||
|
||||
// display button
|
||||
var $el = $(previousSelection = selected.$);
|
||||
var $btn = $('<button type="button" class="btn btn-primary" contenteditable="false">Edit</button>')
|
||||
.insertAfter($el)
|
||||
.click(function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
image_dialog(editor);
|
||||
});
|
||||
|
||||
var position = $el.position();
|
||||
$btn.css({
|
||||
position: 'absolute',
|
||||
top: $el.height() / 2 + position.top - $btn.outerHeight() / 2,
|
||||
left: $el.width() / 2 + position.left - $btn.outerWidth() / 2,
|
||||
});
|
||||
});
|
||||
|
||||
//noinspection JSValidateTypes
|
||||
editor.addCommand('link', {
|
||||
exec: function (editor) {
|
||||
|
@ -361,7 +403,7 @@
|
|||
});
|
||||
// Adding Static Menus
|
||||
menu.append('<li class="divider"></li><li class="js_change_theme"><a href="/page/website.themes">Change Theme</a></li>');
|
||||
menu.append('<li class="divider"></li><li><a data-action="ace" href="#">Advanced view editor</a></li>');
|
||||
menu.append('<li class="divider"></li><li><a data-action="ace" href="#">HTML Editor</a></li>');
|
||||
self.trigger('rte:customize_menu_ready');
|
||||
}
|
||||
);
|
||||
|
@ -805,6 +847,106 @@
|
|||
this.pages = Object.create(null);
|
||||
this.text = null;
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
return $.when(
|
||||
this.fetch_pages().done(this.proxy('fill_pages')),
|
||||
this._super()
|
||||
).done(function () {
|
||||
self.bind_data();
|
||||
});
|
||||
},
|
||||
save: function () {
|
||||
var self = this, _super = this._super.bind(this);
|
||||
var $e = this.$('.list-group-item.active .url-source');
|
||||
var val = $e.val();
|
||||
if (!val || !$e[0].checkValidity()) {
|
||||
// FIXME: error message
|
||||
$e.closest('.form-group').addClass('has-error');
|
||||
$e.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
var done = $.when();
|
||||
if ($e.hasClass('email-address')) {
|
||||
this.make_link('mailto:' + val, false, val);
|
||||
} else if ($e.hasClass('existing')) {
|
||||
self.make_link(val, false, this.pages[val]);
|
||||
} else if ($e.hasClass('pages')) {
|
||||
// Create the page, get the URL back
|
||||
done = $.get(_.str.sprintf(
|
||||
'/pagenew/%s?noredirect', encodeURI(val)))
|
||||
.then(function (response) {
|
||||
self.make_link(response, false, val);
|
||||
});
|
||||
} else {
|
||||
this.make_link(val, this.$('input.window-new').prop('checked'));
|
||||
}
|
||||
done.then(_super);
|
||||
},
|
||||
make_link: function (url, new_window, label) {
|
||||
},
|
||||
bind_data: function (text, href, new_window) {
|
||||
href = href || this.element && (this.element.data( 'cke-saved-href')
|
||||
|| this.element.getAttribute('href'));
|
||||
if (!href) { return; }
|
||||
|
||||
if (new_window === undefined) {
|
||||
new_window = this.element.getAttribute('target') === '_blank';
|
||||
}
|
||||
if (text === undefined) {
|
||||
text = this.element.getText();
|
||||
}
|
||||
|
||||
var match, $control;
|
||||
if ((match = /mailto:(.+)/.exec(href))) {
|
||||
$control = this.$('input.email-address').val(match[1]);
|
||||
} else if (href in this.pages) {
|
||||
$control = this.$('select.existing').val(href);
|
||||
} else if ((match = /\/page\/(.+)/.exec(href))) {
|
||||
var actual_href = '/page/website.' + match[1];
|
||||
if (actual_href in this.pages) {
|
||||
$control = this.$('select.existing').val(actual_href);
|
||||
}
|
||||
}
|
||||
if (!$control) {
|
||||
$control = this.$('input.url').val(href);
|
||||
}
|
||||
|
||||
this.changed($control);
|
||||
|
||||
this.$('input#link-text').val(text);
|
||||
this.$('input.window-new').prop('checked', new_window);
|
||||
},
|
||||
changed: function ($e) {
|
||||
this.$('.url-source').not($e).val('');
|
||||
$e.closest('.list-group-item')
|
||||
.addClass('active')
|
||||
.siblings().removeClass('active')
|
||||
.addBack().removeClass('has-error');
|
||||
},
|
||||
fetch_pages: function () {
|
||||
return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
|
||||
model: 'website',
|
||||
method: 'list_pages',
|
||||
args: [null],
|
||||
kwargs: {
|
||||
context: website.get_context()
|
||||
},
|
||||
});
|
||||
},
|
||||
fill_pages: function (results) {
|
||||
var self = this;
|
||||
var pages = this.$('select.existing')[0];
|
||||
_(results).each(function (result) {
|
||||
self.pages[result.url] = result.name;
|
||||
|
||||
pages.options[pages.options.length] =
|
||||
new Option(result.name, result.url);
|
||||
});
|
||||
},
|
||||
});
|
||||
website.editor.RTELinkDialog = website.editor.LinkDialog.extend({
|
||||
start: function () {
|
||||
var element;
|
||||
if ((element = this.get_selected_link()) && element.hasAttribute('href')) {
|
||||
|
@ -815,10 +957,7 @@
|
|||
this.add_removal_button();
|
||||
}
|
||||
|
||||
return $.when(
|
||||
this.fetch_pages().done(this.proxy('fill_pages')),
|
||||
this._super()
|
||||
).done(this.proxy('bind_data'));
|
||||
return this._super();
|
||||
},
|
||||
add_removal_button: function () {
|
||||
this.$('.modal-footer').prepend(
|
||||
|
@ -884,66 +1023,6 @@
|
|||
}, 0);
|
||||
}
|
||||
},
|
||||
save: function () {
|
||||
var self = this, _super = this._super.bind(this);
|
||||
var $e = this.$('.list-group-item.active .url-source');
|
||||
var val = $e.val();
|
||||
if (!val || !$e[0].checkValidity()) {
|
||||
// FIXME: error message
|
||||
$e.closest('.form-group').addClass('has-error');
|
||||
return;
|
||||
}
|
||||
|
||||
var done = $.when();
|
||||
if ($e.hasClass('email-address')) {
|
||||
this.make_link('mailto:' + val, false, val);
|
||||
} else if ($e.hasClass('existing')) {
|
||||
self.make_link(val, false, this.pages[val]);
|
||||
} else if ($e.hasClass('pages')) {
|
||||
// Create the page, get the URL back
|
||||
done = $.get(_.str.sprintf(
|
||||
'/pagenew/%s?noredirect', encodeURI(val)))
|
||||
.then(function (response) {
|
||||
self.make_link(response, false, val);
|
||||
});
|
||||
} else {
|
||||
this.make_link(val, this.$('input.window-new').prop('checked'));
|
||||
}
|
||||
done.then(_super);
|
||||
},
|
||||
bind_data: function () {
|
||||
var href = this.element && (this.element.data( 'cke-saved-href')
|
||||
|| this.element.getAttribute('href'));
|
||||
if (!href) { return; }
|
||||
|
||||
var match, $control;
|
||||
if (match = /mailto:(.+)/.exec(href)) {
|
||||
$control = this.$('input.email-address').val(match[1]);
|
||||
} else if (href in this.pages) {
|
||||
$control = this.$('select.existing').val(href);
|
||||
} else if (match = /\/page\/(.+)/.exec(href)) {
|
||||
var actual_href = '/page/website.' + match[1];
|
||||
if (actual_href in this.pages) {
|
||||
$control = this.$('select.existing').val(actual_href);
|
||||
}
|
||||
}
|
||||
if (!$control) {
|
||||
$control = this.$('input.url').val(href);
|
||||
}
|
||||
|
||||
this.changed($control);
|
||||
|
||||
this.$('input#link-text').val(this.element.getText());
|
||||
this.$('input.window-new').prop(
|
||||
'checked', this.element.getAttribute('target') === '_blank');
|
||||
},
|
||||
changed: function ($e) {
|
||||
this.$('.url-source').not($e).val('');
|
||||
$e.closest('.list-group-item')
|
||||
.addClass('active')
|
||||
.siblings().removeClass('active')
|
||||
.addBack().removeClass('has-error');
|
||||
},
|
||||
/**
|
||||
* CKEDITOR.plugins.link.getSelectedLink ignores the editor's root,
|
||||
* if the editor is set directly on a link it will thus not work.
|
||||
|
@ -951,27 +1030,8 @@
|
|||
get_selected_link: function () {
|
||||
return get_selected_link(this.editor);
|
||||
},
|
||||
fetch_pages: function () {
|
||||
return openerp.jsonRpc('/web/dataset/call_kw', 'call', {
|
||||
model: 'website',
|
||||
method: 'list_pages',
|
||||
args: [null],
|
||||
kwargs: {
|
||||
context: website.get_context()
|
||||
},
|
||||
});
|
||||
},
|
||||
fill_pages: function (results) {
|
||||
var self = this;
|
||||
var pages = this.$('select.existing')[0];
|
||||
_(results).each(function (result) {
|
||||
self.pages[result.url] = result.name;
|
||||
|
||||
pages.options[pages.options.length] =
|
||||
new Option(result.name, result.url);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* ImageDialog widget. Lets users change an image, including uploading a
|
||||
* new image in OpenERP or selecting the image style (if supported by
|
||||
|
@ -1003,6 +1063,7 @@
|
|||
}),
|
||||
|
||||
start: function () {
|
||||
this.$('.modal-footer [disabled]').text("Uploading…");
|
||||
var $options = this.$('.image-style').children();
|
||||
this.image_styles = $options.map(function () { return this.value; }).get();
|
||||
|
||||
|
@ -1038,6 +1099,7 @@
|
|||
},
|
||||
|
||||
file_selection: function () {
|
||||
this.$el.addClass('nosave');
|
||||
this.$('button.filepicker').removeClass('btn-danger btn-success');
|
||||
|
||||
var self = this;
|
||||
|
@ -1060,6 +1122,7 @@
|
|||
this.set_image(url);
|
||||
},
|
||||
preview_image: function () {
|
||||
this.$el.removeClass('nosave');
|
||||
var image = this.$('input.url').val();
|
||||
if (!image) { return; }
|
||||
|
||||
|
|
|
@ -5,35 +5,30 @@
|
|||
// The following line can be removed in 2017
|
||||
openerp.website = website;
|
||||
|
||||
var templates = website.templates = [
|
||||
'/website/static/src/xml/website.xml'
|
||||
];
|
||||
|
||||
website.get_context = function (dict) {
|
||||
var html = document.documentElement;
|
||||
return _.extend({
|
||||
lang: html.getAttribute('lang').replace('-', '_')
|
||||
lang: html.getAttribute('lang').replace('-', '_'),
|
||||
website_id: html.getAttribute('data-website-id')|0
|
||||
}, dict);
|
||||
};
|
||||
|
||||
/* ----- TEMPLATE LOADING ---- */
|
||||
website.add_template = function(template) {
|
||||
templates.push(template);
|
||||
};
|
||||
website.load_templates = function(templates) {
|
||||
var dones = _(templates).map(function (t) {
|
||||
return new $.Deferred(function (d) {
|
||||
openerp.qweb.add_template(t, function(err) {
|
||||
if (err) {
|
||||
d.reject(err);
|
||||
} else {
|
||||
d.resolve();
|
||||
}
|
||||
});
|
||||
var templates_def = $.Deferred().resolve();
|
||||
website.add_template_file = function(template) {
|
||||
templates_def = templates_def.then(function() {
|
||||
var def = $.Deferred();
|
||||
openerp.qweb.add_template(template, function(err) {
|
||||
if (err) {
|
||||
def.reject(err);
|
||||
} else {
|
||||
def.resolve();
|
||||
}
|
||||
});
|
||||
return def;
|
||||
});
|
||||
return $.when.apply(null, dones);
|
||||
};
|
||||
website.add_template_file('/website/static/src/xml/website.xml');
|
||||
website.reload = function () {
|
||||
location.hash = "scrollTop=" + window.document.body.scrollTop;
|
||||
if (location.search.indexOf("enable_editor") > -1) {
|
||||
|
@ -110,20 +105,33 @@
|
|||
*/
|
||||
website.ready = function() {
|
||||
if (!all_ready) {
|
||||
var tpl = website.load_templates(templates);
|
||||
all_ready = dom_ready.then(function () {
|
||||
all_ready = $.when(dom_ready, templates_def).then(function () {
|
||||
if ($('html').data('editable')) {
|
||||
website.id = $('html').data('website-id');
|
||||
website.session = new openerp.Session();
|
||||
var modules = ['website'];
|
||||
return openerp._t.database.load_translations(website.session, modules, website.get_context().lang);
|
||||
}
|
||||
}).then(tpl).promise();
|
||||
}).promise();
|
||||
}
|
||||
return all_ready;
|
||||
};
|
||||
|
||||
website.error = function(data, url) {
|
||||
var $error = $(openerp.qweb.render('website.error_dialog', {
|
||||
'title': data.data ? data.data.arguments[0] : data.statusText,
|
||||
'message': data.data ? data.data.arguments[1] : "",
|
||||
'backend_url': url
|
||||
}));
|
||||
$error.appendTo("body");
|
||||
$error.modal('show');
|
||||
};
|
||||
|
||||
dom_ready.then(function () {
|
||||
|
||||
/* ----- BOOTSTRAP STUFF ---- */
|
||||
$('.js_tooltip').bstooltip();
|
||||
|
||||
/* ----- PUBLISHING STUFF ---- */
|
||||
$('[data-publish]:has(.js_publish)').each(function () {
|
||||
var $pub = $("[data-publish]", this);
|
||||
|
@ -140,12 +148,15 @@
|
|||
|
||||
$(document).on('click', '.js_publish', function (e) {
|
||||
e.preventDefault();
|
||||
var $data = $(":first", this).parents("[data-publish]");
|
||||
$data.attr("data-publish", $data.first().attr("data-publish") == 'off' ? 'on' : 'off');
|
||||
openerp.jsonRpc('/website/publish', 'call', {'id': $(this).data('id'), 'object': $(this).data('object')})
|
||||
var $a = $(this);
|
||||
var $data = $a.find(":first").parents("[data-publish]");
|
||||
openerp.jsonRpc($a.data('controller') || '/website/publish', 'call', {'id': +$a.data('id'), 'object': $a.data('object')})
|
||||
.then(function (result) {
|
||||
$data.attr("data-publish", +result ? 'on' : 'off');
|
||||
}).fail(function (err, data) {
|
||||
website.error(data, '/web#model='+$a.data('object')+'&id='+$a.data('id'));
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$(document).on('click', '.js_publish_management .js_publish_btn', function () {
|
||||
|
@ -156,11 +167,13 @@
|
|||
$data.toggleClass("css_unpublish css_publish");
|
||||
$btn.removeClass("btn-default btn-success");
|
||||
|
||||
openerp.jsonRpc('/website/publish', 'call', {'id': +$data.data('id'), 'object': $data.data('object')})
|
||||
openerp.jsonRpc($data.data('controller') || '/website/publish', 'call', {'id': +$data.data('id'), 'object': $data.data('object')})
|
||||
.then(function (result) {
|
||||
$btn.toggleClass("btn-default", !result).toggleClass("btn-success", result);
|
||||
$data.toggleClass("css_unpublish", !result).toggleClass("css_publish", result);
|
||||
$data.parents("[data-publish]").attr("data-publish", +result ? 'on' : 'off');
|
||||
}).fail(function (err, data) {
|
||||
website.error(data, '/web#model='+$data.data('object')+'&id='+$data.data('id'));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
(function () {
|
||||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
website.menu = {};
|
||||
website.add_template_file('/website/static/src/xml/website.menu.xml');
|
||||
|
||||
website.EditorBar.include({
|
||||
events: _.extend({}, website.EditorBar.prototype.events, {
|
||||
'click a[data-action="edit-structure"]': 'editStructure',
|
||||
}),
|
||||
editStructure: function () {
|
||||
var context = website.get_context();
|
||||
openerp.jsonRpc('/web/dataset/call_kw', 'call', {
|
||||
model: 'website.menu',
|
||||
method: 'get_tree',
|
||||
args: [[context.website_id]],
|
||||
kwargs: {
|
||||
context: context
|
||||
},
|
||||
}).then(function (menu) {
|
||||
return new website.menu.EditMenuDialog(menu).appendTo(document.body);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
website.menu.EditMenuDialog = website.editor.Dialog.extend({
|
||||
template: 'website.menu.dialog.edit',
|
||||
events: _.extend({}, website.editor.Dialog.prototype.events, {
|
||||
'click button.js_add_menu': 'add_menu',
|
||||
'click button.js_edit_menu': 'edit_menu',
|
||||
'click button.js_delete_menu': 'delete_menu',
|
||||
}),
|
||||
init: function (menu) {
|
||||
this.menu = menu;
|
||||
this.root_menu_id = menu.id;
|
||||
this.flat = this.flatenize(menu);
|
||||
this.to_delete = [];
|
||||
this._super();
|
||||
},
|
||||
start: function () {
|
||||
var r = this._super.apply(this, arguments);
|
||||
var button = openerp.qweb.render('website.menu.dialog.footer-button');
|
||||
this.$('.modal-footer').prepend(button);
|
||||
this.$('.oe_menu_editor').nestedSortable({
|
||||
listType: 'ul',
|
||||
handle: 'div',
|
||||
items: 'li',
|
||||
maxLevels: 2,
|
||||
toleranceElement: '> div',
|
||||
forcePlaceholderSize: true,
|
||||
opacity: 0.6,
|
||||
placeholder: 'oe_menu_placeholder',
|
||||
tolerance: 'pointer',
|
||||
attribute: 'data-menu-id',
|
||||
expression: '()(.+)', // nestedSortable takes the second match of an expression (*sigh*)
|
||||
});
|
||||
return r;
|
||||
},
|
||||
flatenize: function (node, dict) {
|
||||
dict = dict || {};
|
||||
var self = this;
|
||||
dict[node.id] = node;
|
||||
node.children.forEach(function (child) {
|
||||
self.flatenize(child, dict);
|
||||
});
|
||||
return dict;
|
||||
},
|
||||
add_menu: function () {
|
||||
var self = this;
|
||||
var dialog = new website.menu.MenuEntryDialog();
|
||||
dialog.on('add-menu', this, function (link) {
|
||||
var new_menu = {
|
||||
id: _.uniqueId('new-'),
|
||||
name: link[2] || link[0],
|
||||
url: link[0],
|
||||
new_window: link[1],
|
||||
parent_id: false,
|
||||
sequence: 0,
|
||||
children: [],
|
||||
};
|
||||
self.flat[new_menu.id] = new_menu;
|
||||
self.$('.oe_menu_editor').append(
|
||||
openerp.qweb.render(
|
||||
'website.menu.dialog.submenu', { submenu: new_menu }));
|
||||
});
|
||||
dialog.appendTo(document.body);
|
||||
},
|
||||
edit_menu: function (ev) {
|
||||
var self = this;
|
||||
var menu_id = $(ev.currentTarget).closest('[data-menu-id]').data('menu-id');
|
||||
var menu = self.flat[menu_id];
|
||||
if (menu) {
|
||||
var dialog = new website.menu.MenuEntryDialog(undefined, menu);
|
||||
dialog.on('update-menu', this, function (link) {
|
||||
var id = link.shift();
|
||||
var menu_obj = self.flat[id];
|
||||
_.extend(menu_obj, {
|
||||
name: link[2],
|
||||
url: link[0],
|
||||
new_window: link[1],
|
||||
});
|
||||
var $menu = self.$('[data-menu-id="' + id + '"]');
|
||||
$menu.find('> div > span').text(menu_obj.name);
|
||||
});
|
||||
dialog.appendTo(document.body);
|
||||
} else {
|
||||
alert("Could not find menu entry");
|
||||
}
|
||||
},
|
||||
delete_menu: function (ev) {
|
||||
var self = this;
|
||||
var $menu = $(ev.currentTarget).closest('[data-menu-id]');
|
||||
var mid = $menu.data('menu-id')|0;
|
||||
if (mid) {
|
||||
this.to_delete.push(mid);
|
||||
}
|
||||
$menu.remove();
|
||||
},
|
||||
save: function () {
|
||||
var self = this;
|
||||
var new_menu = this.$('.oe_menu_editor').nestedSortable('toArray', {startDepthCount: 0});
|
||||
var levels = [];
|
||||
var data = [];
|
||||
var context = website.get_context();
|
||||
// Resquence, re-tree and remove useless data
|
||||
new_menu.forEach(function (menu) {
|
||||
if (menu.item_id) {
|
||||
levels[menu.depth] = (levels[menu.depth] || 0) + 1;
|
||||
var mobj = self.flat[menu.item_id];
|
||||
mobj.sequence = levels[menu.depth];
|
||||
mobj.parent_id = (menu.parent_id|0) || menu.parent_id || self.root_menu_id;
|
||||
delete(mobj.children);
|
||||
data.push(mobj);
|
||||
}
|
||||
});
|
||||
openerp.jsonRpc('/web/dataset/call_kw', 'call', {
|
||||
model: 'website.menu',
|
||||
method: 'save',
|
||||
args: [[context.website_id], { data: data, to_delete: self.to_delete }],
|
||||
kwargs: {
|
||||
context: context
|
||||
},
|
||||
}).then(function (menu) {
|
||||
self.close();
|
||||
website.reload();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
website.menu.MenuEntryDialog = website.editor.LinkDialog.extend({
|
||||
template: 'website.menu.dialog.add',
|
||||
init: function (editor, data) {
|
||||
this.data = data;
|
||||
this.update_mode = !!this.data;
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
return $.when(this._super.apply(this, arguments)).then(function () {
|
||||
if (self.data) {
|
||||
self.bind_data(self.data.name, self.data.url, self.data.new_window);
|
||||
}
|
||||
var $link_text = self.$('#link-text').focus();
|
||||
self.$('#link-existing').change(function () {
|
||||
if (!$link_text.val()) {
|
||||
$link_text.val($(this).find('option:selected').text());
|
||||
$link_text.focus();
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
save: function () {
|
||||
var $e = this.$('#link-text');
|
||||
if (!$e.val() || !$e[0].checkValidity()) {
|
||||
$e.closest('.form-group').addClass('has-error');
|
||||
$e.focus();
|
||||
return;
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
make_link: function (url, new_window, label) {
|
||||
var menu_label = this.$('input#link-text').val() || label;
|
||||
if (this.data) {
|
||||
this.trigger('update-menu', [this.data.id, url, new_window, menu_label]);
|
||||
} else {
|
||||
this.trigger('add-menu', [url, new_window, menu_label]);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
})();
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
website.templates.push('/website/static/src/xml/website.seo.xml');
|
||||
website.add_template_file('/website/static/src/xml/website.seo.xml');
|
||||
|
||||
website.EditorBar.include({
|
||||
events: _.extend({}, website.EditorBar.prototype.events, {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
website.templates.push('/website/static/src/xml/website.snippets.xml');
|
||||
website.add_template_file('/website/static/src/xml/website.snippets.xml');
|
||||
|
||||
website.EditorBar.include({
|
||||
start: function () {
|
||||
|
@ -17,9 +17,15 @@
|
|||
return this._super();
|
||||
},
|
||||
edit: function () {
|
||||
var self = this;
|
||||
$("body").off('click');
|
||||
window.snippets = this.snippets = new website.snippet.BuildingBlock(this);
|
||||
this.snippets.appendTo(this.$el);
|
||||
|
||||
this.rte.on('rte:ready', this, function () {
|
||||
self.snippets.$button.removeClass("hidden");
|
||||
});
|
||||
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
save: function () {
|
||||
|
@ -118,10 +124,11 @@
|
|||
start: function() {
|
||||
var self = this;
|
||||
|
||||
var $ul = this.parent.$("#website-top-edit ul");
|
||||
this.$button = $(openerp.qweb.render('website.snippets_button'))
|
||||
.prependTo(this.parent.$("#website-top-edit ul"))
|
||||
.find("button");
|
||||
|
||||
var $button = $(openerp.qweb.render('website.snippets_button')).prependTo($ul);
|
||||
$button.find('button').click(function () {
|
||||
this.$button.click(function () {
|
||||
self.make_active(false);
|
||||
self.$el.toggleClass("hidden");
|
||||
});
|
||||
|
@ -237,6 +244,7 @@
|
|||
}
|
||||
if (this.$active_snipped_id) {
|
||||
this.snippet_blur(this.$active_snipped_id);
|
||||
this.$active_snipped_id = false;
|
||||
}
|
||||
if ($snipped_id) {
|
||||
if(_.indexOf(this.snippets, $snipped_id.get(0)) === -1) {
|
||||
|
@ -245,8 +253,6 @@
|
|||
this.$active_snipped_id = $snipped_id;
|
||||
this.create_overlay(this.$active_snipped_id);
|
||||
this.snippet_focus($snipped_id);
|
||||
} else {
|
||||
self.$active_snipped_id = false;
|
||||
}
|
||||
},
|
||||
create_overlay: function ($snipped_id) {
|
||||
|
@ -361,7 +367,7 @@
|
|||
}
|
||||
|
||||
self.create_overlay($target);
|
||||
$target.data("snippet-editor").build_snippet($target);
|
||||
$target.data("snippet-editor").drop_and_build_snippet($target);
|
||||
|
||||
} else {
|
||||
$target = $(this).data('target');
|
||||
|
@ -369,7 +375,7 @@
|
|||
self.create_overlay($target);
|
||||
if (website.snippet.editorRegistry[snipped_id]) {
|
||||
var snippet = new website.snippet.editorRegistry[snipped_id](self, $target);
|
||||
snippet.build_snippet($target);
|
||||
snippet.drop_and_build_snippet($target);
|
||||
}
|
||||
}
|
||||
setTimeout(function () {self.make_active($target);},0);
|
||||
|
@ -589,7 +595,8 @@
|
|||
* Displayed into the overlay options on focus
|
||||
*/
|
||||
_readXMLData: function() {
|
||||
this.$el = this.parent.$snippets.siblings("[data-snippet-id='"+this.snippet_id+"']").clone();
|
||||
var self = this;
|
||||
this.$el = this.parent.$snippets.filter(function () { return $(this).data("snippet-id") == self.snippet_id; }).clone();
|
||||
this.$editor = this.$el.find(".oe_snippet_options");
|
||||
var $options = this.$overlay.find(".oe_overlay_options");
|
||||
this.$editor.prependTo($options.find(".oe_options ul"));
|
||||
|
@ -710,7 +717,7 @@
|
|||
|
||||
if (website.snippet.editorRegistry[snipped_id]) {
|
||||
var snippet = new website.snippet.editorRegistry[snipped_id](this, this.$target);
|
||||
snippet.build_snippet(this.$target);
|
||||
snippet.drop_and_build_snippet(this.$target);
|
||||
}
|
||||
var _class = "oe_snippet_" + snipped_id + " " + ($li.data("class") || "");
|
||||
if (active) {
|
||||
|
@ -749,6 +756,7 @@
|
|||
var index = _.indexOf(self.parent.snippets, self.$target.get(0));
|
||||
delete self.parent.snippets[index];
|
||||
self.$target.remove();
|
||||
self.$overlay.remove();
|
||||
return false;
|
||||
});
|
||||
this._drag_and_drop();
|
||||
|
@ -756,18 +764,18 @@
|
|||
},
|
||||
|
||||
/*
|
||||
* build_snippet
|
||||
* drop_and_build_snippet
|
||||
* This method is called just after that a thumbnail is drag and dropped into a drop zone
|
||||
* (after the insertion of this.$body, if this.$body exists)
|
||||
*/
|
||||
build_snippet: function ($target) {
|
||||
drop_and_build_snippet: function ($target) {
|
||||
},
|
||||
|
||||
/* onFocus
|
||||
* This method is called when the user click inside the snippet in the dom
|
||||
*/
|
||||
onFocus : function () {
|
||||
this.$overlay.addClass('oe_active');
|
||||
this.$overlay.addClass('oe_active').effect('bounce', {distance: 18, times: 5}, 250);
|
||||
},
|
||||
|
||||
/* onFocus
|
||||
|
@ -1026,7 +1034,7 @@
|
|||
},
|
||||
});
|
||||
website.snippet.editorRegistry.carousel = website.snippet.editorRegistry.resize.extend({
|
||||
build_snippet: function() {
|
||||
drop_and_build_snippet: function() {
|
||||
var id = 0;
|
||||
$("body .carousel").each(function () {
|
||||
var _id = +$(this).attr("id").replace(/^myCarousel/, '');
|
||||
|
@ -1038,10 +1046,6 @@
|
|||
this.$target.find(".carousel-control").attr("href", "#myCarousel" + id);
|
||||
this.$target.find("[data-target='#myCarousel']").attr("data-target", "#myCarousel" + id);
|
||||
|
||||
this.$target.attr('contentEditable', 'false')
|
||||
.find('.content, .carousel-image img')
|
||||
.attr('contentEditable', 'true');
|
||||
|
||||
this.rebind_event();
|
||||
},
|
||||
onFocus: function () {
|
||||
|
@ -1095,6 +1099,10 @@
|
|||
self.$target.carousel($(this).data('slide')); });
|
||||
this.$target.off('click').on('click', '.carousel-indicators [data-target]', function () {
|
||||
self.$target.carousel(+$(this).data('slide-to')); });
|
||||
|
||||
this.$target.attr('contentEditable', 'false')
|
||||
.find('.content, .carousel-image img')
|
||||
.attr('contentEditable', 'true');
|
||||
},
|
||||
on_add: function (e) {
|
||||
e.preventDefault();
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
website.templates.push('/website/static/src/xml/website.tour.xml');
|
||||
website.add_template_file('/website/static/src/xml/website.tour.xml');
|
||||
|
||||
website.tour = {
|
||||
render: function render (template, dict) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
'use strict';
|
||||
|
||||
var website = openerp.website;
|
||||
website.templates.push('/website/static/src/xml/website.translator.xml');
|
||||
website.add_template_file('/website/static/src/xml/website.translator.xml');
|
||||
var nodialog = 'website_translator_nodialog';
|
||||
|
||||
website.EditorBar.include({
|
||||
|
@ -16,8 +16,7 @@
|
|||
self.$('button[data-action=edit]')
|
||||
.text("Translate")
|
||||
.after(openerp.qweb.render('website.TranslatorAdditionalButtons'));
|
||||
self.$('[data-action=snippet]').hide();
|
||||
self.$('#customize-menu-button').hide();
|
||||
self.$('.js_hide_on_translate').hide();
|
||||
});
|
||||
},
|
||||
edit: function () {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<div class="modal-body"><t t-raw="__content__"/></div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn" data-dismiss="modal" aria-hidden="true">Discard</button>
|
||||
<button type="button" class="btn hidden wait" disabled="disabled"/>
|
||||
<button type="button" class="btn btn-primary save">Save Changes</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -39,22 +40,22 @@
|
|||
<form>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item form-group active">
|
||||
<h3 class="list-group-item-heading">
|
||||
<h4 class="list-group-item-heading">
|
||||
<label for="link-existing" class="control-label">
|
||||
Existing page
|
||||
</label>
|
||||
</h3>
|
||||
</h4>
|
||||
<select class="existing form-control url-source"
|
||||
id="link-existing">
|
||||
<option/>
|
||||
</select>
|
||||
</li>
|
||||
<li class="list-group-item form-group">
|
||||
<h3 class="list-group-item-heading">
|
||||
<h4 class="list-group-item-heading">
|
||||
<label for="link-new" class="control-label">
|
||||
New page
|
||||
</label>
|
||||
</h3>
|
||||
</h4>
|
||||
<input type="text" class="form-control pages url-source"
|
||||
id="link-new" placeholder="Page Name"/>
|
||||
</li>
|
||||
|
@ -65,20 +66,20 @@
|
|||
Open in new window
|
||||
</label>
|
||||
</div>
|
||||
<h3 class="list-group-item-heading">
|
||||
<h4 class="list-group-item-heading">
|
||||
<label for="link-external" class="control-label">
|
||||
External Website
|
||||
URL
|
||||
</label>
|
||||
</h3>
|
||||
</h4>
|
||||
<input type="text" class="form-control url url-source"
|
||||
id="link-external" placeholder="http://openerp.com"/>
|
||||
</li>
|
||||
<li class="list-group-item form-group">
|
||||
<h3 class="list-group-item-heading">
|
||||
<h4 class="list-group-item-heading">
|
||||
<label for="link-email" class="control-label">
|
||||
Email Address
|
||||
</label>
|
||||
</h3>
|
||||
</h4>
|
||||
<input type="email" class="form-control email-address url-source"
|
||||
id="link-email" placeholder="you@yourwebsite.com"/>
|
||||
</li>
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="website.menu.dialog.submenu">
|
||||
<li t-att-data-menu-id="submenu.id">
|
||||
<div>
|
||||
<i class="icon-reorder"/>
|
||||
<span><t t-esc="submenu.name"/></span>
|
||||
<div class="btn-group btn-group-xs pull-right oe_menu_buttons">
|
||||
<button type="button" class="btn js_edit_menu">
|
||||
<i class="icon-edit"/>
|
||||
</button>
|
||||
<button type="button" class="btn js_delete_menu">
|
||||
<i class="icon-trash"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<t t-set="children" t-value="submenu.children"/>
|
||||
<ul t-if="children">
|
||||
<t t-foreach="children" t-as="submenu">
|
||||
<t t-call="website.menu.dialog.submenu"/>
|
||||
</t>
|
||||
</ul>
|
||||
</li>
|
||||
</t>
|
||||
<t t-name="website.menu.dialog.footer-button">
|
||||
<button type="button" class="btn pull-left js_add_menu btn-success">
|
||||
<i class="icon-plus-sign"/> Add Menu Entry
|
||||
</button>
|
||||
</t>
|
||||
<t t-name="website.menu.dialog.edit">
|
||||
<t t-call="website.editor.dialog">
|
||||
<t t-set="title">Edit Structure</t>
|
||||
<ul class="oe_menu_editor">
|
||||
<t t-foreach="widget.menu.children" t-as="submenu">
|
||||
<t t-call="website.menu.dialog.submenu"/>
|
||||
</t>
|
||||
</ul>
|
||||
</t>
|
||||
</t>
|
||||
<t t-name="website.menu.dialog.add" t-extend="website.editor.dialog.link">
|
||||
<t t-jquery="t[t-set='title']" t-operation="inner">
|
||||
<t t-if="!widget.update_mode">Add Menu Entry</t>
|
||||
<t t-if="widget.update_mode">Edit Menu Entry</t>
|
||||
</t>
|
||||
<t t-jquery="form > div.form-horizontal" t-operation="replace"/>
|
||||
<t t-jquery="ul.list-group" t-operation="before">
|
||||
<div class="list-group">
|
||||
<div class="form-group list-group-item active">
|
||||
<h3 class="list-group-item-heading">
|
||||
<label for="link-new" class="control-label">
|
||||
Menu Label
|
||||
</label>
|
||||
</h3>
|
||||
<input type="text" class="form-control"
|
||||
id="link-text" required="required"/>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</templates>
|
|
@ -32,7 +32,7 @@
|
|||
<!-- filled in JS -->
|
||||
</section>
|
||||
<section>
|
||||
<h3 class="page-header">2. Reference Your Page <small>using above suggestions</small></h3>
|
||||
<h3 class="page-header">2. Reference Your Page <small>using above suggested keywords</small></h3>
|
||||
<div class="form-horizontal mt16" role="form">
|
||||
<div class="form-group">
|
||||
<label for="seo_page_title" class="col-lg-2 control-label">Title</label>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
<!-- Snippet loader -->
|
||||
<t t-name="website.snippets_button">
|
||||
<li class="navbar-form"><button type="button" data-action="snippet" class="btn btn-primary">Insert Blocks</button></li>
|
||||
<li class="navbar-form js_hide_on_translate"><button type="button" data-action="snippet" class="hidden btn btn-primary">Insert Blocks</button></li>
|
||||
</t>
|
||||
<t t-name="website.snippets_style">
|
||||
<li class="navbar-form">
|
||||
|
@ -63,7 +63,7 @@
|
|||
<div class="btn-group">
|
||||
<a href="#" class="btn btn-default btn-sm oe_snippet_parent" title="Select Container Block"><span class="icon-upload-alt"/></a>
|
||||
<div class="dropdown oe_options hidden btn-group">
|
||||
<a href="#" data-toggle="dropdown" class="btn btn-default btn-sm" title="Customize">Customize <span class="caret"/></a>
|
||||
<a href="#" data-toggle="dropdown" class="btn btn-primary btn-sm" title="Customize">Customize <span class="caret"/></a>
|
||||
<ul class="dropdown-menu" role="menu"></ul>
|
||||
</div>
|
||||
<a href="#" class="btn btn-default btn-sm oe_snippet_move" title="Drag to Move">&nbsp; <span class="icon-move"/> &nbsp;</a>
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<templates id="template" xml:space="preserve">
|
||||
<t t-name="website.TranslatorAdditionalButtons">
|
||||
<strong class="oe_translate_or">or</strong>
|
||||
<a class="btn btn-default" data-action="edit_master" href="#">Edit Master</a>
|
||||
<span class="oe_translate_or">or</span>
|
||||
<a class="btn btn-link" data-action="edit_master" href="#">Edit Master</a>
|
||||
</t>
|
||||
<t t-name="website.TranslatorDialog">
|
||||
<div class="modal fade oe_website_translator" tabindex="-1" role="dialog">
|
||||
|
@ -18,22 +18,23 @@
|
|||
You are about to enter the translation mode.
|
||||
</p>
|
||||
<p>
|
||||
In this mode you can not change the structure of the document, you can only translate text.
|
||||
</p>
|
||||
<p>
|
||||
Here are the visuals used in order to help you in your translation flow:
|
||||
Here are the visuals used to help you translate efficiently:
|
||||
<ul class="oe_translate_examples">
|
||||
<li class="oe_translatable_text">
|
||||
Normal translatable content
|
||||
</li>
|
||||
<li class="oe_translatable_field">
|
||||
ERP translatable object data
|
||||
</li>
|
||||
<li class="oe_translatable_todo">
|
||||
Translation TODO
|
||||
Content to translate
|
||||
</li>
|
||||
<li class="oe_translatable_text">
|
||||
Translated content
|
||||
</li>
|
||||
</ul>
|
||||
</p>
|
||||
<p>
|
||||
In this mode, you can only translate texts. To
|
||||
change the structure of the page, you must edit the
|
||||
master page. Each modification on the master page
|
||||
is automatically applied to all translated
|
||||
versions.
|
||||
</p>
|
||||
</section>
|
||||
<hr/>
|
||||
<section class="row">
|
||||
|
|
|
@ -21,8 +21,15 @@
|
|||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a data-action="show-mobile-preview" href="#"><span title="Mobile preview" class="icon-mobile-phone"/></a></li>
|
||||
<li class="divider-vertical"></li>
|
||||
<li class="dropdown js_hide_on_translate">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">Structure <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a data-action="edit-structure" href="#"><span title="Edit Top Menu">Top Menu</span></a></li>
|
||||
<!-- <a data-action="edit-footer" href="#"><span title="Edit Footer">Footer</span></a></li> -->
|
||||
</ul>
|
||||
</li>
|
||||
<li><a data-action="promote-current-page" href="#"><span title="Promote page on the web">Promote</span></a></li>
|
||||
<li class="dropdown">
|
||||
<li class="dropdown js_hide_on_translate">
|
||||
<a id="customize-menu-button" class="dropdown-toggle" data-toggle="dropdown" href="#">Customize <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu" id="customize-menu">
|
||||
<!-- filled in JS -->
|
||||
|
@ -61,4 +68,27 @@
|
|||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="website.error_dialog">
|
||||
<div class="modal fade" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button title="Close" type="button" class="close" data-dismiss="modal">×</button>
|
||||
<div class="h2"><t t-esc="title"/></div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<section>
|
||||
<t t-esc="message"/>
|
||||
</section>
|
||||
<section class="mt32">
|
||||
You have encountered an error. You can edit in the <a t-att-href="backend_url">backend</a> this data.
|
||||
</section>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" data-dismiss="modal" href="#" class="close btn btn-default">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import test_views, test_converter
|
||||
import test_views, test_converter, test_requests
|
||||
|
||||
checks = [ test_views, test_converter, ]
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import unittest2
|
||||
|
||||
class URLCase(unittest2.TestCase):
|
||||
"""
|
||||
URLCase moved out of test_requests, otherwise discovery attempts to
|
||||
instantiate and run it
|
||||
"""
|
||||
def __init__(self, user, url, result):
|
||||
super(URLCase, self).__init__()
|
||||
self.user = user
|
||||
self.url = url
|
||||
self.result = result
|
||||
|
||||
@property
|
||||
def username(self):
|
||||
return self.user or "Anonymous Coward"
|
||||
|
||||
def __str__(self):
|
||||
return "%s (as %s)" % (self.url, self.username)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def shortDescription(self):
|
||||
return ""
|
||||
|
||||
def runTest(self):
|
||||
code = self.result.getcode()
|
||||
self.assertIn(
|
||||
code, xrange(200, 300),
|
||||
"Fetching %s as %s returned an error response (%d)" % (
|
||||
self.url, self.username, code))
|
|
@ -0,0 +1,120 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import collections
|
||||
import urllib
|
||||
import urlparse
|
||||
import unittest2
|
||||
import urllib2
|
||||
|
||||
import lxml.html
|
||||
|
||||
from openerp import tools
|
||||
|
||||
from . import cases
|
||||
|
||||
__all__ = ['load_tests', 'CrawlSuite']
|
||||
|
||||
class RedirectHandler(urllib2.HTTPRedirectHandler):
|
||||
"""
|
||||
HTTPRedirectHandler is predicated upon HTTPErrorProcessor being used and
|
||||
works by intercepting 3xy "errors".
|
||||
|
||||
Inherit from it to handle 3xy non-error responses instead, as we're not
|
||||
using the error processor
|
||||
"""
|
||||
|
||||
def http_response(self, request, response):
|
||||
code, msg, hdrs = response.code, response.msg, response.info()
|
||||
|
||||
if 300 <= code < 400:
|
||||
return self.parent.error(
|
||||
'http', request, response, code, msg, hdrs)
|
||||
|
||||
return response
|
||||
|
||||
https_response = http_response
|
||||
|
||||
class CrawlSuite(unittest2.TestSuite):
|
||||
""" Test suite crawling an openerp 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
|
||||
starting the crawl
|
||||
"""
|
||||
|
||||
def __init__(self, user=None, password=None):
|
||||
super(CrawlSuite, self).__init__()
|
||||
|
||||
self.opener = urllib2.OpenerDirector()
|
||||
self.opener.add_handler(urllib2.UnknownHandler())
|
||||
self.opener.add_handler(urllib2.HTTPHandler())
|
||||
self.opener.add_handler(urllib2.HTTPSHandler())
|
||||
self.opener.add_handler(RedirectHandler())
|
||||
self.opener.add_handler(urllib2.HTTPCookieProcessor())
|
||||
|
||||
self._authenticate(user, password)
|
||||
self.user = user
|
||||
|
||||
def _request(self, path):
|
||||
return self.opener.open(urlparse.urlunsplit([
|
||||
'http', 'localhost:%s' % tools.config['xmlrpc_port'],
|
||||
path, '', ''
|
||||
]))
|
||||
|
||||
def _authenticate(self, user, password):
|
||||
# force tools.config['db_name'] in user session so opening `/` doesn't
|
||||
# blow up in multidb situations
|
||||
self.opener.open('http://localhost:{port}/web/?db={db}'.format(
|
||||
port=tools.config['xmlrpc_port'],
|
||||
db=urllib.quote_plus(tools.config['db_name']),
|
||||
))
|
||||
if user is not None:
|
||||
url = 'http://localhost:{port}/login?{query}'.format(
|
||||
port=tools.config['xmlrpc_port'],
|
||||
query=urllib.urlencode({
|
||||
'db': tools.config['db_name'],
|
||||
'login': user,
|
||||
'key': password,
|
||||
})
|
||||
)
|
||||
auth = self.opener.open(url)
|
||||
assert auth.getcode() < 400, "Auth failure %d" % auth.getcode()
|
||||
|
||||
def _wrapped_run(self, result, debug=False):
|
||||
paths = collections.deque(['/'])
|
||||
seen = set(paths)
|
||||
|
||||
while paths:
|
||||
url = paths.popleft()
|
||||
r = self._request(url)
|
||||
cases.URLCase(self.user, url, r).run(result)
|
||||
|
||||
if r.info().gettype() != 'text/html':
|
||||
continue
|
||||
|
||||
doc = lxml.html.fromstring(r.read())
|
||||
for link in doc.xpath('//a[@href]'):
|
||||
href = link.get('href')
|
||||
|
||||
# avoid repeats, even for links we won't crawl no need to
|
||||
# bother splitting them if we've already ignored them
|
||||
# previously
|
||||
if href in seen: continue
|
||||
seen.add(href)
|
||||
|
||||
parts = urlparse.urlsplit(href)
|
||||
|
||||
if parts.netloc or \
|
||||
not parts.path.startswith('/') or \
|
||||
parts.path == '/web' or\
|
||||
parts.path.startswith('/web/') or \
|
||||
(parts.scheme and parts.scheme not in ('http', 'https')):
|
||||
continue
|
||||
|
||||
paths.append(href)
|
||||
|
||||
def load_tests(loader, base, _):
|
||||
base.addTest(CrawlSuite())
|
||||
# blog duplicate (&al?) are on links
|
||||
base.addTest(CrawlSuite('admin', tools.config['admin_passwd']))
|
||||
base.addTest(CrawlSuite('demo', 'demo'))
|
||||
return base
|
|
@ -109,7 +109,7 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="col-md-6 mt16 mb16">
|
||||
<img class="img-responsive shadow" src="/website/static/src/img/text_image.png"/>
|
||||
<img class="img img-responsive shadow" src="/website/static/src/img/text_image.png"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -126,7 +126,7 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6 mt16 mb16">
|
||||
<img class="img-responsive shadow" src="/website/static/src/img/image_text.jpg"/>
|
||||
<img class="img img-responsive shadow" src="/website/static/src/img/image_text.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-6 mt32">
|
||||
<h3>A Section Subtitle</h3>
|
||||
|
@ -243,7 +243,7 @@
|
|||
<h2>A Punchy Headline</h2>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<img class="img-responsive" src="/website/static/src/img/big_picture.png" style="margin: 0 auto;"/>
|
||||
<img class="img img-responsive" src="/website/static/src/img/big_picture.png" style="margin: 0 auto;"/>
|
||||
</div>
|
||||
<div class="col-md-6 col-md-offset-3 mb16 mt16">
|
||||
<p class="text-center">
|
||||
|
@ -277,7 +277,7 @@
|
|||
<h3 class="text-muted">And a good subtitle</h3>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<img class="img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg"/>
|
||||
<img class="img img-rounded img-responsive" src="/website/static/src/img/china_thumb.jpg"/>
|
||||
<h4 class="mt16">Streamline Recruitments</h4>
|
||||
<p>
|
||||
Post job offers and keep track of each application
|
||||
|
@ -290,7 +290,7 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<img class="img-rounded img-responsive" src="/website/static/src/img/desert_thumb.jpg"/>
|
||||
<img class="img img-rounded img-responsive" src="/website/static/src/img/desert_thumb.jpg"/>
|
||||
<h4 class="mt16">Enterprise Social Network</h4>
|
||||
<p>
|
||||
Break down information silos. Share knowledge and best
|
||||
|
@ -302,7 +302,7 @@
|
|||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<img class="img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg"/>
|
||||
<img class="img img-rounded img-responsive" src="/website/static/src/img/deers_thumb.jpg"/>
|
||||
<h4 class="mt16">Leaves Management</h4>
|
||||
<p>
|
||||
Keep track of the vacation days accrued by each
|
||||
|
@ -323,6 +323,7 @@
|
|||
|
||||
<div data-snippet-id="well" data-selector-siblings="p, h1, h2, h3, blockquote">
|
||||
<div class="oe_snippet_thumbnail">
|
||||
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_well.png"/>
|
||||
<span class="oe_snippet_thumbnail_title">Well</span>
|
||||
</div>
|
||||
<div class="oe_snippet_body well">
|
||||
|
@ -381,19 +382,19 @@
|
|||
<h4 class="text-muted">More than 500 successful projects</h4>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<img class="img-thumbnail img-responsive" src="/website/static/src/img/deers.jpg"/>
|
||||
<img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg"/>
|
||||
<img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive" src="/website/static/src/img/deers.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive" src="/website/static/src/img/china.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg"/>
|
||||
<img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg"/>
|
||||
<img class="img-thumbnail img-responsive" src="/website/static/src/img/deers.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive" src="/website/static/src/img/china.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive" src="/website/static/src/img/deers.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<img class="img-thumbnail img-responsive" src="/website/static/src/img/landscape.jpg"/>
|
||||
<img class="img-thumbnail img-responsive" src="/website/static/src/img/china.jpg"/>
|
||||
<img class="img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive" src="/website/static/src/img/landscape.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive" src="/website/static/src/img/china.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive" src="/website/static/src/img/desert.jpg"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -414,28 +415,28 @@
|
|||
<h4 class="text-muted">More than 500 successful projects</h4>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="img-thumbnail img-responsive mb16" src="/website/static/src/img/desert.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive mb16" src="/website/static/src/img/desert.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<img class="img-thumbnail img-responsive mb16" src="/website/static/src/img/china_thumb.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive mb16" src="/website/static/src/img/china_thumb.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<img class="img-thumbnail img-responsive mb16" src="/website/static/src/img/deers_thumb.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive mb16" src="/website/static/src/img/deers_thumb.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<img class="img-thumbnail img-responsive mb16" src="/website/static/src/img/desert_thumb.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive mb16" src="/website/static/src/img/desert_thumb.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<img class="img-thumbnail img-responsive mb16" src="/website/static/src/img/china_thumb.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive mb16" src="/website/static/src/img/china_thumb.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<img class="img-thumbnail img-responsive mb16" src="/website/static/src/img/deers_thumb.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive mb16" src="/website/static/src/img/deers_thumb.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<img class="img-thumbnail img-responsive mb16" src="/website/static/src/img/landscape.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive mb16" src="/website/static/src/img/landscape.jpg"/>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<img class="img-thumbnail img-responsive mb16" src="/website/static/src/img/china_thumb.jpg"/>
|
||||
<img class="img img-thumbnail img-responsive mb16" src="/website/static/src/img/china_thumb.jpg"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -554,7 +555,7 @@
|
|||
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_button.png"/>
|
||||
<span class="oe_snippet_thumbnail_title">Button</span>
|
||||
</div>
|
||||
<section class="oe_snippet_body dark">
|
||||
<section class="oe_snippet_body oe_snippet_darken dark">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12 text-center mt16 mb16">
|
||||
|
@ -649,19 +650,19 @@
|
|||
</blockquote>
|
||||
</div>
|
||||
<div class="col-md-2 col-md-offset-1">
|
||||
<img src="/website/static/src/img/openerp_logo.png" class="img-responsive img-thumbnail"/>
|
||||
<img src="/website/static/src/img/openerp_logo.png" class="img img-responsive img-thumbnail"/>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<img src="/website/static/src/img/openerp_logo.png" class="img-responsive img-thumbnail"/>
|
||||
<img src="/website/static/src/img/openerp_logo.png" class="img img-responsive img-thumbnail"/>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<img src="/website/static/src/img/openerp_logo.png" class="img-responsive img-thumbnail"/>
|
||||
<img src="/website/static/src/img/openerp_logo.png" class="img img-responsive img-thumbnail"/>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<img src="/website/static/src/img/openerp_logo.png" class="img-responsive img-thumbnail"/>
|
||||
<img src="/website/static/src/img/openerp_logo.png" class="img img-responsive img-thumbnail"/>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<img src="/website/static/src/img/openerp_logo.png" class="img-responsive img-thumbnail"/>
|
||||
<img src="/website/static/src/img/openerp_logo.png" class="img img-responsive img-thumbnail"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -716,7 +717,7 @@
|
|||
|
||||
<div data-snippet-id="parallax_quote" data-selector-children=".oe_structure, [data-oe-type=html]">
|
||||
<div class="oe_snippet_thumbnail">
|
||||
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_parallax.png"/>
|
||||
<img class="oe_snippet_thumbnail_img" src="/website/static/src/img/blocks/block_quotes_slider.png"/>
|
||||
<span class="oe_snippet_thumbnail_title">Quotes Slider</span>
|
||||
</div>
|
||||
<section class="oe_snippet_body parallax_quote oe_structure">
|
||||
|
|
|
@ -10,9 +10,27 @@
|
|||
<link id="website_css" rel='stylesheet' href='/website/static/src/css/website.css' t-ignore="true"/>
|
||||
</template>
|
||||
|
||||
<template id="layout" name="Main layout">
|
||||
<!DOCTYPE html>
|
||||
<template id="website.submenu" name="Submenu">
|
||||
<li t-if="not submenu.child_id">
|
||||
<a t-att-href="url_for(submenu.url)" t-ignore="true">
|
||||
<span t-field="submenu.name"/>
|
||||
</a>
|
||||
</li>
|
||||
<li t-if="submenu.child_id" class="dropdown">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<span t-field="submenu.name"/> <span class="caret" t-ignore="true"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<t t-foreach="submenu.child_id" t-as="submenu">
|
||||
<t t-call="website.submenu"/>
|
||||
</t>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<template id="layout" name="Main layout"><!DOCTYPE html>
|
||||
<html t-att-lang="lang.replace('_', '-')"
|
||||
t-att-data-website-id="website.id if editable else None"
|
||||
t-att-data-editable="'1' if editable else None"
|
||||
t-att-data-translatable="'1' if translatable else None"
|
||||
t-att-data-view-xmlid="xmlid if editable else None"
|
||||
|
@ -49,6 +67,11 @@
|
|||
<script type="text/javascript" src="/web/static/lib/underscore.string/lib/underscore.string.js"></script>
|
||||
<script type="text/javascript" src="/web/static/lib/jquery/jquery.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/bootstrap/js/bootstrap.js"></script>
|
||||
<script type="text/javascript">
|
||||
// Bootstrap and jQuery UI conflicts
|
||||
$.fn.bstooltip = $.fn.tooltip;
|
||||
$.fn.bsbutton = $.fn.button;
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="/web/static/lib/qweb/qweb2.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/openerpframework.js"></script>
|
||||
|
@ -65,11 +88,13 @@
|
|||
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
|
||||
<link rel='stylesheet' href="/web/static/lib/jquery.ui/css/smoothness/jquery-ui-1.9.1.custom.css"/>
|
||||
<!-- mutation observers shim backed by mutation events (8 < IE < 11, Safari < 6, FF < 14, Chrome < 17) -->
|
||||
<script type="text/javascript" src="/website/static/lib//jquery.mjs.nestedSortable/jquery.mjs.nestedSortable.js"></script>
|
||||
<script type="text/javascript" src="/website/static/lib/MutationObservers/test/sidetable.js"></script>
|
||||
<script type="text/javascript" src='/website/static/lib/nearest/jquery.nearest.js'></script>
|
||||
<script type="text/javascript" src="/website/static/lib/MutationObservers/MutationObserver.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/website/static/src/js/website.editor.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.menu.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.mobile.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.seo.js"></script>
|
||||
<script type="text/javascript" src="/website/static/src/js/website.tour.js"></script>
|
||||
|
@ -93,38 +118,28 @@
|
|||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" t-href="/page/website.homepage"><em>Your</em><b>Company</b></a>
|
||||
<a class="navbar-brand" href="/"><em>Your</em> <b>Company</b></a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-top-collapse">
|
||||
<ul class="nav navbar-nav navbar-right" id="top_menu">
|
||||
<li name="contactus"><a t-href="/page/website.contactus">Contact us</a></li>
|
||||
<li t-if="user_id.id == website.public_user.id"><a href="/web">Sign in</a></li>
|
||||
<li t-if="user_id.id != website.public_user.id"><a href="/web"><span t-field="user_id.name"/></a></li>
|
||||
<li t-if="request.multilang and
|
||||
(len(website.language_ids) > 1 or editable)" class="dropdown">
|
||||
<!-- TODO: use flags for language selection -->
|
||||
<a class="dropdown-toggle js_language_selected" data-toggle="dropdown" href="#">
|
||||
<t t-esc="lang_selected[0]['name'].split('/').pop()"/> <span class="caret"></span>
|
||||
<t t-foreach="website.get_menu().child_id" t-as="submenu">
|
||||
<t t-call="website.submenu"/>
|
||||
</t>
|
||||
|
||||
<li class="active" t-if="user_id.id == website.public_user.id">
|
||||
<a t-attf-href="/web#redirect=#{ url_for('') }">
|
||||
Sign in
|
||||
</a>
|
||||
<ul class="dropdown-menu js_language_selector" role="menu">
|
||||
<li t-foreach="website.language_ids" t-as="lg">
|
||||
<a t-att-href="url_for('', lang=lg.code)" role="menuitem"
|
||||
t-att-data-default-lang="editable and 'true' if lg.code == website.default_lang_id.code else None">
|
||||
<strong t-att-class="'icon-circle' if lg.code == lang
|
||||
else 'icon-circle-blank'"></strong>
|
||||
<t t-esc="lg.name.split('/').pop()"/>
|
||||
</a>
|
||||
</li>
|
||||
<li t-if="editable" class="divider"/>
|
||||
<li t-if="editable">
|
||||
<t t-set="url_return" t-value="request.multilang and url_for(request.httprequest.path, '[lang]') or request.httprequest.path"/>
|
||||
<t t-if="request.httprequest.query_string">
|
||||
<t t-set="url_return" t-value="url_return + '?' + request.httprequest.query_string"/>
|
||||
</t>
|
||||
<a t-attf-href="/web#action=base.action_view_base_language_install&website_id=#{website.id}&url_return=#{url_return}">
|
||||
Add a language...
|
||||
</a>
|
||||
</li>
|
||||
</li>
|
||||
<li class="active dropdown" t-ignore="true" t-if="user_id.id != website.public_user.id">
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<span t-esc="user_id.name"/>
|
||||
<span class="caret"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu js_usermenu" role="menu">
|
||||
<li><a href="/web" role="menuitem">Administration</a></li>
|
||||
<li class="divider"/>
|
||||
<li><a t-attf-href="/web/session/logout?redirect=#{ url_for('') }" role="menuitem">Logout</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -161,24 +176,42 @@
|
|||
</h2>
|
||||
</div>
|
||||
<div class="col-md-5 col-lg-offset-1" name="about_us">
|
||||
<h4>
|
||||
<span t-field="res_company.name">Your Company</span>
|
||||
<small> - <a href="/page/website.aboutus">About us</a></small>
|
||||
</h4>
|
||||
<p>
|
||||
We are a team of passionated people whose goal is to improve everyone's
|
||||
life through disruptive products. We build great products to solve your
|
||||
business problems.
|
||||
</p>
|
||||
<p>
|
||||
Our products are designed for small to medium companies willing to optimize
|
||||
their performance.
|
||||
</p>
|
||||
<div>
|
||||
<h4>
|
||||
<span t-field="res_company.name">Your Company</span>
|
||||
<small> - <a href="/page/website.aboutus">About us</a></small>
|
||||
</h4>
|
||||
<p>
|
||||
We are a team of passionated people whose goal is to improve everyone's
|
||||
life through disruptive products. We build great products to solve your
|
||||
business problems.
|
||||
</p>
|
||||
<p>
|
||||
Our products are designed for small to medium companies willing to optimize
|
||||
their performance.
|
||||
</p>
|
||||
</div>
|
||||
<ul class="nav nav-pills js_language_selector" t-if="request.multilang and
|
||||
(len(website.language_ids) > 1 or editable)">
|
||||
<li t-foreach="website.language_ids" t-as="lg">
|
||||
<a t-att-href="url_for('', lang=lg.code)"
|
||||
t-att-data-default-lang="editable and 'true' if lg.code == website.default_lang_id.code else None">
|
||||
<t t-esc="lg.name.split('/').pop()"/>
|
||||
</a>
|
||||
</li>
|
||||
<li t-if="editable">
|
||||
<t t-set="url_return" t-value="request.multilang and url_for(request.httprequest.path, '[lang]') or request.httprequest.path"/>
|
||||
<a t-attf-href="/web#action=base.action_view_base_language_install&website_id=#{website.id}&url_return=#{url_return}">
|
||||
<i class="icon-plus-sign"/>
|
||||
Add a language...
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container mt16">
|
||||
<div class="pull-right" t-ignore="1">
|
||||
<div class="pull-right" t-ignore="true" t-if="not editable">
|
||||
Create a <a href="http://openerp.com/apps/website">free website</a> with
|
||||
<a class="label label-danger" href="https://openerp.com/apps/website">OpenERP</a>
|
||||
</div>
|
||||
|
@ -194,30 +227,32 @@
|
|||
|
||||
<template id="footer_custom" inherit_option_id="website.layout" name="Custom Footer">
|
||||
<xpath expr="//div[@id='footer_container']" position="before">
|
||||
<section data-snippet-id='three-columns' class="mt16 mb16">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h4 class="mt16">Subtitle</h4>
|
||||
<p>
|
||||
<a t-href="/">Homepage</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4 class="mt16">Subtitle 2</h4>
|
||||
<p>
|
||||
...
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4 class="mt16">Subtitle 3</h4>
|
||||
<p>
|
||||
...
|
||||
</p>
|
||||
<div class="oe_structure">
|
||||
<section data-snippet-id='three-columns' class="mt16 mb16">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<h4 class="mt16">Subtitle</h4>
|
||||
<p>
|
||||
<a href="/">Homepage</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4 class="mt16">Subtitle 2</h4>
|
||||
<p>
|
||||
...
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<h4 class="mt16">Subtitle 3</h4>
|
||||
<p>
|
||||
...
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='footer_container']" position="attributes">
|
||||
<attribute name="style">display: none</attribute>
|
||||
|
@ -225,9 +260,8 @@
|
|||
</template>
|
||||
|
||||
<template id="publish_management">
|
||||
<t t-if="editable" t-ignore="true">
|
||||
<div class="pull-right">
|
||||
<div t-attf-class="btn-group dropdown js_publish_management #{object.id and object.website_published and 'css_publish' or 'css_unpublish'}" t-att-data-id="object.id" t-att-data-object="object._name">
|
||||
<div t-if="editable" t-ignore="true" class="pull-right hidden-xs" t-att-style="style or ''">
|
||||
<div t-attf-class="btn-group dropdown js_publish_management #{object.id and object.website_published and 'css_publish' or 'css_unpublish'}" t-att-data-id="object.id" t-att-data-object="object._name" t-att-data-controller="publish_controller">
|
||||
<a t-attf-class="btn btn-sm btn-#{object.id and object.website_published and 'success' or 'default'}" t-att-id="'dopprod-%s' % object.id" role="button" data-toggle="dropdown">Options <span class="caret"></span></a>
|
||||
<ul class="dropdown-menu" role="menu" t-att-aria-labelledby="'dopprod-%s' % object.id">
|
||||
<t t-raw="0"/>
|
||||
|
@ -235,9 +269,6 @@
|
|||
<a href="#" class="js_publish_btn css_unpublish">Unpublish</a>
|
||||
<a href="#" class="js_publish_btn css_publish">Publish</a>
|
||||
</li>
|
||||
<li t-if="publish_duplicate">
|
||||
<a t-att-href="publish_duplicate">Duplicate</a>
|
||||
</li>
|
||||
<li t-if="publish_edit">
|
||||
<a t-att-href="'/web#model=%s&id=%s' % (object._name, object.id)"
|
||||
title='Edit in backend'>Edit</a>
|
||||
|
@ -245,7 +276,6 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="publish_short">
|
||||
|
@ -262,7 +292,7 @@
|
|||
</template>
|
||||
|
||||
<template id="pager" name="Pager">
|
||||
<ul t-if="pager['page_count'] > 1" t-attf-class="#{ classname or '' } pagination">
|
||||
<ul t-if="pager['page_count'] > 1" t-attf-class="#{ classname or '' } pagination" t-att-style="style or ''">
|
||||
<li t-att-class=" 'disabled' if pager['page']['num'] == 1 else '' ">
|
||||
<a t-att-href=" pager['page_previous']['url'] if pager['page']['num'] != 1 else '' ">Prev</a>
|
||||
</li>
|
||||
|
@ -364,8 +394,8 @@
|
|||
<h1 class="container mt32">500: Internal Server Error!</h1>
|
||||
</div>
|
||||
<t t-if="editable">
|
||||
<h3>An exception appear in the template: <span id="exception_template" t-esc="template"/></h3>
|
||||
<b id="exception_message" t-esc="message"/>
|
||||
<h3>Exception in template: <span id="exception_template" t-esc="template"/></h3>
|
||||
<h4 t-if="expr">Expression: <t t-esc="expr"/></h4>
|
||||
<pre id="exception_node" t-esc="node"/>
|
||||
<pre id="exception_traceback" t-esc="traceback"/>
|
||||
</t>
|
||||
|
@ -401,7 +431,7 @@ User-agent: *
|
|||
Sitemap: <t t-esc="url_root"/>sitemap.xml
|
||||
</template>
|
||||
|
||||
<template id="sitemap" name="Site Map" page="True">
|
||||
<template id="sitemap" name="Site Map">
|
||||
<t t-call="website.layout">
|
||||
<ul>
|
||||
<li t-foreach="pages" t-as="page">
|
||||
|
@ -439,8 +469,8 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml
|
|||
<span>&#x2706; <span t-field="res_company.phone"></span></span><br />
|
||||
<i class="icon-envelope"></i> <span t-field="res_company.email"></span>
|
||||
</address>
|
||||
<a t-att-href="res_company.partner_id.google_map_link()" target="_BLANK">
|
||||
<img class="thumbnail img-responsive" t-att-src="res_company.partner_id.google_map_img()" />
|
||||
<a t-att-href="res_company.google_map_link()" target="_BLANK">
|
||||
<img class="thumbnail img-responsive" t-att-src="res_company.google_map_img()" />
|
||||
</a>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -159,7 +159,9 @@ class WebsiteBlog(http.Controller):
|
|||
|
||||
return request.website.render("website_blog.index", values)
|
||||
|
||||
@website.route(['/blog/nav'], type='http', auth="public")
|
||||
# TODO: Refactor (used in website_blog.js for archive links)
|
||||
# => the archive links should be generated server side
|
||||
@website.route(['/blog/nav'], type='http', auth="public", multilang=True)
|
||||
def nav(self, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
blog_post_ids = request.registry['blog.post'].search(
|
||||
|
@ -171,32 +173,25 @@ class WebsiteBlog(http.Controller):
|
|||
blog_post_data = [
|
||||
{
|
||||
'id': blog_post.id,
|
||||
'name': blog_post.name,
|
||||
'website_published': blog_post.website_published,
|
||||
'category_id': blog_post.category_id and blog_post.category_id.id or False,
|
||||
'fragment': request.website.render("website_blog.blog_archive_link", {
|
||||
'blog_post': blog_post
|
||||
}),
|
||||
}
|
||||
for blog_post in request.registry['blog.post'].browse(cr, uid, blog_post_ids, context=context)
|
||||
]
|
||||
return simplejson.dumps(blog_post_data)
|
||||
|
||||
@website.route(['/blog/<int:blog_post_id>/post'], type='http', auth="public")
|
||||
@website.route(['/blog/<int:blog_post_id>/comment'], type='http', auth="public")
|
||||
def blog_post_comment(self, blog_post_id=None, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
url = request.httprequest.host_url
|
||||
request.session.body = post.get('body')
|
||||
if request.context['is_public_user']: # purpose of this ?
|
||||
return '%s/web#action=redirect&url=%s/blog/%s/post' % (url, url, blog_post_id)
|
||||
|
||||
if request.session.get('body') and blog_post_id:
|
||||
request.registry['blog.post'].message_post(
|
||||
cr, uid, blog_post_id,
|
||||
body=request.session.body,
|
||||
type='comment',
|
||||
subtype='mt_comment',
|
||||
context=dict(context, mail_create_nosubcribe=True))
|
||||
request.session.body = False
|
||||
|
||||
return werkzeug.utils.redirect("/blog/%s/?enable_editor=1" % (blog_post_id))
|
||||
request.registry['blog.post'].message_post(
|
||||
cr, uid, blog_post_id,
|
||||
body=post.get('comment'),
|
||||
type='comment',
|
||||
subtype='mt_comment',
|
||||
context=dict(context, mail_create_nosubcribe=True))
|
||||
return werkzeug.utils.redirect(request.httprequest.referrer + "#comments")
|
||||
|
||||
@website.route(['/blog/<int:category_id>/new'], type='http', auth="public")
|
||||
def blog_post_create(self, category_id=None, **post):
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<openerp>
|
||||
<!-- <data noupdate="1"> -->
|
||||
<data noupdate="1">
|
||||
<record id="menu_blog" model="website.menu">
|
||||
<field name="name">News</field>
|
||||
<field name="url">/blog</field>
|
||||
<field name="parent_id" ref="website.main_menu"/>
|
||||
<field name="sequence" type="int">50</field>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
<data>
|
||||
|
||||
<!-- jump to blog at install -->
|
||||
|
|
|
@ -8,15 +8,14 @@ $(document).ready(function () {
|
|||
e.preventDefault();
|
||||
var $ul = $(this).next("ul");
|
||||
if (!$ul.find('li').length) {
|
||||
// TODO: Why POST? (to pass the domain) A GET would be more appropriate...
|
||||
// This should be done server side anyway...
|
||||
$.post('/blog/nav', {'domain': $(this).data("domain")}, function (result) {
|
||||
var blog_id = +window.location.pathname.split("/").pop();
|
||||
$(JSON.parse(result)).each(function () {
|
||||
var $a = $('<a href="/blog/' + this.id + '"/>').text(this.name);
|
||||
var $li = $("<li/>").append($a);
|
||||
if (blog_id == this.id)
|
||||
$li.addClass("active");
|
||||
if (!this.website_published)
|
||||
$a.css("color", "red");
|
||||
var $li = $($.parseHTML(this.fragment));
|
||||
if (blog_id == this.id) $li.addClass("active");
|
||||
if (!this.website_published) $li.find('a').css("color", "red");
|
||||
$ul.append($li);
|
||||
});
|
||||
|
||||
|
@ -26,16 +25,4 @@ $(document).ready(function () {
|
|||
}
|
||||
});
|
||||
|
||||
var $form = $('.js_website_blog form#comment');
|
||||
$form.submit(function (e) {
|
||||
e.preventDefault();
|
||||
var error = $form.find("textarea").val().length < 3;
|
||||
$form.find("textarea").toggleClass("has-error", error);
|
||||
if (!error) {
|
||||
$form.css("visibility", "hidden");
|
||||
$.post(window.location.pathname + '/post', {'body': $form.find("textarea").val()}, function (url) {
|
||||
window.location.href = url
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
|
@ -2,21 +2,12 @@
|
|||
<openerp>
|
||||
<data>
|
||||
<!-- Layout add nav and footer -->
|
||||
<template id="header_footer_custom" inherit_id="website.layout">
|
||||
<xpath expr="//header//ul[@id='top_menu']/li[@name='contactus']" position="before">
|
||||
<li><a t-href="/blog/cat/%(website_blog.blog_category_1)d/">News</a></li>
|
||||
</xpath>
|
||||
<template id="header_footer_custom" inherit_id="website.layout" name="Footer News Blog Link">
|
||||
<xpath expr="//footer//div[@name='info']/ul" position="inside">
|
||||
<li><a t-href="/blog/cat/%(website_blog.blog_category_1)d/">News</a></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="footer_custom" inherit_option_id="website.layout" name="Job Announces">
|
||||
<xpath expr="//footer//div[@name='info']/ul" position="inside">
|
||||
<li><a t-href="/blog/%(website_blog.blog_category_2)d/">Jobs</a></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Blog Post Summary -->
|
||||
<template id="blog_post_short" name="Blog Post Summary">
|
||||
<div>
|
||||
|
@ -24,7 +15,14 @@
|
|||
<t t-call="website.publish_management">
|
||||
<t t-set="object" t-value="blog_post"/>
|
||||
<t t-set="publish_edit" t-value="True"/>
|
||||
<t t-set="publish_duplicate" t-value="'/blog/#{blog_post.id}/duplicate'"/>
|
||||
<li>
|
||||
<a href="#" t-attf-data-href="/blog/#{blog_post.id}/duplicate">Duplicate</a>
|
||||
<script>
|
||||
var $a=$("[data-href$='/duplicate']");
|
||||
$a.attr("href", $a.data('href')).removeAttr('data-href');
|
||||
$a.next("script").remove();
|
||||
</script>
|
||||
</li>
|
||||
</t>
|
||||
</div>
|
||||
<h2 class="text-center">
|
||||
|
@ -81,7 +79,14 @@
|
|||
<t t-call="website.publish_management">
|
||||
<t t-set="object" t-value="blog_post"/>
|
||||
<t t-set="publish_edit" t-value="True"/>
|
||||
<t t-set="publish_duplicate" t-value="'/blog/%s/duplicate' % (blog_post.id)"/>
|
||||
<li>
|
||||
<a href="#" t-attf-data-href="/blog/#{blog_post.id}/duplicate">Duplicate</a>
|
||||
<script>
|
||||
var $a=$("[data-href$='/duplicate']");
|
||||
$a.attr("href", $a.data('href')).removeAttr('data-href');
|
||||
$a.next("script").remove();
|
||||
</script>
|
||||
</li>
|
||||
</t>
|
||||
</div>
|
||||
<div class="clearfix"/>
|
||||
|
@ -132,17 +137,19 @@
|
|||
<template id="opt_blog_post_complete_comment" name="Comment Form"
|
||||
inherit_option_id="website_blog.blog_post_complete" inherit_id="website_blog.blog_post_complete">
|
||||
<xpath expr="//ul[last()]" position="after">
|
||||
<section groups="group_website_blog_reply" class="mb32">
|
||||
<h4>Leave a Comment</h4>
|
||||
<form id="comment" t-attf-action="/blog/#{blog_post.id}/post#post"
|
||||
method="POST">
|
||||
<img class="img pull-left img-rounded" t-att-src="'/website/image?model=res.partner&field=image_small&id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
|
||||
<div class="pull-left mb32" style="width: 75%%">
|
||||
<textarea rows="3" class="form-control" placeholder="Write a comment..."></textarea>
|
||||
<button type="submit" class="btn btn-danger mt8">Post</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<t t-if="not is_public_user">
|
||||
<section groups="group_website_blog_reply" class="mb32">
|
||||
<h4>Leave a Comment</h4>
|
||||
<form id="comment" t-attf-action="/blog/#{blog_post.id}/comment"
|
||||
method="POST">
|
||||
<img class="img pull-left img-rounded" t-att-src="'/website/image?model=res.partner&field=image_small&id='+str(user_id.partner_id.id)" style="width: 50px; margin-right: 10px;"/>
|
||||
<div class="pull-left mb32" style="width: 75%%">
|
||||
<textarea rows="3" name="comment" class="form-control" placeholder="Write a comment..."></textarea>
|
||||
<button type="submit" class="btn btn-danger mt8">Post</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
@ -176,7 +183,7 @@
|
|||
</template>
|
||||
|
||||
<!-- Page -->
|
||||
<template id="index" name="Blog" page="True">
|
||||
<template id="index" name="Blog">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_blog/static/src/js/website_blog.js"></script>
|
||||
|
@ -187,7 +194,7 @@
|
|||
<div class="row">
|
||||
<div class="col-lg-12 col-sm-12" t-if="not blog_post" id="blog_post">
|
||||
<t t-if="category and editable">
|
||||
<div class="row">
|
||||
<div class="row hidden-xs">
|
||||
<a t-href="/blog/#{category.id}/new" class="btn btn-primary pull-right">New Blog Post</a>
|
||||
</div>
|
||||
</t>
|
||||
|
@ -311,5 +318,11 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="blog_archive_link">
|
||||
<li>
|
||||
<a t-href="/blog/#{blog_post.id}"><t t-esc="blog_post.name"/></a>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="contactus_thanks" name="Contact us" page="True">
|
||||
<template id="contactus_thanks" name="Contact us">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
|
|
|
@ -28,7 +28,7 @@ class WebsiteCrmPartnerAssign(http.Controller):
|
|||
if request.context['is_public_user']:
|
||||
base_partner_domain += [('website_published', '=', True)]
|
||||
partner_domain = list(base_partner_domain)
|
||||
if grade_id and grade_id != 'all':
|
||||
if grade_id and grade_id != "all":
|
||||
partner_domain += [('grade_id', '=', int(grade_id))] # try/catch int
|
||||
if country_id:
|
||||
country = country_obj.browse(request.cr, request.uid, country_id, request.context)
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
|
||||
<!-- Layout add nav and footer -->
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Footer Partners Link">
|
||||
<xpath expr="//footer//div[@name='info']/ul" position="inside">
|
||||
<li><a t-href="/partners/">Partners</a></li>
|
||||
</xpath>
|
||||
|
|
|
@ -14,7 +14,7 @@ class WebsiteCustomer(http.Controller):
|
|||
@website.route([
|
||||
'/customers/', '/customers/page/<int:page>/',
|
||||
'/customers/country/<int:country_id>', '/customers/country/<int:country_id>/page/<int:page>/'
|
||||
], type='http', auth="public")
|
||||
], type='http', auth="public", multilang=True)
|
||||
def customers(self, country_id=None, page=0, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
partner_obj = request.registry['res.partner']
|
||||
|
@ -70,7 +70,7 @@ class WebsiteCustomer(http.Controller):
|
|||
}
|
||||
return request.website.render("website_customer.index", values)
|
||||
|
||||
@website.route(['/customers/<int:partner_id>/'], type='http', auth="public")
|
||||
@website.route(['/customers/<int:partner_id>/'], type='http', auth="public", multilang=True)
|
||||
def customer(self, partner_id=None, **post):
|
||||
""" Route for displaying a single partner / customer.
|
||||
|
||||
|
|
|
@ -3,13 +3,13 @@
|
|||
<data>
|
||||
|
||||
<!-- Layout add nav and footer -->
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Footer Customer References Link">
|
||||
<xpath expr="//footer//div[@name='info']/ul" position="inside">
|
||||
<li><a href="/customers/">Customer References</a></li>
|
||||
<li><a t-href="/customers/">Customer References</a></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Page -->
|
||||
<!-- Page -->
|
||||
<template id="layout" name="Customer References Layout">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="additional_title">Customer References</t>
|
||||
|
@ -53,7 +53,7 @@
|
|||
<t t-set="classname" t-value="'pull-left'"/>
|
||||
</t>
|
||||
<form action="/customers/" method="get" class="navbar-search pull-right pagination form-inline">
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<input type="text" name="search" class="search-query form-control"
|
||||
placeholder="Search" t-att-value="post.get('search', '')"/>
|
||||
</div>
|
||||
|
@ -63,11 +63,11 @@
|
|||
<div>
|
||||
<div t-foreach="partner_ids" t-as="partner" class="media thumbnail" data-publish="">
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="partner"/></t>
|
||||
<a class="pull-left" t-attf-href="/customers/#{ partner.id }/">
|
||||
<a class="pull-left" t-href="/customers/#{ partner.id }/">
|
||||
<img class="media-object" t-att-src="partner.img('image_small')"/>
|
||||
</a>
|
||||
<div class="media-body" style="min-height: 64px;">
|
||||
<a class="media-heading" t-attf-href="/customers/#{ partner.id }/"><span t-field="partner.parent_id"/> <span t-field="partner.name"/></a>
|
||||
<a class="media-heading" t-href="/customers/#{ partner.id }/"><span t-field="partner.parent_id"/> <span t-field="partner.name"/></a>
|
||||
<t t-if="partner.website_short_description"><div t-field="partner.website_short_description"/></t><a t-if="partner.website_short_description" class="pull-right" t-attf-href="/customers/#{ partner.id }/">Read More</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -15,6 +15,7 @@ Online Events
|
|||
'data': [
|
||||
'data/event_data.xml',
|
||||
'views/website_event.xml',
|
||||
'views/website_event_sale_backend.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/website_event.xml',
|
||||
],
|
||||
|
|
|
@ -25,6 +25,9 @@ from openerp.addons.web.http import request
|
|||
from openerp.tools.translate import _
|
||||
from openerp.addons import website_sale
|
||||
from openerp.addons.website.models import website
|
||||
from openerp.addons.website.controllers.main import Website as controllers
|
||||
controllers = controllers()
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
@ -216,3 +219,15 @@ class website_event(http.Controller):
|
|||
if not _values:
|
||||
return request.redirect("/event/%s/" % event_id)
|
||||
return request.redirect("/shop/checkout")
|
||||
|
||||
@website.route(['/event/publish'], type='json', auth="public")
|
||||
def publish(self, id, object):
|
||||
# if a user publish an event, he publish all linked res.partner
|
||||
event = request.registry[object].browse(request.cr, request.uid, int(id))
|
||||
if not event.website_published:
|
||||
if event.organizer_id and not event.organizer_id.website_published:
|
||||
event.organizer_id.write({'website_published': True})
|
||||
if event.address_id and not event.address_id.website_published:
|
||||
event.address_id.write({'website_published': True})
|
||||
|
||||
return controllers.publish(id, object)
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="menu_events" model="website.menu">
|
||||
<field name="name">Events</field>
|
||||
<field name="url">/event</field>
|
||||
<field name="parent_id" ref="website.main_menu"/>
|
||||
<field name="sequence" type="int">40</field>
|
||||
</record>
|
||||
<record id="action_open_website" model="ir.actions.act_url">
|
||||
<field name="name">Website Home</field>
|
||||
<field name="target">self</field>
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
<openerp>
|
||||
<data>
|
||||
|
||||
<record id="base.res_partner_6" model="res.partner">
|
||||
<field name="website_published">True</field>
|
||||
</record>
|
||||
<record id="event.event_0" model="event.event">
|
||||
<field name="website_published">True</field>
|
||||
<field name="twitter_hashtag">openerp</field>
|
||||
|
@ -162,6 +165,9 @@
|
|||
</div>
|
||||
]]></field> </record>
|
||||
|
||||
<record id="base.res_partner_5" model="res.partner">
|
||||
<field name="website_published">True</field>
|
||||
</record>
|
||||
<record id="event.event_1" model="event.event">
|
||||
<field name="website_published">True</field>
|
||||
<field name="twitter_hashtag">openerp</field>
|
||||
|
@ -323,11 +329,20 @@
|
|||
]]></field>
|
||||
</record>
|
||||
|
||||
<record id="base.res_partner_14" model="res.partner">
|
||||
<field name="website_published">True</field>
|
||||
</record>
|
||||
<record id="event.event_2" model="event.event">
|
||||
<field name="website_published">True</field>
|
||||
<field name="twitter_hashtag">openerp</field>
|
||||
</record>
|
||||
|
||||
<record id="base.res_partner_2" model="res.partner">
|
||||
<field name="website_published">True</field>
|
||||
</record>
|
||||
<record id="base.res_partner_address_4" model="res.partner">
|
||||
<field name="website_published">True</field>
|
||||
</record>
|
||||
<record id="event.event_3" model="event.event">
|
||||
<field name="website_published">True</field>
|
||||
<field name="twitter_hashtag">openerp</field>
|
||||
|
|
|
@ -51,6 +51,21 @@ class event(osv.osv):
|
|||
'website_published': False,
|
||||
}
|
||||
|
||||
def _check_organizer_id_published(self, cr, uid, ids, context=None):
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.website_published and obj.organizer_id and not obj.organizer_id.website_published:
|
||||
return False
|
||||
return True
|
||||
def _check_address_id_published(self, cr, uid, ids, context=None):
|
||||
for obj in self.browse(cr, uid, ids, context=context):
|
||||
if obj.website_published and obj.address_id and not obj.address_id.website_published:
|
||||
return False
|
||||
return True
|
||||
_constraints = [
|
||||
(_check_organizer_id_published, "This event can't be published if the field Orginizer is not website published.", ['organizer_id','website_published']),
|
||||
(_check_address_id_published, "This event can't be published if the field Location is not website published.", ['address_id','website_published']),
|
||||
]
|
||||
|
||||
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)
|
||||
if partner.address_id:
|
||||
|
|
|
@ -3,17 +3,14 @@
|
|||
<data>
|
||||
|
||||
<!-- Layout add nav and footer -->
|
||||
<template id="header_footer_custom" inherit_id="website.layout">
|
||||
<xpath expr="//header//ul[@id='top_menu']/li[@name='contactus']" position="before">
|
||||
<li><a t-href="/event">Events</a></li>
|
||||
</xpath>
|
||||
<xpath expr="//footer//div[@name='info']/ul" position="inside">
|
||||
<template id="header_footer_custom" inherit_id="website.layout" name="Footer Events Link">
|
||||
<xpath expr="//footer//ul[@name='products']" position="inside">
|
||||
<li><a t-href="/event">Events</a></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Page -->
|
||||
<template id="index" name="Events" page="True">
|
||||
<template id="index" name="Events">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="container">
|
||||
|
@ -48,7 +45,10 @@
|
|||
</div>
|
||||
<ul class="media-list">
|
||||
<li t-foreach="event_ids" t-as="event" class="media" data-publish="">
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="event"/></t>
|
||||
<t t-call="website.publish_management">
|
||||
<t t-set="object" t-value="event"/>
|
||||
<t t-set="publish_controller">/event/publish</t>
|
||||
</t>
|
||||
<div class="media-body">
|
||||
<span t-if="not event.event_ticket_ids" class="label label-danger pull-right">Registration Closed</span>
|
||||
<t t-if="event.event_ticket_ids">
|
||||
|
@ -177,7 +177,11 @@
|
|||
<ul class="media-list" id="comment">
|
||||
<li t-foreach="event_id.website_message_ids" t-as="comment" class="media">
|
||||
<div class="media-body">
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="comment"/></t>
|
||||
<t t-call="website.publish_management">
|
||||
<t t-set="object" t-value="comment"/>
|
||||
<t t-set="publish_edit" t-value="True"/>
|
||||
<t t-set="publish_controller">/event/publish</t>
|
||||
</t>
|
||||
<t t-raw="comment.body"/>
|
||||
<small class="pull-right muted text-right">
|
||||
<div t-field="comment.author_id"/>
|
||||
|
@ -192,7 +196,12 @@
|
|||
|
||||
<div class="panel panel-default" t-if="event_id.address_id">
|
||||
<div class="panel-heading">
|
||||
<t t-call="website.publish_management"><t t-set="object" t-value="event_id"/></t>
|
||||
<t t-call="website.publish_management">
|
||||
<t t-set="object" t-value="event_id"/>
|
||||
<t t-set="publish_edit" t-value="True"/>
|
||||
<t t-set="publish_controller">/event/publish</t>
|
||||
<t t-set="style">position: relative; top: -80px;</t>
|
||||
</t>
|
||||
<h4>Where</h4>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<record model="ir.ui.view" id="view_event_form">
|
||||
<field name="name">event.event.website.form</field>
|
||||
<field name="model">event.event</field>
|
||||
<field name="inherit_id" ref="event_sale.view_event_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="organizer_id" position="after">
|
||||
<field name="website_published"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
|
@ -11,4 +11,4 @@ class hr(osv.osv):
|
|||
}
|
||||
|
||||
def img(self, cr, uid, ids, field='image_small', context=None):
|
||||
return "/web/binary/image?model=%s&field=%s&id=%s" % (self._name, field, ids[0])
|
||||
return "/website/image?model=%s&field=%s&id=%s" % (self._name, field, ids[0])
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
from openerp.addons.web import http
|
||||
from openerp.addons.web.http import request
|
||||
from openerp.addons.website.models import website
|
||||
from openerp.addons.website.controllers.main import Website as controllers
|
||||
controllers = controllers()
|
||||
|
||||
import base64
|
||||
|
||||
|
||||
|
@ -48,7 +51,7 @@ class website_hr_recruitment(http.Controller):
|
|||
return request.website.render("website_hr_recruitment.index", values)
|
||||
|
||||
@website.route(['/job/detail/<model("hr.job"):job>'], type='http', auth="public", multilang=True)
|
||||
def detail(self, job=None):
|
||||
def detail(self, job=None, **kwargs):
|
||||
values = {
|
||||
'job': job,
|
||||
'vals_date': job.write_date.split(' ')[0],
|
||||
|
@ -68,28 +71,23 @@ class website_hr_recruitment(http.Controller):
|
|||
'res_id': id
|
||||
}
|
||||
request.registry['ir.attachment'].create(request.cr, request.uid, attachment_values)
|
||||
website = request.registry['website']
|
||||
values = {
|
||||
'jobid': post['job_id']
|
||||
}
|
||||
return request.website.render("website_hr_recruitment.thankyou", values)
|
||||
|
||||
@website.route(['/apply/<int:id>'], type='http', auth="public", multilang=True)
|
||||
def applyjobpost(self, id=0, **kwargs):
|
||||
id = id and int(id) or 0
|
||||
job = request.registry['hr.job'].browse(request.cr, request.uid, id)
|
||||
values = {
|
||||
'job': job
|
||||
}
|
||||
return request.website.render("website_hr_recruitment.applyjobpost", values)
|
||||
@website.route(['/apply/<model("hr.job"):job>'], type='http', auth="public", multilang=True)
|
||||
def applyjobpost(self, job=None, **kwargs):
|
||||
return request.website.render("website_hr_recruitment.applyjobpost", { 'job': job })
|
||||
|
||||
@website.route('/recruitment/published', type='json', auth="admin", multilang=True)
|
||||
def published (self, id, **post):
|
||||
hr_job = request.registry['hr.job']
|
||||
@website.route('/job/publish', type='json', auth="admin", multilang=True)
|
||||
def publish (self, id, object):
|
||||
res = controllers.publish(id, object)
|
||||
|
||||
hr_job = request.registry[object]
|
||||
id = int(id)
|
||||
rec = hr_job.browse(request.cr, request.uid, id)
|
||||
vals = {}
|
||||
|
||||
if rec.website_published:
|
||||
vals['state'] = 'recruit'
|
||||
if not rec.no_of_recruitment:
|
||||
|
@ -98,6 +96,5 @@ class website_hr_recruitment(http.Controller):
|
|||
vals['state'] = 'open'
|
||||
hr_job.write(request.cr, request.uid, [rec.id], vals, context=request.context)
|
||||
|
||||
obj = hr_job.browse(request.cr, request.uid, id, context=request.context)
|
||||
return { 'count': obj.no_of_recruitment, 'state': obj.state, 'published': obj.website_published }
|
||||
return res
|
||||
# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="menu_jobs" model="website.menu">
|
||||
<field name="name">Jobs</field>
|
||||
<field name="url">/jobs</field>
|
||||
<field name="parent_id" ref="website.main_menu"/>
|
||||
<field name="sequence" type="int">60</field>
|
||||
</record>
|
||||
<record id="action_open_website" model="ir.actions.act_url">
|
||||
<field name="name">Website Recruitment Form</field>
|
||||
<field name="target">self</field>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
$(function () {
|
||||
$(this).ajaxComplete(function(event, xhr, settings) {
|
||||
var data = JSON.parse(settings.data).params;
|
||||
if (settings.url == "/website/publish") {
|
||||
var $data = $(".oe_website_hr_recruitment .js_publish_management[data-id='" + data.id + "'][data-object='" + data.object + "']");
|
||||
if ($data.length) {
|
||||
settings.jsonpCallback = openerp.jsonRpc('/recruitment/published', 'call', data);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
|
@ -7,16 +7,13 @@
|
|||
<field name="description">Job Posts on your website</field>
|
||||
</record>
|
||||
|
||||
<template id="job_footer_custom" inherit_id="website.layout" name="Custom Footer Job">
|
||||
<xpath expr="//header//ul[@id='top_menu']/li[@name='contactus']" position="before">
|
||||
<li><a t-href="/jobs">Jobs</a></li>
|
||||
</xpath>
|
||||
<template id="job_footer_custom" inherit_id="website.layout" name="Footer Job Link">
|
||||
<xpath expr="//footer//div[@name='info']/ul" position="inside">
|
||||
<li><a t-href="/jobs">Jobs</a></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="index" name="Jobs" page="True">
|
||||
<template id="index" name="Jobs">
|
||||
<t t-call="website.layout">
|
||||
<div id="wrap">
|
||||
<div class="oe_structure"/>
|
||||
|
@ -56,10 +53,6 @@
|
|||
|
||||
<template id="detail">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_hr_recruitment/static/src/js/recruitment.js"></script>
|
||||
<t t-raw="head or ''"/>
|
||||
</t>
|
||||
<t t-set="additional_title">Job Detail</t>
|
||||
<div id="wrap">
|
||||
<div class="container oe_website_hr_recruitment">
|
||||
|
@ -73,6 +66,7 @@
|
|||
<t t-call="website.publish_management">
|
||||
<t t-set="object" t-value="job"/>
|
||||
<t t-set="publish_edit" t-value="True"/>
|
||||
<t t-set="publish_controller">/job/publish</t>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
<data>
|
||||
|
||||
<!-- Layout add nav and footer -->
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Footer Associations Link">
|
||||
<xpath expr="//footer//div[@name='info']/ul" position="inside">
|
||||
<li><a t-href="/members/">Associations</a></li>
|
||||
</xpath>
|
||||
|
|
|
@ -28,13 +28,13 @@ from openerp.osv import osv
|
|||
class Website(osv.Model):
|
||||
_inherit = "website"
|
||||
|
||||
def preprocess_request(self, cr, uid, ids, *args, **kwargs):
|
||||
def preprocess_request(self, cr, uid, ids, request, context=None):
|
||||
project_obj = request.registry['project.project']
|
||||
project_ids = project_obj.search(cr, uid, [('privacy_visibility', "=", "public")], context=request.context)
|
||||
|
||||
request.context['website_project_ids'] = project_obj.browse(cr, uid, project_ids, request.context)
|
||||
|
||||
return super(Website, self).preprocess_request(cr, uid, ids, *args, **kwargs)
|
||||
return super(Website, self).preprocess_request(cr, uid, ids, request, context)
|
||||
|
||||
|
||||
class website_project(http.Controller):
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
<data>
|
||||
|
||||
<!-- Layout add nav and footer -->
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Custom Footer">
|
||||
<template id="footer_custom" inherit_id="website.layout" name="Footer Project's Links">
|
||||
<xpath expr="//footer//ul[@name='products']" position="inside">
|
||||
<li t-foreach="website_project_ids" t-as="project"><a t-href="/project/#{ project.id }/"><t t-esc="project.name"/></a></li>
|
||||
<li t-foreach="website_project_ids" t-as="project"><a t-href="/project/#{ project.id }/" t-field="project.name"/></li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import random
|
||||
import uuid
|
||||
import urllib
|
||||
import simplejson
|
||||
|
||||
import werkzeug.exceptions
|
||||
|
@ -54,11 +53,11 @@ def get_current_order():
|
|||
|
||||
class Website(osv.osv):
|
||||
_inherit = "website"
|
||||
def preprocess_request(self, cr, uid, ids, *args, **kwargs):
|
||||
def preprocess_request(self, cr, uid, ids, request, context=None):
|
||||
request.context.update({
|
||||
'website_sale_order': get_current_order(),
|
||||
})
|
||||
return super(Website, self).preprocess_request(cr, uid, ids, *args, **kwargs)
|
||||
return super(Website, self).preprocess_request(cr, uid, ids, request, context=None)
|
||||
|
||||
class Ecommerce(http.Controller):
|
||||
|
||||
|
@ -214,7 +213,6 @@ class Ecommerce(http.Controller):
|
|||
col += 1
|
||||
line += 1
|
||||
|
||||
print bin_packing_list
|
||||
return bin_packing_list
|
||||
|
||||
def get_products(self, product_ids):
|
||||
|
@ -224,20 +222,24 @@ class Ecommerce(http.Controller):
|
|||
product_ids = [id for id in product_ids if id in product_obj.search(request.cr, request.uid, [("id", 'in', product_ids)], context=request.context)]
|
||||
return product_obj.browse(request.cr, request.uid, product_ids, context=request.context)
|
||||
|
||||
def has_search_attributes(self, attribute_id, value_id=None):
|
||||
if request.httprequest.args.get('attributes'):
|
||||
attributes = simplejson.loads(request.httprequest.args['attributes'])
|
||||
def has_search_filter(self, attribute_id, value_id=None):
|
||||
if request.httprequest.args.get('filter'):
|
||||
filter = simplejson.loads(request.httprequest.args['filter'])
|
||||
else:
|
||||
attributes = []
|
||||
for key_val in attributes:
|
||||
filter = []
|
||||
for key_val in filter:
|
||||
if key_val[0] == attribute_id and (not value_id or value_id in key_val[1:]):
|
||||
return key_val
|
||||
return False
|
||||
|
||||
@website.route(['/shop/attributes/'], type='http', auth="public", multilang=True)
|
||||
def attributes(self, **post):
|
||||
attributes = []
|
||||
@website.route(['/shop/filter/'], type='http', auth="public", multilang=True)
|
||||
def filter(self, add_filter="", **post):
|
||||
index = []
|
||||
filter = []
|
||||
if add_filter:
|
||||
filter = simplejson.loads(add_filter)
|
||||
for filt in filter:
|
||||
index.append(filt[0])
|
||||
for key, val in post.items():
|
||||
cat = key.split("-")
|
||||
if len(cat) < 3 or cat[2] in ('max','minmem','maxmem'):
|
||||
|
@ -249,16 +251,23 @@ class Ecommerce(http.Controller):
|
|||
_max = int(post.pop("att-%s-max" % cat[1]))
|
||||
_min = int(val)
|
||||
if (minmem != _min or maxmem != _max) and cat_id not in index:
|
||||
attributes.append([cat_id , [_min, _max] ])
|
||||
filter.append([cat_id , [_min, _max] ])
|
||||
index.append(cat_id)
|
||||
elif cat_id not in index:
|
||||
attributes.append([ cat_id, int(cat[2]) ])
|
||||
filter.append([ cat_id, int(cat[2]) ])
|
||||
index.append(cat_id)
|
||||
else:
|
||||
attributes[index.index(cat_id)].append( int(cat[2]) )
|
||||
cat[2] = int(cat[2])
|
||||
if cat[2] not in filter[index.index(cat_id)][1:]:
|
||||
filter[index.index(cat_id)].append( cat[2] )
|
||||
post.pop(key)
|
||||
|
||||
return request.redirect("/shop/?attributes=%s&%s" % (simplejson.dumps(attributes).replace(" ", ""), urllib.urlencode(post)))
|
||||
return request.redirect("/shop/?filter=%s%s%s%s" % (
|
||||
simplejson.dumps(filter),
|
||||
add_filter and "&add_filter=%s" % add_filter or "",
|
||||
post.get("search") and "&search=%s" % post.get("search") or "",
|
||||
post.get("category") and "&category=%s" % post.get("category") or ""
|
||||
))
|
||||
|
||||
def attributes_to_ids(self, attributes):
|
||||
obj = request.registry.get('product.attribute.product')
|
||||
|
@ -274,7 +283,7 @@ class Ecommerce(http.Controller):
|
|||
return [r["product_id"][0] for r in att]
|
||||
|
||||
@website.route(['/shop/', '/shop/page/<int:page>/'], type='http', auth="public", multilang=True)
|
||||
def category(self, category=0, attributes="", page=0, **post):
|
||||
def category(self, category=0, filter="", page=0, **post):
|
||||
# TDE-NOTE: shouldn't we do somethign about product_template without variants ???
|
||||
# TDE-NOTE: is there a reason to call a method category when the route is
|
||||
# basically a shop without category_id speceified ?
|
||||
|
@ -296,10 +305,10 @@ class Ecommerce(http.Controller):
|
|||
cat_id = int(category)
|
||||
domain = [('product_variant_ids.public_categ_id.id', 'child_of', cat_id)] + domain
|
||||
|
||||
if attributes:
|
||||
attributes = simplejson.loads(attributes)
|
||||
if attributes:
|
||||
ids = self.attributes_to_ids(attributes)
|
||||
if filter:
|
||||
filter = simplejson.loads(filter)
|
||||
if filter:
|
||||
ids = self.attributes_to_ids(filter)
|
||||
domain = [('id', 'in', ids or [0] )] + domain
|
||||
|
||||
step = 20
|
||||
|
@ -321,7 +330,11 @@ class Ecommerce(http.Controller):
|
|||
'Ecommerce': self,
|
||||
'product_ids': product_ids,
|
||||
'product_ids_for_holes': fill_hole,
|
||||
'search': post or dict(),
|
||||
'search': {
|
||||
'search': post.get('search') or '',
|
||||
'category': category,
|
||||
'filter': filter or '',
|
||||
},
|
||||
'pager': pager,
|
||||
'styles': styles,
|
||||
'style_in_product': lambda style, product: style.id in [s.id for s in product.website_style_ids],
|
||||
|
@ -342,8 +355,8 @@ class Ecommerce(http.Controller):
|
|||
category_list = sorted(category_list, key=lambda category: category[1])
|
||||
|
||||
category = None
|
||||
if post.get('category_id') and int(post.get('category_id')):
|
||||
category = category_obj.browse(request.cr, request.uid, int(post.get('category_id')), context=request.context)
|
||||
if post.get('category') and int(post.get('category')):
|
||||
category = category_obj.browse(request.cr, request.uid, int(post.get('category')), context=request.context)
|
||||
|
||||
request.context['pricelist'] = self.get_pricelist()
|
||||
product = product_obj.browse(request.cr, request.uid, product_id, context=request.context)
|
||||
|
@ -354,7 +367,11 @@ class Ecommerce(http.Controller):
|
|||
'category_list': category_list,
|
||||
'main_object': product,
|
||||
'product': product,
|
||||
'search': post or dict(),
|
||||
'search': {
|
||||
'search': post.get('search') or '',
|
||||
'category': post.get('category') or '',
|
||||
'filter': post.get('filter') or '',
|
||||
}
|
||||
}
|
||||
return request.website.render("website_sale.product", values)
|
||||
|
||||
|
@ -452,8 +469,7 @@ class Ecommerce(http.Controller):
|
|||
else:
|
||||
order_line_id = order_line_obj.create(request.cr, SUPERUSER_ID, values, context=request.context)
|
||||
order_obj.write(request.cr, SUPERUSER_ID, [order.id], {'order_line': [(4, order_line_id)]}, context=request.context)
|
||||
|
||||
return [quantity, order.get_total_quantity()]
|
||||
return quantity
|
||||
|
||||
@website.route(['/shop/mycart/'], type='http', auth="public", multilang=True)
|
||||
def mycart(self, **post):
|
||||
|
@ -493,7 +509,11 @@ class Ecommerce(http.Controller):
|
|||
|
||||
@website.route(['/shop/add_cart_json/'], type='json', auth="public")
|
||||
def add_cart_json(self, product_id=None, order_line_id=None, remove=None):
|
||||
return self.add_product_to_cart(product_id=product_id, order_line_id=order_line_id, number=(remove and -1 or 1))
|
||||
quantity = self.add_product_to_cart(product_id=product_id, order_line_id=order_line_id, number=(remove and -1 or 1))
|
||||
order = get_current_order()
|
||||
return [quantity, order.get_total_quantity(), order.amount_total, request.website.render("website_sale.total", {
|
||||
'website_sale_order': order
|
||||
}).strip()]
|
||||
|
||||
@website.route(['/shop/set_cart_json/'], type='json', auth="public")
|
||||
def set_cart_json(self, path=None, product_id=None, order_line_id=None, set_number=0, json=None):
|
||||
|
|
|
@ -38,7 +38,7 @@ class attributes(osv.Model):
|
|||
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=64, translate=True, required=True),
|
||||
'type': fields.selection([('distinct', 'Distinct'), ('float', 'Float')], "Type", required=True),
|
||||
'type': fields.selection([('distinct', 'Textual Value'), ('float', 'Numeric Value')], "Type", required=True),
|
||||
'value_ids': fields.one2many('product.attribute.value', 'attribute_id', 'Values'),
|
||||
'product_ids': fields.one2many('product.attribute.product', 'attribute_id', 'Products'),
|
||||
|
||||
|
@ -48,9 +48,11 @@ class attributes(osv.Model):
|
|||
'float_min': fields.function(_get_float_min, type='float', string="Min", store={
|
||||
'product.attribute.product': (_get_min_max, ['value','attribute_id'], 20),
|
||||
}),
|
||||
'visible': fields.boolean('Display Filter on Website'),
|
||||
}
|
||||
_defaults = {
|
||||
'type': 'distinct'
|
||||
'type': 'distinct',
|
||||
'visible': True,
|
||||
}
|
||||
|
||||
class attributes_value(osv.Model):
|
||||
|
@ -63,9 +65,10 @@ class attributes_value(osv.Model):
|
|||
|
||||
class attributes_product(osv.Model):
|
||||
_name = "product.attribute.product"
|
||||
_order = 'attribute_id, value_id, value'
|
||||
_columns = {
|
||||
'value': fields.float('Value'),
|
||||
'value_id': fields.many2one('product.attribute.value', 'Distinct Value'),
|
||||
'value': fields.float('Numeric Value'),
|
||||
'value_id': fields.many2one('product.attribute.value', 'Textual Value'),
|
||||
'attribute_id': fields.many2one('product.attribute', 'Attribute', required=True),
|
||||
'product_id': fields.many2one('product.template', 'Product', required=True),
|
||||
|
||||
|
|
|
@ -4,6 +4,10 @@ access_product_template_public,product.template.public,product.model_product_tem
|
|||
access_product_category_,product.category.public,product.model_product_category,base.group_public,1,0,0,0
|
||||
access_product_category_public,product.category.public,product.model_product_public_category,base.group_public,1,0,0,0
|
||||
access_product_pricelist_version_public,product.pricelist.version.public,product.model_product_pricelist_version,base.group_public,1,0,0,0
|
||||
access_product_pricelist_public,product.pricelist.public,product.model_product_pricelist,base.group_public,1,0,0,0
|
||||
access_product_product_price_type_public,product.price.type.public,product.model_product_price_type,base.group_public,1,0,0,0
|
||||
access_sale_order_public,sale.order.public,model_sale_order,base.group_public,1,0,0,0
|
||||
access_sale_order_line_public,sale.order.line.public,model_sale_order_line,base.group_public,1,0,0,0
|
||||
access_sale_order_line_public,sale.order.line.public,model_sale_order_line,base.group_public,1,0,0,0
|
||||
access_product_attribute,product.attribute.public,website_sale.model_product_attribute,base.group_public,1,0,0,0
|
||||
access_product_attribute_value,product.attribute.value.public,website_sale.model_product_attribute_value,base.group_public,1,0,0,0
|
||||
access_product_attribute_product,product.attribute.product.public,website_sale.model_product_attribute_product,base.group_public,1,0,0,0
|
|
|
@ -42,5 +42,16 @@
|
|||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_unlink" eval="False"/>
|
||||
</record>
|
||||
|
||||
<record id="product_pricelist_public" model="ir.rule">
|
||||
<field name="name">Public product pricelist</field>
|
||||
<field name="model_id" ref="product.model_product_pricelist"/>
|
||||
<field name="domain_force">[('id','=',session.get('ecommerce_pricelist'))]</field>
|
||||
<field name="groups" eval="[(4, ref('base.group_public'))]"/>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_unlink" eval="False"/>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
</openerp>
|
|
@ -133,7 +133,7 @@
|
|||
height: 560px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
@media (max-width: 768px) {
|
||||
#products_grid table, #products_grid tbody, #products_grid tr, #products_grid td {
|
||||
float: left;
|
||||
width: 100%;
|
||||
|
@ -145,6 +145,16 @@
|
|||
height: 300px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.products_pager {
|
||||
text-align: center;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.products_pager .pagination {
|
||||
float: none !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
}
|
||||
@media (max-width: 400px) {
|
||||
#products_grid .oe_product {
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
.oe-height-8
|
||||
height: 560px
|
||||
|
||||
@media (max-width: 600px)
|
||||
@media (max-width: 768px)
|
||||
#products_grid
|
||||
table, tbody,tr, td
|
||||
float: left
|
||||
|
@ -132,6 +132,13 @@
|
|||
width: 100%
|
||||
height: 300px
|
||||
display: inline-block
|
||||
.products_pager
|
||||
text-align: center
|
||||
margin: 10px 0
|
||||
.pagination
|
||||
float: none !important
|
||||
margin: 0 !important
|
||||
padding: 0 !important
|
||||
|
||||
@media (max-width: 400px)
|
||||
#products_grid
|
||||
|
|
|
@ -17,7 +17,7 @@ $(document).ready(function () {
|
|||
|
||||
function set_my_cart_quantity(qty) {
|
||||
var $q = $(".my_cart_quantity");
|
||||
$q.parent().parent().toggleClass("hidden", !qty);
|
||||
$q.parent().parent().removeClass("hidden", !qty);
|
||||
$q.html(qty)
|
||||
.hide()
|
||||
.fadeIn(600);
|
||||
|
@ -43,13 +43,21 @@ $(document).ready(function () {
|
|||
var $link = $(ev.currentTarget);
|
||||
var product = $link.attr("href").match(/product_id=([0-9]+)/);
|
||||
var product_id = product ? +product[1] : 0;
|
||||
openerp.jsonRpc("/shop/add_cart_json/", 'call', {'product_id': product_id, 'order_line_id': $link.data('id'), 'remove': $link.is('[href*="/remove_cart/"]')})
|
||||
if (!product) {
|
||||
var line = $link.attr("href").match(/order_line_id=([0-9]+)/);
|
||||
order_line_id = line ? +line[1] : 0;
|
||||
}
|
||||
openerp.jsonRpc("/shop/add_cart_json/", 'call', {
|
||||
'product_id': product_id,
|
||||
'order_line_id': order_line_id,
|
||||
'remove': $link.is('[href*="remove"]')})
|
||||
.then(function (data) {
|
||||
if (!data[0]) {
|
||||
location.reload();
|
||||
}
|
||||
set_my_cart_quantity(data[1]);
|
||||
$link.parents(".input-group:first").find(".js_quantity").val(data[0]);
|
||||
$('[data-oe-model="sale.order"][data-oe-field="amount_total"]').replaceWith(data[3]);
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
|
|
@ -4,9 +4,8 @@
|
|||
|
||||
<!-- Layout add nav and footer -->
|
||||
|
||||
<template id="header" inherit_id="website.layout" name="Custom Header">
|
||||
<template id="header" inherit_id="website.layout" name="Header Shop My Cart Link">
|
||||
<xpath expr="//header//ul[@id='top_menu']/li" position="before">
|
||||
<li><a t-href="/shop/">Shop</a></li>
|
||||
<li t-att-class="(not website_sale_order or not website_sale_order.get_total_quantity()) and 'hidden' or ''">
|
||||
<a t-href="/shop/mycart/">
|
||||
<i class="icon-shopping-cart"></i>
|
||||
|
@ -20,7 +19,7 @@
|
|||
<!-- List of categories -->
|
||||
|
||||
<template id="categories_recursive" name="Category list">
|
||||
<li t-att-class="category.id == search.get('category') and 'active' or ''">
|
||||
<li t-att-class="str(category.id) == search.get('category') and 'active' or ''">
|
||||
<a t-att-class="category.id not in categ[1] and 'unpublish' or ''" t-href="/shop/?category=#{ category.id }" t-field="category.name" t-keep-query="search,facettes"></a>
|
||||
<ul t-if="category.child_id" class="nav nav-pills nav-stacked nav-hierarchy">
|
||||
<t t-foreach="category.child_id" t-as="category">
|
||||
|
@ -34,6 +33,12 @@
|
|||
|
||||
<!-- Product list -->
|
||||
|
||||
<template id="search" name="Search hidden fields">
|
||||
<input type="hidden" name="category" t-att-value="search.get('category') or ''"/>
|
||||
<input type="hidden" name="filter" t-att-value="search.get('filter') or ''"/>
|
||||
<input type="text" name="search" class="search-query form-control" placeholder="Search..." t-att-value="search.get('search') or ''"/>
|
||||
</template>
|
||||
|
||||
<template id="products_cart" name="Shopping cart">
|
||||
<div class="ribbon-wrapper">
|
||||
<div class="ribbon">Promo</div>
|
||||
|
@ -65,9 +70,11 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<template id="products" name="Products" page="True">
|
||||
<template id="products" name="Products">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
|
||||
<link rel='stylesheet' href="/web/static/lib/jquery.ui/css/smoothness/jquery-ui-1.9.1.custom.css"/>
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
|
||||
<link rel='stylesheet' href='/website_sale/static/src/css/website_sale.css'/>
|
||||
<t t-raw="head or ''"/>
|
||||
|
@ -77,26 +84,27 @@
|
|||
<div class="oe_structure"/>
|
||||
<div class="container oe_website_sale">
|
||||
<div class="row">
|
||||
<div class="col-sm-6 pagination" style="padding-left: 15px;">
|
||||
<div class="col-sm-6 pagination hidden-xs" style="padding-left: 15px;">
|
||||
<form t-if="editable" t-keep-query="category,search,facettes"
|
||||
method="POST" t-action="/shop/add_product">
|
||||
<button class="btn btn-primary">New Product</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="col-sm-6 products_pager">
|
||||
<t t-call="website.pager">
|
||||
<t t-set="classname">pull-right</t>
|
||||
<t t-set="style">padding-left: 5px;</t>
|
||||
</t>
|
||||
<form t-action="/shop/" method="get" class="pull-right pagination form-inline" style="padding-right: 5px;" t-keep-query="category,search,facettes">
|
||||
<div class="form-group">
|
||||
<input type="text" name="search" class="search-query form-control" placeholder="Search..." t-att-value="search.get('search') or ''"/>
|
||||
</div>
|
||||
<form t-action="/shop/" method="get" class="pull-right pagination form-inline" style="padding-right: 5px;">
|
||||
<t t-call="website_sale.search" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='style_default row'>
|
||||
<div class="hidden" id="products_grid_before"></div>
|
||||
<div class="col-md-12" id="products_grid">
|
||||
<t t-if="product_ids">
|
||||
<table width="100%">
|
||||
<tbody>
|
||||
<t t-set="table_products" t-value="Ecommerce.get_bin_packing_products(product_ids, product_ids_for_holes, 4)"/>
|
||||
|
@ -170,9 +178,13 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
<t t-if="not product_ids">
|
||||
<h3 class="text-center text-muted">No product found for this search</h3>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="products_pager">
|
||||
<t t-call="website.pager">
|
||||
<t t-set="classname">pull-right</t>
|
||||
</t>
|
||||
|
@ -234,7 +246,7 @@
|
|||
<div class="col-sm-5">
|
||||
<ol class="breadcrumb">
|
||||
<li><a t-href="/shop">Products</a></li>
|
||||
<li t-if="search.get('category')"><a t-att-href="'/shop/" t-keep-query="category,search,facettes"><span t-field="category.name"/></a></li>
|
||||
<li t-if="search.get('category')"><a t-href="/shop/" t-keep-query="category,search,facettes"><span t-field="category.name"/></a></li>
|
||||
<li class="active"><span t-field="product.name"></span></li>
|
||||
</ol>
|
||||
</div><div class="col-sm-3">
|
||||
|
@ -250,17 +262,9 @@
|
|||
</li>
|
||||
</t>
|
||||
</div><div class="col-sm-3 col-sm-offset-1">
|
||||
<form t-action="/shop/" method="get" class="pull-right" t-keep-query="category,facettes">
|
||||
<div class="input-group">
|
||||
<t t-if="search">
|
||||
<t t-foreach="search.items()" t-as="key">
|
||||
<input t-att-name="key[0]" t-att-value="key[1]"/>
|
||||
</t>
|
||||
</t>
|
||||
<span class="input-group-addon"><span class="glyphicon glyphicon-search"/></span>
|
||||
<input type="text" name="search" class="search-query form-control" placeholder="Search..." t-att-value="search or ''"/>
|
||||
</div>
|
||||
</form>
|
||||
<form t-action="/shop/" method="get" class="pull-right">
|
||||
<t t-call="website_sale.search" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
@ -268,7 +272,7 @@
|
|||
<section class="container oe_website_sale mb16" id="product_detail">
|
||||
<div class="row">
|
||||
<div class="col-sm-7 col-md-7 col-lg-7">
|
||||
<span t-field="product.image" style="max-height: 500px" t-field-options='{"widget": "image"}'/>
|
||||
<span t-field="product.image" style="max-height: 500px" t-field-options='{"widget": "image", "class": "img img-responsive"}'/>
|
||||
</div><div class="col-sm-5 col-md-5 col-lg-4 col-lg-offset-1">
|
||||
<h1 t-field="product.name">Product Name</h1>
|
||||
|
||||
|
@ -341,9 +345,19 @@
|
|||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="product_attributes" inherit_option_id="website_sale.product" name="Product Attributes">
|
||||
<xpath expr="//p[@t-field='product.description_sale']" position="after">
|
||||
<hr t-if="product.website_attribute_ids"/>
|
||||
<p class="text-muted">
|
||||
<t t-set="attr" t-value="None"/>
|
||||
<t t-foreach="product.website_attribute_ids" t-as="attribute"><br t-if="attr and attribute.attribute_id.id != attr"/><t t-if="attribute.attribute_id.id != attr"><span t-field="attribute.attribute_id"/>: </t><t t-if="attribute.attribute_id.id == attr">, </t><t t-if="attribute.attribute_id.type == 'distinct'"><span t-field="attribute.value_id"/></t><t t-if="attribute.attribute_id.type == 'float'"><span t-field="attribute.value"/></t><t t-set="attr" t-value="attribute.attribute_id.id"/></t>
|
||||
</p>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Page Shop my cart -->
|
||||
|
||||
<template id="mycart" name="Your Cart" page="True">
|
||||
<template id="mycart" name="Your Cart">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
<script type="text/javascript" src="/website_sale/static/src/js/website_sale.js"></script>
|
||||
|
@ -361,18 +375,6 @@
|
|||
</ul>
|
||||
<h1 class="mb32">Shopping Cart</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-3 text-muted" id="right_column">
|
||||
<h4>Policies</h4>
|
||||
<ul class="list-unstyled mb32">
|
||||
<li>☑ 30-days money-back guarantee</li>
|
||||
<li>☑ Invoice sent by e-Mail</li>
|
||||
</ul>
|
||||
<h4>Secure Payment</h4>
|
||||
<ul class="list-unstyled mb32">
|
||||
<li>☑ 256 bit encryption</li>
|
||||
<li>☑ Processed by Ogone</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-8 col-md-offset-1 oe_mycart">
|
||||
<div t-if="not website_sale_order or not website_sale_order.order_line" class="well well-lg">
|
||||
Your cart is empty!
|
||||
|
@ -421,14 +423,14 @@
|
|||
<td>
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon">
|
||||
<a t-href="./remove_cart/?order_line_id=#{ line.id }" t-att-data-id="line.id" class="mb8 js_add_cart_json">
|
||||
<a t-href="./add_cart/?remove=True&order_line_id=#{ line.id }" class="mb8 js_add_cart_json">
|
||||
<span class="icon-minus"/>
|
||||
</a>
|
||||
</span>
|
||||
<input type="text" class="js_quantity form-control"
|
||||
t-att-data-id="line.id" t-att-value="int(line.product_uom_qty)"/>
|
||||
<span class="input-group-addon">
|
||||
<a t-href="./add_cart/?order_line_id=#{ line.id }" t-att-data-id="line.id" class="mb8 float_left js_add_cart_json">
|
||||
<a t-href="./add_cart/?order_line_id=#{ line.id }" class="mb8 float_left js_add_cart_json">
|
||||
<span class="icon-plus"/>
|
||||
</a>
|
||||
</span>
|
||||
|
@ -438,7 +440,7 @@
|
|||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class='pull-right mb16' id="mycart_total">
|
||||
<table class='pull-right mb16' id="mycart_total" t-if="website_sale_order">
|
||||
<colgroup>
|
||||
<col width="100"/>
|
||||
<col width="120"/>
|
||||
|
@ -446,11 +448,8 @@
|
|||
<thead>
|
||||
<tr style="border-top: 1px solid #000">
|
||||
<th><h3>Total:</h3></th>
|
||||
<th class="text-right"><h3>
|
||||
<span t-field="website_sale_order.amount_total" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></h3>
|
||||
<th class="text-right">
|
||||
<h3><t t-call="website_sale.total"/></h3>
|
||||
</th>
|
||||
</tr>
|
||||
<tr class="text-muted">
|
||||
|
@ -469,6 +468,18 @@
|
|||
<a t-if="website_sale_order and website_sale_order.order_line" t-href="/shop/checkout/" class="btn btn-primary pull-right mb32">Process Checkout <span class="icon-long-arrow-right"/></a>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
<div class="col-md-3 text-muted" id="right_column">
|
||||
<h4>Policies</h4>
|
||||
<ul class="list-unstyled mb32">
|
||||
<li>☑ 30-days money-back guarantee</li>
|
||||
<li>☑ Invoice sent by e-Mail</li>
|
||||
</ul>
|
||||
<h4>Secure Payment</h4>
|
||||
<ul class="list-unstyled mb32">
|
||||
<li>☑ 256 bit encryption</li>
|
||||
<li>☑ Processed by Ogone</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -480,36 +491,38 @@
|
|||
<!-- Page Shop -->
|
||||
|
||||
<template id="products_categories" inherit_option_id="website_sale.products" name="Product Categories">
|
||||
<xpath expr="//div[@id='products_grid']" position="before">
|
||||
<div id="categories" class="col-md-3">
|
||||
<ul class="nav nav-pills nav-stacked mt16">
|
||||
<li t-att-class=" '' if search.get('category') else 'active' "><a t-href="/shop/">All Products</a></li>
|
||||
<t t-set="categ" t-value="Ecommerce.get_categories()"/>
|
||||
<t t-foreach="categ[0]" t-as="category">
|
||||
<t t-call="website_sale.categories_recursive"/>
|
||||
</t>
|
||||
</ul>
|
||||
</div>
|
||||
<xpath expr="//div[@id='products_grid_before']" position="inside">
|
||||
<ul class="nav nav-pills nav-stacked mt16">
|
||||
<li t-att-class=" '' if search.get('category') else 'active' "><a t-href="/shop/">All Products</a></li>
|
||||
<t t-set="categ" t-value="Ecommerce.get_categories()"/>
|
||||
<t t-foreach="categ[0]" t-as="category">
|
||||
<t t-call="website_sale.categories_recursive"/>
|
||||
</t>
|
||||
</ul>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='products_grid_before']" position="attributes">
|
||||
<attribute name="class">col-md-3</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='products_grid']" position="attributes">
|
||||
<attribute name="class">col-md-9</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="products_attributes" inherit_option_id="website_sale.products_categories" name="Product Attributes">
|
||||
<xpath expr="//div[@id='categories']" position="inside">
|
||||
<form t-action="/shop/attributes/" method="post" t-keep-query="category,search">
|
||||
<template id="products_attributes" inherit_option_id="website_sale.products" name="Product Filters and Attributes">
|
||||
<xpath expr="//div[@id='products_grid_before']" position="inside">
|
||||
<form t-action="/shop/filter/" method="post" t-keep-query="category,search,add_filter">
|
||||
<ul class="nav nav-pills nav-stacked mt16">
|
||||
<t t-set="attribute_ids" t-value="Ecommerce.get_attribute_ids()"/>
|
||||
<t t-foreach="attribute_ids" t-as="attribute_id">
|
||||
<t t-if="attribute_id.visible">
|
||||
<li t-if="attribute_id.value_ids and attribute_id.type == 'distinct'">
|
||||
<div t-field="attribute_id.name"/>
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<t t-foreach="attribute_id.value_ids" t-as="value_id">
|
||||
<li t-att-class="Ecommerce.has_search_attributes(attribute_id.id, value_id.id) and 'active' or ''">
|
||||
<li t-att-class="Ecommerce.has_search_filter(attribute_id.id, value_id.id) and 'active' or ''">
|
||||
<label style="margin: 0 20px;">
|
||||
<input type="checkbox" t-att-name="'att-%s-%s' % (attribute_id.id, value_id.id)"
|
||||
t-att-checked="Ecommerce.has_search_attributes(attribute_id.id, value_id.id) and 'checked' or ''"/>
|
||||
t-att-checked="Ecommerce.has_search_filter(attribute_id.id, value_id.id) and 'checked' or ''"/>
|
||||
<span style="font-weight: normal" t-field="value_id.name"/>
|
||||
</label>
|
||||
</li>
|
||||
|
@ -518,7 +531,7 @@
|
|||
</li>
|
||||
<li t-if="attribute_id.type == 'float' and attribute_id.float_min != attribute_id.float_max">
|
||||
<div t-field="attribute_id.name"/>
|
||||
<t t-set="attribute" t-value="Ecommerce.has_search_attributes(attribute_id.id)"/>
|
||||
<t t-set="attribute" t-value="Ecommerce.has_search_filter(attribute_id.id)"/>
|
||||
<div style="margin: 0 20px;" class="js_slider"
|
||||
t-att-data-id="attribute_id.id"
|
||||
t-att-data-value-min="attribute and attribute[1][0] or attribute_id.float_min"
|
||||
|
@ -526,14 +539,22 @@
|
|||
t-att-data-min="attribute_id.float_min"
|
||||
t-att-data-max="attribute_id.float_max"></div>
|
||||
</li>
|
||||
</t>
|
||||
</t>
|
||||
</ul>
|
||||
<button class="btn btn-xs btn-primary mt16">Apply filter</button>
|
||||
<a t-href="/shop/" t-keep-query="category,search,add_filter" class="btn btn-xs btn-default mt16">Cancel filter</a>
|
||||
</form>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='products_grid_before']" position="attributes">
|
||||
<attribute name="class">col-md-3</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//div[@id='products_grid']" position="attributes">
|
||||
<attribute name="class">col-md-9</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="suggested_products_list" inherit_id="website_sale.mycart" inherit_option_id="website_sale.mycart" name="Suggested Products in list view">
|
||||
<template id="suggested_products_list" inherit_id="website_sale.mycart" inherit_option_id="website_sale.mycart" name="Suggested Products in my cart">
|
||||
<xpath expr="//table[@id='mycart_products']" position="after">
|
||||
<table t-if="suggested_products" class='table table-striped table-condensed'>
|
||||
<colgroup>
|
||||
|
@ -622,7 +643,7 @@
|
|||
<div class="col-md-8 oe_mycart">
|
||||
<h3 class="page-header mt16">Billing Information
|
||||
<small t-if="user_id.id == website.public_user.id"> or
|
||||
<a t-if="not partner" t-attf-href="/web#action=redirect&url=#{ request.httprequest.host_url }/shop/checkout/">sign in</a>
|
||||
<a t-if="not partner" t-attf-href="/web#action=redirect&url=#{ request.httprequest.url }">sign in</a>
|
||||
</small>
|
||||
</h3>
|
||||
<div class="row">
|
||||
|
@ -639,7 +660,7 @@
|
|||
<input type="email" name="email" class="form-control" t-att-value="checkout.get('email')"/>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{ error.get('phone') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="phone">Telephone</label>
|
||||
<label class="control-label" for="phone">Phone</label>
|
||||
<input type="tel" name="phone" class="form-control" t-att-value="checkout.get('phone')"/>
|
||||
</div>
|
||||
|
||||
|
@ -695,7 +716,7 @@
|
|||
<input type="text" name="shipping_name" class="form-control" t-att-value="checkout.get('shipping_name', '')"/>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('shipping_phone') and 'has-error' or ''} col-lg-6">
|
||||
<label class="control-label" for="contact_name">Telephone</label>
|
||||
<label class="control-label" for="contact_name">Phone</label>
|
||||
<input type="tel" name="shipping_phone" class="form-control" t-att-value="checkout.get('shipping_phone', '')"/>
|
||||
</div>
|
||||
<div t-attf-class="form-group #{error.get('shipping_street') and 'has-error' or ''} col-lg-6">
|
||||
|
@ -879,5 +900,12 @@
|
|||
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="total">
|
||||
<span t-field="website_sale_order.amount_total" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</template>
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
<field name="website_published"/>
|
||||
</xpath>
|
||||
<xpath expr="//page[@string='Information']" position="inside">
|
||||
<group colspan="4" string="Products On Ecommerce">
|
||||
<group colspan="4" string="Website Options">
|
||||
<field name="suggested_product_ids" widget="many2many_tags"/>
|
||||
<field name="website_style_ids" widget="many2many_tags"/>
|
||||
<field colspan="4" name="website_attribute_ids" nolabel="1">
|
||||
|
@ -81,9 +81,22 @@
|
|||
</group>
|
||||
</xpath>
|
||||
|
||||
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="view_product_attribute_form">
|
||||
<field name="name">product.attribute.form</field>
|
||||
<field name="model">product.attribute</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Product Attributes" version="7.0">
|
||||
<group>
|
||||
<field name="name"/>
|
||||
<field name="type"/>
|
||||
<field name="visible"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="menu_shop" model="website.menu">
|
||||
<field name="name">Shop</field>
|
||||
<field name="url">/shop</field>
|
||||
<field name="parent_id" ref="website.main_menu"/>
|
||||
<field name="sequence" type="int">30</field>
|
||||
</record>
|
||||
<record id="action_open_website" model="ir.actions.act_url">
|
||||
<field name="name">Website Shop</field>
|
||||
<field name="target">self</field>
|
||||
|
|
Loading…
Reference in New Issue