From 40c6bc3ad21233465a72c08fff3f7951840cd47e Mon Sep 17 00:00:00 2001 From: Romain Derie Date: Tue, 2 Oct 2018 09:57:55 +0000 Subject: [PATCH] [IMP] web_unsplash: Implement Beacon Alternative and credit Unsplash Unsplash API requirements does not directly allow to store their images on our server (We should use hotlinking unless the image is edited (cropped etc)). Still, they have the `Image Beacon Alternative` to allow to store their images on our server if we really need it with our use case. With the Beacon Alternative, we should ping Unsplash server to notify an image view. To do so, we need to be able to recognize Unsplash imagesin the DOM, this is why we are now using /unsplash/ in the URL. The Unsplash image name should be the image ID as it will be the identifier needed to ping their server. This commit also adds credit to Unsplash in the media dialog next to the unsplash input search. It also add the `w-100` on the media dialog card as we lost that UI behavior from BS3 to BS4. opw-35060 closes odoo/odoo#27377 --- addons/web_editor/static/src/xml/editor.xml | 2 +- addons/web_unsplash/controllers/main.py | 40 ++++++---- .../static/src/js/unsplash_beacon.js | 26 +++++++ .../static/src/js/unsplash_image_widget.js | 40 ++++++---- .../static/src/scss/unsplash.scss | 23 ++---- .../static/src/xml/unsplash_image_widget.xml | 73 ++++++++++--------- .../views/res_config_settings_view.xml | 18 +++-- .../views/web_unsplash_templates.xml | 7 +- 8 files changed, 140 insertions(+), 89 deletions(-) create mode 100644 addons/web_unsplash/static/src/js/unsplash_beacon.js diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml index ac5cbe6d07b..85cf336130d 100644 --- a/addons/web_editor/static/src/xml/editor.xml +++ b/addons/web_editor/static/src/xml/editor.xml @@ -90,7 +90,7 @@ -
+
diff --git a/addons/web_unsplash/controllers/main.py b/addons/web_unsplash/controllers/main.py index 0c318192bdc..c53c03ba5bf 100644 --- a/addons/web_unsplash/controllers/main.py +++ b/addons/web_unsplash/controllers/main.py @@ -7,7 +7,7 @@ import requests import werkzeug.utils from PIL import Image -from odoo import http, tools +from odoo import http, tools, _ from odoo.http import request logger = logging.getLogger(__name__) @@ -39,9 +39,12 @@ class Web_Unsplash(http.Controller): else: res_id = None - for key in unsplashurls: - url = unsplashurls[key].get('url') + for key, value in unsplashurls.items(): + url = value.get('url') try: + if not url.startswith('https://images.unsplash.com/'): + logger.exception("ERROR: Unknown Unsplash URL!: " + url) + raise Exception(_("ERROR: Unknown Unsplash URL!")) req = requests.get(url) if req.status_code != requests.codes.ok: continue @@ -56,20 +59,19 @@ class Web_Unsplash(http.Controller): logger.exception("Timeout: " + str(e)) continue - image = Image.open(io.BytesIO(datas)) - image_format = image.format + name = key # optimized image before save - if image_format and image_format in ('PNG', 'JPEG'): - datas = tools.image_save_for_web(image) - - name = key - # append image extension in name - if image_format: - name += '.' + image_format.lower() + if mimetype in ('image/jpeg', 'image/png'): + image = Image.open(io.BytesIO(datas)) + if image.format in ('PNG', 'JPEG'): + datas = tools.image_save_for_web(image) + # append image extension in name + name += '.' + image.format attachment = Attachments.create({ 'name': name, + 'url': '/unsplash/' + name, 'datas_fname': name, 'mimetype': mimetype, 'datas': base64.b64encode(datas), @@ -78,7 +80,7 @@ class Web_Unsplash(http.Controller): 'res_model': res_model, }) attachment.generate_access_token() - uploads.extend(attachment.read(['name', 'mimetype', 'checksum', 'res_id', 'res_model', 'access_token'])) + uploads.extend(attachment.read(['name', 'mimetype', 'checksum', 'res_id', 'res_model', 'access_token', 'url'])) return uploads @@ -88,8 +90,14 @@ class Web_Unsplash(http.Controller): return request.env['ir.config_parameter'].sudo().get_param('unsplash.access_key') raise werkzeug.exceptions.NotFound() - @http.route("/web_unsplash/set_client_id", type='json', auth="user") - def set_unsplash_client_id(self, key, **post): + @http.route("/web_unsplash/get_app_id", type='json', auth="public") + def get_unsplash_app_id(self, **post): + return request.env['ir.config_parameter'].sudo().get_param('unsplash.app_id') + + @http.route("/web_unsplash/save_unsplash", type='json', auth="user") + def save_unsplash(self, **post): if request.env.user._has_unsplash_key_rights(): - return request.env['ir.config_parameter'].sudo().set_param('unsplash.access_key', key) + request.env['ir.config_parameter'].sudo().set_param('unsplash.app_id', post.get('appId')) + request.env['ir.config_parameter'].sudo().set_param('unsplash.access_key', post.get('key')) + return True raise werkzeug.exceptions.NotFound() diff --git a/addons/web_unsplash/static/src/js/unsplash_beacon.js b/addons/web_unsplash/static/src/js/unsplash_beacon.js new file mode 100644 index 00000000000..d287a3dd80d --- /dev/null +++ b/addons/web_unsplash/static/src/js/unsplash_beacon.js @@ -0,0 +1,26 @@ +odoo.define('web_unsplash.beacon', function (require) { +'use strict'; + +var base = require('web_editor.base'); +var ajax = require('web.ajax'); + +base.ready().then(function () { + var unsplash_images = []; + _.each($('img[src*="/unsplash/"]'), function (img, index) { + // get image id from URL (`/unsplash/xYdf5feoI.jpg` -> `xYdf5feoI`) + unsplash_images.push(img.src.split('/unsplash/')[1].split('.')[0]); + }); + if (unsplash_images) { + ajax.jsonRpc('/web_unsplash/get_app_id', 'call').then(function (appID) { + if (appID) { + var unsplash_view_url = 'https://views.unsplash.com/v'; + $.get(unsplash_view_url, { + photo_id: unsplash_images.join(','), + app_id: appID, + }); + } + }); + } +}); + +}); diff --git a/addons/web_unsplash/static/src/js/unsplash_image_widget.js b/addons/web_unsplash/static/src/js/unsplash_image_widget.js index 7ba5f4961da..d353694ddea 100644 --- a/addons/web_unsplash/static/src/js/unsplash_image_widget.js +++ b/addons/web_unsplash/static/src/js/unsplash_image_widget.js @@ -17,7 +17,7 @@ ImageWidget.include({ 'input input.unsplash_search': '_onChangeUnsplashSearch', 'dblclick .unsplash_img_container [data-imgid]': '_onUnsplashImgDblClick', 'click .unsplash_img_container [data-imgid]': '_onUnsplashImgClick', - 'click button.access_key': '_onSetAccessKey', + 'click button.save_unsplash': '_onSaveUnsplash', }), /** * @override @@ -84,7 +84,7 @@ ImageWidget.include({ route: '/web_unsplash/attachment/add', params: { unsplashurls: self._unsplash.selectedImages, - res_model : self.options.res_model || "ir.ui.view", + res_model : self.options.res_model, res_id: self.options.res_id, } }).then(function (images) { @@ -93,7 +93,7 @@ ImageWidget.include({ } _.each(images, function (image) { - image.src = _.str.sprintf('/web/image/%s', image.id); + image.src = image.url; image.isDocument = !(/gif|jpe|jpg|png/.test(image.mimetype)); }); self.images = images; @@ -130,7 +130,7 @@ ImageWidget.include({ if (!this._unsplash.query) { return this._super.apply(this, arguments); } - this.unsplashAPI.getImages(self._unsplash.query, this.IMAGES_PER_PAGE, this.page).then(function (res) { + this.unsplashAPI.getImages(this._unsplash.query, this.IMAGES_PER_PAGE, this.page).then(function (res) { self._unsplash.isMaxed = res.isMaxed; var rows = _(res.images).chain() .groupBy(function (a, index) { return Math.floor(index / self.IMAGES_PER_ROW); }) @@ -161,19 +161,29 @@ ImageWidget.include({ /** * @private */ - _onSetAccessKey: function () { + _onSaveUnsplash: function () { var self = this; var key = this.$('#accessKeyInput').val().trim(); - if (key) { - this._rpc({ - route: '/web_unsplash/set_client_id', - params: { - 'key': key, - }, - }).then(function () { - self.unsplashAPI.clientId = key; - self._renderImages(); - }); + var appId = this.$('#appIdInput').val().trim(); + + this.$('#accessKeyInput').toggleClass('is-invalid', !key); + this.$('#appIdInput').toggleClass('is-invalid', !appId); + + if (key && appId) { + var params = { + 'key': key, + 'appId': appId, + }; + + if (!this.$el.find('.is-invalid').length) { + this._rpc({ + route: '/web_unsplash/save_unsplash', + params: params, + }).then(function () { + self.unsplashAPI.clientId = key; + self._renderImages(); + }); + } } }, /** diff --git a/addons/web_unsplash/static/src/scss/unsplash.scss b/addons/web_unsplash/static/src/scss/unsplash.scss index 71e97b362b7..39b8bdde40c 100644 --- a/addons/web_unsplash/static/src/scss/unsplash.scss +++ b/addons/web_unsplash/static/src/scss/unsplash.scss @@ -29,24 +29,17 @@ .not-found { font-size: 25px; - padding: 50px 0; + padding: 30px 0; } .unsplash_error { - padding: 50px 0; - .title { - font-size: 25px; - text-transform: uppercase; - } - .details { - font-size: 20px; - .access_key_box { - padding: 9px; - background-color: #fcfcfc; - border: 1px solid #ededee; - #accessKeyInput { - width: 300px; - } + padding: 30px 0; + .access_key_box { + padding: 9px; + background-color: #fcfcfc; + border: 1px solid #ededee; + input { + min-width: 300px; } } } diff --git a/addons/web_unsplash/static/src/xml/unsplash_image_widget.xml b/addons/web_unsplash/static/src/xml/unsplash_image_widget.xml index e1104b6014f..6514297af23 100644 --- a/addons/web_unsplash/static/src/xml/unsplash_image_widget.xml +++ b/addons/web_unsplash/static/src/xml/unsplash_image_widget.xml @@ -2,10 +2,10 @@ - -
- — or — - + +
+ Photos by Unsplash +
@@ -29,49 +29,56 @@
+ +

+ +

+ +
+
-

- Access key is not set -

-
- Generate an access key -
-
- - -
-
-
+ + + Unsplash requires an access key and an application ID + +
-

- Search is temporary unavailable -

+

+ Search is temporarily unavailable +

The max number of searches is exceeded. Please retry in an hour or extend to a better account.
-

- Unauthorized Key -

-
- Please check your unsplash api key. -
-
- - -
-
-
+ + + Unauthorized Key + + + Please check your Unsplash access key and application ID. + +
-

+

Something went wrong -

+

Please check your internet connection or contact administrator.
diff --git a/addons/web_unsplash/views/res_config_settings_view.xml b/addons/web_unsplash/views/res_config_settings_view.xml index 1aca1e2d747..2952cca50c4 100644 --- a/addons/web_unsplash/views/res_config_settings_view.xml +++ b/addons/web_unsplash/views/res_config_settings_view.xml @@ -6,14 +6,16 @@
-
-
- diff --git a/addons/web_unsplash/views/web_unsplash_templates.xml b/addons/web_unsplash/views/web_unsplash_templates.xml index 33a95d8baa0..71951874998 100644 --- a/addons/web_unsplash/views/web_unsplash_templates.xml +++ b/addons/web_unsplash/views/web_unsplash_templates.xml @@ -1,10 +1,15 @@ -