Browse Source

[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 icon
pull/853/head
Brenton Hughes 7 years ago
committed by Jairo Llopis
parent
commit
6cbd754f51
  1. 94
      web_widget_slickroom/README.rst
  2. 3
      web_widget_slickroom/__init__.py
  3. 22
      web_widget_slickroom/__manifest__.py
  4. BIN
      web_widget_slickroom/static/description/icon.png
  5. 67
      web_widget_slickroom/static/src/js/web_widget_slickroom.js
  6. 6
      web_widget_slickroom/static/src/less/web_widget_slickroom.less
  7. 179
      web_widget_slickroom/static/tests/js/web_widget_slickroom.js
  8. 25
      web_widget_slickroom/templates/assets.xml
  9. 5
      web_widget_slickroom/tests/__init__.py
  10. 15
      web_widget_slickroom/tests/test_ui.py

94
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::
<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.

3
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).

22
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",
],
}

BIN
web_widget_slickroom/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 9.2 KiB

67
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};
});

6
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;
}

179
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 $(
'<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
);
}
);
});

25
web_widget_slickroom/templates/assets.xml

@ -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>

5
web_widget_slickroom/tests/__init__.py

@ -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

15
web_widget_slickroom/tests/test_ui.py

@ -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…
Cancel
Save