diff --git a/web_widget_slickroom/README.rst b/web_widget_slickroom/README.rst new file mode 100644 index 00000000..9cb99875 --- /dev/null +++ b/web_widget_slickroom/README.rst @@ -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:: + + + +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 +`_. 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 `_. + +Contributors +------------ + +* Brent Hughes + +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. diff --git a/web_widget_slickroom/__init__.py b/web_widget_slickroom/__init__.py new file mode 100644 index 00000000..25e7ed3c --- /dev/null +++ b/web_widget_slickroom/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 LasLabs Inc. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). diff --git a/web_widget_slickroom/__manifest__.py b/web_widget_slickroom/__manifest__.py new file mode 100644 index 00000000..1cc5b16e --- /dev/null +++ b/web_widget_slickroom/__manifest__.py @@ -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", + ], +} diff --git a/web_widget_slickroom/static/description/icon.png b/web_widget_slickroom/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/web_widget_slickroom/static/description/icon.png differ diff --git a/web_widget_slickroom/static/src/js/web_widget_slickroom.js b/web_widget_slickroom/static/src/js/web_widget_slickroom.js new file mode 100644 index 00000000..4ec6750e --- /dev/null +++ b/web_widget_slickroom/static/src/js/web_widget_slickroom.js @@ -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}; + +}); diff --git a/web_widget_slickroom/static/src/less/web_widget_slickroom.less b/web_widget_slickroom/static/src/less/web_widget_slickroom.less new file mode 100644 index 00000000..23aff24d --- /dev/null +++ b/web_widget_slickroom/static/src/less/web_widget_slickroom.less @@ -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; +} diff --git a/web_widget_slickroom/static/tests/js/web_widget_slickroom.js b/web_widget_slickroom/static/tests/js/web_widget_slickroom.js new file mode 100644 index 00000000..ee1bca1c --- /dev/null +++ b/web_widget_slickroom/static/tests/js/web_widget_slickroom.js @@ -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 $( + '
' + ); + } + + 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 + ); + } + ); + +}); diff --git a/web_widget_slickroom/templates/assets.xml b/web_widget_slickroom/templates/assets.xml new file mode 100644 index 00000000..a8608d69 --- /dev/null +++ b/web_widget_slickroom/templates/assets.xml @@ -0,0 +1,25 @@ + + + + +