@@ -812,7 +812,12 @@
-
+
+
+
+
+
+
diff --git a/addons/product/report/product_pricelist.py b/addons/product/report/product_pricelist.py
index e6ccde44d8e..abdb705c529 100644
--- a/addons/product/report/product_pricelist.py
+++ b/addons/product/report/product_pricelist.py
@@ -28,7 +28,8 @@ class report_product_pricelist(models.AbstractModel):
}
def _get_quantity(self, data):
- return sorted([data['form'][key] for key in data['form'] if key.startswith('qty') and data['form'][key]])
+ form = data and data.get('form') or {}
+ return sorted([form[key] for key in form if key.startswith('qty') and form[key]])
def _get_categories(self, pricelist, products, quantities):
categ_data = []
diff --git a/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py b/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py
index 74b73173972..4b89e197c03 100644
--- a/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py
+++ b/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py
@@ -46,6 +46,7 @@ class TestTimesheetHolidays(TestCommonTimesheet):
'timesheet_generate': True,
'timesheet_project_id': self.internal_project.id,
'timesheet_task_id': self.internal_task_leaves.id,
+ 'validity_start': False,
})
self.hr_leave_type_no_ts = self.env['hr.leave.type'].create({
'name': 'Leave Type without timesheet generation',
@@ -53,6 +54,7 @@ class TestTimesheetHolidays(TestCommonTimesheet):
'timesheet_generate': False,
'timesheet_project_id': False,
'timesheet_task_id': False,
+ 'validity_start': False,
})
# HR Officer allocates some leaves to the employee 1
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index 48b10e8a9c6..1c1bfc1db54 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -201,6 +201,9 @@ class SaleOrder(models.Model):
@api.depends('pricelist_id', 'date_order', 'company_id')
def _compute_currency_rate(self):
for order in self:
+ if not order.company_id:
+ order.currency_rate = order.currency_id.with_context(date=order.date_order).rate or 1.0
+ continue
order.currency_rate = self.env['res.currency']._get_conversion_rate(order.company_id.currency_id, order.currency_id, order.company_id, order.date_order)
def _compute_access_url(self):
diff --git a/addons/sale_mrp/tests/test_sale_mrp_flow.py b/addons/sale_mrp/tests/test_sale_mrp_flow.py
index 459892631b1..6f8710a34e8 100644
--- a/addons/sale_mrp/tests/test_sale_mrp_flow.py
+++ b/addons/sale_mrp/tests/test_sale_mrp_flow.py
@@ -1459,3 +1459,48 @@ class TestSaleMrpFlow(common.SavepointCase):
move_component_kg = order.picking_ids[0].move_lines - move_component_unit
self.assertEquals(move_component_unit.product_uom_qty, 0.5)
self.assertEquals(move_component_kg.product_uom_qty, 0.583)
+
+ def test_product_type_service_1(self):
+ route_manufacture = self.warehouse.manufacture_pull_id.route_id.id
+ route_mto = self.warehouse.mto_pull_id.route_id.id
+ self.uom_unit = self.env.ref('uom.product_uom_unit')
+
+ # Create finished product
+ finished_product = self.env['product.product'].create({
+ 'name': 'Geyser',
+ 'type': 'product',
+ 'route_ids': [(4, route_mto), (4, route_manufacture)],
+ })
+
+ # Create service type product
+ product_raw = self.env['product.product'].create({
+ 'name': 'raw Geyser',
+ 'type': 'service',
+ })
+
+ # Create bom for finish product
+ bom = self.env['mrp.bom'].create({
+ 'product_id': finished_product.id,
+ 'product_tmpl_id': finished_product.product_tmpl_id.id,
+ 'product_uom_id': self.env.ref('uom.product_uom_unit').id,
+ 'product_qty': 1.0,
+ 'type': 'normal',
+ 'bom_line_ids': [(5, 0), (0, 0, {'product_id': product_raw.id})]
+ })
+
+ # Create sale order
+ sale_form = Form(self.env['sale.order'])
+ sale_form.partner_id = self.env.ref('base.res_partner_1')
+ with sale_form.order_line.new() as line:
+ line.name = finished_product.name
+ line.product_id = finished_product
+ line.product_uom_qty = 1.0
+ line.product_uom = self.uom_unit
+ line.price_unit = 10.0
+ sale_order = sale_form.save()
+
+ sale_order.action_confirm()
+
+ mo = self.env['mrp.production'].search([('product_id', '=', finished_product.id)])
+
+ self.assertTrue(mo, 'Manufacturing order created.')
diff --git a/addons/survey_crm/i18n/az.po b/addons/survey_crm/i18n/az.po
deleted file mode 100644
index 868166d06d8..00000000000
--- a/addons/survey_crm/i18n/az.po
+++ /dev/null
@@ -1,26 +0,0 @@
-# Translation of Odoo Server.
-# This file contains the translation of the following modules:
-# * survey_crm
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: Odoo Server saas~11.5\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-09-18 09:49+0000\n"
-"PO-Revision-Date: 2018-08-24 09:26+0000\n"
-"Language-Team: Azerbaijani (https://www.transifex.com/odoo/teams/41243/az/)\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: \n"
-"Language: az\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#. module: survey_crm
-#: model:ir.model,name:survey_crm.model_survey_mail_compose_message
-msgid "Email composition wizard for Survey"
-msgstr ""
-
-#. module: survey_crm
-#: model:ir.actions.act_window,name:survey_crm.action_partner_survey_mail_crm
-msgid "Partner Survey Mailing"
-msgstr ""
diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py
index e5384c9b167..9d95ae485a9 100644
--- a/addons/web/controllers/main.py
+++ b/addons/web/controllers/main.py
@@ -1048,7 +1048,7 @@ class Binary(http.Controller):
filename_field=filename_field, download=download, mimetype=mimetype,
default_mimetype='image/png', access_token=access_token)
- if status == 301 or (status != 200 and download):
+ if status in [301, 304] or (status != 200 and download):
return request.env['ir.http']._response_by_status(status, headers, content)
if not content:
content = base64.b64encode(self.placeholder(image=placeholder))
diff --git a/addons/web/static/lib/moment/locale/ar.js b/addons/web/static/lib/moment/locale/ar.js
index d263e4c4f8d..dbe4e7dd940 100644
--- a/addons/web/static/lib/moment/locale/ar.js
+++ b/addons/web/static/lib/moment/locale/ar.js
@@ -58,18 +58,18 @@ var pluralize = function (u) {
};
};
var months = [
- 'كانون الثاني يناير',
- 'شباط فبراير',
- 'آذار مارس',
- 'نيسان أبريل',
- 'أيار مايو',
- 'حزيران يونيو',
- 'تموز يوليو',
- 'آب أغسطس',
- 'أيلول سبتمبر',
- 'تشرين الأول أكتوبر',
- 'تشرين الثاني نوفمبر',
- 'كانون الأول ديسمبر'
+ 'يناير',
+ 'فبراير',
+ 'مارس',
+ 'أبريل',
+ 'مايو',
+ 'يونيو',
+ 'يوليو',
+ 'أغسطس',
+ 'سبتمبر',
+ 'أكتوبر',
+ 'نوفمبر',
+ 'ديسمبر'
];
var ar = moment.defineLocale('ar', {
diff --git a/addons/web/static/src/js/views/control_panel/search/add_new_favorite_menu.js b/addons/web/static/src/js/views/control_panel/search/add_new_favorite_menu.js
index 0a2498c69df..421fec3e04b 100644
--- a/addons/web/static/src/js/views/control_panel/search/add_new_favorite_menu.js
+++ b/addons/web/static/src/js/views/control_panel/search/add_new_favorite_menu.js
@@ -12,6 +12,7 @@ var AddNewFavoriteMenu = Widget.extend({
events: _.extend({}, Widget.prototype.events, {
'click .o_save_favorite': '_onSaveFavoriteClick',
'click .o_add_favorite.o_menu_header': '_onMenuHeaderClick',
+ 'click input[type="checkbox"]': '_onCheckboxClick',
'keyup .o_save_name input': '_onKeyUp',
}),
/**
@@ -107,6 +108,33 @@ var AddNewFavoriteMenu = Widget.extend({
// Handlers
//--------------------------------------------------------------------------
+ /**
+ * Additional behavior when a checkbox is clicked upon
+ * Namely, some checkbox are mutually exclusive
+ * this function allows this
+ *
+ * @private
+ * @param {jQueryEvent} ev
+ */
+ _onCheckboxClick: function (ev) {
+ function mutuallyExclusive (elem) {
+ if (!elem.id) return false;
+ return ['use_by_default', 'share_all_users'].some(function (str) {
+ return elem.id.indexOf(str);
+ });
+ }
+ var $checkboxes = this.$('input[type="checkbox"]');
+ var currentCheckBox = ev.target;
+
+ if (mutuallyExclusive(currentCheckBox)) {
+ for (var i=0; i < $checkboxes.length; i++) {
+ var checkbox = $checkboxes[i];
+ if (checkbox !== currentCheckBox && mutuallyExclusive(checkbox)) {
+ checkbox.checked = false;
+ }
+ }
+ }
+ },
/**
* @private
* @param {jQueryEvent} ev
diff --git a/addons/web/static/src/js/views/pivot/pivot_controller.js b/addons/web/static/src/js/views/pivot/pivot_controller.js
index ac48ab769ff..fbc08e93b1e 100644
--- a/addons/web/static/src/js/views/pivot/pivot_controller.js
+++ b/addons/web/static/src/js/views/pivot/pivot_controller.js
@@ -53,7 +53,6 @@ var PivotController = AbstractController.extend({
* @override
*/
start: function () {
- this.$el.toggleClass('o_enable_linking', this.enableLinking);
this.$fieldSelection = this.$('.o_field_selection');
core.bus.on('click', this, function () {
this.$fieldSelection.empty();
diff --git a/addons/web/static/src/js/views/pivot/pivot_renderer.js b/addons/web/static/src/js/views/pivot/pivot_renderer.js
index 101f3734392..29c3424e141 100644
--- a/addons/web/static/src/js/views/pivot/pivot_renderer.js
+++ b/addons/web/static/src/js/views/pivot/pivot_renderer.js
@@ -283,7 +283,7 @@ var PivotRenderer = AbstractRenderer.extend({
$cell = $('| ')
.data('id', rows[i].id)
.data('col_id', rows[i].col_ids[Math.floor(j / nbrMeasures)])
- .toggleClass('o_empty', !value)
+ .toggleClass('o_empty', _.isUndefined(value))
.addClass('o_pivot_cell_value text-right');
if (value !== undefined) {
$cell.append($(' ', {class: 'o_value'}).html(formatter(value, measure)));
diff --git a/addons/web/static/src/js/widgets/pie_chart.js b/addons/web/static/src/js/widgets/pie_chart.js
index ddeca1fdcec..a9b0bc01e45 100644
--- a/addons/web/static/src/js/widgets/pie_chart.js
+++ b/addons/web/static/src/js/widgets/pie_chart.js
@@ -34,6 +34,11 @@ var PieChart = Widget.extend({
title: node.attrs.title || modifiers.title || modifiers.measure,
});
+ var pieChartContext = JSON.parse(JSON.stringify(record.context));
+ delete pieChartContext.graph_mode;
+ delete pieChartContext.graph_measure;
+ delete pieChartContext.graph_groupbys;
+
this.subViewParams = {
modelName: record.model,
withControlPanel: false,
@@ -41,7 +46,7 @@ var PieChart = Widget.extend({
mode: 'pie',
};
this.subViewParams.searchQuery = {
- context: record.context,
+ context: pieChartContext,
domain: domain,
groupBy: [],
};
diff --git a/addons/web/static/src/scss/pivot_view.scss b/addons/web/static/src/scss/pivot_view.scss
index 83f8fbc58e7..6bc25ee114e 100644
--- a/addons/web/static/src/scss/pivot_view.scss
+++ b/addons/web/static/src/scss/pivot_view.scss
@@ -87,4 +87,9 @@
right: 6px;
top: 9px;
}
+
+ .o_pivot_field_menu .dropdown-item.disabled {
+ color: $text-muted;
+ cursor: default;
+ }
}
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 564bbc8582e..c925809fab5 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -532,7 +532,7 @@
-
+
diff --git a/addons/web/static/tests/views/control_panel_tests.js b/addons/web/static/tests/views/control_panel_tests.js
index 2e0a369ab4a..7d033e5f1e4 100644
--- a/addons/web/static/tests/views/control_panel_tests.js
+++ b/addons/web/static/tests/views/control_panel_tests.js
@@ -418,6 +418,47 @@ QUnit.module('Views', {
controlPanel.destroy();
});
+ QUnit.test('Favorites Use by Default and Share are exclusive', async function (assert) {
+ assert.expect(11);
+ var controlPanel = await createControlPanel({
+ model: 'partner',
+ arch: "",
+ data: this.data,
+ searchMenuTypes: ['favorite'],
+ });
+ testUtils.dom.click(controlPanel.$('.o_favorites_menu_button'));
+ testUtils.dom.click(controlPanel.$('button.o_add_favorite'));
+ var $checkboxes = controlPanel.$('input[type="checkbox"]');
+
+ assert.strictEqual($checkboxes.length, 2,
+ '2 checkboxes are present')
+
+ assert.notOk($checkboxes[0].checked, 'Start: None of the checkboxes are checked (1)');
+ assert.notOk($checkboxes[1].checked, 'Start: None of the checkboxes are checked (2)');
+
+ testUtils.dom.click($checkboxes.eq(0));
+ assert.ok($checkboxes[0].checked, 'The first checkbox is checked');
+ assert.notOk($checkboxes[1].checked, 'The second checkbox is not checked');
+
+ testUtils.dom.click($checkboxes.eq(1));
+ assert.notOk($checkboxes[0].checked,
+ 'Clicking on the second checkbox checks it, and unchecks the first (1)');
+ assert.ok($checkboxes[1].checked,
+ 'Clicking on the second checkbox checks it, and unchecks the first (2)');
+
+ testUtils.dom.click($checkboxes.eq(0));
+ assert.ok($checkboxes[0].checked,
+ 'Clicking on the first checkbox checks it, and unchecks the second (1)');
+ assert.notOk($checkboxes[1].checked,
+ 'Clicking on the first checkbox checks it, and unchecks the second (2)');
+
+ testUtils.dom.click($checkboxes.eq(0));
+ assert.notOk($checkboxes[0].checked, 'End: None of the checkboxes are checked (1)');
+ assert.notOk($checkboxes[1].checked, 'End: None of the checkboxes are checked (2)');
+
+ controlPanel.destroy();
+ });
+
QUnit.test('load filter', async function (assert) {
assert.expect(1);
diff --git a/addons/web/static/tests/views/pivot_tests.js b/addons/web/static/tests/views/pivot_tests.js
index acd8b11b635..0907e471b39 100644
--- a/addons/web/static/tests/views/pivot_tests.js
+++ b/addons/web/static/tests/views/pivot_tests.js
@@ -106,7 +106,7 @@ QUnit.module('Views', {
},
});
- assert.hasClass(pivot.$el,'o_enable_linking',
+ assert.hasClass(pivot.$('.o_pivot'),'o_enable_linking',
"root node should have classname 'o_enable_linking'");
assert.strictEqual(pivot.$('td.o_pivot_cell_value:contains(32)').length, 1,
"should contain a pivot cell with the sum of all records");
@@ -269,7 +269,7 @@ QUnit.module('Views', {
}
});
- assert.hasClass(pivot.$el,'o_enable_linking',
+ assert.hasClass(pivot.$('.o_pivot'),'o_enable_linking',
"root node should have classname 'o_enable_linking'");
await testUtils.dom.click(pivot.$('.o_pivot_cell_value:contains(12)')); // should trigger a do_action
@@ -293,7 +293,7 @@ QUnit.module('Views', {
},
});
- assert.doesNotHaveClass(pivot.$el, 'o_enable_linking',
+ assert.doesNotHaveClass(pivot.$('.o_pivot'), 'o_enable_linking',
"root node should not have classname 'o_enable_linking'");
assert.containsOnce(pivot, '.o_pivot_cell_value',
"should have one cell");
diff --git a/addons/web/tests/test_image.py b/addons/web/tests/test_image.py
index e17073d0fb7..d26528b88e3 100644
--- a/addons/web/tests/test_image.py
+++ b/addons/web/tests/test_image.py
@@ -2,6 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import io
+import base64
from PIL import Image
@@ -42,3 +43,23 @@ class TestImage(HttpCase):
response = self.url_open('/web/image/fake/0/image_no_size')
image = Image.open(io.BytesIO(response.content))
self.assertEqual(image.size, (256, 256))
+
+ def test_02_content_image_Etag_304(self):
+ """This test makes sure that the 304 response is properly returned if the ETag is properly set"""
+
+ attachment = self.env['ir.attachment'].create({
+ 'datas': b"R0lGODdhAQABAIAAAP///////ywAAAAAAQABAAACAkQBADs=",
+ 'name': 'testEtag.gif',
+ 'public': True,
+ 'datas_fname': 'testEtag.gif',
+ 'mimetype': 'image/gif',
+ })
+ response = self.url_open('/web/image/%s' % attachment.id)
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(base64.b64encode(response.content), attachment.datas)
+
+ etag = response.headers.get('ETag')
+
+ response2 = self.url_open('/web/image/%s' % attachment.id, headers={"If-None-Match": etag})
+ self.assertEqual(response2.status_code, 304)
+ self.assertEqual(len(response2.content), 0)
diff --git a/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js
index aacdfff9e0c..fa642338399 100644
--- a/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js
+++ b/addons/web_editor/static/src/js/wysiwyg/wysiwyg_iframe.js
@@ -68,6 +68,8 @@ Wysiwyg.include({
}
$(document.body).off('.' + this.id);
+ this.$iframe.parents().removeClass('o_wysiwyg_no_transform');
+
this.$target.insertBefore(this.$iframe);
delete window.top[this._onUpdateIframeId];
@@ -100,6 +102,10 @@ Wysiwyg.include({
}
self.$iframe.toggleClass('o_fullscreen');
self.$iframe.contents().find('body').toggleClass('o_fullscreen');
+
+ // Hack to avoid a parent of the fullscreen iframe to have a
+ // transform (otherwise the position: fixed won't work)
+ self.$iframe.parents().toggleClass('o_wysiwyg_no_transform');
},
isFullscreen: function () {
if (!self.$iframe) {
diff --git a/addons/web_editor/static/src/scss/wysiwyg_iframe.scss b/addons/web_editor/static/src/scss/wysiwyg_iframe.scss
index d019fdce753..1124d903353 100644
--- a/addons/web_editor/static/src/scss/wysiwyg_iframe.scss
+++ b/addons/web_editor/static/src/scss/wysiwyg_iframe.scss
@@ -10,6 +10,10 @@ iframe.wysiwyg_iframe.o_fullscreen {
border: 0;
}
+.o_wysiwyg_no_transform {
+ transform: none !important;
+}
+
body.o_in_iframe {
background-color: $o-view-background-color;
overflow: hidden;
diff --git a/addons/website/static/src/scss/compatibility/bs3_for_12_0.scss b/addons/website/static/src/scss/compatibility/bs3_for_12_0.scss
index 4f93429aa58..179584b78b9 100644
--- a/addons/website/static/src/scss/compatibility/bs3_for_12_0.scss
+++ b/addons/website/static/src/scss/compatibility/bs3_for_12_0.scss
@@ -9,10 +9,12 @@ $-compat-gray-map: (
'gray': '700',
'gray-light': '600',
'gray-lighter': '200',
-);
+) !default;
@each $old, $new in $-compat-gray-map {
- @include bg-variant(".bg-#{$old}", gray($new));
- @include text-emphasis-variant(".text-#{$old}", gray($new));
+ @if not map-has-key($grays, $old) {
+ @include bg-variant(".bg-#{$old}", gray($new));
+ @include text-emphasis-variant(".text-#{$old}", gray($new));
+ }
}
// Restore media ?
@@ -210,9 +212,13 @@ $-compat-breakpoints: (
.hide {
display: none !important;
}
-.show {
- display: block !important;
-}
+// The 'show' class could be supported if defined here and like that,
+// unfortunately, BS4 still defines a 'show' class for other purposes which
+// conflict with this (tab-pane, fade effects, ...). Adding more complex rules
+// won't solve the problem as they would change css rules priorities.
+// .show {
+// display: block !important;
+// }
.hidden {
display: none !important;
}
diff --git a/addons/website/static/src/scss/website.wysiwyg.scss b/addons/website/static/src/scss/website.wysiwyg.scss
index a4d8cea8729..d3fb73c5591 100644
--- a/addons/website/static/src/scss/website.wysiwyg.scss
+++ b/addons/website/static/src/scss/website.wysiwyg.scss
@@ -1,84 +1,11 @@
+//------------------------------------------------------------------------------
+// Modifications applied on normal website DOM while being in the editor.
+//------------------------------------------------------------------------------
+
body.editor_enable {
overflow: hidden;
padding-top: 0 !important;
- // TOP BAR (EDIT)
- #web_editor-top-edit {
- height: $o-navbar-height;
- background-color: $o-we-color-dark;
- z-index: $zindex-modal - 9;
- position: fixed;
- top: 0px;
- right: 0px;
- width: auto;
- white-space: nowrap;
-
- background-color: rgba(0, 0, 0, 0);
- transition: background-color 400ms $o-we-md-ease 0s;
- font-family: $o-we-font-family;
-
- form.navbar-form {
- height: $o-navbar-height;
- z-index: 1060;
- margin: 0;
- padding: 0;
- position: absolute;
- top: -1px;
- right: 0;
- transition: right 0.4s $o-we-md-ease 0s;
- border-left: 1px solid $o-we-color-divider;
- background-color: inherit;
-
- .btn-group {
- height: 100%;
- }
-
- .btn {
- height: 100%;
- margin: 0;
- padding: 10px;
- line-height: 1.2;
- font-size: 13px;
- font-family: $o-we-font-family;
-
- transition: all 0.3s ease 0s;
- border: none;
- border-radius: 0;
-
- .fa {
- margin-right: $grid-gutter-width/4;
-
- &.fa-times {
- color: $o-we-color-danger;
- }
- }
-
- &.btn-primary {
- @include button-variant($o-brand-odoo, $o-brand-odoo);
- }
-
- &.btn-secondary {
- @include button-variant($o-we-color-dark, $o-we-color-dark);
-
- &:hover {
- background: rgba(black, 0.5);
- }
- }
-
- &:focus,
- &:active,
- &:focus:active {
- outline: none;
- }
- }
-
- .dropdown-menu {
- left: auto;
- right: 0;
- }
- }
- }
-
#wrapwrap {
height: calc(100vh - #{$o-navbar-height});
padding-top: 0px;
@@ -87,555 +14,636 @@ body.editor_enable {
-webkit-user-select: none;
}
}
+}
- // Translations
- .oe_translate_examples li {
- margin: 10px;
- padding: 4px;
- }
+//------------------------------------------------------------------------------
+// Editor components style.
+//------------------------------------------------------------------------------
- html[lang]>body.editor_enable [data-oe-translation-state] {
- background: rgba($o-we-content-to-translate-color, 0.5) !important;
+// TOP BAR (EDIT)
+#web_editor-top-edit {
+ height: $o-navbar-height;
+ background-color: $o-we-color-dark;
+ z-index: $zindex-modal - 9;
+ position: fixed;
+ top: 0px;
+ right: 0px;
+ width: auto;
+ white-space: nowrap;
- &[data-oe-translation-state="translated"] {
- background: rgba($o-we-translated-content-color, 0.5) !important;
+ background-color: rgba(0, 0, 0, 0);
+ transition: background-color 400ms $o-we-md-ease 0s;
+ font-family: $o-we-font-family;
+
+ form.navbar-form {
+ height: $o-navbar-height;
+ z-index: 1060;
+ margin: 0;
+ padding: 0;
+ position: absolute;
+ top: -1px;
+ right: 0;
+ transition: right 0.4s $o-we-md-ease 0s;
+ border-left: 1px solid $o-we-color-divider;
+ background-color: inherit;
+
+ .btn-group {
+ height: 100%;
}
- &.o_dirty {
- background: rgba($o-we-translated-content-color, 0.25) !important;
- }
- }
-
- // NOTE EDITOR
-
- // SNIPPET PANEL
- #oe_snippets {
- @include o-w-preserve-btn;
- @include o-position-absolute(0, auto, 0, -$o-we-sidebar-width);
- width: $o-we-sidebar-width;
-
- border-right: 1px solid $o-we-color-divider;
- transition: left 400ms $o-we-md-ease 0s;
- background-image: linear-gradient(45deg, $o-we-color-normal, darken($o-we-color-normal, 10%));
-
- #snippets_menu {
- color: #999999;
- background: $o-we-color-dark;
+ .btn {
+ height: 100%;
+ margin: 0;
+ padding: 10px;
+ line-height: 1.2;
+ font-size: 13px;
font-family: $o-we-font-family;
- height: $o-navbar-height;
- line-height: $o-navbar-height;
- }
- #o_scroll {
- .o_panel_header {
- color: #999999;
- }
- }
- }
+ transition: all 0.3s ease 0s;
+ border: none;
+ border-radius: 0;
- .oe_snippet {
- background-color: $o-we-color-normal;
+ .fa {
+ margin-right: $grid-gutter-width/4;
- .oe_snippet_thumbnail {
- .oe_snippet_thumbnail_img {
- border: 1px solid $o-we-color-normal;
- }
-
- img.oe_snippet_thumbnail_img {}
-
- .oe_snippet_thumbnail_title {
- border: 1px solid $o-we-color-dark;
- color: $o-we-color-text-light;
- }
-
- &:hover .oe_snippet_thumbnail_title {
- color: $o-we-color-text-lighter;
- }
- }
- }
-
- // SNIPPET OPTIONS
- .colorpicker {
- .o_colorpicker_sections {
- .note-color-palette>div {
- color: $o-we-color-text-light;
- }
- }
-
- .o_colorpicker_section {
- &:after {
- content: "";
- display: table;
- clear: both;
- }
-
- >button {
-
- &:hover,
- &.selected {
- box-shadow: 0px 0px 2px 2px $o-we-color-light;
- }
-
- &[data-event="foreColor"] {
- background-color: $o-we-color-normal;
-
- &:before {
- background-color: rgba(white, 0.3);
- }
- }
- }
- }
-
- .note-palette-title {
- color: $o-we-color-text-light;
- }
-
- .palette-reset {
- .note-color-reset {
- color: desaturate(rgba($o-we-color-danger, 0.6), 40%);
-
- &:hover {
+ &.fa-times {
color: $o-we-color-danger;
}
}
- }
- }
- // DROPZONES
- #wrapwrap .oe_drop_zone {
- &.oe_insert {
- height: $o-we-dropzone-size;
- margin: (-$o-we-dropzone-size/2) 0;
-
- &:after {
- border-bottom: $o-we-dropzone-border;
+ &.btn-primary {
+ @include button-variant($o-brand-odoo, $o-brand-odoo);
}
- &.oe_vertical {
- width: $o-we-dropzone-size;
- margin: 0 (-$o-we-dropzone-size/2);
+ &.btn-secondary {
+ @include button-variant($o-we-color-dark, $o-we-color-dark);
- &:after {
- border-right: $o-we-dropzone-border;
- }
- }
- }
- }
-
- // MANIPULATORS
- #oe_manipulators {
-
- // SNIPPET MANIPULATORS
- .oe_overlay {
-
- // OVERLAY OPTIONS
- >.oe_overlay_options {
- font-family: $o-we-font-family;
-
- >.btn-group {
- .btn {
- height: $o-we-overlay-option-size;
- border: 1px solid $o-we-color-dark;
- line-height: #{$o-we-overlay-option-size - 2px};
-
- color: $o-we-color-text-light;
- background-color: $o-we-color-light;
-
- &:hover {
- background-color: lighten($o-we-color-dark, 10%);
- }
-
- &.oe_snippet_remove {
- background-color: $o-we-color-danger;
- border-color: darken($o-we-color-danger, 20%);
-
- &:hover {
- background-color: darken($o-we-color-danger, 20%);
- }
- }
-
- >.fa {
- color: $o-we-color-text-light;
- }
- }
-
- // CUSTOMIZE MENU BUTTON
- >.oe_options {
- .btn {
- background-color: $o-we-color-dark;
- color: $o-we-color-text-light;
-
- &:hover,
- &:active,
- &:focus {
- background-color: lighten($o-we-color-dark, 5%);
- border-color: lighten($o-we-color-dark, 10%);
- }
-
- &:before,
- &:after {
- background-color: $o-we-color-text-light;
- }
- }
-
- // Open menu
- &.show .btn {
- // background-color: $o-we-color-light;
- }
-
- // CUSTOMIZE MENU
- .dropdown-menu {
- border: 1px solid $o-we-color-dark;
- background-color: $o-we-color-light;
-
- .dropdown-item {
- color: $o-we-color-text-light;
-
- &:hover {
- background-color: fade-out($o-we-color-dark, 0.5);
- }
-
- &.active {
- background-color: fade-out($o-we-color-dark, 0.5);
- }
- }
-
- .dropdown-header {
- color: wheat;
-
- &.o_parent_editor_header {
- color: wheat;
- }
- }
-
- .dropdown-submenu {
- &::before {
- border-left-color: $o-we-color-text-light;
- }
- }
- }
- }
+ &:hover {
+ background: rgba(black, 0.5);
}
}
- &.o_top_cover>.oe_overlay_options {
- top: $o-we-handle-border-width;
- right: $o-we-handle-border-width;
- }
-
- }
- }
-
- // Animations
- @keyframes fadeInDownSmall {
- 0% {
- opacity: 0;
- transform: translate(0, -5px);
- }
-
- 100% {
- opacity: 1;
- transform: translate(0, 0);
- }
- }
-
- @keyframes fadeInOut {
- 0% {
- opacity: 0;
- }
-
- 25% {
- opacity: 1;
- }
-
- 75% {
- opacity: 1;
- }
-
- 100% {
- opacity: 0;
- }
- }
-
- @keyframes inputHighlighter {
- from {
- background: $o-brand-primary;
- }
-
- to {
- width: 0;
- background: transparent;
- }
- }
-
- // MODALS
- body .modal {
-
- // BACKGROUND IMAGE OPTIONS
- .o_bg_img_opt_modal .o_bg_img_opt {
- margin: 15px 0;
-
- .help-control {
- @include o-position-absolute(15px, 15px);
- }
-
- .help {
- margin: 15px 0;
-
- .simulator {
- position: relative;
- float: left;
- margin-right: 15px;
- width: 120px;
- height: 100px;
-
- .bg {
- @include o-position-absolute(0, 0, 0, 0);
- border: 1px dotted #888787;
- background-image: url(/web/image);
- background-position: center center;
- background-size: cover;
- }
-
- .el {
- @include o-position-absolute(10px, 0, 10px, 0);
- border: 1px solid #222;
- }
-
- &.contain_bg {
- .bg {
- @include o-position-absolute(0, 12px, 20px, 12px);
- }
-
- .el {
- @include o-position-absolute(0, 0, 20px, 0);
- }
- }
- }
-
- +* {
- clear: left;
- }
- }
-
- .o_bg_img_opt_cover_edition {
- @include o-we-preview-box;
- padding: 0 25px 25px 25px;
-
- h6 {
- color: white;
- font-weight: bold;
- }
-
- .o_bg_img_opt_object {
- position: relative;
- background: white;
- min-height: 10px;
- @include o-we-preview-content;
-
- >img {
- cursor: crosshair;
- border-top: 1px solid #5A5A5A;
- border-bottom: 1px solid #111;
- }
-
- &:hover .o_focus_point:before {
- opacity: 0.5;
- }
- }
-
- .o_bg_img_opt_ui_info {
- animation: fadeInOut 2s ease forwards;
- @include o-position-absolute($bottom: 1px, $right: 0);
- display: block;
- padding: 3px 13px;
- background-color: rgba(255, 255, 255, 0.8);
- color: #333;
- text-align: center;
- font-weight: bold;
- pointer-events: none;
-
- span {
- font-weight: normal;
- }
-
- .o_x {
- margin-right: 10px
- }
- }
-
- .grid {
- @include o-position-absolute($top: 33.33%, $left: 0);
- display: block;
- width: 100%;
- height: 1px;
- background: fade-out(lighten($o-brand-primary, 30%), 0.5);
- pointer-events: none;
-
- &.grid-2 {
- top: 66.66%
- }
-
- &.grid-3 {
- top: 0;
- left: 33.33%;
- width: 1px;
- height: 100%
- }
-
- &.grid-4 {
- top: 0;
- left: 66.66%;
- width: 1px;
- height: 100%
- }
- }
-
- .o_focus_point {
- @include o-position-absolute($top: 0, $left: 0);
- width: 30px;
- height: 30px;
- border: 2px solid white;
- border-radius: 100%;
- margin-top: -15px;
- margin-left: -15px;
- box-shadow: 0 0 1px #333;
- pointer-events: none;
-
- &.o_with_transition {
- transition: all 0.2s ease 0s;
- }
-
- &:before {
- pointer-events: none;
- content: "";
- display: block;
- width: 100px;
- height: 100px;
- margin-top: -37px;
- margin-left: -37px;
- border: 1px solid #EAEAEA;
- background: rgba(247, 76, 76, 0);
- border-radius: 100em;
- opacity: 1;
- box-shadow: 0 0 0 100em rgba(0, 0, 0, 0.33);
- transition: opacity 0.2s ease 0s;
- }
- }
- }
- }
- }
-
- .wysiwyg_multizone.note-editor {
- &>.note-toolbar-wrapper .note-toolbar {
- height: $o-navbar-height;
-
- .btn {
- height: $o-navbar-height !important; // force this because of themes
- }
- }
-
- &>.note-statusbar {
- display: none;
- }
-
- &>.note-editing-area {
- overflow: visible;
- }
- }
-
- .wysiwyg_multizone.note-popover.popover {
- border-width: 0 1px;
-
- .popover-content .btn {
- height: $o-navbar-height;
- }
- }
-
- .wysiwyg_multizone.note-popover,
- .wysiwyg_multizone.note-editor>.note-toolbar-wrapper .note-toolbar {
- // force this because of themes
- background-color: $o-we-color-dark !important;
-
- .btn {
- background-color: $o-we-color-dark !important;
- color: $o-we-color-text-normal !important;
- border: none !important;
- padding: 0.0625rem 0.3125rem !important;
-
- >.fa {
- color: $o-we-color-text-normal !important;
- }
-
- &:hover {
- color: $o-we-color-text-light !important;
-
- >.fa {
- color: $o-we-color-text-light !important;
- }
- }
-
- &.active {
- background-color: $o-we-color-dark !important;
- color: $o-we-color-text-light !important;
-
- >.fa {
- color: $o-we-color-text-light !important;
- }
- }
- }
-
- >.btn-group {
- margin-top: 0 !important;
-
- &.show,
- .btn-group.show {
- .dropdown-toggle {
- border-top: 2px solid white !important;
- background-color: $o-we-color-darker !important;
- color: white !important;
- box-shadow: none !important;
-
- >.fa,
- >span {
- color: white !important;
- }
- }
+ &:focus,
+ &:active,
+ &:focus:active {
+ outline: none;
}
}
.dropdown-menu {
- margin: 0 !important;
- border-radius: 0 !important;
- background-color: $o-we-color-darker !important;
- color: $o-we-color-text-normal !important;
- white-space: normal !important;
+ left: auto;
+ right: 0;
+ }
+ }
+}
- .dropdown-item {
- width: 100% !important;
- color: $o-we-color-text-light !important;
+// Translations
+.oe_translate_examples li {
+ margin: 10px;
+ padding: 4px;
+}
- >.fa {
- color: $o-we-color-text-light !important;
+html[lang] > body.editor_enable [data-oe-translation-state] {
+ background: rgba($o-we-content-to-translate-color, 0.5) !important;
+
+ &[data-oe-translation-state="translated"] {
+ background: rgba($o-we-translated-content-color, 0.5) !important;
+ }
+
+ &.o_dirty {
+ background: rgba($o-we-translated-content-color, 0.25) !important;
+ }
+}
+
+// NOTE EDITOR
+
+// SNIPPET PANEL
+#oe_snippets {
+ @include o-w-preserve-btn;
+ @include o-position-absolute(0, auto, 0, -$o-we-sidebar-width);
+ width: $o-we-sidebar-width;
+
+ border-right: 1px solid $o-we-color-divider;
+ transition: left 400ms $o-we-md-ease 0s;
+ background-image: linear-gradient(45deg, $o-we-color-normal, darken($o-we-color-normal, 10%));
+
+ #snippets_menu {
+ color: #999999;
+ background: $o-we-color-dark;
+ font-family: $o-we-font-family;
+ height: $o-navbar-height;
+ line-height: $o-navbar-height;
+ }
+
+ #o_scroll {
+ .o_panel_header {
+ color: #999999;
+ }
+ }
+}
+
+.oe_snippet {
+ background-color: $o-we-color-normal;
+
+ .oe_snippet_thumbnail {
+ .oe_snippet_thumbnail_img {
+ border: 1px solid $o-we-color-normal;
+ }
+
+ img.oe_snippet_thumbnail_img {}
+
+ .oe_snippet_thumbnail_title {
+ border: 1px solid $o-we-color-dark;
+ color: $o-we-color-text-light;
+ }
+
+ &:hover .oe_snippet_thumbnail_title {
+ color: $o-we-color-text-lighter;
+ }
+ }
+}
+
+// SNIPPET OPTIONS
+.colorpicker {
+ .o_colorpicker_sections {
+ .note-color-palette>div {
+ color: $o-we-color-text-light;
+ }
+ }
+
+ .o_colorpicker_section {
+ &:after {
+ content: "";
+ display: table;
+ clear: both;
+ }
+
+ >button {
+
+ &:hover,
+ &.selected {
+ box-shadow: 0px 0px 2px 2px $o-we-color-light;
+ }
+
+ &[data-event="foreColor"] {
+ background-color: $o-we-color-normal;
+
+ &:before {
+ background-color: rgba(white, 0.3);
}
+ }
+ }
+ }
- &:hover {
- background-color: $o-we-tab-active-bg !important;
- color: white !important;
+ .note-palette-title {
+ color: $o-we-color-text-light;
+ }
+
+ .palette-reset {
+ .note-color-reset {
+ color: desaturate(rgba($o-we-color-danger, 0.6), 40%);
+
+ &:hover {
+ color: $o-we-color-danger;
+ }
+ }
+ }
+}
+
+// DROPZONES
+#wrapwrap .oe_drop_zone {
+ &.oe_insert {
+ height: $o-we-dropzone-size;
+ margin: (-$o-we-dropzone-size/2) 0;
+
+ &:after {
+ border-bottom: $o-we-dropzone-border;
+ }
+
+ &.oe_vertical {
+ width: $o-we-dropzone-size;
+ margin: 0 (-$o-we-dropzone-size/2);
+
+ &:after {
+ border-right: $o-we-dropzone-border;
+ }
+ }
+ }
+}
+
+// MANIPULATORS
+#oe_manipulators {
+
+ // SNIPPET MANIPULATORS
+ .oe_overlay {
+
+ // OVERLAY OPTIONS
+ >.oe_overlay_options {
+ font-family: $o-we-font-family;
+
+ >.btn-group {
+ .btn {
+ height: $o-we-overlay-option-size;
+ border: 1px solid $o-we-color-dark;
+ line-height: #{$o-we-overlay-option-size - 2px};
+
+ color: $o-we-color-text-light;
+ background-color: $o-we-color-light;
+
+ &:hover {
+ background-color: lighten($o-we-color-dark, 10%);
+ }
+
+ &.oe_snippet_remove {
+ background-color: $o-we-color-danger;
+ border-color: darken($o-we-color-danger, 20%);
+
+ &:hover {
+ background-color: darken($o-we-color-danger, 20%);
+ }
+ }
>.fa {
- color: white !important;
+ color: $o-we-color-text-light;
+ }
+ }
+
+ // CUSTOMIZE MENU BUTTON
+ >.oe_options {
+ .btn {
+ background-color: $o-we-color-dark;
+ color: $o-we-color-text-light;
+
+ &:hover,
+ &:active,
+ &:focus {
+ background-color: lighten($o-we-color-dark, 5%);
+ border-color: lighten($o-we-color-dark, 10%);
+ }
+
+ &:before,
+ &:after {
+ background-color: $o-we-color-text-light;
+ }
+ }
+
+ // Open menu
+ &.show .btn {
+ // background-color: $o-we-color-light;
+ }
+
+ // CUSTOMIZE MENU
+ .dropdown-menu {
+ border: 1px solid $o-we-color-dark;
+ background-color: $o-we-color-light;
+
+ .dropdown-item {
+ color: $o-we-color-text-light;
+
+ &:hover {
+ background-color: fade-out($o-we-color-dark, 0.5);
+ }
+
+ &.active {
+ background-color: fade-out($o-we-color-dark, 0.5);
+ }
+ }
+
+ .dropdown-header {
+ color: wheat;
+
+ &.o_parent_editor_header {
+ color: wheat;
+ }
+ }
+
+ .dropdown-submenu {
+ &::before {
+ border-left-color: $o-we-color-text-light;
+ }
+ }
}
}
}
}
- .note-para .dropdown-menu {
- min-width: 147px !important;
+ &.o_top_cover>.oe_overlay_options {
+ top: $o-we-handle-border-width;
+ right: $o-we-handle-border-width;
}
- .note-style .dropdown-item * {
- color: $o-we-color-text-light !important;
+ }
+}
+
+// Animations
+@keyframes fadeInDownSmall {
+ 0% {
+ opacity: 0;
+ transform: translate(0, -5px);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: translate(0, 0);
+ }
+}
+
+@keyframes fadeInOut {
+ 0% {
+ opacity: 0;
+ }
+
+ 25% {
+ opacity: 1;
+ }
+
+ 75% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+
+@keyframes inputHighlighter {
+ from {
+ background: $o-brand-primary;
+ }
+
+ to {
+ width: 0;
+ background: transparent;
+ }
+}
+
+// MODALS
+body .modal {
+
+ // BACKGROUND IMAGE OPTIONS
+ .o_bg_img_opt_modal .o_bg_img_opt {
+ margin: 15px 0;
+
+ .help-control {
+ @include o-position-absolute(15px, 15px);
+ }
+
+ .help {
+ margin: 15px 0;
+
+ .simulator {
+ position: relative;
+ float: left;
+ margin-right: 15px;
+ width: 120px;
+ height: 100px;
+
+ .bg {
+ @include o-position-absolute(0, 0, 0, 0);
+ border: 1px dotted #888787;
+ background-image: url(/web/image);
+ background-position: center center;
+ background-size: cover;
+ }
+
+ .el {
+ @include o-position-absolute(10px, 0, 10px, 0);
+ border: 1px solid #222;
+ }
+
+ &.contain_bg {
+ .bg {
+ @include o-position-absolute(0, 12px, 20px, 12px);
+ }
+
+ .el {
+ @include o-position-absolute(0, 0, 20px, 0);
+ }
+ }
+ }
+
+ +* {
+ clear: left;
+ }
+ }
+
+ .o_bg_img_opt_cover_edition {
+ @include o-we-preview-box;
+ padding: 0 25px 25px 25px;
+
+ h6 {
+ color: white;
+ font-weight: bold;
+ }
+
+ .o_bg_img_opt_object {
+ position: relative;
+ background: white;
+ min-height: 10px;
+ @include o-we-preview-content;
+
+ >img {
+ cursor: crosshair;
+ border-top: 1px solid #5A5A5A;
+ border-bottom: 1px solid #111;
+ }
+
+ &:hover .o_focus_point:before {
+ opacity: 0.5;
+ }
+ }
+
+ .o_bg_img_opt_ui_info {
+ animation: fadeInOut 2s ease forwards;
+ @include o-position-absolute($bottom: 1px, $right: 0);
+ display: block;
+ padding: 3px 13px;
+ background-color: rgba(255, 255, 255, 0.8);
+ color: #333;
+ text-align: center;
+ font-weight: bold;
+ pointer-events: none;
+
+ span {
+ font-weight: normal;
+ }
+
+ .o_x {
+ margin-right: 10px
+ }
+ }
+
+ .grid {
+ @include o-position-absolute($top: 33.33%, $left: 0);
+ display: block;
+ width: 100%;
+ height: 1px;
+ background: fade-out(lighten($o-brand-primary, 30%), 0.5);
+ pointer-events: none;
+
+ &.grid-2 {
+ top: 66.66%
+ }
+
+ &.grid-3 {
+ top: 0;
+ left: 33.33%;
+ width: 1px;
+ height: 100%
+ }
+
+ &.grid-4 {
+ top: 0;
+ left: 66.66%;
+ width: 1px;
+ height: 100%
+ }
+ }
+
+ .o_focus_point {
+ @include o-position-absolute($top: 0, $left: 0);
+ width: 30px;
+ height: 30px;
+ border: 2px solid white;
+ border-radius: 100%;
+ margin-top: -15px;
+ margin-left: -15px;
+ box-shadow: 0 0 1px #333;
+ pointer-events: none;
+
+ &.o_with_transition {
+ transition: all 0.2s ease 0s;
+ }
+
+ &:before {
+ pointer-events: none;
+ content: "";
+ display: block;
+ width: 100px;
+ height: 100px;
+ margin-top: -37px;
+ margin-left: -37px;
+ border: 1px solid #EAEAEA;
+ background: rgba(247, 76, 76, 0);
+ border-radius: 100em;
+ opacity: 1;
+ box-shadow: 0 0 0 100em rgba(0, 0, 0, 0.33);
+ transition: opacity 0.2s ease 0s;
+ }
+ }
}
}
}
+
+.wysiwyg_multizone.note-editor {
+ &>.note-toolbar-wrapper .note-toolbar {
+ height: $o-navbar-height;
+
+ .btn {
+ height: $o-navbar-height !important; // force this because of themes
+ }
+ }
+
+ &>.note-statusbar {
+ display: none;
+ }
+
+ &>.note-editing-area {
+ overflow: visible;
+ }
+}
+
+.wysiwyg_multizone.note-popover.popover {
+ border-width: 0 1px;
+
+ .popover-content .btn {
+ height: $o-navbar-height;
+ }
+}
+
+.wysiwyg_multizone.note-popover,
+.wysiwyg_multizone.note-editor>.note-toolbar-wrapper .note-toolbar {
+ // force this because of themes
+ background-color: $o-we-color-dark !important;
+
+ .btn {
+ background-color: $o-we-color-dark !important;
+ color: $o-we-color-text-normal !important;
+ border: none !important;
+ padding: 0.0625rem 0.3125rem !important;
+
+ >.fa {
+ color: $o-we-color-text-normal !important;
+ }
+
+ &:hover {
+ color: $o-we-color-text-light !important;
+
+ >.fa {
+ color: $o-we-color-text-light !important;
+ }
+ }
+
+ &.active {
+ background-color: $o-we-color-dark !important;
+ color: $o-we-color-text-light !important;
+
+ >.fa {
+ color: $o-we-color-text-light !important;
+ }
+ }
+ }
+
+ >.btn-group {
+ margin-top: 0 !important;
+
+ &.show,
+ .btn-group.show {
+ .dropdown-toggle {
+ border-top: 2px solid white !important;
+ background-color: $o-we-color-darker !important;
+ color: white !important;
+ box-shadow: none !important;
+
+ >.fa,
+ >span {
+ color: white !important;
+ }
+ }
+ }
+ }
+
+ .dropdown-menu {
+ margin: 0 !important;
+ border-radius: 0 !important;
+ background-color: $o-we-color-darker !important;
+ color: $o-we-color-text-normal !important;
+ white-space: normal !important;
+
+ .dropdown-item {
+ width: 100% !important;
+ color: $o-we-color-text-light !important;
+
+ >.fa {
+ color: $o-we-color-text-light !important;
+ }
+
+ &:hover {
+ background-color: $o-we-tab-active-bg !important;
+ color: white !important;
+
+ >.fa {
+ color: white !important;
+ }
+ }
+ }
+ }
+
+ .note-para .dropdown-menu {
+ min-width: 147px !important;
+ }
+
+ .note-style .dropdown-item * {
+ color: $o-we-color-text-light !important;
+ }
+}
diff --git a/addons/website_mass_mailing/views/snippets_templates.xml b/addons/website_mass_mailing/views/snippets_templates.xml
index f0fee84ad53..7bc1c4ac2d6 100644
--- a/addons/website_mass_mailing/views/snippets_templates.xml
+++ b/addons/website_mass_mailing/views/snippets_templates.xml
@@ -3,6 +3,8 @@
+
+
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index 0429f21de5f..112b209bfcd 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -608,9 +608,12 @@ class WebsiteSale(http.Controller):
new_values['customer'] = True
new_values['team_id'] = request.website.salesteam_id and request.website.salesteam_id.id
new_values['user_id'] = request.website.salesperson_id and request.website.salesperson_id.id
- new_values['company_id'] = request.website.company_id.id
new_values['website_id'] = request.website.id
+ if order.partner_id.id == request.website.user_id.sudo().partner_id.id:
+ # If the partner is public, we assign the company of the website
+ new_values['company_id'] = request.website.company_id.id
+
lang = request.lang if request.lang in request.website.mapped('language_ids.code') else None
if lang:
new_values['lang'] = lang
diff --git a/addons/website_sale_comparison/static/src/js/website_sale_comparison.js b/addons/website_sale_comparison/static/src/js/website_sale_comparison.js
index 306ee1a44f9..48eb44494c4 100644
--- a/addons/website_sale_comparison/static/src/js/website_sale_comparison.js
+++ b/addons/website_sale_comparison/static/src/js/website_sale_comparison.js
@@ -1,6 +1,7 @@
odoo.define('website_sale_comparison.comparison', function (require) {
'use strict';
+var concurrency = require('web.concurrency');
var core = require('web.core');
var publicWidget = require('web.public.widget');
var utils = require('web.utils');
@@ -30,6 +31,7 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
this.product_data = {};
this.comparelist_product_ids = JSON.parse(utils.get_cookie('comparelist_product_ids') || '[]');
this.product_compare_limit = 4;
+ this.guard = new concurrency.Mutex();
},
/**
* @override
@@ -154,6 +156,9 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
* @private
*/
_addNewProducts: function (product_id) {
+ this.guard.exec(this._addNewProductsImpl.bind(this, product_id));
+ },
+ _addNewProductsImpl: function (product_id) {
var self = this;
$('.o_product_feature_panel').addClass('d-md-block');
if (!_.contains(self.comparelist_product_ids, product_id)) {
@@ -161,8 +166,9 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
if (_.has(self.product_data, product_id)){
self._updateContent();
} else {
- self._loadProducts([product_id]).then(function () {
+ return self._loadProducts([product_id]).then(function () {
self._updateContent();
+ self._updateCookie();
});
}
}
@@ -189,6 +195,9 @@ var ProductComparison = publicWidget.Widget.extend(VariantMixin, {
* @private
*/
_removeFromComparelist: function (e) {
+ this.guard.exec(this._removeFromComparelistImpl.bind(this, e));
+ },
+ _removeFromComparelistImpl: function (e) {
this.comparelist_product_ids = _.without(this.comparelist_product_ids, $(e.currentTarget).data('product_product_id'));
$(e.currentTarget).parents('.o_product_row').remove();
this._updateCookie();
diff --git a/addons/website_slides/controllers/main.py b/addons/website_slides/controllers/main.py
index f4a02f8937a..dfcc4e4e7de 100644
--- a/addons/website_slides/controllers/main.py
+++ b/addons/website_slides/controllers/main.py
@@ -379,6 +379,16 @@ class WebsiteSlides(WebsiteProfile):
step=self._slides_per_page, url_args=pager_args,
scope=page_count if page_count < self._pager_max_pages else self._pager_max_pages)
+ query_string = None
+ if category:
+ query_string = "?search_category=%s" % category.id
+ elif tag:
+ query_string = "?search_tag=%s" % tag.id
+ elif slide_type:
+ query_string = "?search_slide_type=%s" % slide_type
+ elif uncategorized:
+ query_string = "?search_uncategorized=1"
+
values = {
'channel': channel,
'active_tab': kw.get('active_tab', 'home'),
@@ -387,6 +397,7 @@ class WebsiteSlides(WebsiteProfile):
'search_tag': tag,
'search_slide_type': slide_type,
'search_uncategorized': uncategorized,
+ 'query_string': query_string,
'slide_types': slide_types,
'sorting': actual_sorting,
'search': search,
@@ -506,7 +517,7 @@ class WebsiteSlides(WebsiteProfile):
@http.route('''/slides/slide/''', type='http', auth="public", website=True)
def slide_view(self, slide, **kwargs):
- if not slide.channel_id.can_access_from_current_website():
+ if not slide.channel_id.can_access_from_current_website() or not slide.active:
raise werkzeug.exceptions.NotFound()
self._set_viewed_slide(slide)
@@ -517,6 +528,15 @@ class WebsiteSlides(WebsiteProfile):
# sidebar: update with user channel progress
values['channel_progress'] = self._get_channel_progress(slide.channel_id, include_quiz=True)
+ # Allows to have breadcrumb for the previously used filter
+ values.update({
+ 'search_category': slide.category_id.id if kwargs.get('search_category') else None,
+ 'search_tag': request.env['slide.tag'].browse(int(kwargs.get('search_tag'))) if kwargs.get('search_tag') else None,
+ 'slide_types': dict(request.env['slide.slide']._fields['slide_type']._description_selection(request.env)) if kwargs.get('search_slide_type') else None,
+ 'search_slide_type': kwargs.get('search_slide_type'),
+ 'search_uncategorized': kwargs.get('search_uncategorized')
+ })
+
if kwargs.get('fullscreen') == '1':
return request.render("website_slides.slide_fullscreen", values)
return request.render("website_slides.slide_main", values)
diff --git a/addons/website_slides/models/slide_slide.py b/addons/website_slides/models/slide_slide.py
index b0567dcc627..851ec30fd4d 100644
--- a/addons/website_slides/models/slide_slide.py
+++ b/addons/website_slides/models/slide_slide.py
@@ -135,7 +135,7 @@ class Slide(models.Model):
('video', 'Video'),
('quiz', "Quiz")],
string='Type', required=True,
- default='document', readonly=True,
+ default='document',
help="The document type will be set automatically based on the document URL and properties (e.g. height and width for presentation and document).")
index_content = fields.Text('Transcript')
datas = fields.Binary('Content', attachment=True)
@@ -553,11 +553,16 @@ class Slide(models.Model):
return result
def _find_document_data_from_url(self, url):
- expr = re.compile(r'^.*((youtu.be/)|(v/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#\&\?]*).*')
- arg = expr.match(url)
- document_id = arg and arg.group(7) or False
- if document_id:
- return ('youtube', document_id)
+ url_obj = urls.url_parse(url)
+ if url_obj.ascii_host == 'youtu.be':
+ return ('youtube', url_obj.path[1:] if url_obj.path else False)
+ elif url_obj.ascii_host in ('youtube.com', 'www.youtube.com', 'm.youtube.com'):
+ v_query_value = url_obj.decode_query().get('v')
+ if v_query_value:
+ return ('youtube', v_query_value)
+ split_path = url_obj.path.split('/')
+ if len(split_path) >= 3 and split_path[1] in ('v', 'embed'):
+ return ('youtube', split_path[2])
expr = re.compile(r'(^https:\/\/docs.google.com|^https:\/\/drive.google.com).*\/d\/([^\/]*)')
arg = expr.match(url)
diff --git a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js
index 6fa054b6647..8a648a3cb3b 100644
--- a/addons/website_slides/static/src/js/slides_course_fullscreen_player.js
+++ b/addons/website_slides/static/src/js/slides_course_fullscreen_player.js
@@ -5,6 +5,7 @@ odoo.define('website_slides.fullscreen', function (require) {
var publicWidget = require('web.public.widget');
var core = require('web.core');
+ var config = require('web.config');
var QWeb = core.qweb;
var session = require('web.session');
@@ -321,6 +322,9 @@ odoo.define('website_slides.fullscreen', function (require) {
start: function (){
var self = this;
this.on('change:slide', this, this._onChangeSlide);
+ if (config.device.size_class > config.device.SIZES.MD) {
+ this._toggleSidebar();
+ }
return this._super.apply(this, arguments).then(function () {
self._onChangeSlide(); // trigger manually once DOM ready, since slide content is not rendered server side
});
@@ -540,12 +544,20 @@ odoo.define('website_slides.fullscreen', function (require) {
this.sidebar.goNext();
},
/**
- * Show or Hide the sidebar
+ * Called when the sidebar toggle is clicked -> toggles the sidebar visibility.
*
* @private
*/
_onClickToggleSidebar: function (ev){
ev.preventDefault();
+ this._toggleSidebar();
+ },
+ /**
+ * Toggles sidebar visibility.
+ *
+ * @private
+ */
+ _toggleSidebar: function () {
this.$('.o_wslides_fs_sidebar').toggleClass('o_wslides_fs_sidebar_hidden');
this.$('.o_wslides_fs_toggle_sidebar').toggleClass('active');
},
diff --git a/addons/website_slides/static/src/js/slides_share.js b/addons/website_slides/static/src/js/slides_share.js
index 3f921a2229a..d89cdfcb4bf 100644
--- a/addons/website_slides/static/src/js/slides_share.js
+++ b/addons/website_slides/static/src/js/slides_share.js
@@ -49,48 +49,12 @@ publicWidget.registry.websiteSlidesShare = publicWidget.Widget.extend({
* @param {Object} parent
*/
start: function (parent) {
- var self = this;
var defs = [this._super.apply(this, arguments)];
defs.push(new ShareMail(this).attachTo($('.oe_slide_js_share_email')));
- if ($('div#statistic').length) {
- this.slideURL = $('div#statistic').attr('slide-url');
- this.socialURLs = {
- linkedin: 'https://www.linkedin.com/countserv/count/share?url=',
- twitter: 'https://cdn.api.twitter.com/1/urls/count.json?url=',
- facebook: 'https://graph.facebook.com/?id=',
- };
- }
-
- _.each(this.socialURLs, function (value, key) {
- self._updateStatistics(key, self.slideURL);
- });
-
return Promise.all(defs);
},
- //--------------------------------------------------------------------------
- // Private
- //--------------------------------------------------------------------------
-
- /**
- * @private
- * @param {string} socialSite
- * @param {string} slide_url
- */
- _updateStatistics: function (socialSite, slideURL) {
- var self = this;
- $.ajax({
- url: self.socialURLs[socialSite] + slideURL,
- dataType: 'jsonp',
- success: function (data) {
- var shareCount = (socialSite === 'facebook' ? data.shares : data.count) || 0;
- $('#' + socialSite + '-badge').text(shareCount);
- $('#total-share').text(parseInt($('#total-share').text()) + parseInt($('#' + socialSite + '-badge').text()));
- },
- });
- },
-
//--------------------------------------------------------------------------
// Handlers
//--------------------------------------------------------------------------
@@ -100,14 +64,11 @@ publicWidget.registry.websiteSlidesShare = publicWidget.Widget.extend({
* @param {Object} ev
*/
_onSlidesSocialShare: function (ev) {
- var self = this;
ev.preventDefault();
- var key = $(ev.currentTarget).attr('social-key');
var popUpURL = $(ev.currentTarget).attr('href');
var popUp = window.open(popUpURL, 'Share Dialog', 'width=626,height=436');
$(window).on('focus', function () {
if (popUp.closed) {
- self._updateStatistics(key, self.slide_url);
$(window).off('focus');
}
});
diff --git a/addons/website_slides/tests/__init__.py b/addons/website_slides/tests/__init__.py
index 69bfc7de9e2..466b2b82674 100644
--- a/addons/website_slides/tests/__init__.py
+++ b/addons/website_slides/tests/__init__.py
@@ -5,3 +5,5 @@ from . import common
from . import test_security
from . import test_statistics
from . import test_karma
+
+from . import test_from_url
\ No newline at end of file
diff --git a/addons/website_slides/tests/test_from_url.py b/addons/website_slides/tests/test_from_url.py
new file mode 100644
index 00000000000..dc9a1d6768e
--- /dev/null
+++ b/addons/website_slides/tests/test_from_url.py
@@ -0,0 +1,30 @@
+import odoo.tests
+
+
+class TestFromURL(odoo.tests.TransactionCase):
+ def test_youtube_urls(self):
+ urls = {
+ 'W0JQcpGLSFw': [
+ 'https://youtu.be/W0JQcpGLSFw',
+ 'https://www.youtube.com/watch?v=W0JQcpGLSFw',
+ 'https://www.youtube.com/watch?v=W0JQcpGLSFw&list=PL1-aSABtP6ACZuppkBqXFgzpNb2nVctZx',
+ ],
+ 'vmhB-pt7EfA': [ # id starts with v, it is important
+ 'https://youtu.be/vmhB-pt7EfA',
+ 'https://www.youtube.com/watch?feature=youtu.be&v=vmhB-pt7EfA',
+ 'https://www.youtube.com/watch?v=vmhB-pt7EfA&list=PL1-aSABtP6ACZuppkBqXFgzpNb2nVctZx&index=7',
+ ],
+ 'hlhLv0GN1hA': [
+ 'https://www.youtube.com/v/hlhLv0GN1hA',
+ 'https://www.youtube.com/embed/hlhLv0GN1hA',
+ 'https://m.youtube.com/watch?v=hlhLv0GN1hA'
+ ],
+ }
+
+ model = self.env['slide.slide']
+ for id, urls in urls.items():
+ for url in urls:
+ with self.subTest(url=url, id=id):
+ document = model._find_document_data_from_url(url)
+ self.assertEqual(document[0], 'youtube')
+ self.assertEqual(document[1], id)
diff --git a/addons/website_slides/views/slide_slide_views.xml b/addons/website_slides/views/slide_slide_views.xml
index b2dd1585ff0..1a2e45d5119 100644
--- a/addons/website_slides/views/slide_slide_views.xml
+++ b/addons/website_slides/views/slide_slide_views.xml
@@ -71,13 +71,13 @@
-
+
+ attrs="{'required': [('slide_type', 'in', ('video'))], 'invisible': [('slide_type', 'not in', ('document', 'presentation', 'video'))]}" />
-
@@ -93,6 +93,7 @@
+
diff --git a/addons/website_slides/views/website_slides_templates_course.xml b/addons/website_slides/views/website_slides_templates_course.xml
index 5be3e4fb380..fd1e2726270 100644
--- a/addons/website_slides/views/website_slides_templates_course.xml
+++ b/addons/website_slides/views/website_slides_templates_course.xml
@@ -7,30 +7,34 @@
|