diff options
Diffstat (limited to 'addons/website')
69 files changed, 810 insertions, 360 deletions
diff --git a/addons/website/__openerp__.py b/addons/website/__openerp__.py index 7779686c041..4cfabd942ca 100644 --- a/addons/website/__openerp__.py +++ b/addons/website/__openerp__.py @@ -20,12 +20,13 @@ OpenERP Website CMS 'views/snippets.xml', 'views/themes.xml', 'views/res_config.xml', + 'views/ir_actions.xml', ], 'demo': [ 'data/website_demo.xml', ], 'js': ['static/src/js/website.backend.js'], - 'qweb' : ['static/src/xml/website.backend.xml'], + 'qweb': ['static/src/xml/website.backend.xml'], 'css': ['static/src/css/website.backend.css'], 'application': True, } diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index b9fc68bb667..eac1f163cf6 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -9,7 +9,6 @@ import datetime from sys import maxint -import psycopg2 import werkzeug import werkzeug.exceptions import werkzeug.utils @@ -27,6 +26,7 @@ logger = logging.getLogger(__name__) # Completely arbitrary limits MAX_IMAGE_WIDTH, MAX_IMAGE_HEIGHT = IMAGE_LIMITS = (1024, 768) + class Website(openerp.addons.web.controllers.main.Home): #------------------------------------------------------ # View @@ -179,6 +179,7 @@ class Website(openerp.addons.web.controllers.main.Home): result.append({ 'name': v.inherit_option_id.name, 'id': v.id, + 'xml_id': v.xml_id, 'inherit_id': v.inherit_id.id, 'header': True, 'active': False @@ -187,6 +188,7 @@ class Website(openerp.addons.web.controllers.main.Home): result.append({ 'name': v.name, 'id': v.id, + 'xml_id': v.xml_id, 'inherit_id': v.inherit_id.id, 'header': False, 'active': (v.inherit_id.id == v.inherit_option_id.id) or (not optional and v.inherit_id.id) @@ -239,13 +241,20 @@ class Website(openerp.addons.web.controllers.main.Home): @http.route('/website/attach', type='http', auth='user', methods=['POST'], website=True) def attach(self, func, upload): - req = request.httprequest url = message = None try: + image_data = upload.read() + image = Image.open(cStringIO.StringIO(image_data)) + w, h = image.size + if w*h > 42e6: # Nokia Lumia 1020 photo resolution + raise ValueError( + u"Image size excessive, uploaded images must be smaller " + u"than 42 million pixel") + attachment_id = request.registry['ir.attachment'].create(request.cr, request.uid, { 'name': upload.filename, - 'datas': upload.read().encode('base64'), + 'datas': image_data.encode('base64'), 'datas_fname': upload.filename, 'res_model': 'ir.ui.view', }, request.context) @@ -259,7 +268,7 @@ class Website(openerp.addons.web.controllers.main.Home): }) except Exception, e: logger.exception("Failed to upload image to attachment") - message = str(e) + message = unicode(e) return """<script type='text/javascript'> window.parent['%s'](%s, %s); @@ -366,5 +375,34 @@ class Website(openerp.addons.web.controllers.main.Home): return response - -# vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: + #------------------------------------------------------ + # Server actions + #------------------------------------------------------ + @http.route(['/website/action/<id_or_xml_id>'], type='http', auth="public", website=True) + def actions_server(self, id_or_xml_id, **post): + cr, uid, context = request.cr, request.uid, request.context + res, action_id, action = None, None, None + ServerActions = request.registry['ir.actions.server'] + + # find the action_id, either an int, an int into a basestring, or an xml_id + if isinstance(id_or_xml_id, basestring) and '.' in id_or_xml_id: + action_id = request.registry['ir.model.data'].xmlid_to_res_id(request.cr, request.uid, id_or_xml_id, raise_if_not_found=False) + else: + try: + action_id = int(id_or_xml_id) + except ValueError: + pass + # check it effectively exists + if action_id: + action_ids = ServerActions.exists(cr, uid, [action_id], context=context) + action_id = action_ids and action_ids[0] or None + # run it, return only LazyResponse that are templates to be rendered + if action_id: + action = ServerActions.browse(cr, uid, action_id, context=context) + if action.state == 'code' and action.website_published: + action_res = ServerActions.run(cr, uid, [action_id], context=context) + if isinstance(action_res, LazyResponse): + res = action_res + if res: + return res + return request.redirect('/') diff --git a/addons/website/data/website_demo.xml b/addons/website/data/website_demo.xml index d23bc50c817..a7fa4d76a07 100644 --- a/addons/website/data/website_demo.xml +++ b/addons/website/data/website_demo.xml @@ -13,4 +13,122 @@ <field name="rml_header1">Great Product for Great People</field> </record> </data> + + <data noupdate="1"> + + <!-- + Some demo data for front-end server action generating templates + --> + + <template id="template_partner_post" name="Partner Post and Thanks (Website Server Action Demo)"> +<t t-call="website.layout"> + <t t-set="additional_title">Partner Detail</t> + <div id="wrap"> + <div class="oe_structure"> + <section data-snippet-id="title"> + <div class="container"> + <div class="row"> + <div class="col-md-12"> + <h2>Thank you for posting a message !</h2> + </div> + </div> + </div> + </section> + </div> + </div> +</t> + </template> + + <record id="action_partner_post" model="ir.actions.server"> + <field name="name">Website Partner Post and Thanks Demo</field> + <field name="condition">True</field> + <field name="website_published" eval="True"/> + <field name="model_id" ref="base.model_res_partner"/> + <field name="code"> +partner_id, partner = False, None +post_partner_id = request.params.get('partner_id') +post_body = request.params.get('body') +if post_partner_id: + try: + post_partner_id = int(post_partner_id) + except: + post_partner_id = False +if post_partner_id: + partner_ids = pool['res.partner'].search(cr, uid, [('id', '=', post_partner_id)], context=context) + partner_id = partner_ids and partner_ids[0] or False +if partner_id and post_body: + partner = pool['res.partner'].message_post(cr, uid, partner_id, body=post_body, subtype='mail.mt_comment', context=context) +values = { + 'partner': partner, +} +response = request.website.render("website.template_partner_post", values) + </field> + <field name="state">code</field> + <field name="type">ir.actions.server</field> + </record> + + <template id="template_partner_comment" name="Partner Comment (Website Server Action Demo)"> +<t t-call="website.layout"> + <t t-set="additional_title">Partners</t> + <div id="wrap"> + <div class="oe_structure"> + <section data-snippet-id="title"> + <div class="container"> + <div class="row"> + <div class="col-md-12"> + <h1 class="text-center">Send a Message to our Partners</h1> + <h3 class="text-muted text-center">Discuss and Comments</h3> + </div> + </div> + </div> + </section> + </div> + <div class="container"> + <div class="row"> + <form class="form-horizontal" action="/website/action/website.action_partner_post" method="post"> + <div class="form-group"> + <label class="col-sm-2 control-label">Recipient</label> + <div class="col-sm-10"> + <select t-if="partners" name="partner_id" class="form-control"> + <t t-foreach="partners" t-as="partner"><option t-att-value="partner.id" t-esc="partner.name"/></t> + </select> + </div> + </div> + <div class="form-group"> + <label class="col-sm-2 control-label">Message</label> + <div class="col-sm-10"> + <textarea name="body" rows="10" class="form-control"/> + </div> + </div> + <div class="form-group"> + <div class="col-sm-offset-2 col-sm-10"> + <button type="submit" class="btn btn-default btn-primary"> + Send <span class="fa fa-long-arrow-right"/> + </button> + </div> + </div> + </form> + </div> + </div> + </div> +</t> + </template> + + <record id="action_partner_comment" model="ir.actions.server"> + <field name="name">Website Partners Comment Form</field> + <field name="condition">True</field> + <field name="website_published" eval="True"/> + <field name="model_id" ref="base.model_res_partner"/> + <field name="code"> +partner_ids = pool['res.partner'].search(cr, uid, [('customer', '=', True)], context=context) +partners = pool['res.partner'].browse(cr, uid, partner_ids, context=context) +values = { + 'partners': partners, +} +response = request.website.render("website.template_partner_comment", values) + </field> + <field name="state">code</field> + <field name="type">ir.actions.server</field> + </record> + </data> </openerp> diff --git a/addons/website/models/__init__.py b/addons/website/models/__init__.py index e17fba63e39..da3c232a24e 100644 --- a/addons/website/models/__init__.py +++ b/addons/website/models/__init__.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import ir_actions import ir_ui_view import website import ir_qweb diff --git a/addons/website/models/ir_actions.py b/addons/website/models/ir_actions.py new file mode 100644 index 00000000000..731ee4c47e6 --- /dev/null +++ b/addons/website/models/ir_actions.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- + +from openerp.addons.web.http import request +from openerp.osv import fields, osv + + +class actions_server(osv.Model): + """ Add website option in server actions. """ + _name = 'ir.actions.server' + _inherit = ['ir.actions.server'] + + _columns = { + 'website_published': fields.boolean( + 'Available on the Website', + help='A code server action can be executed from the website, using a dedicated' + 'controller. The address is <base>/website/action/<id_or_xml_id>.' + 'Set this field as True to allow users to run this action. If it' + 'set to is False the action cannot be run through the website.'), + } + + def _get_eval_context(self, cr, uid, action, context=None): + """ Override to add the request object in eval_context. """ + eval_context = super(actions_server, self)._get_eval_context(cr, uid, action, context=context) + if action.state == 'code': + eval_context['request'] = request + return eval_context + + def run_action_code_multi(self, cr, uid, action, eval_context=None, context=None): + """ Override to allow returning response the same way action is already + returned by the basic server action behavior. Note that response has + priority over action, avoid using both. """ + res = super(actions_server, self).run_action_code_multi(cr, uid, action, eval_context, context) + if 'response' in eval_context: + return eval_context['response'] + return res diff --git a/addons/website/models/ir_http.py b/addons/website/models/ir_http.py index 6948f102fec..46b9efc15a8 100644 --- a/addons/website/models/ir_http.py +++ b/addons/website/models/ir_http.py @@ -167,5 +167,5 @@ class PageConverter(werkzeug.routing.PathConverter): for view in views: xid = xids[view['id']] - if xid and (not query or query in xid): + if xid and (not query or query.lower() in xid.lower()): yield xid diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py index 95e2179c9ec..a789657dad3 100644 --- a/addons/website/models/ir_ui_view.py +++ b/addons/website/models/ir_ui_view.py @@ -1,8 +1,12 @@ # -*- coding: utf-8 -*- import copy +import simplejson +import werkzeug from lxml import etree, html +from openerp.addons.website.models import website +from openerp.http import request from openerp.osv import osv, fields class view(osv.osv): @@ -117,6 +121,43 @@ class view(osv.osv): return arch + def render(self, cr, uid, id_or_xml_id, values=None, engine='ir.qweb', context=None): + if getattr(request, 'website_enabled', False): + engine='website.qweb' + + if isinstance(id_or_xml_id, list): + id_or_xml_id = id_or_xml_id[0] + if isinstance(id_or_xml_id, (int, long)): + id_or_xml_id = self.get_view_xmlid(cr, uid, id_or_xml_id) + + if not context: + context = {} + + qcontext = dict(editable=False) + qcontext.update( + context.copy(), + website=request.website, + url_for=website.url_for, + slug=website.slug, + res_company=request.website.company_id, + user_id=self.pool.get("res.users").browse(cr, uid, uid), + ) + + # add some values + if values: + qcontext.update(values) + + # in edit mode ir.ui.view will tag nodes + context['inherit_branding'] = qcontext['editable'] + + view_obj = request.website.get_template(id_or_xml_id) + if 'main_object' not in qcontext: + qcontext['main_object'] = view_obj + + values = qcontext + + return super(view, self).render(cr, uid, id_or_xml_id, values=values, engine=engine, context=context) + def save(self, cr, uid, res_id, value, xpath=None, context=None): """ Update a view section. The view section may embed fields to write diff --git a/addons/website/models/res_config.py b/addons/website/models/res_config.py index 9823d286f37..1ae491388a3 100644 --- a/addons/website/models/res_config.py +++ b/addons/website/models/res_config.py @@ -29,6 +29,14 @@ class website_config_settings(osv.osv_memory): values[fname] = v[0] if v and self._columns[fname]._type == 'many2one' else v return {'value' : values} + # FIXME in trunk for god sake. Change the fields above to fields.char instead of fields.related, + # and create the function set_website who will set the value on the website_id + # create does not forward the values to the related many2one. Write does. + def create(self, cr, uid, vals, context=None): + config_id = super(website_config_settings, self).create(cr, uid, vals, context=context) + self.write(cr, uid, config_id, vals, context=context) + return config_id + _defaults = { 'website_id': lambda self,cr,uid,c: self.pool.get('website').search(cr, uid, [], context=c)[0], } diff --git a/addons/website/models/website.py b/addons/website/models/website.py index e3c683861f9..38e47cabf58 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -import fnmatch import inspect import itertools import logging @@ -7,7 +6,6 @@ import math import re import urlparse -import simplejson import werkzeug import werkzeug.exceptions import werkzeug.wrappers @@ -24,24 +22,19 @@ from openerp.addons.web.http import request, LazyResponse logger = logging.getLogger(__name__) -def keep_query(*args, **kw): - if not args and not kw: - args = ('*',) - params = kw.copy() - query_params = frozenset(werkzeug.url_decode(request.httprequest.query_string).keys()) - for keep_param in args: - for param in fnmatch.filter(query_params, keep_param): - if param not in params and param in request.params: - params[param] = request.params[param] - return werkzeug.urls.url_encode(params) - def url_for(path_or_uri, lang=None): + if isinstance(path_or_uri, unicode): + path_or_uri = path_or_uri.encode('utf-8') + current_path = request.httprequest.path + if isinstance(current_path, unicode): + current_path = current_path.encode('utf-8') location = path_or_uri.strip() force_lang = lang is not None url = urlparse.urlparse(location) if request and not url.netloc and not url.scheme and (url.path or force_lang): - location = urlparse.urljoin(request.httprequest.path, location) + location = urlparse.urljoin(current_path, location) + lang = lang or request.context.get('lang') langs = [lg[0] for lg in request.website.get_languages()] @@ -59,7 +52,7 @@ def url_for(path_or_uri, lang=None): ps.insert(1, lang) location = '/'.join(ps) - return location + return location.decode('utf-8') def is_multilang_url(path, langs=None): if not langs: @@ -78,7 +71,11 @@ def is_multilang_url(path, langs=None): def slugify(s, max_length=None): if slugify_lib: - return slugify_lib.slugify(s, max_length) + # There are 2 different libraries only python-slugify is supported + try: + return slugify_lib.slugify(s, max_length=max_length) + except TypeError: + pass spaceless = re.sub(r'\s+', '-', s) specialless = re.sub(r'[^-_A-Za-z0-9]', '', spaceless) return specialless[:max_length] @@ -215,7 +212,6 @@ class website(osv.osv): request.redirect = lambda url: werkzeug.utils.redirect(url_for(url)) request.context.update( - is_master_lang=is_master_lang, editable=is_website_publisher, translatable=not is_master_lang, ) @@ -228,37 +224,8 @@ class website(osv.osv): return self.pool["ir.ui.view"].browse(cr, uid, view_id, context=context) def _render(self, cr, uid, ids, template, values=None, context=None): - user = self.pool.get("res.users") - if not context: - context = {} - - # Take a context - qweb_values = context.copy() - # add some values - if values: - qweb_values.update(values) - # fill some defaults - qweb_values.update( - request=request, - json=simplejson, - website=request.website, - url_for=url_for, - keep_query=keep_query, - slug=slug, - res_company=request.website.company_id, - user_id=user.browse(cr, uid, uid), - quote_plus=werkzeug.url_quote_plus, - ) - qweb_values.setdefault('editable', False) - - # in edit mode ir.ui.view will tag nodes - context['inherit_branding'] = qweb_values['editable'] - - view = self.get_template(cr, uid, ids, template) - - if 'main_object' not in qweb_values: - qweb_values['main_object'] = view - return view.render(qweb_values, engine='website.qweb', context=context) + # TODO: remove this. (just kept for backward api compatibility for saas-3) + return self.pool['ir.ui.view'].render(cr, uid, template, values=values, context=context) def render(self, cr, uid, ids, template, values=None, status_code=None, context=None): def callback(template, values, context): @@ -517,7 +484,7 @@ class website_menu(osv.osv): _description = "Website Menu" _columns = { 'name': fields.char('Menu', size=64, required=True, translate=True), - 'url': fields.char('Url', required=True, translate=True), + 'url': fields.char('Url', translate=True), 'new_window': fields.boolean('New Window'), 'sequence': fields.integer('Sequence'), # TODO: support multiwebsite once done for ir.ui.views diff --git a/addons/website/static/src/css/common.sass b/addons/website/static/src/css/common.sass index 86e1f6655ef..4d5f0673e33 100644 --- a/addons/website/static/src/css/common.sass +++ b/addons/website/static/src/css/common.sass @@ -1,6 +1,7 @@ @import "compass/css3" @import "compass/css3/user-interface" @import "compass/css3/transition" +@import "compass/support" //smartphones, iPhone, portrait 480x320 phones $smart_phone: 320px @@ -23,3 +24,46 @@ $desktop: 1025px @content ::selection @content + +// Lifted from compass-animation +// LICENSED UNDER THE MIT LICENSE (MIT) +// +// Copyright (c) 2012 Eric Meyer +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +@mixin set-experimental-support($moz: false, $webkit: false, $ms: false, $o: false, $khtml: false) + $experimental-support-for-mozilla: $moz + $experimental-support-for-webkit: $webkit + $experimental-support-for-microsoft: $ms + $experimental-support-for-opera: $o + $experimental-support-for-khtml: $khtml + +@mixin with-only-support-for($moz: false, $webkit: false, $ms: false, $o: false, $khtml: false) + // Capture the current state + $original-moz: $experimental-support-for-mozilla + $original-webkit: $experimental-support-for-webkit + $original-o: $experimental-support-for-opera + $original-ms: $experimental-support-for-microsoft + $original-khtml: $experimental-support-for-khtml + + @include set-experimental-support($moz, $webkit, $ms, $o, $khtml) + @content + @include set-experimental-support($original-moz, $original-webkit, $original-ms, $original-o, $original-khtml) + +@mixin keyframes($name) + @-moz-keyframes #{$name} + +with-only-support-for($moz: true) + @content + @-webkit-keyframes #{$name} + +with-only-support-for($webkit: true) + @content + @-o-keyframes #{$name} + +with-only-support-for($o: true) + @content + @keyframes #{$name} + +with-only-support-for + @content diff --git a/addons/website/static/src/css/editor.css b/addons/website/static/src/css/editor.css index 1c54d6f47dd..4fb4496e692 100644 --- a/addons/website/static/src/css/editor.css +++ b/addons/website/static/src/css/editor.css @@ -195,13 +195,19 @@ ul.oe_menu_editor .disclose { text-align: left; } -.modal.nosave .modal-footer button.save { - display: none; -} -.modal.nosave .modal-footer button.wait { +.modal.nosave .wait { display: inline-block !important; visibility: visible !important; } +.modal.nosave .modal-body .filepicker, .modal.nosave .modal-body .image-preview { + display: none; +} +.modal.nosave .modal-body .wait { + width: 100%; +} +.modal.nosave .modal-footer .save { + display: none; +} .modal .font-icons-icons { font-size: 2em; diff --git a/addons/website/static/src/css/editor.sass b/addons/website/static/src/css/editor.sass index 3abfa4cff05..2e6513f7c1d 100644 --- a/addons/website/static/src/css/editor.sass +++ b/addons/website/static/src/css/editor.sass @@ -162,12 +162,17 @@ ul.oe_menu_editor .modal-footer text-align: left -.modal.nosave .modal-footer - button.save - display: none - button.wait +.modal.nosave + .wait display: inline-block !important visibility: visible !important + .modal-body + .filepicker, .image-preview + display: none + .wait + width: 100% + .modal-footer .save + display: none // fontawesome modal .modal diff --git a/addons/website/static/src/css/snippets.css b/addons/website/static/src/css/snippets.css index 00235b3f028..16e85850b2a 100644 --- a/addons/website/static/src/css/snippets.css +++ b/addons/website/static/src/css/snippets.css @@ -223,7 +223,7 @@ -webkit-box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset; -moz-box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset; box-shadow: 0px 0px 0px 1px rgba(255, 255, 255, 0.3), 0px 0px 0px 1px rgba(255, 255, 255, 0.3) inset; - border-color: rgba(0, 0, 0, 0.5); + border-color: rgba(0, 0, 0, 0.3); } .oe_overlay .oe_handle { display: block !important; diff --git a/addons/website/static/src/css/snippets.sass b/addons/website/static/src/css/snippets.sass index ed9c38731db..7e889763572 100644 --- a/addons/website/static/src/css/snippets.sass +++ b/addons/website/static/src/css/snippets.sass @@ -161,7 +161,7 @@ border-style: dashed border-width: 1px +box-shadow(0px 0px 0px 1px rgba(255,255,255,0.3), 0px 0px 0px 1px rgba(255,255,255,0.3) inset) - border-color: rgba(0, 0, 0, 0.5) + border-color: rgba(0, 0, 0, 0.3) .oe_handle display: block !important pointer-events: auto diff --git a/addons/website/static/src/css/website.css b/addons/website/static/src/css/website.css index 27c74508e7f..317d9193d94 100644 --- a/addons/website/static/src/css/website.css +++ b/addons/website/static/src/css/website.css @@ -1,4 +1,3 @@ -@charset "utf-8"; /* THIS CSS FILE IS FOR WEBSITE THEMING CUSTOMIZATION ONLY * * css for editor buttons, openerp widget included in the website and other @@ -166,11 +165,6 @@ footer { width: 100%; } -.row { - min-height: 32px; - width: 100%; -} - /* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */ .oe_structure.oe_editable.oe_empty:empty, .oe_editable[data-oe-type=html]:empty, .oe_structure.oe_editable.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child { background-image: url("/website/static/src/img/drag_here.png") !important; @@ -282,12 +276,16 @@ ul.nav-stacked > li > a { } /* ---- SNIPPETS --- */ +.oe_img_bg { + background-size: 100%; +} + [data-snippet-id], .colmd, .hr, .blockquote { overflow: hidden; } @media (max-width: 400px) { - [data-snippet-id] { + [data-snippet-id]:not([data-snippet-id="carousel"]) { height: auto !important; } } @@ -296,7 +294,7 @@ ul.nav-stacked > li > a { } .carousel-inner .item { height: 100%; - background-size: 100%; + background-size: cover; } .carousel .carousel-caption { @@ -366,7 +364,7 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { .parallax { position: relative; - background-size: 100%; + background-size: cover; display: table; width: 100%; min-height: 100px; @@ -401,7 +399,7 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { } .oe_green { - background-color: #51d466; + background-color: #169c78; color: white; } .oe_green .text-muted { @@ -409,7 +407,7 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { } .oe_blue_light { - background-color: #4791d2; + background-color: #41b6ab; color: white; } .oe_blue_light .text-muted { @@ -422,7 +420,7 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { } .oe_orange { - background-color: #e67e22; + background-color: #f05442; color: white; } .oe_orange .text-muted { @@ -438,7 +436,7 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { } .oe_red { - background-color: #f75353; + background-color: #9c1b31; color: white; } .oe_red .text-muted { @@ -446,10 +444,6 @@ div.carousel[data-snippet-id="slider"] .carousel-indicators .active { } /* Misc */ -.oe_img_bg { - background-size: 100%; -} - .texttop { vertical-align: top; } diff --git a/addons/website/static/src/css/website.sass b/addons/website/static/src/css/website.sass index 2c5f691fae6..2390464bfa7 100644 --- a/addons/website/static/src/css/website.sass +++ b/addons/website/static/src/css/website.sass @@ -128,10 +128,6 @@ footer float: left width: 100% -.row - min-height: 32px - width: 100% - /* ---- HACK FOR COVERING UP CK EDITOR BOGUS P INSERTION --- */ .oe_structure.oe_editable.oe_empty:empty, .oe_editable[data-oe-type=html]:empty, .oe_structure.oe_editable.oe_empty > .oe_drop_zone.oe_insert:only-child, [data-oe-type=html] > .oe_drop_zone.oe_insert:only-child @@ -228,19 +224,24 @@ ul.nav-stacked > li > a a color: white + /* ---- SNIPPETS --- */ + +.oe_img_bg + background-size: 100% + [data-snippet-id],.colmd,.hr,.blockquote overflow: hidden @media (max-width: 400px) - [data-snippet-id] + [data-snippet-id]:not([data-snippet-id="carousel"]) height: auto !important .carousel-inner height: 100% .item height: 100% - background-size: 100% + background-size: cover .carousel .carousel-caption left: auto @@ -300,7 +301,7 @@ div.carousel[data-snippet-id="slider"] .parallax position: relative - background-size: 100% + background-size: cover display: table width: 100% min-height: 100px @@ -327,13 +328,13 @@ div.carousel[data-snippet-id="slider"] color: white .oe_green - background-color: #51d466 + background-color: #169C78 color: white .text-muted color: #ddd .oe_blue_light - background-color: #4791d2 + background-color: #41b6ab color: white .text-muted color: #ddd @@ -343,7 +344,7 @@ div.carousel[data-snippet-id="slider"] color: white .oe_orange - background-color: #e67e22 + background-color: #f05442 color: white .text-muted color: #ddd @@ -355,16 +356,13 @@ div.carousel[data-snippet-id="slider"] color: #ddd .oe_red - background-color: #f75353 + background-color: #9C1b31 color: white .text-muted color: #ddd /* Misc */ -.oe_img_bg - background-size: 100% - .texttop vertical-align: top diff --git a/addons/website/static/src/img/banner/aqua.jpg b/addons/website/static/src/img/banner/aqua.jpg Binary files differindex 47ce77005d4..23f4bb820db 100644 --- a/addons/website/static/src/img/banner/aqua.jpg +++ b/addons/website/static/src/img/banner/aqua.jpg diff --git a/addons/website/static/src/img/banner/baby_blue.jpg b/addons/website/static/src/img/banner/baby_blue.jpg Binary files differindex 38a3d85bc28..d13c6683c1d 100644 --- a/addons/website/static/src/img/banner/baby_blue.jpg +++ b/addons/website/static/src/img/banner/baby_blue.jpg diff --git a/addons/website/static/src/img/banner/black.jpg b/addons/website/static/src/img/banner/black.jpg Binary files differindex 7aaee7a0fb4..800d529d032 100644 --- a/addons/website/static/src/img/banner/black.jpg +++ b/addons/website/static/src/img/banner/black.jpg diff --git a/addons/website/static/src/img/banner/business_guy.jpg b/addons/website/static/src/img/banner/business_guy.jpg Binary files differindex 691729beb7e..04551e6ae01 100644 --- a/addons/website/static/src/img/banner/business_guy.jpg +++ b/addons/website/static/src/img/banner/business_guy.jpg diff --git a/addons/website/static/src/img/banner/color_splash.jpg b/addons/website/static/src/img/banner/color_splash.jpg Binary files differindex 7033d3cb41f..a16cf2367ca 100644 --- a/addons/website/static/src/img/banner/color_splash.jpg +++ b/addons/website/static/src/img/banner/color_splash.jpg diff --git a/addons/website/static/src/img/banner/flower.jpg b/addons/website/static/src/img/banner/flower.jpg Binary files differindex 7208e4c99d9..ed33976aa38 100644 --- a/addons/website/static/src/img/banner/flower.jpg +++ b/addons/website/static/src/img/banner/flower.jpg diff --git a/addons/website/static/src/img/banner/flower_field.jpg b/addons/website/static/src/img/banner/flower_field.jpg Binary files differindex eec572fbb89..23bf90844b7 100644 --- a/addons/website/static/src/img/banner/flower_field.jpg +++ b/addons/website/static/src/img/banner/flower_field.jpg diff --git a/addons/website/static/src/img/banner/greenfields.jpg b/addons/website/static/src/img/banner/greenfields.jpg Binary files differindex 58c7bc49738..205406d24d4 100644 --- a/addons/website/static/src/img/banner/greenfields.jpg +++ b/addons/website/static/src/img/banner/greenfields.jpg diff --git a/addons/website/static/src/img/banner/landscape.jpg b/addons/website/static/src/img/banner/landscape.jpg Binary files differindex 35dfa6206c4..08a7f233cdd 100644 --- a/addons/website/static/src/img/banner/landscape.jpg +++ b/addons/website/static/src/img/banner/landscape.jpg diff --git a/addons/website/static/src/img/banner/landscape.png b/addons/website/static/src/img/banner/landscape.png Binary files differdeleted file mode 100644 index 27b1b92e048..00000000000 --- a/addons/website/static/src/img/banner/landscape.png +++ /dev/null diff --git a/addons/website/static/src/img/banner/mango.jpg b/addons/website/static/src/img/banner/mango.jpg Binary files differindex 9f88e2fb987..7582d4f548e 100644 --- a/addons/website/static/src/img/banner/mango.jpg +++ b/addons/website/static/src/img/banner/mango.jpg diff --git a/addons/website/static/src/img/banner/mountains.jpg b/addons/website/static/src/img/banner/mountains.jpg Binary files differindex 47e53c82c8c..7ea1ea48000 100644 --- a/addons/website/static/src/img/banner/mountains.jpg +++ b/addons/website/static/src/img/banner/mountains.jpg diff --git a/addons/website/static/src/img/banner/orange_red.jpg b/addons/website/static/src/img/banner/orange_red.jpg Binary files differindex 18838410c9f..3f7b2ebeaa9 100644 --- a/addons/website/static/src/img/banner/orange_red.jpg +++ b/addons/website/static/src/img/banner/orange_red.jpg diff --git a/addons/website/static/src/img/banner/velour.jpg b/addons/website/static/src/img/banner/velour.jpg Binary files differindex f3b0cf1d528..8a6b034b815 100644 --- a/addons/website/static/src/img/banner/velour.jpg +++ b/addons/website/static/src/img/banner/velour.jpg diff --git a/addons/website/static/src/img/banner/wood.jpg b/addons/website/static/src/img/banner/wood.jpg Binary files differindex 738ff955918..4554794ff19 100644 --- a/addons/website/static/src/img/banner/wood.jpg +++ b/addons/website/static/src/img/banner/wood.jpg diff --git a/addons/website/static/src/img/banner/yellow_green.jpg b/addons/website/static/src/img/banner/yellow_green.jpg Binary files differindex efd4dad9278..4d98768e8b0 100644 --- a/addons/website/static/src/img/banner/yellow_green.jpg +++ b/addons/website/static/src/img/banner/yellow_green.jpg diff --git a/addons/website/static/src/img/library/baby.jpg b/addons/website/static/src/img/library/baby.jpg Binary files differindex 2e1b0cb877a..c8f7f9925e7 100644 --- a/addons/website/static/src/img/library/baby.jpg +++ b/addons/website/static/src/img/library/baby.jpg diff --git a/addons/website/static/src/img/library/business_conference.jpg b/addons/website/static/src/img/library/business_conference.jpg Binary files differindex 407137d159a..22be370b92b 100644 --- a/addons/website/static/src/img/library/business_conference.jpg +++ b/addons/website/static/src/img/library/business_conference.jpg diff --git a/addons/website/static/src/img/library/business_hands.jpg b/addons/website/static/src/img/library/business_hands.jpg Binary files differindex cb012519301..e26e1de9468 100644 --- a/addons/website/static/src/img/library/business_hands.jpg +++ b/addons/website/static/src/img/library/business_hands.jpg diff --git a/addons/website/static/src/img/library/business_stats.jpg b/addons/website/static/src/img/library/business_stats.jpg Binary files differindex 1123100c675..1ca5223dad4 100644 --- a/addons/website/static/src/img/library/business_stats.jpg +++ b/addons/website/static/src/img/library/business_stats.jpg diff --git a/addons/website/static/src/img/library/engineer.jpg b/addons/website/static/src/img/library/engineer.jpg Binary files differindex bd0fa56c38f..920e99d8e7b 100644 --- a/addons/website/static/src/img/library/engineer.jpg +++ b/addons/website/static/src/img/library/engineer.jpg diff --git a/addons/website/static/src/img/library/flight.jpg b/addons/website/static/src/img/library/flight.jpg Binary files differindex 0071979a015..f044bc66192 100644 --- a/addons/website/static/src/img/library/flight.jpg +++ b/addons/website/static/src/img/library/flight.jpg diff --git a/addons/website/static/src/img/library/helpdesk.jpg b/addons/website/static/src/img/library/helpdesk.jpg Binary files differindex 9591abe164e..4e964ebf259 100644 --- a/addons/website/static/src/img/library/helpdesk.jpg +++ b/addons/website/static/src/img/library/helpdesk.jpg diff --git a/addons/website/static/src/img/library/imac.png b/addons/website/static/src/img/library/imac.png Binary files differindex da52d86f356..304aefc841a 100644 --- a/addons/website/static/src/img/library/imac.png +++ b/addons/website/static/src/img/library/imac.png diff --git a/addons/website/static/src/img/library/ipad.png b/addons/website/static/src/img/library/ipad.png Binary files differindex 455032a4be9..21953c9011d 100644 --- a/addons/website/static/src/img/library/ipad.png +++ b/addons/website/static/src/img/library/ipad.png diff --git a/addons/website/static/src/img/library/lazagne.jpg b/addons/website/static/src/img/library/lazagne.jpg Binary files differindex 21f10a01ca7..c5edff56378 100644 --- a/addons/website/static/src/img/library/lazagne.jpg +++ b/addons/website/static/src/img/library/lazagne.jpg diff --git a/addons/website/static/src/img/library/manufacturing.jpg b/addons/website/static/src/img/library/manufacturing.jpg Binary files differindex dc7266e46a8..298f5e6042c 100644 --- a/addons/website/static/src/img/library/manufacturing.jpg +++ b/addons/website/static/src/img/library/manufacturing.jpg diff --git a/addons/website/static/src/img/library/school_kids.jpg b/addons/website/static/src/img/library/school_kids.jpg Binary files differindex 107b56bba97..bded8656c31 100644 --- a/addons/website/static/src/img/library/school_kids.jpg +++ b/addons/website/static/src/img/library/school_kids.jpg diff --git a/addons/website/static/src/img/library/ship_sea.jpg b/addons/website/static/src/img/library/ship_sea.jpg Binary files differindex cd0057dbdd6..2ad57cfa208 100644 --- a/addons/website/static/src/img/library/ship_sea.jpg +++ b/addons/website/static/src/img/library/ship_sea.jpg diff --git a/addons/website/static/src/img/library/vegetables.png b/addons/website/static/src/img/library/vegetables.png Binary files differindex ae0359e8954..df2adf46d1c 100644 --- a/addons/website/static/src/img/library/vegetables.png +++ b/addons/website/static/src/img/library/vegetables.png diff --git a/addons/website/static/src/img/library/world.jpg b/addons/website/static/src/img/library/world.jpg Binary files differindex b7b36d65360..f0762498601 100644 --- a/addons/website/static/src/img/library/world.jpg +++ b/addons/website/static/src/img/library/world.jpg diff --git a/addons/website/static/src/js/website.editor.js b/addons/website/static/src/js/website.editor.js index ab97ee2bcf9..7e867dca685 100644 --- a/addons/website/static/src/js/website.editor.js +++ b/addons/website/static/src/js/website.editor.js @@ -10,6 +10,9 @@ if (!is_smartphone) { website.ready().then(website.init_editor); + } else { + // remove padding of fake editor bar + document.body.style.padding = 0; } $(document).on('click', 'a.js_link2post', function (ev) { @@ -411,6 +414,7 @@ openerp.jsonRpc('/website/customize_template_get', 'call', { 'xml_id': view_name }).then( function(result) { _.each(result, function (item) { + if (item.xml_id === "website.debugger" && !window.location.search.match(/[&?]debug(&|$)/)) return; if (item.header) { menu.append('<li class="dropdown-header">' + item.name + '</li>'); } else { @@ -437,6 +441,12 @@ }); }, start: function() { + // remove placeholder editor bar + var fakebar = document.getElementById('website-top-navbar-placeholder'); + if (fakebar) { + fakebar.parentNode.removeChild(fakebar); + } + var self = this; this.saving_mutex = new openerp.Mutex(); @@ -628,7 +638,7 @@ image_dialog(editor, new CKEDITOR.dom.element(previous)); $image_button.hide(); previous = null; - }); + }, 'btn-sm'); // previous is the state of the button-trigger: it's the // currently-ish hovered element which can trigger a button showing. @@ -1073,7 +1083,7 @@ } else { // Create the page, get the URL back done = $.get(_.str.sprintf( - '/pagenew/%s?noredirect=1', encodeURI(data.id))) + '/website/add/%s?noredirect=1', encodeURI(data.id))) .then(function (response) { self.make_link(response, false, data.id); }); @@ -1265,7 +1275,7 @@ }), start: function () { - this.$('.modal-footer [disabled]').text("Uploading…"); + this.$('button.wait').text("Uploading…"); var $options = this.$('.image-style').children(); this.image_styles = $options.map(function () { return this.value; }).get(); @@ -1298,13 +1308,16 @@ * Sets the provided image url as the dialog's value-to-save and * refreshes the preview element to use it. */ - set_image: function (url) { + set_image: function (url, error) { + this.$('input.url').val( + error ? '' : url); this.$('input.url').val(url); this.preview_image(); }, file_selection: function () { this.$el.addClass('nosave'); + this.$('form').removeClass('has-error').find('.help-block').empty(); this.$('button.filepicker').removeClass('btn-danger btn-success'); var self = this; @@ -1319,25 +1332,38 @@ }, file_selected: function(url, error) { var $button = this.$('button.filepicker'); - if (error) { + if (!error) { + $button.addClass('btn-success'); + } else { + url = null; + this.$('form').addClass('has-error') + .find('.help-block').text(error); $button.addClass('btn-danger'); - return; } - $button.addClass('btn-success'); - this.set_image(url); + this.set_image(url, error); }, preview_image: function () { - this.$el.removeClass('nosave'); + var loaded = function () { + this.$el.removeClass('nosave'); + }.bind(this); var image = this.$('input.url').val(); - if (!image) { return; } + if (!image) { loaded(); return; } - this.$('img.image-preview') + var $img = this.$('img.image-preview') .attr('src', image) .removeClass(this.image_styles.join(' ')) .addClass(this.$('select.image-style').val()); + + if ($img.prop('complete')) { + loaded(); + } else { + $img.load(loaded) + } }, browse_existing: function (e) { e.preventDefault(); + this.$('form').removeClass('has-error').find('.help-block').empty(); + this.$('button.filepicker').removeClass('btn-danger btn-success'); new website.editor.ExistingImageDialog(this).appendTo(document.body); }, }); diff --git a/addons/website/static/src/js/website.editor.newpage.js b/addons/website/static/src/js/website.editor.newpage.js index 944a24b89d8..8d3b3e9b6b5 100644 --- a/addons/website/static/src/js/website.editor.newpage.js +++ b/addons/website/static/src/js/website.editor.newpage.js @@ -2,6 +2,7 @@ "use strict"; var website = openerp.website; + var _t = openerp._t; website.is_editable = true; website.is_editable_button = true; @@ -16,7 +17,8 @@ 'click a[data-action=new_page]': function (ev) { ev.preventDefault(); website.prompt({ - window_title: "New Page", + id: "editor_new_page", + window_title: _t("New Page"), input: "Page Title", }).then(function (val) { if (val) { diff --git a/addons/website/static/src/js/website.snippets.animation.js b/addons/website/static/src/js/website.snippets.animation.js index 60c5aea49e2..048cf20231f 100644 --- a/addons/website/static/src/js/website.snippets.animation.js +++ b/addons/website/static/src/js/website.snippets.animation.js @@ -105,7 +105,7 @@ var offset = 0; var padding = parseInt($(document.body).css("padding-top")); if (speed < 1) { - var inner_offset = self.$target.outerHeight() - this.height / this.width * document.body.clientWidth; + var inner_offset = - self.$target.outerHeight() + this.height / this.width * document.body.clientWidth; var outer_offset = self.$target.offset().top - (document.body.clientHeight - self.$target.outerHeight()) - padding; offset = - outer_offset * speed + inner_offset; } else { diff --git a/addons/website/static/src/js/website.snippets.editor.js b/addons/website/static/src/js/website.snippets.editor.js index 8a3e7a4421d..e45bee9685b 100644 --- a/addons/website/static/src/js/website.snippets.editor.js +++ b/addons/website/static/src/js/website.snippets.editor.js @@ -9,7 +9,7 @@ var self = this; $("[data-oe-model]").on('click', function (event) { var $this = $(event.srcElement); - var tag = $this[0].tagName.toLowerCase(); + var tag = $this[0] && $this[0].tagName.toLowerCase(); if (!(tag === 'a' || tag === "button") && !$this.parents("a, button").length) { self.$('[data-action="edit"]').parent().effect('bounce', {distance: 18, times: 5}, 250); } @@ -416,6 +416,25 @@ } setTimeout(function () { $("#oe_snippets").trigger('snippet-dropped', $target); + + // reset snippet for rte + $target.removeData("snippet-editor"); + if ($target.data("overlay")) { + $target.data("overlay").remove(); + $target.removeData("overlay"); + } + self.create_overlay($target); + $target.find("[data-snippet-id]").each(function () { + var $snippet = $(this); + $snippet.removeData("snippet-editor"); + if ($snippet.data("overlay")) { + $snippet.data("overlay").remove(); + $snippet.removeData("overlay"); + } + self.create_overlay($snippet); + }); + // end + self.make_active($target); },0); } else { diff --git a/addons/website/static/src/js/website.tests.js b/addons/website/static/src/js/website.tests.js deleted file mode 100644 index 977dc8e1848..00000000000 --- a/addons/website/static/src/js/website.tests.js +++ /dev/null @@ -1,40 +0,0 @@ -var tests = {}; -var droptest = function () { - var errors = []; - for (var snippet_id in tests) { - if (!tests[snippet_id]['activated'] || !tests[snippet_id]['dropped']){ - console.log("Can't dropped or activated snippet: " + snippet_id); - } - } - if (errors.length) { - console.log(tests); - throw new Error("Can't dropped or activated at least one snippet"); - } - $("#oe_snippets").off('snippet-activated snippet-dropped'); -}; -var droptesttime = setTimeout(droptest,0); - -$("#oe_snippets").off('snippet-activated snippet-dropped') - .on('snippet-activated', function (event, dom) { - tests[$(dom).data('src-snippet-id')]['activated'] = true; - clearTimeout(droptesttime); - droptesttime = setTimeout(droptest,0); - }) - .on('snippet-dropped', function (event, dom, src_snipped_id) { - tests[$(dom).data('src-snippet-id')]['dropped'] = true; - clearTimeout(droptesttime); - droptesttime = setTimeout(droptest,0); - }); -var $thumbnails = $('#oe_snippets div.oe_snippet[data-snippet-id] .oe_snippet_thumbnail'); -$thumbnails.each(function () { - var $thumbnail = $(this); - tests[$thumbnail.parent().data('snippet-id')] = {}; - var position = $thumbnail.position(); - $thumbnail.trigger( $.Event( "mousedown", { which: 1, pageX: position.left, pageY: position.top } ) ); - $thumbnail.trigger( $.Event( "mousemove", { which: 1, pageX: position.left+100, pageY: position.top+100 } ) ); - $first_drop = $(".oe_drop_zone").first(); - position = $first_drop.position(); - $first_drop.trigger( $.Event( "mouseup", { which: 1, pageX: position.left+20, pageY: position.top+20 } ) ); - clearTimeout(droptesttime); - droptesttime = setTimeout(droptest,0); -}); diff --git a/addons/website/static/src/js/website.tour.banner.js b/addons/website/static/src/js/website.tour.banner.js index c46efcabce8..7d307bd26d7 100644 --- a/addons/website/static/src/js/website.tour.banner.js +++ b/addons/website/static/src/js/website.tour.banner.js @@ -2,112 +2,114 @@ 'use strict'; var website = openerp.website; + var _t = openerp._t; website.EditorBar.include({ start: function () { - this.registerTour(new website.BannerTour(this)); + this.registerTour(new website.Tour.Banner(this)); return this._super(); }, }); - website.BannerTour = website.Tour.extend({ + website.Tour.Banner = website.Tour.extend({ id: 'banner', - name: "Insert a banner", - path: '/', - init: function (editor) { + name: "Build a page", + path: '/page/website.homepage', + init: function () { var self = this; self.steps = [ { - title: "Welcome to your website!", - content: "This tutorial will guide you to build your home page. We will start by adding a banner.", - template: self.popover({ next: "Start Tutorial", end: "Skip It" }), - backdrop: true, + title: _t("Welcome to your website!"), + content: _t("This tutorial will guide you to build your home page. We will start by adding a banner."), + popover: { next: _t("Start Tutorial"), end: _t("Skip It") }, }, { waitNot: '.popover.tour', element: 'button[data-action=edit]', placement: 'bottom', - title: "Edit this page", - content: "Every page of your website can be modified through the <i>Edit</i> button.", - template: self.popover({ fixed: true }), + title: _t("Edit this page"), + content: _t("Every page of your website can be modified through the <i>Edit</i> button."), + popover: { fixed: true }, }, { element: 'button[data-action=snippet]', placement: 'bottom', - title: "Insert building blocks", - content: "To add content in a page, you can insert building blocks.", - template: self.popover({ fixed: true }), + title: _t("Insert building blocks"), + content: _t("To add content in a page, you can insert building blocks."), + popover: { fixed: true }, }, { snippet: 'carousel', placement: 'bottom', - title: "Drag & Drop a Banner", - content: "Drag the Banner block and drop it in your page.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop a Banner"), + content: _t("Drag the Banner block and drop it in your page."), + popover: { fixed: true }, }, { waitFor: '.oe_overlay_options .oe_options:visible', element: '#wrap [data-snippet-id=carousel]:first .carousel-caption', placement: 'top', - title: "Customize banner's text", - content: "Click in the text and start editing it. Click continue once it's done.", - template: self.popover({ next: "Continue" }), + title: _("Customize banner's text"), + content: _("Click in the text and start editing it."), + popover: { next: _t("Continue") }, }, { element: '.oe_overlay_options .oe_options', placement: 'left', - title: "Customize the banner", - content: "Customize any block through this menu. Try to change the background of the banner.", - template: self.popover({ next: "Continue" }), + title: _t("Customize the banner"), + content: _t("Customize any block through this menu. Try to change the background of the banner."), + popover: { next: _t("Continue") }, }, { waitNot: '.popover.tour', element: 'button[data-action=snippet]', placement: 'bottom', - title: "Add Another Block", - content: "Let's add another building block to your page.", - template: self.popover({ fixed: true }), + title: _t("Add Another Block"), + content: _t("Let's add another building block to your page."), + popover: { fixed: true }, }, { snippet: 'three-columns', placement: 'bottom', - title: "Drag & Drop a Block", - content: "Drag the <em>'3 Columns'</em> block and drop it below the banner.", - template: self.popover({ fixed: true }), + title: _t("Drag & Drop This Block"), + content: _t("Drag the <em>'Three Columns'</em> block and drop it below the banner."), + popover: { fixed: true }, }, { waitFor: '.oe_overlay_options .oe_options:visible', element: 'button[data-action=save]', placement: 'right', - title: "Save your modifications", - content: "Publish your page by clicking on the <em>'Save'</em> button.", - template: self.popover({ fixed: true }), + title: _t("Save your modifications"), + content: _t("Publish your page by clicking on the <em>'Save'</em> button."), + popover: { fixed: true }, }, { waitFor: 'button[data-action=edit]:visible', - title: "Congratulation!", - content: "Your homepage has been updated.", - template: self.popover({ next: "Continue" }), + title: _("Good Job!"), + content: _("Well done, you created your homepage."), + popover: { next: _t("Continue") }, }, { waitNot: '.popover.tour', element: 'a[data-action=show-mobile-preview]', placement: 'bottom', - title: "Test Your Mobile Version", - content: "Let's check how your homepage looks like on mobile devices.", - template: self.popover({ fixed: true }), + title: _t("Test Your Mobile Version"), + content: _t("Let's check how your homepage looks like on mobile devices."), + popover: { fixed: true }, }, { - element: 'button[data-dismiss=modal]', + element: '.modal:has(#mobile-viewport) button[data-dismiss=modal]', placement: 'right', - title: "Close Mobile Preview", - content: "Scroll in the mobile preview to test the rendering. Once it's ok, close this dialog.", + title: _t("Close Mobile Preview"), + content: _t("Scroll in the mobile preview to test the rendering. Once it's ok, close this dialog."), }, { waitNot: '.modal', - title: "Congrats", - content: "Congratulation. This tour is finished.", - template: self.popover({ fixed: true, next: "Close Tutorial" }), + element: '#content-menu-button', + placement: 'bottom', + title: _t("Add new pages and menus"), + content: _t("The 'Content' menu allows you to add pages or add the top menu."), + popover: { next: _t("Close Tutorial") }, }, ]; return this._super(); diff --git a/addons/website/static/src/js/website.tour.js b/addons/website/static/src/js/website.tour.js index 1db813e4cd4..8b1284f0a07 100644 --- a/addons/website/static/src/js/website.tour.js +++ b/addons/website/static/src/js/website.tour.js @@ -2,8 +2,11 @@ 'use strict'; var website = openerp.website; + +if (typeof QWeb2 !== "undefined") website.add_template_file('/website/static/src/xml/website.tour.xml'); +if (website.EditorBar) website.EditorBar.include({ tours: [], start: function () { @@ -17,31 +20,11 @@ website.EditorBar.include({ }); menu.append($menuItem); }); - - this.waitRTEReady = false; - this.on('rte:called', this, function () {self.waitRTEReady = true; }); - this.on('rte:ready', this, function () {self.waitRTEReady = false;}); - - var res = this._super(); - website.Tour.waitReady.call(this, this.testRunning); - return res; + return this._super(); }, registerTour: function (tour) { website.Tour.add(tour); this.tours.push(tour); - }, - testRunning: function () { - if (this.waitRTEReady) { - this.on('rte:ready', this, function () { - website.Tour.each(function () { - this.running(); - }); - }); - } else { - website.Tour.each(function () { - this.running(); - }); - } } }); @@ -78,23 +61,12 @@ $.ajaxSetup({ } }); - website.Tour = openerp.Class.extend({ steps: [], defaultDelay: 50, //ms + defaultOverLaps: 5000, //ms localStorage: window.localStorage, - init: function (url) { - this.tour = new Tour({ - name: this.id, - storage: this.tourStorage, - keyboard: false, - template: this.popover(), - onHide: function () { - window.scrollTo(0, 0); - } - }); - this.registerSteps(); - }, + init: function () {}, run: function (automatic) { this.reset(); @@ -107,7 +79,10 @@ website.Tour = openerp.Class.extend({ if (automatic) { this.localStorage.setItem("tour-"+this.id+"-test-automatic", true); + } else { + this.localStorage.removeItem("tour-"+this.id+"-test-automatic"); } + this.automatic = automatic; if (this.path) { // redirect to begin of the tour in function of the language @@ -123,12 +98,13 @@ website.Tour = openerp.Class.extend({ website.Tour.waitReady.call(this, function () {self._running();}); }, running: function () { + var self = this; if (+this.localStorage.getItem("tour-"+this.id+"-test") >= this.steps.length-1) { this.endTour(); return; } - if (website.Tour.is_busy() || !this.testPathUrl()) return; + if (website.Tour.is_busy()) return; // launch tour with url this.checkRunningUrl(); @@ -136,6 +112,21 @@ website.Tour = openerp.Class.extend({ // mark tour as busy (only one test running) if (this.localStorage.getItem("tour-"+this.id+"-test") != null) { website.Tour.busy = true; + this.automatic = !!this.localStorage.getItem("tour-"+this.id+"-test-automatic"); + } + + if (!this.testPathUrl()) { + if (this.automatic) { + this.timer = setTimeout(function () { + self.reset(); + throw new Error("Wrong url for running " + self.id + + '\ntestPath: ' + self.testPath + + '\nhref: ' + window.location.href + + "\nreferrer: " + document.referrer + ); + },this.defaultOverLaps); + } + return; } var self = this; @@ -143,14 +134,10 @@ website.Tour = openerp.Class.extend({ }, _running: function () { var stepId = this.localStorage.getItem("tour-"+this.id+"-test"); - var automatic = !!this.localStorage.getItem("tour-"+this.id+"-test-automatic"); - + if (stepId != null) { - if (!this.check(this.step(stepId))) { - var step = this.next(stepId); - stepId = step ? step.stepId : stepId; - } - this.nextStep(stepId, automatic ? this.autoNextStep : null, automatic ? 5000 : null); + this.registerTour(); + this.nextStep(stepId, this.automatic ? this.autoNextStep : null, this.automatic ? this.defaultOverLaps : null); } }, @@ -187,20 +174,35 @@ website.Tour = openerp.Class.extend({ } }, + registerTour: function () { + this.tour = new Tour({ + name: this.id, + storage: this.tourStorage, + keyboard: false, + template: this.popover(), + onHide: function () { + window.scrollTo(0, 0); + } + }); + this.registerSteps(); + }, registerSteps: function () { for (var index=0, len=this.steps.length; index<len; index++) { var step = this.steps[index]; step.stepId = step.stepId || ""+index; - if (!step.waitNot && index > 0 && $(this.steps[index-1].template).has("button[data-role='next']").size()) { - step.waitNot = '.popover.tour'; + if (!step.waitNot && index > 0 && this.steps[index-1] && + this.steps[index-1].popover && this.steps[index-1].popover.next) { + step.waitNot = '.popover.tour:visible'; } if (!step.waitFor && index > 0 && this.steps[index-1].snippet) { step.waitFor = '.oe_overlay_options .oe_options:visible'; } - step._title = step.title; - step.title = openerp.qweb.render('website.tour_popover_title', { title: step.title }); + step._title = step._title || step.title; + step.title = this.popoverTitle({ title: step._title }); + step.template = step.template || this.popover( step.popover ); + if (!step.element) step.orphan = true; if (step.snippet) { step.element = '#oe_snippets div.oe_snippet[data-snippet-id="'+step.snippet+'"] .oe_snippet_thumbnail'; @@ -208,9 +210,10 @@ website.Tour = openerp.Class.extend({ } - if ($(this.steps[index-1].template).has("button[data-role='next']").size()) { + if (this.steps[index-1] && + this.steps[index-1].popover && this.steps[index-1].popover.next) { var step = { - stepId: index, + stepId: ""+index, waitNot: '.popover.tour:visible' }; this.steps.push(step); @@ -219,8 +222,21 @@ website.Tour = openerp.Class.extend({ this.tour.addSteps(this.steps); }, + popoverTitle: function (options) { + try { + return openerp.qweb.render('website.tour_popover_title', options); + } catch (e) { + if (!this.automatic) throw e; + return options.title; + } + }, popover: function (options) { - return openerp.qweb.render('website.tour_popover', options); + try { + return openerp.qweb.render('website.tour_popover', options); + } catch (e) { + if (!this.automatic) throw e; + return ""; + } }, timer: null, @@ -268,7 +284,12 @@ website.Tour = openerp.Class.extend({ self.timer = setTimeout(checkNext, self.defaultDelay); } else { self.reset(); - throw new Error("Time overlaps to arrive to step " + step.stepId + ": '" + step._title + "'"); + throw new Error("Time overlaps to arrive to step " + step.stepId + ": '" + step._title + "'" + + '\nelement: ' + Boolean(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden"))) + + '\nwaitNot: ' + Boolean(!step.waitNot || !$(step.waitNot).size()) + + '\nwaitFor: ' + Boolean(!step.waitFor || $(step.waitFor).size()) + + '\n\n' + $("body").html() + ); } } checkNext(); @@ -291,7 +312,7 @@ website.Tour = openerp.Class.extend({ $(".popover.tour").remove(); // go to step in bootstrap tour this.tour.goto(index); - if (step.callback) step.callback(); + if (step.onload) step.onload(); next = steps.shift(); break; } @@ -358,7 +379,10 @@ website.Tour = openerp.Class.extend({ } else if (step.sampleText) { $element.trigger($.Event("keydown", { srcElement: $element })); - if ($element.is("select") || $element.is("input") ) { + if ($element.is("input") ) { + $element.val(step.sampleText); + } if ($element.is("select")) { + $element.find("[value='"+step.sampleText+"'], option:contains('"+step.sampleText+"')").attr("selected", true); $element.val(step.sampleText); } else { $element.html(step.sampleText); @@ -404,7 +428,10 @@ website.Tour.busy = false; website.Tour.add = function (tour) { website.Tour.waitReady(function () { tour = tour.id ? tour : new tour(); - website.Tour.tours[tour.id] = tour; + if (!website.Tour.tours[tour.id]) { + website.Tour.tours[tour.id] = tour; + tour.running(); + } }); }; website.Tour.get = function (id) { @@ -420,7 +447,7 @@ website.Tour.each = function (callback) { website.Tour.waitReady = function (callback) { var self = this; $(document).ready(function () { - if ($.ajaxBusy == null || $.ajaxBusy) { + if ($.ajaxBusy) { $(document).ajaxStop(function() { setTimeout(function () { callback.call(self); @@ -435,18 +462,20 @@ website.Tour.waitReady = function (callback) { }); }; website.Tour.run_test = function (id) { - website.Tour.get(id).run(true); + website.Tour.waitReady(function () { + if (!website.Tour.is_busy()) { + website.Tour.tours[id].run(true); + } + }); }; website.Tour.is_busy = function () { for (var k in this.localStorage) { if (!k.indexOf("tour-")) { - return true; + return k; } } return website.Tour.busy; }; - - }()); diff --git a/addons/website/static/src/js/website.tour.test.admin.js b/addons/website/static/src/js/website.tour.test.admin.js new file mode 100644 index 00000000000..787f05beb23 --- /dev/null +++ b/addons/website/static/src/js/website.tour.test.admin.js @@ -0,0 +1,106 @@ +(function () { + 'use strict'; + + var website = openerp.website; + + website.Tour.LoginEdit = website.Tour.extend({ + id: 'login_edit', + name: "Try to log as admin and check editor", + path: '/', + init: function () { + var self = this; + self.steps = [ + { + title: "click login", + element: '#top_menu a[href*="/web/login"]', + }, + { + title: "insert login", + element: '.oe_login_form input[name="login"]', + sampleText: "admin", + }, + { + title: "insert password", + waitFor: '.oe_login_form input[name="login"][value!=""]', + element: '.oe_login_form input[name="password"]', + sampleText: "admin", + }, + { + title: "select 2 Standard tickets", + waitFor: '.oe_login_form input[name="password"][value!=""]', + element: '.oe_login_form button', + }, + { + title: "go back to website from backend", + element: 'a[data-action-model="ir.actions.act_url"]:contains("Website")', + }, + { + title: 'try to edit', + waitNot: '#wrap .carousel', + element: 'button[data-action=edit]:visible', + }, + { + title: 'check edit mode', + waitFor: 'button[data-action=save]:visible', + }, + { + title: 'check branding', + waitFor: '#wrap[data-oe-model="ir.ui.view"]', + }, + { + title: 'check rte', + waitFor: '#oe_rte_toolbar', + }, + { + title: 'check insert block button', + element: '[data-action="snippet"]:visible', + }, + { + title: 'add snippets', + snippet: 'carousel', + }, + { + title: 'try to save', + waitFor: '.oe_overlay_options .oe_options:visible', + element: 'button[data-action=save]:visible', + }, + { + title: 'check saved', + waitFor: '#wrap div.carousel', + element: 'button[data-action=edit]:visible', + }, + { + title: 'try to re-edit', + waitFor: 'button[data-action=save]:visible', + element: '#wrap .carousel', + }, + { + title: 'remove snippet', + element: '.oe_snippet_remove', + }, + { + title: 'try to re-save', + waitNot: '#wrap .carousel', + element: 'button[data-action=save]:visible', + }, + { + title: "click admin", + waitFor: 'button[data-action=edit]:visible', + element: 'a:contains("Administrator")', + }, + { + title: "click logout", + element: '#top_menu a[href*="/logout"]', + }, + { + title: "check logout", + waitFor: '#top_menu a[href*="/web/login"]', + }, + ]; + return this._super(); + }, + }); + // for test without editor bar + website.Tour.add(website.Tour.LoginEdit); + +}()); diff --git a/addons/website/static/src/js/website.tour.test.js b/addons/website/static/src/js/website.tour.test.js new file mode 100644 index 00000000000..3e85ce20dae --- /dev/null +++ b/addons/website/static/src/js/website.tour.test.js @@ -0,0 +1,34 @@ +(function () { + +function LoadScript(src) { + xmlHttp = new XMLHttpRequest(); + xmlHttp.onreadystatechange = function() { + if(xmlHttp.readyState == 4) { + if (xmlHttp.status == 200 || xmlHttp.status == 304) { + new Function(xmlHttp.responseText)(); + } else { + throw new Error("Can't load JavaScript.\nhref: " + window.location.href + "\nsrc: " + src); + } + } + }; + xmlHttp.open("GET", src, false); + xmlHttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + xmlHttp.send(null); +} + +if (typeof jQuery === "undefined") + LoadScript("/web/static/lib/jquery/jquery.js"); +if (typeof _ === "undefined") + LoadScript("/web/static/lib/underscore/underscore.js"); +if (typeof openerp === "undefined") { + LoadScript("/web/static/lib/qweb/qweb2.js"); + LoadScript("/web/static/src/js/openerpframework.js"); +} +if (typeof openerp === "undefined" || !openerp.website || !openerp.website.add_template_file) + LoadScript("/website/static/src/js/website.js"); +if (typeof Tour === "undefined") + LoadScript("/website/static/lib/bootstrap-tour/bootstrap-tour.js"); +if (typeof openerp === "undefined" || !openerp.website.Tour) + LoadScript("/website/static/src/js/website.tour.js"); + +})();
\ No newline at end of file diff --git a/addons/website/static/src/xml/website.editor.xml b/addons/website/static/src/xml/website.editor.xml index 448c615bb07..6c1e387c958 100644 --- a/addons/website/static/src/xml/website.editor.xml +++ b/addons/website/static/src/xml/website.editor.xml @@ -101,6 +101,7 @@ <button type="button" class="btn btn-primary btn-lg filepicker"> Upload an image from your computer </button> + <button type="button" class="btn btn-lg hidden wait" disabled="disabled"/> <p class="text-muted mt16">— or —</p> </div> <div class="well"> @@ -110,11 +111,15 @@ placeholder="http://openerp.com"/> </div> <input type="hidden" name="func"/> + <div class="help-block"/> </form> <div class="col-sm-4 image-preview-container"> <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAC0lEQVQIHWP4zwAAAgEBAMVfG14AAAAASUVORK5CYII%3D" class="pull-right img-rounded image-preview" width="100%"/> + <img src="/web/static/src/img/throbber-large.gif" + class="pull-right img-rounded wait hidden" + width="100%"/> <select class="form-control image-style"> <option value="">No styling</option> <option value="img-rounded">Rounded corners</option> diff --git a/addons/website/static/src/xml/website.seo.xml b/addons/website/static/src/xml/website.seo.xml index 0c055487dd8..a38d423662f 100644 --- a/addons/website/static/src/xml/website.seo.xml +++ b/addons/website/static/src/xml/website.seo.xml @@ -18,7 +18,7 @@ <div class="form-horizontal" role="form"> <div class="form-group"> <label for="seo_page_keywords" class="col-lg-2 control-label">Add keyword:</label> - <div class="col-lg-4"> + <div class="col-lg-3 col-sm-4"> <div class="input-group"> <input type="text" name="seo_page_keywords" class="form-control" maxlength="30"/> <span class="input-group-btn"> diff --git a/addons/website/static/src/xml/website.tour.xml b/addons/website/static/src/xml/website.tour.xml index 1365ba08307..dcecef021bb 100644 --- a/addons/website/static/src/xml/website.tour.xml +++ b/addons/website/static/src/xml/website.tour.xml @@ -22,6 +22,6 @@ </div> </t> <t t-name="website.tour_popover_title"> - <t t-esc="title"/><button title="Close Tutorial" type="button" class="close" data-role="end">×</button> + <t t-esc="title"/><button title="End This Tutorial" type="button" class="close" data-role="end">×</button> </t> </templates> diff --git a/addons/website/static/src/xml/website.xml b/addons/website/static/src/xml/website.xml index 5890350e2dc..f25a491a106 100644 --- a/addons/website/static/src/xml/website.xml +++ b/addons/website/static/src/xml/website.xml @@ -102,7 +102,7 @@ <h3 class="modal-title"><t t-esc="window_title"/></h3> </div> <div class="modal-body"> - <form class="form-horizontal" role="form"> + <form class="form-horizontal" role="form" t-att-id="id"> <div class="form-group mb0"> <label for="page-name" class="col-sm-3 control-label"> <t t-esc="field_name"/>: diff --git a/addons/website/tests/__init__.py b/addons/website/tests/__init__.py index 665bbc9f535..e8c23ffd78d 100644 --- a/addons/website/tests/__init__.py +++ b/addons/website/tests/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- -import test_views, test_converter, test_requests, test_ui - -checks = [ test_views, test_converter, ] +import test_converter +#import test_requests +#import test_ui +import test_views diff --git a/addons/website/tests/cases.py b/addons/website/tests/cases.py deleted file mode 100644 index 3e777293ff9..00000000000 --- a/addons/website/tests/cases.py +++ /dev/null @@ -1,35 +0,0 @@ -# -*- 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, source, result): - super(URLCase, self).__init__() - self.user = user - self.url = url - self.source = source - self.result = result - - @property - def username(self): - return self.user or "Anonymous Coward" - - def __str__(self): - if self.source: - return "%s (from %s, as %s)" % (self.url, self.source, self.username) - 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)) diff --git a/addons/website/tests/test_requests.py b/addons/website/tests/test_requests.py index ecdb8d99d7d..391eee57d0a 100644 --- a/addons/website/tests/test_requests.py +++ b/addons/website/tests/test_requests.py @@ -9,10 +9,41 @@ import lxml.html from openerp import tools -from . import cases - __all__ = ['load_tests', 'CrawlSuite'] +class URLCase(unittest2.TestCase): + """ + URLCase moved out of test_requests, otherwise discovery attempts to + instantiate and run it + """ + def __init__(self, user, url, source, result): + super(URLCase, self).__init__() + self.user = user + self.url = url + self.source = source + self.result = result + + @property + def username(self): + return self.user or "Anonymous Coward" + + def __str__(self): + if self.source: + return "%s (from %s, as %s)" % (self.url, self.source, self.username) + 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)) + class RedirectHandler(urllib2.HTTPRedirectHandler): """ HTTPRedirectHandler is predicated upon HTTPErrorProcessor being used and @@ -118,11 +149,10 @@ class URL(object): self.source = source def to_case(self, user, result): - return cases.URLCase(user, self.url, self.source, result) + return URLCase(user, self.url, self.source, result) 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 diff --git a/addons/website/tests/ui_suite/dummy_test.js b/addons/website/tests/ui_suite/dummy_test.js deleted file mode 100644 index 7646e4ec76a..00000000000 --- a/addons/website/tests/ui_suite/dummy_test.js +++ /dev/null @@ -1,4 +0,0 @@ -console.log('{ "event": "success" }'); -// For a failure: -// console.log('{ "event": "failure", "message": "The test failed" }'); -phantom.exit();
\ No newline at end of file diff --git a/addons/website/tests/ui_suite/homepage_test.js b/addons/website/tests/ui_suite/homepage_test.js deleted file mode 100644 index 2560e5af4d5..00000000000 --- a/addons/website/tests/ui_suite/homepage_test.js +++ /dev/null @@ -1,17 +0,0 @@ -var testRunner = require('./ui_test_runner.js'); - -var waitFor = testRunner.waitFor; - -testRunner.run(function homepageTest (page, timeout) { - page.evaluate(function () { localStorage.clear(); }); - waitFor(function clientReady () { - return page.evaluate(function () { - return window.$ && window.openerp && window.openerp.website - && window.openerp.website.Tour; - }); - }, function executeTest () { - page.evaluate(function () { - window.openerp.website.Tour.run_test('banner'); - }); - }, timeout); -}); diff --git a/addons/website/tests/ui_suite/simple_dom_test.js b/addons/website/tests/ui_suite/simple_dom_test.js deleted file mode 100644 index 5fe5240c1de..00000000000 --- a/addons/website/tests/ui_suite/simple_dom_test.js +++ /dev/null @@ -1,14 +0,0 @@ -var testRunner = require('./ui_test_runner.js'); - -var waitFor = testRunner.waitFor; - -testRunner.run(function simpleDomTest (page, timeout) { - waitFor(function clientReady () { - return page.evaluate(function () { - return window.openerp && window.openerp.website; - }); - }, function finish () { - console.log('{ "event": "success" }'); - phantom.exit(); - }, timeout); -});
\ No newline at end of file diff --git a/addons/website/views/ir_actions.xml b/addons/website/views/ir_actions.xml new file mode 100644 index 00000000000..0b017a43192 --- /dev/null +++ b/addons/website/views/ir_actions.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<openerp> + <data> + + <record model="ir.ui.view" id="view_server_action_form_website"> + <field name="name">ir.actions.server.form.website</field> + <field name="model">ir.actions.server</field> + <field name="inherit_id" ref="base.view_server_action_form"/> + <field name="arch" type="xml"> + <xpath expr="//field[@name='state']" position="after"> + <field name="website_published" + attrs="{'invisible': [('state', '!=', 'code')]}"/> + </xpath> + </field> + </record> + + </data> +</openerp> diff --git a/addons/website/views/snippets.xml b/addons/website/views/snippets.xml index 2012340dea1..16cbaf9d066 100644 --- a/addons/website/views/snippets.xml +++ b/addons/website/views/snippets.xml @@ -560,13 +560,12 @@ <div data-snippet-id="cta" 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_button.png"/> - <span class="oe_snippet_thumbnail_title">Button</span> + <span class="oe_snippet_thumbnail_title">Button</span> </div> - <section class="oe_snippet_body oe_snippet_darken oe_dark"> + <section class="oe_snippet_body oe_snippet_darken oe_dark mb32 mt32"> <div class="container"> <div class="row"> <div class="col-md-12 text-center mt16 mb16"> - <a href="/shop" class="btn btn-info btn-lg">View Products</a> <a href="/page/website.contactus" class="btn btn-primary btn-lg">Contact us</a> </div> </div> @@ -688,25 +687,21 @@ <span class="fa fa-suitcase fa-5x"></span> <h3 class="mt8 mb0">First Feature</h3> <p class="text-muted">Tell what's the value for the customer for this feature.</p> - <p><a class="btn btn-primary">Learn More</a></p> </div> <div class="col-md-3 text-center"> <span class="fa fa-lock fa-5x"></span> <h3 class="mt8 mb0">Second Feature</h3> <p class="text-muted">Write what the customer would like to know, not what you want to show.</p> - <p><a class="btn btn-success">Take Tour</a></p> </div> <div class="col-md-3 text-center"> <span class="fa fa-heart fa-5x"></span> <h3 class="mt8 mb0">Third Feature</h3> <p class="text-muted">A small explanation of this great feature, in clear words.</p> - <p><a class="btn btn-info">Buy Now</a></p> </div> <div class="col-md-3 text-center"> <span class="fa fa-credit-card fa-5x"></span> <h3 class="mt8 mb0">Fourth Feature</h3> <p class="text-muted">A small explanation of this great feature on two lines.</p> - <p><a class="btn btn-danger">Buy Now</a></p> </div> </div> </div> @@ -903,7 +898,7 @@ </div> <div data-snippet-style-id='background' data-selector="section, section[data-snippet-id='parallax'], div[data-snippet-id='carousel'], div[data-snippet-id='slider']"> - <li class="dropdown-submenu"> + <li class="dropdown-submenu" data-required="true"> <a tabindex="-1" href="#">Background</a> <ul class="dropdown-menu"> <li class="dropdown-submenu"> @@ -912,7 +907,7 @@ <li data-class='oe_dark'><a>Darken</a></li> <li data-class='oe_green'><a>Green</a></li> <li data-class='oe_red'><a>Red</a></li> - <li data-class='oe_blue_light'><a>Light Blue</a></li> + <li data-class='oe_blue_light'><a>Turquoise</a></li> <li data-class='oe_blue'><a>Dark Blue</a></li> <li data-class='oe_orange'><a>Orange</a></li> <li data-class='oe_purple'><a>Purple</a></li> diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index ebcd52c6b67..69452bc6a5f 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -213,7 +213,7 @@ </html> </template> -<template id="layout_logo_show" inherit_option_id="website.layout" name="Show Logo"> +<template id="layout_logo_show" inherit_id="website.layout" inherit_option_id="website.layout" name="Show Logo"> <xpath expr="//header//a[@class='navbar-brand']" position="replace"> <a href="/" class="navbar-brand logo"> <img src="/logo.png"/> @@ -222,6 +222,34 @@ </template> <template id="editor_head" inherit_id="website.layout" name="Editor" groups="base.group_website_publisher"> + <xpath expr="//body" position="attributes"> + <attribute name="style">padding-top: 51px;</attribute> + </xpath> + <xpath expr="//body" position="inside"> + <div id="website-top-navbar-placeholder" class="navbar navbar-inverse navbar-fixed-top hidden-xs"> + <div class="navbar-header"> + <form class="navbar-form navbar-left"> + <button type="button" class="btn btn-primary">Edit</button> + </form> + </div> + <div class="collapse navbar-collapse navbar-edit-collapse"> + <ul class="nav navbar-nav navbar-right"> + <li><a href="#" onclick="return false;"><i class="fa fa-mobile" title="Mobile preview"/></a></li> + <li class="divider-vertical"/> + <li><a href="#" onclick="return false;"><span title="Promote page on the web">Promote</span></a></li> + <li class="dropdown"> + <a href="#" onclick="return false;">Content <span class="caret"/></a> + </li> + <li class="dropdown"> + <a href="#" onclick="return false;">Customize <span class="caret"/></a> + </li> + <li class="dropdown"> + <a href="#" onclick="return false;">Help <span class="caret"/></a> + </li> + </ul> + </div> + </div> + </xpath> <xpath expr='//t[@name="layout_head"]' position="before"> <link rel='stylesheet' href='/website/static/src/css/snippets.css'/> <link rel='stylesheet' href='/website/static/src/css/editor.css'/> @@ -257,6 +285,13 @@ </xpath> </template> +<template id="debugger" inherit_option_id="website.layout" name="Debugger & Tests"> + <xpath expr='//t[@name="layout_head"]' position="after"> + <script type="text/javascript" src="/website/static/src/js/website.tour.test.js"></script> + <script type="text/javascript" src="/website/static/src/js/website.tour.test.admin.js"></script> + </xpath> +</template> + <template id="login_layout" inherit_id="web.login_layout" name="Website Login Layout"> <xpath expr="t" position="replace"> <t t-call="website.layout"> @@ -638,11 +673,11 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml </template> <template id="company_description" name="Company Description"> - <address> - <div t-field="res_company.name">Name</div> + <address itemscope="itemscope" itemtype="http://schema.org/Organization"> + <div itemprop="name" t-field="res_company.name">Name</div> <br /> - <div><i class="fa fa-phone"/> <span t-field="res_company.phone"/></div> - <div><i class="fa fa-envelope"/> <span t-field="res_company.email"/></div> + <div><i class="fa fa-phone"/> <span itemprop="telephone" t-field="res_company.phone"/></div> + <div><i class="fa fa-envelope"/> <span itemprop="email" t-field="res_company.email"/></div> </address> <a t-att-href="res_company.google_map_link()" target="_BLANK"> <img class="thumbnail img-responsive" t-att-src="res_company.google_map_img()" /> @@ -724,18 +759,20 @@ Sitemap: <t t-esc="url_root"/>sitemap.xml </template> <template id="contact"> - <address t-ignore="true" class="mb0"> - <div t-att-class="'name' not in fields and 'css_non_editable_mode_hidden'"><span t-esc="name"/></div> - <div t-if="address and 'address' in fields" class='css_editable_mode_hidden'> - <i class='fa fa-map-marker'/> <span t-raw="address.replace('\n', options.get('no_tag_br') and ', ' or '<br/> &nbsp; &nbsp; ')"/> - </div> - <div t-if="city and 'city' in fields" class='css_editable_mode_hidden'> - <i class='fa fa-map-marker'/> <span t-raw="city"/>, <span t-raw="country_id"/> + <address t-ignore="true" class="mb0" itemscope="itemscope" itemtype="http://schema.org/Organization"> + <div t-att-class="'name' not in fields and 'css_non_editable_mode_hidden'"><span itemprop="name" t-esc="name"/></div> + <div itemprop="address" itemscope="itemscope" itemtype="http://schema.org/PostalAddress"> + <div t-if="address and 'address' in fields" class='css_editable_mode_hidden'> + <i class='fa fa-map-marker'/> <span itemprop="streetAddress" t-raw="address.replace('\n', options.get('no_tag_br') and ', ' or '<br/> &nbsp; &nbsp; ')"/> + </div> + <div t-if="city and 'city' in fields" class='css_editable_mode_hidden'> + <i class='fa fa-map-marker'/> <span itemprop="addressLocality" t-raw="city"/>, <span itemprop="addressCountry" t-raw="country_id"/> + </div> + <div t-if="phone and 'phone' in fields" class='css_editable_mode_hidden'><i class='fa fa-phone'/> <span itemprop="telephone" t-esc="phone"/></div> + <div t-if="mobile and 'mobile' in fields" class='css_editable_mode_hidden'><i class='fa fa-mobile-phone'/> <span itemprop="telephone" t-esc="mobile"/></div> + <div t-if="fax and 'fax' in fields" class='css_editable_mode_hidden'><i class='fa fa-file-text-o'/> <span itemprop="faxNumber" t-esc="fax"/></div> + <div t-if="email and 'email' in fields" class='css_editable_mode_hidden'><i class='fa fa-envelope'/> <span itemprop="email" t-esc="email"/></div> </div> - <div t-if="phone and 'phone' in fields" class='css_editable_mode_hidden'><i class='fa fa-phone'/> <span t-esc="phone"/></div> - <div t-if="mobile and 'mobile' in fields" class='css_editable_mode_hidden'><i class='fa fa-mobile-phone'/> <span t-esc="mobile"/></div> - <div t-if="fax and 'fax' in fields" class='css_editable_mode_hidden'><i class='fa fa-file-text-o'/> <span t-esc="fax"/></div> - <div t-if="email and 'email' in fields" class='css_editable_mode_hidden'><i class='fa fa-envelope'/> <span t-esc="email"/></div> </address> </template> |
