[MERGE] from trunk

bzr revid: fva@openerp.com-20140428152938-ib1yl6f7lgat51vm
This commit is contained in:
Frédéric van der Essen 2014-04-28 17:29:38 +02:00
commit 1aaed44874
31 changed files with 949 additions and 1605 deletions

View File

@ -642,7 +642,7 @@ class account_move_line(osv.osv):
(_check_date, 'The date of your Journal Entry is not in the defined period! You should change the date or remove this constraint from the journal.', ['date']),
(_check_currency, 'The selected account of your Journal Entry forces to provide a secondary currency. You should remove the secondary currency on the account or select a multi-currency view on the journal.', ['currency_id']),
(_check_currency_and_amount, "You cannot create journal items with a secondary currency without recording both 'currency' and 'amount currency' field.", ['currency_id','amount_currency']),
(_check_currency_amount, 'The amount expressed in the secondary currency must be positive when the journal item is a debit and negative when if it is a credit.', ['amount_currency']),
(_check_currency_amount, 'The amount expressed in the secondary currency must be positive when account is debited and negative when account is credited.', ['amount_currency']),
(_check_currency_company, "You cannot provide a secondary currency if it is the same than the company one." , ['currency_id']),
]

View File

@ -384,6 +384,7 @@
<group expand="0" string="Group By...">
<filter string="User" context="{'group_by':'user_id'}" icon="terp-personal"/>
<filter string="Type" context="{'group_by':'type'}" icon="terp-stock_symbol-selection"/>
<filter string="Company" context="{'group_by':'company_id'}" icon="terp-go-home" groups="base.group_multi_company"/>
</group>
</search>
</field>
@ -881,6 +882,7 @@
<field name="price_include"/>
<field name="description"/>
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
<field name="type_tax_use" invisible="1"/>
</tree>
</field>
</record>
@ -891,6 +893,12 @@
<search string="Search Taxes">
<field name="name" filter_domain="['|', ('name','ilike',self), ('description','ilike',self)]" string="Tax"/>
<field name="company_id" groups="base.group_multi_company"/>
<filter string="Sale" domain="[('type_tax_use','=','sale')]" />
<filter string="Purchase" domain="[('type_tax_use','=','purchase')]" />
<group string="Group By...">
<filter string="Company" domain="[]" context="{'group_by':'company_id'}"/>
<filter string="Tax Application" domain="[]" context="{'group_by':'type_tax_use'}"/>
</group>
</search>
</field>
</record>

View File

@ -97,7 +97,7 @@
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<page name="sales_purchases" position="after" version="7.0">
<page string="Accounting" col="4" name="accounting" attrs="{'invisible': [('is_company','=',False),('parent_id','!=',False)]}">
<page string="Accounting" col="4" name="accounting" attrs="{'invisible': [('is_company','=',False),('parent_id','!=',False)]}" groups="account.group_account_invoice">
<group>
<group>
<field name="property_account_position" widget="selection"/>
@ -127,7 +127,7 @@
</tree>
</field>
</page>
<page string="Accounting" name="accounting_disabled" attrs="{'invisible': ['|',('is_company','=',True),('parent_id','=',False)]}">
<page string="Accounting" name="accounting_disabled" attrs="{'invisible': ['|',('is_company','=',True),('parent_id','=',False)]}" groups="account.group_account_invoice">
<div>
<p>Accounting-related settings are managed on <button name="open_commercial_entity" type="object" string="the parent company" class="oe_link"/></p>
</div>

View File

@ -259,6 +259,12 @@ class account_config_settings(osv.osv_memory):
def onchange_tax_rate(self, cr, uid, ids, rate, context=None):
return {'value': {'purchase_tax_rate': rate or False}}
def onchange_multi_currency(self, cr, uid, ids, group_multi_currency, context=None):
res = {}
if not group_multi_currency:
res['value'] = {'income_currency_exchange_account_id': False, 'expense_currency_exchange_account_id': False}
return res
def onchange_start_date(self, cr, uid, id, start_date):
if start_date:
start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d")

View File

@ -122,7 +122,7 @@
<label for="id" string="Features"/>
<div>
<div name="group_multi_currency">
<field name="group_multi_currency" class="oe_inline"/>
<field name="group_multi_currency" class="oe_inline" on_change="onchange_multi_currency(group_multi_currency)"/>
<label for="group_multi_currency"/>
</div>
<div>

View File

@ -189,7 +189,7 @@ class account_voucher(osv.osv):
if not ids:
return []
if context is None: context = {}
return [(r['id'], (str("%.2f" % r['amount']) or '')) for r in self.read(cr, uid, ids, ['amount'], context, load='_classic_write')]
return [(r['id'], (r['number'] or _('Voucher'))) for r in self.read(cr, uid, ids, ['number'], context, load='_classic_write')]
def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False):
mod_obj = self.pool.get('ir.model.data')

View File

@ -259,7 +259,7 @@
<!-- Short thread: Admin ask, Agrolait answer [DEMO: mark thread as done] -->
<record id="msg_discus1" model="mail.message">
<field name="subject">Feedback about our On Site Assistance</field>
<field name="body"><![CDATA[<p>Hi Virginie,</p><p>I writing to you about our <i>On Site Assistance Service</i> that we delivered to Agrolait last week. Do you have any feedback or remark about our service? I noticed you requested new IP phones. Will it be used for new employees, or did you have any issue with the ones we provided?<br />Best regards,</p>]]></field>
<field name="body"><![CDATA[<p>Hi Virginie,</p><p>I wrote to you about our <i>On Site Assistance Service</i> that we delivered to Agrolait last week. Do you have any feedback or remark about our service? I noticed you requested new IP phones. Will it be used for new employees, or did you have any issue with the ones we provided?<br />Best regards,</p>]]></field>
<field name="type">comment</field>
<field name="subtype_id" ref="mt_comment"/>
<field name="author_id" ref="base.partner_root"/>

View File

@ -49,17 +49,11 @@
<field name="name">Members Analysis</field>
<field name="res_model">report.membership</field>
<field name="view_type">form</field>
<field name="view_mode">graph</field>
<field name="search_view_id" ref="view_report_membership_search"/>
<field name="context">{"search_default_year":1,"search_default_member":1, 'search_default_Revenue':1, 'search_default_this_month':1, 'search_default_salesman':1,'group_by_no_leaf':1}</field>
</record>
<record model="ir.actions.act_window.view" id="action_report_membership_tree_view2">
<field name="sequence" eval="3"/>
<field name="view_mode">graph</field>
<field name="view_id" ref="view_report_membership_graph1"/>
<field name="act_window_id" ref="action_report_membership_tree"/>
</record>
<menuitem name="Members Analysis" parent="base.menu_report_association"
action="action_report_membership_tree"
id="menu_report_membership"

View File

@ -9,20 +9,6 @@
<!-- Report for Users' Timesheet and Task Hours per Month -->
<record id="view_report_timesheet_task_user_tree" model="ir.ui.view">
<field name="name">report.timesheet.task.user.tree</field>
<field name="model">report.timesheet.task.user</field>
<field name="arch" type="xml">
<tree string="Timesheet/Task hours Report Per Month" >
<field name="name"/>
<field name="year" invisible="1"/>
<field name="month" invisible="1"/>
<field name="user_id"/>
<field name="timesheet_hrs" widget="float_time" />
<field name="task_hrs" widget="float_time"/>
</tree>
</field>
</record>
<record id="view_report_timesheet_task_user_search" model="ir.ui.view">
<field name="name">report.timesheet.task.user.search</field>
<field name="model">report.timesheet.task.user</field>
@ -55,7 +41,7 @@
<field name="name">Task Hours Per Month</field>
<field name="res_model">report.timesheet.task.user</field>
<field name="view_type">form</field>
<field name="view_mode">tree,graph</field>
<field name="view_mode">graph</field>
<field name="context">{'search_default_year':1,'search_default_month':1, 'search_default_group_user_id':1}</field>
</record>
<menuitem id="menu_timesheet_task_user" parent="hr.menu_hr_reporting_timesheet"

View File

@ -2506,7 +2506,7 @@ class stock_move(osv.osv):
source_location = move.location_dest_id
if source_location.usage != 'internal':
#restrict to scrap from a virtual location because it's meaningless and it may introduce errors in stock ('creating' new products from nowhere)
raise osv.except_osv(_('Error!'), _('Forbidden operation: it is not allowed to scrap products from a virtual location.'))
raise osv.except_osv(_('Error!'), _('Operation Forbidden! it is not allowed to scrap products from a virtual location: %s' %(move.location_id.complete_name)))
move_qty = move.product_qty
uos_qty = quantity / move_qty * move.product_uos_qty
default_val = {

View File

@ -1,43 +0,0 @@
.tour-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1009;
background-color: #000;
opacity: 0.8;
}
.tour-step-backdrop {
position: relative;
z-index: 1011;
}
.tour-step-background {
position: absolute;
z-index: 1010;
background: #fff;
border-radius: 6px;
}
.popover[class*="tour-"] .popover-navigation {
padding: 9px 14px;
}
.popover[class*="tour-"] .popover-navigation *[data-role=end] {
float: right;
}
.popover[class*="tour-"] .popover-navigation *[data-role=prev],
.popover[class*="tour-"] .popover-navigation *[data-role=next],
.popover[class*="tour-"] .popover-navigation *[data-role=end] {
cursor: pointer;
}
.popover[class*="tour-"] .popover-navigation *[data-role=prev].disabled,
.popover[class*="tour-"] .popover-navigation *[data-role=next].disabled,
.popover[class*="tour-"] .popover-navigation *[data-role=end].disabled {
cursor: default;
}
.popover[class*="tour-"].orphan {
position: fixed;
margin-top: 0;
}
.popover[class*="tour-"].orphan .arrow {
display: none;
}

View File

@ -1,559 +0,0 @@
/* ===========================================================
# bootstrap-tour - v0.6.1
# http://bootstraptour.com
# ==============================================================
# Copyright 2012-2013 Ulrich Sossou
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
*/
(function() {
(function($, window) {
var Tour, document;
document = window.document;
Tour = (function() {
function Tour(options) {
this._options = $.extend({
name: "tour",
container: "body",
keyboard: true,
storage: window.localStorage,
debug: false,
backdrop: false,
redirect: true,
orphan: false,
basePath: "",
template: "<div class='popover'> <div class='arrow'></div> <h3 class='popover-title'></h3> <div class='popover-content'></div> <nav class='popover-navigation'> <div class='btn-group'> <button class='btn btn-sm btn-default' data-role='prev'>&laquo; Prev</button> <button class='btn btn-sm btn-default' data-role='next'>Next &raquo;</button> </div> <button class='btn btn-sm btn-default' data-role='end'>End tour</button> </nav> </div>",
afterSetState: function(key, value) {},
afterGetState: function(key, value) {},
afterRemoveState: function(key) {},
onStart: function(tour) {},
onEnd: function(tour) {},
onShow: function(tour) {},
onShown: function(tour) {},
onHide: function(tour) {},
onHidden: function(tour) {},
onNext: function(tour) {},
onPrev: function(tour) {}
}, options);
this._steps = [];
this.setCurrentStep();
this.backdrop = {
overlay: null,
$element: null,
$background: null
};
}
Tour.prototype.setState = function(key, value) {
var keyName;
if (this._options.storage) {
keyName = "" + this._options.name + "_" + key;
this._options.storage.setItem(keyName, value);
return this._options.afterSetState(keyName, value);
} else {
if (this._state == null) {
this._state = {};
}
return this._state[key] = value;
}
};
Tour.prototype.removeState = function(key) {
var keyName;
if (this._options.storage) {
keyName = "" + this._options.name + "_" + key;
this._options.storage.removeItem(keyName);
return this._options.afterRemoveState(keyName);
} else {
if (this._state != null) {
return delete this._state[key];
}
}
};
Tour.prototype.getState = function(key) {
var keyName, value;
if (this._options.storage) {
keyName = "" + this._options.name + "_" + key;
value = this._options.storage.getItem(keyName);
} else {
if (this._state != null) {
value = this._state[key];
}
}
if (value === void 0 || value === "null") {
value = null;
}
this._options.afterGetState(key, value);
return value;
};
Tour.prototype.addSteps = function(steps) {
var step, _i, _len, _results;
_results = [];
for (_i = 0, _len = steps.length; _i < _len; _i++) {
step = steps[_i];
_results.push(this.addStep(step));
}
return _results;
};
Tour.prototype.addStep = function(step) {
return this._steps.push(step);
};
Tour.prototype.getStep = function(i) {
if (this._steps[i] != null) {
return $.extend({
id: "step-" + i,
path: "",
placement: "right",
title: "",
content: "<p></p>",
next: i === this._steps.length - 1 ? -1 : i + 1,
prev: i - 1,
animation: true,
container: this._options.container,
backdrop: this._options.backdrop,
redirect: this._options.redirect,
orphan: this._options.orphan,
template: this._options.template,
onShow: this._options.onShow,
onShown: this._options.onShown,
onHide: this._options.onHide,
onHidden: this._options.onHidden,
onNext: this._options.onNext,
onPrev: this._options.onPrev
}, this._steps[i]);
}
};
Tour.prototype.start = function(force) {
var promise,
_this = this;
if (force == null) {
force = false;
}
if (this.ended() && !force) {
return this._debug("Tour ended, start prevented.");
}
$(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=next]").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=next]:not(.disabled)", function(e) {
e.preventDefault();
return _this.next();
});
$(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=prev]").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=prev]:not(.disabled)", function(e) {
e.preventDefault();
return _this.prev();
});
$(document).off("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=end]").on("click.tour-" + this._options.name, ".popover.tour-" + this._options.name + " *[data-role=end]", function(e) {
e.preventDefault();
return _this.end();
});
this._onResize(function() {
return _this.showStep(_this._current);
});
this._setupKeyboardNavigation();
promise = this._makePromise(this._options.onStart != null ? this._options.onStart(this) : void 0);
return this._callOnPromiseDone(promise, this.showStep, this._current);
};
Tour.prototype.next = function() {
var promise;
if (this.ended()) {
return this._debug("Tour ended, next prevented.");
}
promise = this.hideStep(this._current);
return this._callOnPromiseDone(promise, this._showNextStep);
};
Tour.prototype.prev = function() {
var promise;
if (this.ended()) {
return this._debug("Tour ended, prev prevented.");
}
promise = this.hideStep(this._current);
return this._callOnPromiseDone(promise, this._showPrevStep);
};
Tour.prototype.goto = function(i) {
var promise;
if (this.ended()) {
return this._debug("Tour ended, goto prevented.");
}
promise = this.hideStep(this._current);
return this._callOnPromiseDone(promise, this.showStep, i);
};
Tour.prototype.end = function() {
var endHelper, hidePromise,
_this = this;
endHelper = function(e) {
$(document).off("click.tour-" + _this._options.name);
$(document).off("keyup.tour-" + _this._options.name);
$(window).off("resize.tour-" + _this._options.name);
_this.setState("end", "yes");
if (_this._options.onEnd != null) {
return _this._options.onEnd(_this);
}
};
hidePromise = this.hideStep(this._current);
return this._callOnPromiseDone(hidePromise, endHelper);
};
Tour.prototype.ended = function() {
return !!this.getState("end");
};
Tour.prototype.restart = function() {
this.removeState("current_step");
this.removeState("end");
this.setCurrentStep(0);
return this.start();
};
Tour.prototype.hideStep = function(i) {
var hideStepHelper, promise, step,
_this = this;
step = this.getStep(i);
promise = this._makePromise(step.onHide != null ? step.onHide(this, i) : void 0);
hideStepHelper = function(e) {
var $element;
$element = _this._isOrphan(step) ? $("body") : $(step.element);
$element.popover("destroy");
if (step.reflex) {
$element.css("cursor", "").off("click.tour-" + _this._options.name);
}
if (step.backdrop) {
_this._hideBackdrop();
}
if (step.onHidden != null) {
return step.onHidden(_this);
}
};
this._callOnPromiseDone(promise, hideStepHelper);
return promise;
};
Tour.prototype.showStep = function(i) {
var promise, showStepHelper, skipToPrevious, step,
_this = this;
step = this.getStep(i);
if (!step) {
return;
}
skipToPrevious = i < this._current;
promise = this._makePromise(step.onShow != null ? step.onShow(this, i) : void 0);
showStepHelper = function(e) {
var current_path, path;
_this.setCurrentStep(i);
path = $.isFunction(step.path) ? step.path.call() : _this._options.basePath + step.path;
current_path = [document.location.pathname, document.location.hash].join("");
if (_this._isRedirect(path, current_path)) {
_this._redirect(step, path);
return;
}
if (_this._isOrphan(step)) {
if (!step.orphan) {
_this._debug("Skip the orphan step " + (_this._current + 1) + ". Orphan option is false and the element doesn't exist or is hidden.");
if (skipToPrevious) {
_this._showPrevStep();
} else {
_this._showNextStep();
}
return;
}
_this._debug("Show the orphan step " + (_this._current + 1) + ". Orphans option is true.");
}
if (step.backdrop) {
_this._showBackdrop(!_this._isOrphan(step) ? step.element : void 0);
}
_this._showPopover(step, i);
if (step.onShown != null) {
step.onShown(_this);
}
return _this._debug("Step " + (_this._current + 1) + " of " + _this._steps.length);
};
return this._callOnPromiseDone(promise, showStepHelper);
};
Tour.prototype.setCurrentStep = function(value) {
if (value != null) {
this._current = value;
return this.setState("current_step", value);
} else {
this._current = this.getState("current_step");
return this._current = this._current === null ? 0 : parseInt(this._current, 10);
}
};
Tour.prototype._showNextStep = function() {
var promise, showNextStepHelper, step,
_this = this;
step = this.getStep(this._current);
showNextStepHelper = function(e) {
return _this.showStep(step.next);
};
promise = this._makePromise((step.onNext != null ? step.onNext(this) : void 0));
return this._callOnPromiseDone(promise, showNextStepHelper);
};
Tour.prototype._showPrevStep = function() {
var promise, showPrevStepHelper, step,
_this = this;
step = this.getStep(this._current);
showPrevStepHelper = function(e) {
return _this.showStep(step.prev);
};
promise = this._makePromise((step.onPrev != null ? step.onPrev(this) : void 0));
return this._callOnPromiseDone(promise, showPrevStepHelper);
};
Tour.prototype._debug = function(text) {
if (this._options.debug) {
return window.console.log("Bootstrap Tour '" + this._options.name + "' | " + text);
}
};
Tour.prototype._isRedirect = function(path, currentPath) {
return (path != null) && path !== "" && path.replace(/\?.*$/, "").replace(/\/?$/, "") !== currentPath.replace(/\/?$/, "");
};
Tour.prototype._redirect = function(step, path) {
if ($.isFunction(step.redirect)) {
return step.redirect.call(this, path);
} else if (step.redirect === true) {
this._debug("Redirect to " + path);
return document.location.href = path;
}
};
Tour.prototype._isOrphan = function(step) {
return (step.element == null) || !$(step.element).length || $(step.element).is(":hidden");
};
Tour.prototype._showPopover = function(step, i) {
var $element, $navigation, $template, $tip, isOrphan, options,
_this = this;
options = $.extend({}, this._options);
$template = $.isFunction(step.template) ? $(step.template(i, step)) : $(step.template);
$navigation = $template.find(".popover-navigation");
isOrphan = this._isOrphan(step);
if (isOrphan) {
step.element = "body";
step.placement = "top";
$template = $template.addClass("orphan");
}
$element = $(step.element);
$template.addClass("tour-" + this._options.name);
if (step.options) {
$.extend(options, step.options);
}
if (step.reflex) {
$element.css("cursor", "pointer").on("click.tour-" + this._options.name, function(e) {
if (_this._current < _this._steps.length - 1) {
return _this.next();
} else {
return _this.end();
}
});
}
if (step.prev < 0) {
$navigation.find("*[data-role=prev]").addClass("disabled");
}
if (step.next < 0) {
$navigation.find("*[data-role=next]").addClass("disabled");
}
step.template = $template.clone().wrap("<div>").parent().html();
$element.popover({
placement: step.placement,
trigger: "manual",
title: step.title,
content: step.content,
html: true,
animation: step.animation,
container: step.container,
template: step.template,
selector: step.element
}).popover("show");
$tip = $element.data("bs.popover") ? $element.data("bs.popover").tip() : $element.data("popover").tip();
$tip.attr("id", step.id);
this._scrollIntoView($tip);
this._reposition($tip, step);
if (isOrphan) {
return this._center($tip);
}
};
Tour.prototype._reposition = function($tip, step) {
var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
offsetWidth = $tip[0].offsetWidth;
offsetHeight = $tip[0].offsetHeight;
tipOffset = $tip.offset();
originalLeft = tipOffset.left;
originalTop = tipOffset.top;
offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight();
if (offsetBottom < 0) {
tipOffset.top = tipOffset.top + offsetBottom;
}
offsetRight = $("html").outerWidth() - tipOffset.left - $tip.outerWidth();
if (offsetRight < 0) {
tipOffset.left = tipOffset.left + offsetRight;
}
if (tipOffset.top < 0) {
tipOffset.top = 0;
}
if (tipOffset.left < 0) {
tipOffset.left = 0;
}
$tip.offset(tipOffset);
if (step.placement === "bottom" || step.placement === "top") {
if (originalLeft !== tipOffset.left) {
return this._replaceArrow($tip, (tipOffset.left - originalLeft) * 2, offsetWidth, "left");
}
} else {
if (originalTop !== tipOffset.top) {
return this._replaceArrow($tip, (tipOffset.top - originalTop) * 2, offsetHeight, "top");
}
}
};
Tour.prototype._center = function($tip) {
return $tip.css("top", $(window).outerHeight() / 2 - $tip.outerHeight() / 2);
};
Tour.prototype._replaceArrow = function($tip, delta, dimension, position) {
return $tip.find(".arrow").css(position, delta ? 50 * (1 - delta / dimension) + "%" : "");
};
Tour.prototype._scrollIntoView = function(tip) {
return $("html, body").stop().animate({
scrollTop: Math.ceil(tip.offset().top - ($(window).height() / 2))
});
};
Tour.prototype._onResize = function(callback, timeout) {
return $(window).on("resize.tour-" + this._options.name, function() {
clearTimeout(timeout);
return timeout = setTimeout(callback, 100);
});
};
Tour.prototype._setupKeyboardNavigation = function() {
var _this = this;
if (this._options.keyboard) {
return $(document).on("keyup.tour-" + this._options.name, function(e) {
if (!e.which) {
return;
}
switch (e.which) {
case 39:
e.preventDefault();
if (_this._current < _this._steps.length - 1) {
return _this.next();
} else {
return _this.end();
}
break;
case 37:
e.preventDefault();
if (_this._current > 0) {
return _this.prev();
}
break;
case 27:
e.preventDefault();
return _this.end();
}
});
}
};
Tour.prototype._makePromise = function(result) {
if (result && $.isFunction(result.then)) {
return result;
} else {
return null;
}
};
Tour.prototype._callOnPromiseDone = function(promise, cb, arg) {
var _this = this;
if (promise) {
return promise.then(function(e) {
return cb.call(_this, arg);
});
} else {
return cb.call(this, arg);
}
};
Tour.prototype._showBackdrop = function(element) {
if (this.backdrop.overlay !== null) {
return;
}
this._showOverlay();
if (element != null) {
return this._showOverlayElement(element);
}
};
Tour.prototype._hideBackdrop = function() {
if (this.backdrop.overlay === null) {
return;
}
if (this.backdrop.$element) {
this._hideOverlayElement();
}
return this._hideOverlay();
};
Tour.prototype._showOverlay = function() {
this.backdrop = $("<div/>", {
"class": "tour-backdrop"
});
return $("body").append(this.backdrop);
};
Tour.prototype._hideOverlay = function() {
this.backdrop.remove();
return this.backdrop.overlay = null;
};
Tour.prototype._showOverlayElement = function(element) {
var $background, $element, offset;
$element = $(element);
$background = $("<div/>");
offset = $element.offset();
offset.top = offset.top;
offset.left = offset.left;
$background.width($element.innerWidth()).height($element.innerHeight()).addClass("tour-step-background").offset(offset);
$element.addClass("tour-step-backdrop");
$("body").append($background);
this.backdrop.$element = $element;
return this.backdrop.$background = $background;
};
Tour.prototype._hideOverlayElement = function() {
this.backdrop.$element.removeClass("tour-step-backdrop");
this.backdrop.$background.remove();
this.backdrop.$element = null;
return this.backdrop.$background = null;
};
return Tour;
})();
return window.Tour = Tour;
})(jQuery, window);
}).call(this);

View File

@ -518,10 +518,30 @@ div.tour-backdrop {
z-index: 2009;
}
.popover.tour {
z-index: 2010;
.popover.tour.orphan .arrow {
display: none;
}
.popover.tour .popover-navigation {
padding: 9px 14px;
}
.popover.tour .popover-navigation *[data-role="end"] {
float: right;
}
.popover.tour .popover-navigation *[data-role="next"], .popover.tour .popover-navigation *[data-role="end"] {
cursor: pointer;
}
.popover.fixed {
position: fixed;
}
.tour-backdrop {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1100;
background-color: black;
opacity: 0.8;
}

View File

@ -451,9 +451,26 @@ $editorbar_height: 30px
div.tour-backdrop
z-index: 2009
.popover.tour
z-index: 2010
&.orphan .arrow
display: none
.popover-navigation
padding: 9px 14px
*[data-role="end"]
float: right
*[data-role="next"],*[data-role="end"]
cursor: pointer
.popover.fixed
position: fixed
.tour-backdrop
position: fixed
top: 0
right: 0
bottom: 0
left: 0
z-index: 1100
background-color: #000
opacity: 0.8
// }}}

View File

@ -545,7 +545,7 @@
observer.disconnect();
var editor = this.rte.editor;
var root = editor.element.$;
var root = editor.element && editor.element.$;
editor.destroy();
// FIXME: select editables then filter by dirty?
var defs = this.rte.fetch_editables(root)

View File

@ -362,7 +362,6 @@
},
clean_for_save: function () {
var self = this;
$("*[contentEditable], *[attributeEditable]")
.removeAttr('contentEditable')
.removeAttr('attributeEditable');

View File

@ -4,127 +4,116 @@
var website = openerp.website;
var _t = openerp._t;
website.EditorBar.include({
start: function () {
this.registerTour(new website.Tour.Banner(this));
return this._super();
},
});
website.Tour.Banner = website.Tour.extend({
website.Tour.register({
id: 'banner',
name: "Build a page",
name: _t("Build a page"),
path: '/page/website.homepage',
init: function () {
var self = this;
self.steps = [
{
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: _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: _t("Insert building blocks"),
content: _t("Click here to insert blocks of content in the page."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:first',
placement: 'bottom',
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 .carousel:first div.carousel-content',
placement: 'top',
title: _t("Customize banner's text"),
content: _t("Click in the text and start editing it."),
sampleText: 'Here, a customized text',
},
{
waitNot: '#wrap .carousel:first div.carousel-content:has(h2:'+
'containsExact('+_t('Your Banner Title')+')):has(h3:'+
'containsExact('+_t('Click to customize this text')+'))',
element: '.oe_snippet_parent:visible',
placement: 'bottom',
title: _t("Get banner properties"),
content: _t("Select the parent container to get the global options of the banner."),
popover: { fixed: true },
},
{
element: '.oe_overlay_options .oe_options:visible',
placement: 'left',
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: _t("Add Another Block"),
content: _t("Let's add another building block to your page."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(6)',
placement: 'bottom',
title: _t("Drag & Drop This Block"),
content: _t("Drag the <em>'Features'</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: _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: _t("Good Job!"),
content: _t("Well done, you created your homepage."),
popover: { next: _t("Continue") },
},
{
waitNot: '.popover.tour',
element: 'a[data-action=show-mobile-preview]',
placement: 'bottom',
title: _t("Test Your Mobile Version"),
content: _t("Let's check how your homepage looks like on mobile devices."),
popover: { fixed: true },
},
{
element: '.modal:has(#mobile-viewport) button[data-dismiss=modal]',
placement: 'right',
title: _t("Check Mobile Preview"),
content: _t("Scroll to check rendering and then close the mobile preview."),
popover: { next: _t("Continue") },
},
{
waitNot: '.modal',
element: '#content-menu-button',
placement: 'left',
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();
},
steps: [
{
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: _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: _t("Insert building blocks"),
content: _t("Click here to insert blocks of content in the page."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:first',
placement: 'bottom',
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 .carousel:first div.carousel-content',
placement: 'top',
title: _t("Customize banner's text"),
content: _t("Click in the text and start editing it."),
sampleText: 'Here, a customized text',
},
{
waitNot: '#wrap .carousel:first div.carousel-content:has(h2:'+
'containsExact('+_t('Your Banner Title')+')):has(h3:'+
'containsExact('+_t('Click to customize this text')+'))',
element: '.oe_snippet_parent:visible',
placement: 'bottom',
title: _t("Get banner properties"),
content: _t("Select the parent container to get the global options of the banner."),
popover: { fixed: true },
},
{
element: '.oe_overlay_options .oe_options:visible',
placement: 'left',
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: _t("Add Another Block"),
content: _t("Let's add another building block to your page."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(6)',
placement: 'bottom',
title: _t("Drag & Drop This Block"),
content: _t("Drag the <em>'Features'</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: _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: _t("Good Job!"),
content: _t("Well done, you created your homepage."),
popover: { next: _t("Continue") },
},
{
waitNot: '.popover.tour',
element: 'a[data-action=show-mobile-preview]',
placement: 'bottom',
title: _t("Test Your Mobile Version"),
content: _t("Let's check how your homepage looks like on mobile devices."),
popover: { fixed: true },
},
{
element: '.modal:has(#mobile-viewport) button[data-dismiss=modal]',
placement: 'right',
title: _t("Check Mobile Preview"),
content: _t("Scroll to check rendering and then close the mobile preview."),
popover: { next: _t("Continue") },
},
{
waitNot: '.modal',
element: '#content-menu-button',
placement: 'left',
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") },
},
]
});
}());

View File

@ -6,7 +6,7 @@ if (typeof openerp === "undefined") {
var error = "openerp is undefined"
+ "\nhref: " + window.location.href
+ "\nreferrer: " + document.referrer
+ "\nlocalStorage: " + JSON.stringify(window.localStorage);
+ "\nlocalStorage: " + window.localStorage.getItem("tour");
if (typeof $ !== "undefined") {
error += '\n\n' + $("body").html();
}
@ -15,7 +15,7 @@ if (typeof openerp === "undefined") {
var website = window.openerp.website;
// don't rewrite website.Tour in test mode
// don't rewrite T in test mode
if (typeof website.Tour !== "undefined") {
return;
}
@ -23,40 +23,26 @@ if (typeof website.Tour !== "undefined") {
// don't need template to use bootstrap Tour in automatic mode
if (typeof QWeb2 !== "undefined") {
website.add_template_file('/website/static/src/xml/website.tour.xml');
}
// don't need to use bootstrap Tour to launch an automatic tour
function bootstrap_tour_stub () {
if (typeof Tour === "undefined") {
window.Tour = function Tour() {};
Tour.prototype.addSteps = function () {};
Tour.prototype.end = function () {};
Tour.prototype.goto = function () {};
}
}
if (website.EditorBar) {
website.EditorBar.include({
tours: [],
start: function () {
var self = this;
var menu = $('#help-menu');
_.each(this.tours, function (tour) {
_.each(T.tours, function (tour) {
if (tour.mode === "test") {
return;
}
var $menuItem = $($.parseHTML('<li><a href="#">'+tour.name+'</a></li>'));
$menuItem.click(function () {
tour.reset();
tour.run();
T.reset();
T.run(tour.id);
});
menu.append($menuItem);
});
return this._super();
},
registerTour: function (tour) {
website.Tour.add(tour);
this.tours.push(tour);
}
});
}
@ -94,351 +80,401 @@ $.ajaxSetup({
}
});
website.Tour = openerp.Class.extend({
steps: [],
defaultDelay: 50, //ms
defaultOverLaps: 5000, //ms
localStorage: window.localStorage,
init: function () {},
/////////////////////////////////////////////////
run: function (automatic) {
this.reset();
var localStorage = window.localStorage;
for (var k in this.localStorage) {
if (!k.indexOf("tour-") && k.indexOf("-test") > -1) return;
}
website.Tour.busy = true;
if (automatic) {
this.localStorage.setItem("tour-"+this.id+"-test-automatic", true);
var T = website.Tour = {
tours: {},
defaultDelay: 50,
retryRunningDelay: 1000,
errorDelay: 5000,
state: null,
$element: null,
timer: null,
testtimer: null,
currentTimer: null,
register: function (tour) {
if (tour.mode !== "test") tour.mode = "tutorial";
T.tours[tour.id] = tour;
},
run: function (tour_id, mode) {
var tour = T.tours[tour_id];
this.time = new Date().getTime();
if (tour.path && !window.location.href.match(new RegExp("("+T.getLang()+")?"+tour.path+"#?$", "i"))) {
var href = "/"+T.getLang()+tour.path;
console.log("Tour Begin from run method (redirection to "+href+")");
T.saveState(tour.id, mode || tour.mode, -1);
window.location.href = href;
} else {
this.localStorage.removeItem("tour-"+this.id+"-test-automatic");
console.log("Tour Begin from run method");
T.saveState(tour.id, mode || tour.mode, 0);
T.running();
}
this.automatic = automatic;
if (this.path) {
// redirect to begin of the tour in function of the language
if (!this.testUrl(this.path+"(#.*)?$")) {
var path = this.path.split('#');
window.location.href = "/"+this.getLang()+path[0] + "#tutorial."+this.id+"=true&" + path.slice(1, path.length).join("#");
return;
}
}
var self = this;
this.localStorage.setItem("tour-"+this.id+"-test", 0);
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();
registerSteps: function (tour) {
if (tour.register) {
return;
}
tour.register = true;
if (website.Tour.is_busy()) return;
for (var index=0, len=tour.steps.length; index<len; index++) {
var step = tour.steps[index];
step.id = index;
// launch tour with url
this.checkRunningUrl();
// 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);
if (!step.waitNot && index > 0 && tour.steps[index-1] &&
tour.steps[index-1].popover && tour.steps[index-1].popover.next) {
step.waitNot = '.popover.tour.fade.in:visible';
}
return;
}
var self = this;
website.Tour.waitReady.call(this, function () {self._running();});
},
_running: function () {
var stepId = this.localStorage.getItem("tour-"+this.id+"-test");
if (stepId != null) {
this.registerTour();
this.nextStep(stepId, this.automatic ? this.autoNextStep : null, this.automatic ? this.defaultOverLaps : null);
}
},
reset: function () {
website.Tour.busy = false;
for (var k in this.steps) {
this.steps[k].busy = false;
}
clearTimeout(self.timer);
clearTimeout(self.testtimer);
for (var k in this.localStorage) {
if (!k.indexOf("tour-") || !k.indexOf(this.id)) {
this.localStorage.removeItem(k);
}
}
$('.popover.tour').remove();
},
getLang: function () {
return $("html").attr("lang").replace(/-/, '_');
},
testUrl: function (url) {
return new RegExp("(/"+this.getLang()+")?"+url, "i").test(window.location.href);
},
testPathUrl: function () {
if (!this.testPath || this.testUrl(this.testPath)) return true;
},
checkRunningUrl: function () {
if (window.location.hash.indexOf("tutorial."+this.id+"=true") > -1) {
this.localStorage.setItem("tour-"+this.id+"-test", 0);
window.location.hash = window.location.hash.replace(/tutorial.+=true&?/, '');
}
},
registerTour: function () {
if (this.automatic) {
bootstrap_tour_stub();
}
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] &&
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) {
if (!step.waitFor && index > 0 && tour.steps[index-1].snippet) {
step.waitFor = '.oe_overlay_options .oe_options:visible';
}
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) {
var snippet = step.element && step.element.match(/#oe_snippets (.*) \.oe_snippet_thumbnail/);
if (snippet) {
step.snippet = snippet[1];
} else if (step.snippet) {
step.element = '#oe_snippets '+step.snippet+' .oe_snippet_thumbnail';
}
if (!step.element) {
step.element = "body";
step.orphan = true;
step.backdrop = true;
}
}
if (this.steps[index-1] &&
this.steps[index-1].popover && this.steps[index-1].popover.next) {
if (tour.steps[index-1] &&
tour.steps[index-1].popover && tour.steps[index-1].popover.next) {
var step = {
stepId: ""+index,
waitNot: '.popover.tour:visible'
_title: "",
id: index,
waitNot: '.popover.tour.fade.in:visible'
};
this.steps.push(step);
tour.steps.push(step);
}
this.tour.addSteps(this.steps);
// rendering bootstrap tour and popover
if (tour.mode !== "test") {
for (var index=0, len=tour.steps.length; index<len; index++) {
var step = tour.steps[index];
step._title = step._title || step.title;
step.title = T.popoverTitle(tour, { title: step._title });
step.template = step.template || T.popover( step.popover );
}
}
},
popoverTitle: function (options) {
try {
return openerp.qweb.render('website.tour_popover_title', options);
} catch (e) {
if (!this.automatic) throw e;
return options.title;
closePopover: function () {
if (T.$element) {
T.$element.popover('destroy');
T.$element.removeData("tour");
T.$element.removeData("tour-step");
$(".tour-backdrop").remove();
$(".popover.tour").remove();
T.$element = null;
}
},
autoTogglePopover: function () {
var state = T.getState();
var step = state.step;
if (T.$element &&
T.$element.is(":visible") &&
T.$element.data("tour") === state.id &&
T.$element.data("tour-step") === step.id) {
T.repositionPopover();
return;
}
if (step.busy) {
return;
}
T.closePopover();
var $element = $(step.element).first();
if (!step.element || !$element.size() || !$element.is(":visible")) {
return;
}
T.$element = $element;
$element.data("tour", state.id);
$element.data("tour-step", step.id);
$element.popover({
placement: step.placement || "auto",
animation: true,
trigger: "manual",
title: step.title,
content: step.content,
html: true,
container: "body",
template: step.template,
orphan: step.orphan
}).popover("show");
var $tip = $element.data("bs.popover").tip();
// add popover style (orphan, static, backdrop)
if (step.orphan) {
$tip.addClass("orphan");
}
var node = $element[0];
var css;
do {
css = window.getComputedStyle(node);
if (!css || css.position == "fixed") {
$tip.addClass("fixed");
break;
}
} while ((node = node.parentNode) && node !== document);
if (step.backdrop) {
$("body").append('<div class="tour-backdrop"></div>');
}
if (step.backdrop || $element.parents("#website-top-navbar, .modal").size()) {
$tip.css("z-index", 2010);
}
// button click event
$tip.find("button")
.one("click", function () {
step.busy = true;
if (!$(this).is("[data-role='next']")) {
clearTimeout(T.timer);
T.endTour();
}
T.closePopover();
});
T.repositionPopover();
},
repositionPopover: function() {
var popover = T.$element.data("bs.popover");
var $tip = T.$element.data("bs.popover").tip();
if (popover.options.orphan) {
return $tip.css("top", $(window).outerHeight() / 2 - $tip.outerHeight() / 2);
}
var offsetBottom, offsetHeight, offsetRight, offsetWidth, originalLeft, originalTop, tipOffset;
offsetWidth = $tip[0].offsetWidth;
offsetHeight = $tip[0].offsetHeight;
tipOffset = $tip.offset();
originalLeft = tipOffset.left;
originalTop = tipOffset.top;
offsetBottom = $(document).outerHeight() - tipOffset.top - $tip.outerHeight();
if (offsetBottom < 0) {
tipOffset.top = tipOffset.top + offsetBottom;
}
offsetRight = $("html").outerWidth() - tipOffset.left - $tip.outerWidth();
if (offsetRight < 0) {
tipOffset.left = tipOffset.left + offsetRight;
}
if (tipOffset.top < 0) {
tipOffset.top = 0;
}
if (tipOffset.left < 0) {
tipOffset.left = 0;
}
$tip.offset(tipOffset);
if (popover.options.placement === "bottom" || popover.options.placement === "top") {
var left = T.$element.offset().left + T.$element.outerWidth()/2 - tipOffset.left;
$tip.find(".arrow").css("left", left ? left + "px" : "");
} else if (popover.options.placement !== "auto") {
var top = T.$element.offset().top + T.$element.outerHeight()/2 - tipOffset.top;
$tip.find(".arrow").css("top", top ? top + "px" : "");
}
},
popoverTitle: function (tour, options) {
return openerp.qweb ? openerp.qweb.render('website.tour_popover_title', options) : options.title;
},
popover: function (options) {
try {
return openerp.qweb.render('website.tour_popover', options);
} catch (e) {
if (!this.automatic) throw e;
return "";
}
return openerp.qweb ? openerp.qweb.render('website.tour_popover', options) : options.title;
},
getLang: function () {
return $("html").attr("lang").replace(/-/, '_');
},
getState: function () {
var state = JSON.parse(localStorage.getItem("tour") || 'false') || {};
if (state) { this.time = state.time; }
var tour_id,mode,step_id;
if (!state.id && window.location.href.indexOf("#tutorial.") > -1) {
state = {
"id": window.location.href.match(/#tutorial\.(.*)=true/)[1],
"mode": "tutorial",
"step_id": 0
};
window.location.hash = "";
console.log("Tour Begin from url hash");
T.saveState(state.id, state.mode, state.step_id);
}
if (!state.id) {
return;
}
state.tour = T.tours[state.id];
state.step = state.tour && state.tour.steps[state.step_id === -1 ? 0 : state.step_id];
return state;
},
error: function (step, message) {
var state = T.getState();
message += '\n tour: ' + state.id
+ '\n step: ' + step.id + ": '" + (step._title || step.title) + "'"
+ '\n href: ' + window.location.href
+ '\n referrer: ' + document.referrer
+ '\n element: ' + Boolean(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden")))
+ '\n waitNot: ' + Boolean(!step.waitNot || !$(step.waitNot).size())
+ '\n waitFor: ' + Boolean(!step.waitFor || $(step.waitFor).size())
+ "\n localStorage: " + JSON.stringify(localStorage)
+ '\n\n' + $("body").html();
T.reset();
throw new Error(message);
},
lists: function () {
var tour_ids = [];
for (var k in T.tours) {
tour_ids.push(k);
}
return tour_ids;
},
saveState: function (tour_id, mode, step_id) {
localStorage.setItem("tour", JSON.stringify({"id":tour_id, "mode":mode, "step_id":step_id || 0, "time": this.time}));
},
reset: function () {
var state = T.getState();
if (state) {
for (var k in state.tour.steps) {
state.tour.steps[k].busy = false;
}
}
localStorage.removeItem("tour");
clearTimeout(T.timer);
clearTimeout(T.testtimer);
T.closePopover();
},
running: function () {
function run () {
var state = T.getState();
if (!state) return;
if (state.tour) {
console.log("Tour '"+state.id+"' is running");
T.registerSteps(state.tour);
T.nextStep();
} else {
console.log("Tour '"+state.id+"' wait for running (tour undefined)");
setTimeout(T.running, state.mode === "test" ? T.defaultDelay : T.retryRunningDelay);
}
}
setTimeout(function () {
if ($.ajaxBusy) {
$(document).ajaxStop(run);
} else {
run();
}
},0);
},
timer: null,
testtimer: null,
check: function (step) {
return (step &&
(!step.element || ($(step.element).size() && $(step.element).is(":visible") && !$(step.element).is(":hidden"))) &&
(!step.waitNot || !$(step.waitNot).size()) &&
(!step.waitFor || $(step.waitFor).size()));
},
waitNextStep: function (step, callback, overlaps) {
var self = this;
waitNextStep: function () {
var state = T.getState();
var time = new Date().getTime();
var timer;
var next = state.tour.steps[state.step.id+1];
var overlaps = state.mode === "test" ? T.errorDelay : 0;
window.onbeforeunload = function () {
clearTimeout(self.timer);
clearTimeout(self.testtimer);
clearTimeout(T.timer);
clearTimeout(T.testtimer);
};
// check popover activity
$(".popover.tour button")
.off()
.on("click", function () {
var help = $("#help-menu-button");
var offset = help.offset();
var left = (offset.left > 0) ? (offset.left + help.width()) : offset.left;
var top = (help.height() > 0) ? (offset.top + help.height()) : offset.top;
if ($(this).is("[data-role='next']") && step.element) {
$(".popover.tour").remove();
}
if (step.busy) return;
if (!$(this).is("[data-role='next']") || !step.element) {
$('.popover.tour')
.animate({
left: left,
top: top,
width: '1px',
height: '1px',
opacity: 0
}, 800,
function(){
$(".popover.tour").remove();
clearTimeout(self.timer);
step.busy = true;
self.tour.end();
self.endTour(callback);
});
}
});
function checkNext () {
clearTimeout(self.timer);
if (step.busy) return;
if (self.check(step)) {
step.busy = true;
T.autoTogglePopover();
clearTimeout(T.timer);
if (T.check(next)) {
clearTimeout(T.currentTimer);
// use an other timeout for cke dom loading
setTimeout(function () {
self.nextStep(step.stepId, callback, overlaps);
}, self.defaultDelay);
T.nextStep(next);
}, T.defaultDelay);
} else if (!overlaps || new Date().getTime() - time < overlaps) {
if (self.current.element) {
var $popover = $(".popover.tour");
if(!$(self.current.element).is(":visible")) {
$popover.data("hide", true).fadeOut(300);
} else if($popover.data("hide")) {
$popover.data("hide", false).fadeIn(150);
}
}
self.timer = setTimeout(checkNext, self.defaultDelay);
T.timer = setTimeout(checkNext, T.defaultDelay);
} else {
self.reset();
throw new Error("Can't arrive to step " + step.stepId + ": '" + step._title + "'"
+ '\nhref: ' + window.location.href
+ '\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()
);
T.error(next, "Can't reach the next step");
}
}
checkNext();
},
step: function (stepId) {
var steps = this.steps.slice(0,this.steps.length),
step;
while (step = steps.shift()) {
if (!stepId || step.stepId === stepId)
return step;
nextStep: function (step) {
var state = T.getState();
if (!state) {
return;
}
return null;
},
next: function (stepId) {
var steps = this.steps.slice(0,this.steps.length),
step, next, index=0;
while (step = steps.shift()) {
if (!stepId || step.stepId === stepId) {
// clear popover (fix for boostrap tour if the element is removed before destroy popover)
$(".popover.tour").remove();
// go to step in bootstrap tour
this.tour.goto(index);
if (step.onload) step.onload();
next = steps.shift();
break;
}
index++;
step = step || state.step;
T.saveState(state.id, state.mode, step.id);
if (step.id !== state.step_id) {
console.log("Tour Step: '" + (step._title || step.title) + "' (" + (new Date().getTime() - this.time) + "ms)");
}
return next;
},
nextStep: function (stepId, callback, overlaps) {
var self = this;
if (!this.localStorage.getItem("tour-"+this.id+"-test")) return;
this.localStorage.setItem("tour-"+this.id+"-test", stepId || 0);
T.autoTogglePopover(true);
this.current = this.step(stepId);
var next = this.next(stepId);
if (step.onload) {
step.onload();
}
var next = state.tour.steps[step.id+1];
if (next) {
setTimeout(function () {
self.waitNextStep(next, callback, overlaps);
if (callback) setTimeout(function(){callback.call(self, next);}, self.defaultDelay);
}, next && next.wait || 0);
T.waitNextStep();
if (state.mode === "test") {
setTimeout(function(){
T.autoNextStep(state.tour, step);
}, T.defaultDelay);
}
}, next.wait || 0);
} else {
this.endTour();
T.endTour();
}
},
endTour: function () {
var test = parseInt(this.localStorage.getItem("tour-"+this.id+"-test"),10) >= this.steps.length-1;
this.reset();
var state = T.getState();
var test = state.step.id >= state.tour.steps.length-1;
T.reset();
if (test) {
console.log('ok');
} else {
console.log('error');
}
},
autoNextStep: function () {
var self = this;
clearTimeout(self.testtimer);
autoNextStep: function (tour, step) {
clearTimeout(T.testtimer);
function autoStep () {
var step = self.current;
if (!step) return;
if (step.autoComplete) {
step.autoComplete(tour);
}
var $popover = $(".popover.tour");
if ($popover.find("button[data-role='next']:visible").size()) {
$popover.find("button[data-role='next']:visible").click();
$popover.remove();
}
$(".popover.tour [data-role='next']").click();
var $element = $(step.element);
if (!$element.size()) return;
if (step.snippet) {
var selector = '#oe_snippets '+step.snippet+' .oe_snippet_thumbnail';
self.autoDragAndDropSnippet(selector);
} else if (step.element.match(/#oe_snippets .* \.oe_snippet_thumbnail/)) {
self.autoDragAndDropSnippet($element);
T.autoDragAndDropSnippet($element);
} else if ($element.is(":visible")) {
@ -473,7 +509,7 @@ website.Tour = openerp.Class.extend({
}
}
self.testtimer = setTimeout(autoStep, 100);
T.testtimer = setTimeout(autoStep, 100);
},
autoDragAndDropSnippet: function (selector) {
var $thumbnail = $(selector).first();
@ -483,64 +519,11 @@ website.Tour = openerp.Class.extend({
var $dropZone = $(".oe_drop_zone").first();
var dropPosition = $dropZone.position();
$dropZone.trigger($.Event("mouseup", { which: 1, pageX: dropPosition.left, pageY: dropPosition.top }));
},
});
website.Tour.tours = {};
website.Tour.busy = false;
website.Tour.add = function (tour) {
website.Tour.waitReady(function () {
tour = tour.id ? tour : new tour();
if (!website.Tour.tours[tour.id]) {
website.Tour.tours[tour.id] = tour;
tour.running();
}
});
};
website.Tour.get = function (id) {
return website.Tour.tours[id];
};
website.Tour.each = function (callback) {
website.Tour.waitReady(function () {
for (var k in website.Tour.tours) {
callback.call(website.Tour.tours[k]);
}
});
};
website.Tour.waitReady = function (callback) {
var self = this;
$(document).ready(function () {
if ($.ajaxBusy) {
$(document).ajaxStop(function() {
setTimeout(function () {
callback.call(self);
},0);
});
}
else {
setTimeout(function () {
callback.call(self);
},0);
}
});
};
website.Tour.run_test = function (id) {
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 k;
}
}
return website.Tour.busy;
};
//$(document).ready(T.running);
website.ready().then(T.running);
}());

View File

@ -8,6 +8,6 @@ class TestUi(openerp.tests.HttpCase):
self.phantom_js("/", "console.log('ok')", "openerp.website.editor", login='admin')
def test_04_admin_tour_banner(self):
self.phantom_js("/", "openerp.website.Tour.run_test('banner')", "openerp.website.Tour.tours.banner", login='admin')
self.phantom_js("/", "openerp.website.Tour.run('banner', 'test')", "openerp.website.Tour.tours.banner", login='admin')
# vim:et:

View File

@ -256,7 +256,6 @@
<xpath expr='//script[@src="/web/static/lib/bootstrap/js/bootstrap.js"]' position="before">
<link rel='stylesheet' href='/website/static/src/css/snippets.css'/>
<link rel='stylesheet' href='/website/static/src/css/editor.css'/>
<link rel='stylesheet' href='/website/static/lib/bootstrap-tour/bootstrap-tour.css'/>
<link rel="stylesheet" href="/web/static/lib/select2/select2.css"/>
@ -268,7 +267,6 @@
<script t-if="not translatable" type="text/javascript" src="/website/static/lib/ace/ace.js"></script>
<script type="text/javascript" src="/website/static/lib/vkbeautify/vkbeautify.0.99.00.beta.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
<script type="text/javascript" src="/website/static/lib/bootstrap-tour/bootstrap-tour.js"></script>
<!-- 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>

View File

@ -4,117 +4,105 @@
var website = openerp.website;
var _t = openerp._t;
website.EditorBar.include({
start: function () {
this.registerTour(new website.Tour.Blog(this));
return this._super();
},
});
website.Tour.Blog = website.Tour.extend({
id: 'blog',
name: "Create a blog post",
testPath: '/(blog|blogpost)',
init: function () {
var self = this;
self.steps = [
{
title: _t("New Blog Post"),
content: _t("Let's go through the first steps to write beautiful blog posts."),
popover: { next: _t("Start Tutorial"), end: _t("Skip") },
},
{
element: '#content-menu-button',
placement: 'left',
title: _t("Add Content"),
content: _t("Use this <em>'Content'</em> menu to create a new blog post like any other document (page, menu, products, event, ...)."),
popover: { fixed: true },
},
{
element: 'a[data-action=new_blog_post]',
placement: 'left',
title: _t("New Blog Post"),
content: _t("Select this menu item to create a new blog post."),
popover: { fixed: true },
},
{
element: '.modal:has(#editor_new_blog) button.btn-primary',
placement: 'right',
title: _t("Create Blog Post"),
content: _t("Click <em>Continue</em> to create the blog post."),
},
{
waitFor: 'body:has(button[data-action=save]:visible):has(.js_blog)',
title: _t("Blog Post Created"),
content: _t("This is your new blog post. Let's edit it."),
popover: { next: _t("Continue") },
},
{
element: 'h1[data-oe-expression="blog_post.name"]',
placement: 'bottom',
sampleText: 'New Blog',
title: _t("Set a Title"),
content: _t("Click on this area and set a catchy title for your blog post."),
},
{
waitNot: '#wrap h1[data-oe-model="blog.post"]:contains("Blog Post Title")',
element: 'button[data-action=snippet]',
placement: 'left',
title: _t("Layout Your Blog Post"),
content: _t("Use well designed building blocks to structure the content of your blog. Click 'Insert Blocks' to add new content."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(2)',
placement: 'bottom',
title: _t("Drag & Drop a Block"),
content: _t("Drag this block and drop it in your page."),
popover: { fixed: true },
},
{
element: 'button[data-action=snippet]',
placement: 'bottom',
title: _t("Add Another Block"),
content: _t("Let's add another block to your post."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(4)',
placement: 'bottom',
title: _t("Drag & Drop a block"),
content: _t("Drag this block and drop it below the image block."),
popover: { fixed: true },
},
{
element: '.oe_active .oe_snippet_remove',
placement: 'top',
title: _t("Delete the block"),
content: _t("From this toolbar you can move, duplicate or delete the selected zone. Click on the garbage can image to delete the block. Or click on the Title and delete it."),
},
{
waitNot: '.oe_active .oe_snippet_remove:visible',
element: 'button[data-action=save]',
placement: 'right',
title: _t("Save Your Blog"),
content: _t("Click the <em>Save</em> button to record changes on the page."),
popover: { fixed: true },
},
{
waitFor: 'button[data-action=edit]:visible',
element: 'button.btn-danger.js_publish_btn',
placement: 'top',
title: _t("Publish Your Post"),
content: _t("Your blog post is not yet published. You can update this draft version and publish it once you are ready."),
},
{
waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible',
title: "Thanks!",
content: _t("This tutorial is finished. To discover more features, improve the content of this page and try the <em>Promote</em> button in the top right menu."),
popover: { next: _t("Close Tutorial") },
},
];
return this._super();
},
website.Tour.register({
id: 'blog',
name: _t("Create a blog post"),
steps: [
{
title: _t("New Blog Post"),
content: _t("Let's go through the first steps to write beautiful blog posts."),
popover: { next: _t("Start Tutorial"), end: _t("Skip") },
},
{
element: '#content-menu-button',
placement: 'left',
title: _t("Add Content"),
content: _t("Use this <em>'Content'</em> menu to create a new blog post like any other document (page, menu, products, event, ...)."),
popover: { fixed: true },
},
{
element: 'a[data-action=new_blog_post]',
placement: 'left',
title: _t("New Blog Post"),
content: _t("Select this menu item to create a new blog post."),
popover: { fixed: true },
},
{
element: '.modal:has(#editor_new_blog) button.btn-primary',
placement: 'right',
title: _t("Create Blog Post"),
content: _t("Click <em>Continue</em> to create the blog post."),
},
{
waitFor: 'body:has(button[data-action=save]:visible):has(.js_blog)',
title: _t("Blog Post Created"),
content: _t("This is your new blog post. Let's edit it."),
popover: { next: _t("Continue") },
},
{
element: 'h1[data-oe-expression="blog_post.name"]',
placement: 'bottom',
sampleText: 'New Blog',
title: _t("Set a Title"),
content: _t("Click on this area and set a catchy title for your blog post."),
},
{
waitNot: '#wrap h1[data-oe-model="blog.post"]:contains("Blog Post Title")',
element: 'button[data-action=snippet]',
placement: 'left',
title: _t("Layout Your Blog Post"),
content: _t("Use well designed building blocks to structure the content of your blog. Click 'Insert Blocks' to add new content."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(2)',
placement: 'bottom',
title: _t("Drag & Drop a Block"),
content: _t("Drag this block and drop it in your page."),
popover: { fixed: true },
},
{
element: 'button[data-action=snippet]',
placement: 'bottom',
title: _t("Add Another Block"),
content: _t("Let's add another block to your post."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(4)',
placement: 'bottom',
title: _t("Drag & Drop a block"),
content: _t("Drag this block and drop it below the image block."),
popover: { fixed: true },
},
{
element: '.oe_active .oe_snippet_remove',
placement: 'top',
title: _t("Delete the block"),
content: _t("From this toolbar you can move, duplicate or delete the selected zone. Click on the garbage can image to delete the block. Or click on the Title and delete it."),
},
{
waitNot: '.oe_active .oe_snippet_remove:visible',
element: 'button[data-action=save]',
placement: 'right',
title: _t("Save Your Blog"),
content: _t("Click the <em>Save</em> button to record changes on the page."),
popover: { fixed: true },
},
{
waitFor: 'button[data-action=edit]:visible',
element: 'button.btn-danger.js_publish_btn',
placement: 'top',
title: _t("Publish Your Post"),
content: _t("Your blog post is not yet published. You can update this draft version and publish it once you are ready."),
},
{
waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible',
title: "Thanks!",
content: _t("This tutorial is finished. To discover more features, improve the content of this page and try the <em>Promote</em> button in the top right menu."),
popover: { next: _t("Close Tutorial") },
},
]
});
}());

View File

@ -2,5 +2,5 @@ import openerp.tests
class TestUi(openerp.tests.HttpCase):
def test_admin(self):
self.phantom_js("/", "openerp.website.Tour.run_test('blog')", "openerp.website.Tour")
self.phantom_js("/", "openerp.website.Tour.run('blog', 'test')", "openerp.website.Tour.tours.blog")

View File

@ -4,115 +4,103 @@
var website = openerp.website;
var _t = openerp._t;
website.EditorBar.include({
start: function () {
this.registerTour(new website.EventTour(this));
return this._super();
},
});
website.EventTour = website.Tour.extend({
id: 'event',
name: "Create an event",
testPath: '/event(/[0-9]+/register)?',
init: function (editor) {
var self = this;
self.steps = [
{
title: _t("Create an Event"),
content: _t("Let's go through the first steps to publish a new event."),
popover: { next: _t("Start Tutorial"), end: _t("Skip It") },
},
{
element: '#content-menu-button',
placement: 'left',
title: _t("Add Content"),
content: _t("The <em>Content</em> menu allows you to create new pages, events, menus, etc."),
popover: { fixed: true },
},
{
element: 'a[data-action=new_event]',
placement: 'left',
title: _t("New Event"),
content: _t("Click here to create a new event."),
popover: { fixed: true },
},
{
element: '.modal #editor_new_event input[type=text]',
sampleText: 'Advanced Technical Training',
placement: 'right',
title: _t("Create an Event Name"),
content: _t("Create a name for your new event and click <em>'Continue'</em>. e.g: Technical Training"),
},
{
waitNot: '.modal input[type=text]:not([value!=""])',
element: '.modal button.btn-primary',
placement: 'right',
title: _t("Create Event"),
content: _t("Click <em>Continue</em> to create the event."),
},
{
waitFor: 'body:has(button[data-action=save]:visible):has(.js_event)',
title: _t("New Event Created"),
content: _t("This is your new event page. We will edit the event presentation page."),
popover: { next: _t("Continue") },
},
{
element: 'button[data-action=snippet]',
placement: 'bottom',
title: _t("Layout your event"),
content: _t("Insert blocks to layout the body of your event."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(2)',
placement: 'bottom',
title: _t("Drag & Drop a block"),
content: _t("Drag the 'Image-Text' block and drop it in your page."),
popover: { fixed: true },
},
{
element: 'button[data-action=snippet]',
placement: 'bottom',
title: _t("Layout your event"),
content: _t("Insert another block to your event."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(4)',
placement: 'bottom',
title: _t("Drag & Drop a block"),
content: _t("Drag the 'Text Block' in your event page."),
popover: { fixed: true },
},
{
element: 'button[data-action=save]',
placement: 'right',
title: _t("Save your modifications"),
content: _t("Once you click on save, your event is updated."),
popover: { fixed: true },
},
{
waitFor: 'button[data-action=edit]:visible',
element: 'button.btn-danger.js_publish_btn',
placement: 'top',
title: _t("Publish your event"),
content: _t("Click to publish your event."),
},
{
waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible',
element: '.js_publish_management button[data-toggle="dropdown"]',
placement: 'left',
title: _t("Customize your event"),
content: _t("Click here to customize your event further."),
},
{
element: '.js_publish_management ul>li>a:last:visible',
},
];
return this._super();
}
website.Tour.register({
id: 'event',
name: _t("Create an event"),
steps: [
{
title: _t("Create an Event"),
content: _t("Let's go through the first steps to publish a new event."),
popover: { next: _t("Start Tutorial"), end: _t("Skip It") },
},
{
element: '#content-menu-button',
placement: 'left',
title: _t("Add Content"),
content: _t("The <em>Content</em> menu allows you to create new pages, events, menus, etc."),
popover: { fixed: true },
},
{
element: 'a[data-action=new_event]',
placement: 'left',
title: _t("New Event"),
content: _t("Click here to create a new event."),
popover: { fixed: true },
},
{
element: '.modal #editor_new_event input[type=text]',
sampleText: 'Advanced Technical Training',
placement: 'right',
title: _t("Create an Event Name"),
content: _t("Create a name for your new event and click <em>'Continue'</em>. e.g: Technical Training"),
},
{
waitNot: '.modal input[type=text]:not([value!=""])',
element: '.modal button.btn-primary',
placement: 'right',
title: _t("Create Event"),
content: _t("Click <em>Continue</em> to create the event."),
},
{
waitFor: 'body:has(button[data-action=save]:visible):has(.js_event)',
title: _t("New Event Created"),
content: _t("This is your new event page. We will edit the event presentation page."),
popover: { next: _t("Continue") },
},
{
element: 'button[data-action=snippet]',
placement: 'bottom',
title: _t("Layout your event"),
content: _t("Insert blocks to layout the body of your event."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(2)',
placement: 'bottom',
title: _t("Drag & Drop a block"),
content: _t("Drag the 'Image-Text' block and drop it in your page."),
popover: { fixed: true },
},
{
element: 'button[data-action=snippet]',
placement: 'bottom',
title: _t("Layout your event"),
content: _t("Insert another block to your event."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(4)',
placement: 'bottom',
title: _t("Drag & Drop a block"),
content: _t("Drag the 'Text Block' in your event page."),
popover: { fixed: true },
},
{
element: 'button[data-action=save]',
placement: 'right',
title: _t("Save your modifications"),
content: _t("Once you click on save, your event is updated."),
popover: { fixed: true },
},
{
waitFor: 'button[data-action=edit]:visible',
element: 'button.btn-danger.js_publish_btn',
placement: 'top',
title: _t("Publish your event"),
content: _t("Click to publish your event."),
},
{
waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible',
element: '.js_publish_management button[data-toggle="dropdown"]',
placement: 'left',
title: _t("Customize your event"),
content: _t("Click here to customize your event further."),
},
{
element: '.js_publish_management ul>li>a:last:visible',
},
]
});
}());

View File

@ -2,5 +2,5 @@ import openerp.tests
class TestUi(openerp.tests.HttpCase):
def test_admin(self):
self.phantom_js("/", "openerp.website.Tour.run_test('event')", "openerp.website.Tour")
self.phantom_js("/", "openerp.website.Tour.run('event', 'test')", "openerp.website.Tour.tours.event")

View File

@ -3,77 +3,63 @@
var website = openerp.website;
website.Tour.EventSaleTest = website.Tour.extend({
id: 'event_buy_tickets',
website.Tour.register({
id: 'event_buy_tickets',
name: "Try to buy tickets for event",
path: '/event',
init: function () {
var self = this;
self.steps = [
{
title: "select event",
element: 'a[href*="/event"]:contains("Open Days in Los Angeles")',
mode: 'test',
steps: [
{
title: "select event",
element: 'a[href*="/event"]:contains("Conference on Business Applications"):first',
},
{
waitNot: 'a[href*="/event"]:contains("Conference on Business Applications")',
title: "select 2 Standard tickets",
element: 'select:eq(0)',
sampleText: '2',
},
{
title: "select 3 VIP tickets",
waitFor: 'select:eq(0) option:contains(2):selected',
element: 'select:eq(1)',
sampleText: '3',
},
{
title: "Order Now",
waitFor: 'select:eq(1) option:contains(3):selected',
element: '.btn-primary:contains("Order Now")',
},
{
title: "Complete checkout",
waitFor: '#top_menu .my_cart_quantity:contains(5)',
element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")',
autoComplete: function (tour) {
if ($("input[name='name']").val() === "")
$("input[name='name']").val("website_sale-test-shoptest");
if ($("input[name='email']").val() === "")
$("input[name='email']").val("website_event_sale_test_shoptest@websiteeventsaletest.optenerp.com");
$("input[name='phone']").val("123");
$("input[name='street']").val("123");
$("input[name='city']").val("123");
$("input[name='zip']").val("123");
$("select[name='country_id']").val("21");
},
{
title: "go to register page",
waitNot: 'a[href*="/event"]:contains("Functional Webinar")',
onload: function () {
// use onload if website_event_track is installed
if (!$('form:contains("Ticket Type")').size()) {
window.location.href = $('a[href*="/event"][href*="/register"]').attr("href");
}
},
},
{
title: "select 2 Standard tickets",
element: 'select[name="ticket-1"]',
sampleText: '2',
},
{
title: "select 3 VIP tickets",
waitFor: 'select[name="ticket-1"] option:contains(2):selected',
element: 'select[name="ticket-2"]',
sampleText: '3',
},
{
title: "Order Now",
waitFor: 'select[name="ticket-2"] option:contains(3):selected',
element: '.btn-primary:contains("Order Now")',
},
{
title: "Complete checkout",
waitFor: '#top_menu .my_cart_quantity:contains(5)',
element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")',
onload: function (tour) {
if ($("input[name='name']").val() === "")
$("input[name='name']").val("website_sale-test-shoptest");
if ($("input[name='email']").val() === "")
$("input[name='email']").val("website_event_sale_test_shoptest@websiteeventsaletest.optenerp.com");
$("input[name='phone']").val("123");
$("input[name='street']").val("123");
$("input[name='city']").val("123");
$("input[name='zip']").val("123");
$("select[name='country_id']").val("21");
},
},
{
title: "select payment",
element: '#payment_method label:has(img[title="transfer"]) input',
},
{
title: "Pay Now",
waitFor: '#payment_method label:has(input:checked):has(img[title="transfer"])',
element: '.oe_sale_acquirer_button .btn[name="submit"]:visible',
},
{
title: "finish",
waitFor: '.oe_website_sale:contains("Thank you for your order")',
}
];
return this._super();
},
},
{
title: "select payment",
element: '#payment_method label:has(img[title="Wire Transfer"]) input',
},
{
title: "Pay Now",
waitFor: '#payment_method label:has(input:checked):has(img[title="Wire Transfer"])',
element: '.oe_sale_acquirer_button .btn[name="submit"]:visible',
},
{
title: "finish",
waitFor: '.oe_website_sale:contains("Thank you for your order")',
}
]
});
// for test without editor bar
website.Tour.add(website.Tour.EventSaleTest);
}());

View File

@ -1 +1 @@
#import test_ui
import test_ui

View File

@ -1,19 +1,21 @@
import os
import openerp.tests
inject = [
"./../../../website/static/src/js/website.tour.test.js",
"./../../../website_event_sale/static/src/js/website.tour.event_sale.js",
("openerp.website.Tour", os.path.join(os.path.dirname(__file__), '../../website/static/src/js/website.tour.js')),
("openerp.website.Tour.ShopTest", os.path.join(os.path.dirname(__file__), "../static/src/js/website.tour.event_sale.js")),
]
@openerp.tests.common.at_install(False)
@openerp.tests.common.post_install(True)
class TestUi(openerp.tests.HttpCase):
def test_admin(self):
self.phantom_js("/", "openerp.website.Tour.run_test('event_buy_tickets')", "openerp.website.Tour", inject=inject)
self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour.tours.event_buy_tickets", inject=inject)
def test_demo(self):
self.phantom_js("/", "openerp.website.Tour.run_test('event_buy_tickets')", "openerp.website.Tour", login="demo", password="demo", inject=inject);
self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour.tours.event_buy_tickets", login="demo", password="demo", inject=inject);
def test_public(self):
self.phantom_js("/", "openerp.website.Tour.run_test('event_buy_tickets')", "openerp.website.Tour", login=None, inject=inject);
self.phantom_js("/", "openerp.website.Tour.run('event_buy_tickets', 'test')", "openerp.website.Tour.tours.event_buy_tickets", login=None, inject=inject);

View File

@ -38,7 +38,6 @@
<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'/>
<link rel='stylesheet' href='/website/static/lib/bootstrap-tour/bootstrap-tour.css'/>
<link rel="stylesheet" href="/web/static/lib/select2/select2.css"/>
@ -50,7 +49,6 @@
<script t-if="not translatable" type="text/javascript" src="/website/static/lib/ace/ace.js"></script>
<script type="text/javascript" src="/website/static/lib/vkbeautify/vkbeautify.0.99.00.beta.js"></script>
<script type="text/javascript" src="/web/static/lib/jquery.ui/js/jquery-ui-1.9.1.custom.js"></script>
<script type="text/javascript" src="/website/static/lib/bootstrap-tour/bootstrap-tour.js"></script>
<!-- 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>

View File

@ -3,92 +3,87 @@
var website = openerp.website;
website.Tour.ShopTest = website.Tour.extend({
id: 'shop_buy_product',
website.Tour.register({
id: 'shop_buy_product',
name: "Try to buy products",
path: '/shop',
init: function () {
var self = this;
self.steps = [
{
title: "select ipod",
element: '.oe_product_cart a:contains("iPod")',
mode: 'test',
steps: [
{
title: "select ipod",
element: '.oe_product_cart a:contains("iPod")',
},
{
title: "select ipod 32Go",
element: 'input[name="product_id"]:not([checked])',
},
{
title: "click on add to cart",
waitFor: 'input[name="product_id"]:eq(1)[checked]',
element: 'form[action="/shop/add_cart"] .btn',
},
{
title: "add suggested",
element: 'form[action="/shop/add_cart"] .btn-link:contains("Add to Cart")',
},
{
title: "add one more iPod",
waitFor: '.my_cart_quantity:contains(2)',
element: '#mycart_products tr:contains("iPod: 32 Gb") a.js_add_cart_json:eq(1)',
},
{
title: "remove Headphones",
waitFor: '#mycart_products tr:contains("iPod: 32 Gb") input.js_quantity[value=2]',
element: '#mycart_products tr:contains("Apple In-Ear Headphones") a.js_add_cart_json:first',
},
{
title: "set one iPod",
waitNot: '#mycart_products tr:contains("Apple In-Ear Headphones")',
element: '#mycart_products input.js_quantity',
sampleText: '1',
},
{
title: "go to checkout",
waitFor: '#mycart_products input.js_quantity[value=1]',
element: 'a[href="/shop/checkout"]',
},
{
title: "test with input error",
element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")',
onload: function (tour) {
$("input[name='phone']").val("");
},
{
title: "select ipod 32Go",
element: 'input[name="product_id"]:not([checked])',
},
{
title: "test without input error",
waitFor: 'form[action="/shop/confirm_order"] .has-error',
element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")',
onload: function (tour) {
if ($("input[name='name']").val() === "")
$("input[name='name']").val("website_sale-test-shoptest");
if ($("input[name='email']").val() === "")
$("input[name='email']").val("website_sale_test_shoptest@websitesaletest.optenerp.com");
$("input[name='phone']").val("123");
$("input[name='street']").val("123");
$("input[name='city']").val("123");
$("input[name='zip']").val("123");
$("select[name='country_id']").val("21");
},
{
title: "click on add to cart",
waitFor: 'input[name="product_id"]:eq(1)[checked]',
element: 'form[action="/shop/add_cart"] .btn',
},
{
title: "add suggested",
element: 'form[action="/shop/add_cart"] .btn-link:contains("Add to Cart")',
},
{
title: "add one more iPod",
waitFor: '.my_cart_quantity:contains(2)',
element: '#mycart_products tr:contains("iPod: 32 Gb") a.js_add_cart_json:eq(1)',
},
{
title: "remove Headphones",
waitFor: '#mycart_products tr:contains("iPod: 32 Gb") input.js_quantity[value=2]',
element: '#mycart_products tr:contains("Apple In-Ear Headphones") a.js_add_cart_json:first',
},
{
title: "set one iPod",
waitNot: '#mycart_products tr:contains("Apple In-Ear Headphones")',
element: '#mycart_products input.js_quantity',
sampleText: '1',
},
{
title: "go to checkout",
waitFor: '#mycart_products input.js_quantity[value=1]',
element: 'a[href="/shop/checkout"]',
},
{
title: "test with input error",
element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")',
onload: function (tour) {
$("input[name='phone']").val("");
},
},
{
title: "test without input error",
waitFor: 'form[action="/shop/confirm_order"] .has-error',
element: 'form[action="/shop/confirm_order"] .btn:contains("Confirm")',
onload: function (tour) {
if ($("input[name='name']").val() === "")
$("input[name='name']").val("website_sale-test-shoptest");
if ($("input[name='email']").val() === "")
$("input[name='email']").val("website_sale_test_shoptest@websitesaletest.optenerp.com");
$("input[name='phone']").val("123");
$("input[name='street']").val("123");
$("input[name='city']").val("123");
$("input[name='zip']").val("123");
$("select[name='country_id']").val("21");
},
},
{
title: "select payment",
element: '#payment_method label:has(img[title="Wire Transfer"]) input',
},
{
title: "Pay Now",
waitFor: '#payment_method label:has(input:checked):has(img[title="Wire Transfer"])',
element: '.oe_sale_acquirer_button .btn[name="submit"]:visible',
},
{
title: "finish",
waitFor: '.oe_website_sale:contains("Thank you for your order")',
}
];
return this._super();
},
},
{
title: "select payment",
element: '#payment_method label:has(img[title="Wire Transfer"]) input',
},
{
title: "Pay Now",
waitFor: '#payment_method label:has(input:checked):has(img[title="Wire Transfer"])',
element: '.oe_sale_acquirer_button .btn[name="submit"]:visible',
},
{
title: "finish",
waitFor: '.oe_website_sale:contains("Thank you for your order")',
}
]
});
// for test without editor bar
website.Tour.add(website.Tour.ShopTest);
}());

View File

@ -4,125 +4,114 @@
var website = openerp.website;
var _t = openerp._t;
website.EditorBar.include({
start: function () {
this.registerTour(new website.Tour.Shop(this));
return this._super();
},
});
website.Tour.Shop = website.Tour.extend({
website.Tour.register({
id: 'shop',
name: "Create a product",
testPath: '/shop',
init: function () {
var self = this;
self.steps = [
{
title: _t("Welcome to your shop"),
content: _t("You successfully installed the e-commerce. This guide will help you to create your product and promote your sales."),
popover: { next: _t("Start Tutorial"), end: _t("Skip It") },
},
{
element: '#content-menu-button',
placement: 'left',
title: _t("Create your first product"),
content: _t("Click here to add a new product."),
popover: { fixed: true },
},
{
element: 'a[data-action=new_product]',
placement: 'left',
title: _t("Create a new product"),
content: _t("Select 'New Product' to create it and manage its properties to boost your sales."),
popover: { fixed: true },
},
{
element: '.modal #editor_new_product input[type=text]',
sampleText: 'New Product',
placement: 'right',
title: _t("Choose name"),
content: _t("Enter a name for your new product then click 'Continue'."),
},
{
waitNot: '.modal input[type=text]:not([value!=""])',
element: '.modal button.btn-primary',
placement: 'right',
title: _t("Create Product"),
content: _t("Click <em>Continue</em> to create the product."),
},
{
waitFor: 'body:has(button[data-action=save]:visible):has(.js_sale)',
title: _t("New product created"),
content: _t("This page contains all the information related to the new product."),
popover: { next: _t("Continue") },
},
{
element: '.product_price .oe_currency_value',
sampleText: '20.50',
placement: 'left',
title: _t("Change the price"),
content: _t("Edit the price of this product by clicking on the amount."),
},
{
waitNot: '.product_price .oe_currency_value:containsExact(1.00)',
element: '#wrap img.product_detail_img',
placement: 'top',
title: _t("Update image"),
content: _t("Click here to set an image describing your product."),
},
{
element: 'img[alt=ipad]',
placement: 'top',
title: _t("Select an Image"),
content: _t("Let's select an ipad image."),
},
{
waitFor: '.media_selected img[alt=ipad]',
element: '.modal-content button.save',
placement: 'top',
title: _t("Save this Image"),
content: _t("Click on save to add the image to the product decsription."),
},
{
waitNot: '.modal-content:visible',
element: 'button[data-action=snippet]',
placement: 'bottom',
title: _t("Describe the Product"),
content: _t("Insert blocks like text-image, or gallery to fully describe the product."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(7)',
placement: 'bottom',
title: _t("Drag & Drop a block"),
content: _t("Drag the 'Big Picture' block and drop it in your page."),
popover: { fixed: true },
},
{
element: 'button[data-action=save]',
placement: 'right',
title: _t("Save your modifications"),
content: _t("Once you click on save, your product is updated."),
popover: { fixed: true },
name: _t("Create a product"),
steps: [
{
title: _t("Welcome to your shop"),
content: _t("You successfully installed the e-commerce. This guide will help you to create your product and promote your sales."),
popover: { next: _t("Start Tutorial"), end: _t("Skip It") },
},
{
element: '#content-menu-button',
placement: 'left',
title: _t("Create your first product"),
content: _t("Click here to add a new product."),
popover: { fixed: true },
},
{
element: 'a[data-action=new_product]',
placement: 'left',
title: _t("Create a new product"),
content: _t("Select 'New Product' to create it and manage its properties to boost your sales."),
popover: { fixed: true },
},
{
element: '.modal #editor_new_product input[type=text]',
sampleText: 'New Product',
placement: 'right',
title: _t("Choose name"),
content: _t("Enter a name for your new product then click 'Continue'."),
},
{
waitNot: '.modal input[type=text]:not([value!=""])',
element: '.modal button.btn-primary',
placement: 'right',
title: _t("Create Product"),
content: _t("Click <em>Continue</em> to create the product."),
},
{
waitFor: 'body:has(button[data-action=save]:visible):has(.js_sale)',
title: _t("New product created"),
content: _t("This page contains all the information related to the new product."),
popover: { next: _t("Continue") },
},
{
element: '.product_price .oe_currency_value',
sampleText: '20.50',
placement: 'left',
title: _t("Change the price"),
content: _t("Edit the price of this product by clicking on the amount."),
},
},
{
waitFor: '#website-top-navbar button[data-action="edit"]:visible',
element: '.js_publish_management button.js_publish_btn.btn-danger',
placement: 'top',
title: _t("Publish your product"),
content: _t("Click to publish your product so your customers can see it."),
},
{
waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible',
title: _t("Congratulations"),
content: _t("Congratulations! You just created and published your first product."),
popover: { next: _t("Close Tutorial") },
},
];
return this._super();
}
{
waitNot: '.product_price .oe_currency_value:containsExact(1.00)',
element: '#wrap img.product_detail_img',
placement: 'top',
title: _t("Update image"),
content: _t("Click here to set an image describing your product."),
},
{
element: 'img[alt=ipad]',
placement: 'top',
title: _t("Select an Image"),
content: _t("Let's select an ipad image."),
},
{
waitFor: '.media_selected img[alt=ipad]',
element: '.modal-content button.save',
placement: 'top',
title: _t("Save this Image"),
content: _t("Click on save to add the image to the product decsription."),
},
{
waitNot: '.modal-content:visible',
element: 'button[data-action=snippet]',
placement: 'bottom',
title: _t("Describe the Product"),
content: _t("Insert blocks like text-image, or gallery to fully describe the product."),
popover: { fixed: true },
},
{
snippet: '#snippet_structure .oe_snippet:eq(7)',
placement: 'bottom',
title: _t("Drag & Drop a block"),
content: _t("Drag the 'Big Picture' block and drop it in your page."),
popover: { fixed: true },
},
{
element: 'button[data-action=save]',
placement: 'right',
title: _t("Save your modifications"),
content: _t("Once you click on save, your product is updated."),
popover: { fixed: true },
},
{
waitFor: '#website-top-navbar button[data-action="edit"]:visible',
element: '.js_publish_management button.js_publish_btn.btn-danger',
placement: 'top',
title: _t("Publish your product"),
content: _t("Click to publish your product so your customers can see it."),
},
{
waitFor: '.js_publish_management button.js_publish_btn.btn-success:visible',
title: _t("Congratulations"),
content: _t("Congratulations! You just created and published your first product."),
popover: { next: _t("Close Tutorial") },
},
]
});
}());

View File

@ -11,13 +11,13 @@ inject = [
@openerp.tests.common.post_install(True)
class TestUi(openerp.tests.HttpCase):
def test_01_admin_shop_tour(self):
self.phantom_js("/", "openerp.website.Tour.run_test('shop')", "openerp.website.Tour.Shop", login="admin")
self.phantom_js("/", "openerp.website.Tour.run('shop', 'test')", "openerp.website.Tour.tours.shop", login="admin")
def test_02_admin_checkout(self):
self.phantom_js("/", "openerp.website.Tour.run_test('shop_buy_product')", "openerp.website.Tour.ShopTest", login="admin", inject=inject)
self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.tours.shop_buy_product", login="admin", inject=inject)
def test_03_demo_checkout(self):
self.phantom_js("/", "openerp.website.Tour.run_test('shop_buy_product')", "openerp.website.Tour.ShopTest", login="demo", inject=inject)
self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.tours.shop_buy_product", login="demo", inject=inject)
def test_04_public_checkout(self):
self.phantom_js("/", "openerp.website.Tour.run_test('shop_buy_product')", "openerp.website.Tour.ShopTest", inject=inject)
self.phantom_js("/", "openerp.website.Tour.run('shop_buy_product', 'test')", "openerp.website.Tour.tours.shop_buy_product", inject=inject)