mirror of https://github.com/OCA/web.git
[ADD] web_widget_slickroom: Create module
* Extend web_widget_slick widget to open web_widget_darkroom modal and update selected carousel image on close * Override _slickRender method from web_widget_slick to add data attribute w/ record id * Change cursor to pointer on slickroom images * Disable features in read-only mode * Keep default cursor * Prevent modal from opening * Add tests [IMP] web_widget_slickroom: Make requested changes * Use jquery .data instead of .attr for image record IDs * Add explicit img selector when finding images by record ID * Remove svg version of iconpull/853/head
parent
9298796eef
commit
6cbd754f51
|
@ -0,0 +1,94 @@
|
|||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
||||
:target: http://www.gnu.org/licenses/agpl
|
||||
:alt: License: AGPL-3
|
||||
|
||||
===================================================
|
||||
Slick Carousel Widget with DarkroomJS Image Editing
|
||||
===================================================
|
||||
|
||||
This module extends the `Slick`_ carousel widget provided by
|
||||
`web_widget_slick` to include the `DarkroomJS`_ image editing features provided
|
||||
by `web_widget_darkroom`.
|
||||
|
||||
.. _Slick: http://kenwheeler.github.io/slick
|
||||
.. _DarkroomJS: https://github.com/MattKetmo/darkroomjs
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
To create a Slick carousel widget with DarkroomJS support, follow the
|
||||
usage instructions in the `web_widget_slick` documentation, but replace
|
||||
"one2many_slick_images" with "slickroom" in the field definition, as shown
|
||||
here::
|
||||
|
||||
<field name="image_ids" widget="slickroom" options="{}"/>
|
||||
|
||||
To edit an image in a carousel, simply click the Edit button in the form view,
|
||||
then click on the image you wish to edit to open a DarkroomJS modal. Edit the
|
||||
image as desired according to the `web_widget_darkroom` documentation, and
|
||||
click Save to save the changes and update the carousel.
|
||||
|
||||
Example Module
|
||||
--------------
|
||||
|
||||
An example implementation, for instructional purposes as well as convenient
|
||||
functional testing, is provided in the `web_widget_slick_example` module.
|
||||
|
||||
* Install `web_widget_slick_example`.
|
||||
* Activate Developer Mode.
|
||||
* Go to Settings / Technical / Slick, and open the record.
|
||||
* The standard Slick carousel widget (from `web_widget_slick`) is displayed on
|
||||
top, followed by the slickroom widget with DarkroomJS support. Click the Edit
|
||||
button in the form view to try out the DarkroomJS features.
|
||||
|
||||
To try out different Slick settings:
|
||||
|
||||
* Go to Settings/User Interface/Views and search for 'slick.example.view.form'.
|
||||
* Open the form view record.
|
||||
* Click the Edit button.
|
||||
* In the Architecture editor, find `options="{'slidesToShow': 2}`, and add
|
||||
any desired settings (separated by commas) inside the curly braces.
|
||||
* Save the changes and browse to the widget, as described above, to see the
|
||||
widget with the new settings in effect.
|
||||
|
||||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||
:alt: Try me on Runbot
|
||||
:target: https://runbot.odoo-community.org/runbot/162/10.0
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues
|
||||
<https://github.com/OCA/web/issues>`_. In case of trouble, please
|
||||
check there if your issue has already been reported. If you spotted it first,
|
||||
help us smash it by providing detailed and welcomed feedback.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Images
|
||||
------
|
||||
|
||||
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
* Brent Hughes <brent.hughes@laslabs.com>
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Maintainer
|
||||
----------
|
||||
|
||||
.. image:: https://odoo-community.org/logo.png
|
||||
:alt: Odoo Community Association
|
||||
:target: https://odoo-community.org
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
To contribute to this module, please visit https://odoo-community.org.
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 LasLabs Inc.
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
|
@ -0,0 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 LasLabs Inc.
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
{
|
||||
"name": "Slick Carousel Widget with DarkroomJS Image Editing",
|
||||
"summary": "Provides Slick Carousel Widget with DarkroomJS image editing",
|
||||
"version": "10.0.1.0.0",
|
||||
"category": "Web",
|
||||
"website": "https://laslabs.com/",
|
||||
"author": "LasLabs, Odoo Community Association (OCA)",
|
||||
"license": "LGPL-3",
|
||||
"application": False,
|
||||
"installable": True,
|
||||
"depends": [
|
||||
"web_widget_darkroom",
|
||||
"web_widget_slick",
|
||||
],
|
||||
"data": [
|
||||
"templates/assets.xml",
|
||||
],
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 9.2 KiB |
|
@ -0,0 +1,67 @@
|
|||
/* Copyright 2017 LasLabs Inc.
|
||||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */
|
||||
|
||||
odoo.define('web_widget_slickroom', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var FieldSlickImages = require('web_widget_slick').FieldSlickImages;
|
||||
|
||||
var FieldSlickroomImages = FieldSlickImages.extend({
|
||||
|
||||
widget_class: FieldSlickImages.prototype.widget_class + ' o_slickroom',
|
||||
events: _.extend({}, FieldSlickImages.prototype.events, {
|
||||
'click img': '_openModal'
|
||||
}),
|
||||
|
||||
_openModal: function (ev) {
|
||||
if (this.get("effective_readonly")) {
|
||||
return;
|
||||
}
|
||||
|
||||
var recordId = $(ev.target).data('record-id');
|
||||
var modalAction = {
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'darkroom.modal',
|
||||
name: 'Darkroom',
|
||||
views: [[false, 'form']],
|
||||
target: 'new',
|
||||
context: {
|
||||
active_field: this.options.fieldName,
|
||||
active_model: this.options.modelName,
|
||||
active_record_id: recordId
|
||||
}
|
||||
};
|
||||
|
||||
this.do_action(modalAction, {
|
||||
on_close: $.proxy(this._updateImage, this, recordId)
|
||||
});
|
||||
},
|
||||
|
||||
_slickRender: function(baseUrl, id) {
|
||||
this._super(baseUrl, id);
|
||||
this.$slick.find('img:last').data('record-id', id);
|
||||
},
|
||||
|
||||
_updateImage: function (recordId) {
|
||||
// SlickJS creates 'clones', so multiple elements need updated src
|
||||
var $imgs = this.$slick.find('img').filter(function () {
|
||||
return $(this).data('record-id') === recordId;
|
||||
});
|
||||
var $loaded = $imgs.filter('[src]');
|
||||
var $notLoaded = $imgs.filter('[data-lazy]');
|
||||
|
||||
var imgUrl = $loaded.first().attr('src');
|
||||
var imgUrlNew = imgUrl + "?unique=" + new Date().getTime();
|
||||
|
||||
$loaded.attr('src', imgUrlNew);
|
||||
$notLoaded.attr('data-lazy', imgUrlNew);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
core.form_widget_registry.add("slickroom", FieldSlickroomImages);
|
||||
|
||||
return {FieldSlickroomImages: FieldSlickroomImages};
|
||||
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
/* Copyright 2017 LasLabs Inc.
|
||||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */
|
||||
|
||||
.o_form_editable .o_slickroom img {
|
||||
cursor: pointer;
|
||||
}
|
|
@ -0,0 +1,179 @@
|
|||
/* Copyright 2017 LasLabs Inc.
|
||||
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). */
|
||||
|
||||
odoo.define_section('web_widget_slickroom', ['web.core', 'web.form_common'], function (test) {
|
||||
"use strict";
|
||||
|
||||
function appendWidget (core, formCommon, $fix) {
|
||||
var fieldManager = new formCommon.DefaultFieldManager(null, {});
|
||||
var node = {'attrs': {}};
|
||||
var FieldSlickroomImages = core.form_widget_registry.get('slickroom');
|
||||
var widget = new FieldSlickroomImages(fieldManager, node);
|
||||
widget.appendTo($fix);
|
||||
return widget;
|
||||
}
|
||||
|
||||
function imgHTML (id, attr) {
|
||||
return $(
|
||||
'<div><img data-record-id="' +id + '" ' + attr +
|
||||
'="/web/image/ir.attachment/' + id + '/datas"></div>'
|
||||
);
|
||||
}
|
||||
|
||||
test('._openModal() should open a darkroom modal with provided options',
|
||||
function (assert, core, formCommon) {
|
||||
var $fix = $('#qunit-fixture');
|
||||
var widget = appendWidget(core, formCommon, $fix);
|
||||
var recordId = 1;
|
||||
widget.$slick.slick('slickAdd', imgHTML(recordId, 'src'));
|
||||
|
||||
var modalAction = {};
|
||||
widget.do_action = function (action, options) {
|
||||
modalAction = action;
|
||||
};
|
||||
|
||||
var expectedAction = {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "darkroom.modal",
|
||||
"name": "Darkroom",
|
||||
"views": [[false, "form"]],
|
||||
"target": "new",
|
||||
"context": {
|
||||
"active_field": widget.options.fieldName,
|
||||
"active_model": widget.options.modelName,
|
||||
"active_record_id": recordId
|
||||
}
|
||||
};
|
||||
|
||||
widget.$('img').click();
|
||||
|
||||
assert.deepEqual(modalAction, expectedAction);
|
||||
}
|
||||
);
|
||||
|
||||
test('._openModal() should open a darkroom modal with on_close action ' +
|
||||
'that calls ._updateImage()',
|
||||
function (assert, core, formCommon) {
|
||||
var $fix = $('#qunit-fixture');
|
||||
var widget = appendWidget(core, formCommon, $fix);
|
||||
var recordId = 1;
|
||||
widget.$slick.slick('slickAdd', imgHTML(recordId, 'src'));
|
||||
|
||||
var modalOptions = {};
|
||||
widget.do_action = function (action, options) {
|
||||
modalOptions = options;
|
||||
};
|
||||
|
||||
var $img = widget.$('img');
|
||||
$img.click();
|
||||
modalOptions.on_close();
|
||||
|
||||
assert.notStrictEqual($img.attr('src').indexOf('?unique'), -1);
|
||||
}
|
||||
);
|
||||
|
||||
test('._slickRender() should add data-record-id to images',
|
||||
function (assert, core, formCommon) {
|
||||
var $fix = $('#qunit-fixture');
|
||||
var widget = appendWidget(core, formCommon, $fix);
|
||||
|
||||
var values = [1, 2, 3];
|
||||
|
||||
_.each(values, function(recordId) {
|
||||
widget._slickRender('/web/image/ir.attachments/', recordId);
|
||||
});
|
||||
|
||||
var slickImageIds = widget.$slick.find('img').map(function () {
|
||||
return $(this).data('record-id');
|
||||
}).get();
|
||||
|
||||
assert.deepEqual(slickImageIds, values);
|
||||
}
|
||||
);
|
||||
|
||||
test('._updateImage() should update source of matching/loaded slick images',
|
||||
function (assert, core, formCommon) {
|
||||
var $fix = $('#qunit-fixture');
|
||||
var widget = appendWidget(core, formCommon, $fix);
|
||||
var imgId = 1;
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
widget.$slick.slick('slickAdd', imgHTML(imgId, 'src'));
|
||||
}
|
||||
|
||||
widget._updateImage(imgId);
|
||||
|
||||
var $matches = widget.$slick.find(
|
||||
'[data-record-id="' + imgId + '"]'
|
||||
);
|
||||
$matches.each(function () {
|
||||
var newSrc = $(this).attr('src');
|
||||
assert.notStrictEqual(newSrc.indexOf('?unique'), -1);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
test('._updateImage() should update lazy data attribute of matching/unloaded slick images',
|
||||
function (assert, core, formCommon) {
|
||||
var $fix = $('#qunit-fixture');
|
||||
var widget = appendWidget(core, formCommon, $fix);
|
||||
var imgId = 1;
|
||||
|
||||
for(var i = 0; i < 5; i++) {
|
||||
widget.$slick.slick('slickAdd', imgHTML(imgId, 'data-lazy'));
|
||||
}
|
||||
|
||||
widget._updateImage(1);
|
||||
|
||||
var $matches = widget.$slick.find(
|
||||
'[data-record-id="' + imgId + '"]'
|
||||
);
|
||||
$matches.each(function () {
|
||||
var newSrc = $(this).attr('data-lazy');
|
||||
assert.notStrictEqual(newSrc.indexOf('?unique'), -1);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
test('._updateImage() should not update source of non-matching/loaded slick images',
|
||||
function (assert, core, formCommon) {
|
||||
var $fix = $('#qunit-fixture');
|
||||
var widget = appendWidget(core, formCommon, $fix);
|
||||
var imgId = 1;
|
||||
var img2Id = 2;
|
||||
|
||||
widget.$slick.slick('slickAdd', imgHTML(imgId, 'src'));
|
||||
widget.$slick.slick('slickAdd', imgHTML(img2Id, 'src'));
|
||||
|
||||
widget._updateImage(1);
|
||||
|
||||
var $notMatch = widget.$slick.find(
|
||||
'[data-record-id="' + img2Id + '"]'
|
||||
);
|
||||
assert.strictEqual($notMatch.attr('src').indexOf('?unique'), -1);
|
||||
}
|
||||
);
|
||||
|
||||
test('._updateImage() should not update lazy data attribute of ' +
|
||||
'non-matching/unloaded slick images',
|
||||
function (assert, core, formCommon) {
|
||||
var $fix = $('#qunit-fixture');
|
||||
var widget = appendWidget(core, formCommon, $fix);
|
||||
var imgId = 1;
|
||||
var img2Id = 2;
|
||||
|
||||
widget.$slick.slick('slickAdd', imgHTML(imgId, 'data-lazy'));
|
||||
widget.$slick.slick('slickAdd', imgHTML(img2Id, 'data-lazy'));
|
||||
|
||||
widget._updateImage(1);
|
||||
|
||||
var $notMatch = widget.$slick.find(
|
||||
'[data-record-id="' + img2Id + '"]'
|
||||
);
|
||||
assert.strictEqual(
|
||||
$notMatch.attr('data-lazy').indexOf('?unique'), -1
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
});
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright 2017 LasLabs Inc.
|
||||
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
|
||||
|
||||
<odoo>
|
||||
<template id="assets_slickroom" inherit_id="web.assets_backend">
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<link rel="stylesheet"
|
||||
type="text/less"
|
||||
href="/web_widget_slickroom/static/src/less/web_widget_slickroom.less"
|
||||
/>
|
||||
<script type="application/javascript"
|
||||
src="/web_widget_slickroom/static/src/js/web_widget_slickroom.js"
|
||||
/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="qunit_suite" inherit_id="web.qunit_suite">
|
||||
<xpath expr="//t[@t-set='head']" position="inside">
|
||||
<script type="application/javascript"
|
||||
src="/web_widget_slickroom/static/tests/js/web_widget_slickroom.js"
|
||||
/>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
|
@ -0,0 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 LasLabs Inc.
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from . import test_ui
|
|
@ -0,0 +1,15 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 LasLabs Inc.
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from odoo.tests.common import HttpCase
|
||||
|
||||
|
||||
class UICase(HttpCase):
|
||||
def test_ui_web(self):
|
||||
"""Test backend tests."""
|
||||
self.phantom_js(
|
||||
"/web/tests?debug=assets&module=web_widget_slickroom",
|
||||
"",
|
||||
login="admin",
|
||||
)
|
Loading…
Reference in New Issue