Browse Source
[ADD] web_widget_slickroom: Create module
[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
Brenton Hughes
7 years ago
committed by
Jairo Llopis
10 changed files with 416 additions and 0 deletions
-
94web_widget_slickroom/README.rst
-
3web_widget_slickroom/__init__.py
-
22web_widget_slickroom/__manifest__.py
-
BINweb_widget_slickroom/static/description/icon.png
-
67web_widget_slickroom/static/src/js/web_widget_slickroom.js
-
6web_widget_slickroom/static/src/less/web_widget_slickroom.less
-
179web_widget_slickroom/static/tests/js/web_widget_slickroom.js
-
25web_widget_slickroom/templates/assets.xml
-
5web_widget_slickroom/tests/__init__.py
-
15web_widget_slickroom/tests/test_ui.py
@ -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", |
|||
], |
|||
} |
After Width: 128 | Height: 128 | 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", |
|||
) |
Write
Preview
Loading…
Cancel
Save
Reference in new issue