diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php
index bfb142322..b92692f17 100644
--- a/application/cmdbabstract.class.inc.php
+++ b/application/cmdbabstract.class.inc.php
@@ -2099,16 +2099,13 @@ EOF
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/edit_image.js');
$oDocument = $value; // Value is an ormDocument objectm
$sDefaultUrl = $oAttDef->Get('default_image');
- if (is_object($oDocument) && !$oDocument->IsEmpty())
- {
+ if (is_object($oDocument) && !$oDocument->IsEmpty()) {
$sUrl = 'data:'.$oDocument->GetMimeType().';base64,'.base64_encode($oDocument->GetData());
- }
- else
- {
+ } else {
$sUrl = null;
}
- $sHTMLValue = "
\n";
+ $sHTMLValue = "\n";
$sHTMLValue .= "{$sValidationSpan}{$sReloadSpan}\n";
$aEditImage = array(
diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php
index 904cbbbb3..18a251542 100644
--- a/core/attributedef.class.inc.php
+++ b/core/attributedef.class.inc.php
@@ -8112,10 +8112,10 @@ class AttributeImage extends AttributeBlob
$sRet = $this->GetHtmlForImageUrl($sCustomImageUrl, $iMaxWidthPx, $iMaxHeightPx);
}
- $sCssClasses = 'view-image attribute-image';
+ $sCssClasses = 'ibo-input-image--image-view attribute-image';
$sCssClasses .= ' '.(($bIsCustomImage) ? 'attribute-image-custom' : 'attribute-image-default');
- return ''.$sRet.'
';
+ return ''.$sRet.'
';
}
/**
diff --git a/core/pdfbulkexport.class.inc.php b/core/pdfbulkexport.class.inc.php
index 2812fb9ca..5f8df7c6d 100644
--- a/core/pdfbulkexport.class.inc.php
+++ b/core/pdfbulkexport.class.inc.php
@@ -229,7 +229,7 @@ EOF
$sUrl = 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
}
$sRet = ($sUrl !== null) ? '
' : '';
- $sRet = ''.$sRet.'
';
+ $sRet = ''.$sRet.'
';
}
else
{
diff --git a/css/backoffice/components/input/_all.scss b/css/backoffice/components/input/_all.scss
index 615c9ecdf..088f7cd3e 100644
--- a/css/backoffice/components/input/_all.scss
+++ b/css/backoffice/components/input/_all.scss
@@ -4,11 +4,12 @@
*/
@import "input";
-@import "input-select";
@import "input-checkbox";
@import "input-date";
@import "input-datetime";
@import "input-duration";
+@import "input-image";
+@import "input-select";
@import "input-select-icon";
@import "input-string";
@import "input-tagset";
diff --git a/css/backoffice/components/input/_input-image.scss b/css/backoffice/components/input/_input-image.scss
new file mode 100644
index 000000000..ef55d510f
--- /dev/null
+++ b/css/backoffice/components/input/_input-image.scss
@@ -0,0 +1,45 @@
+/*!
+ * @copyright Copyright (C) 2010-2021 Combodo SARL
+ * @license http://opensource.org/licenses/AGPL-3.0
+ */
+
+.ibo-input-image {
+ display: flex;
+ justify-content: flex-start;
+ align-items: flex-start;
+}
+
+.ibo-input-image--image-view {
+ position: relative;
+ overflow: hidden;
+ background-color: $ibo-color-grey-200;
+ border-radius: $ibo-border-radius-500;
+ @extend %ibo-fully-centered-content;
+
+ img[src=""],
+ img[src="null"] {
+ // Hiding "broken" image when src is not set
+ visibility: hidden;
+ }
+
+ input[type="file"] {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ opacity: 0;
+ }
+}
+
+.ibo-input-image--edit-buttons {
+ display: flex;
+ flex-direction: column;
+ margin-left: 0.5rem;
+
+ // Overload original siblings rule as buttons are displayed vertically
+ .ibo-button + .ibo-button {
+ margin-top: 0.5rem;
+ margin-left: 0;
+ }
+}
\ No newline at end of file
diff --git a/css/light-grey.scss b/css/light-grey.scss
index f4de46447..5d4bb1eb4 100644
--- a/css/light-grey.scss
+++ b/css/light-grey.scss
@@ -154,60 +154,6 @@
// color: $text-color;
//}
- .edit-image {
- .view-image {
- display: inline-block;
-
- img[src=""],
- img[src="null"] {
- // Hiding "broken" image when src is not set
- visibility: hidden;
- }
-
- &.dirty {
- // The image will be modified when saving the changes
-
- &.compat {
- // Browser not supporting FileReader
- background-image: url($approot-relative + "css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png?v=" + $version);
-
- img {
- opacity: 0.3;
- }
- }
- }
- }
-
- .edit-buttons {
- display: inline-block;
- vertical-align: top;
- margin-top: 4px;
- margin-left: 3px;
-
- .button {
- cursor: pointer;
- margin-bottom: 3px;
- padding: 2px;
- background-color: $highlight-color;
-
- &.disabled {
- cursor: default;
- background-color: $grey-color;
- opacity: 0.3;
- }
-
- .ui-icon {
- background-image: url($approot-relative + "css/ui-lightness/images/ui-icons_ffffff_256x240.png?v=" + $version);
- }
- }
- }
-
- .file-input {
- display: block;
- position: relative;
- }
- }
-
/* Center the image both horizontally and vertically, withing a box which size is fixed (depends on the attribute definition) */
.details .view-image {
text-align: center;
diff --git a/js/edit_image.js b/js/edit_image.js
index 3d094423c..f0e977174 100644
--- a/js/edit_image.js
+++ b/js/edit_image.js
@@ -27,108 +27,10 @@ $(function()
// the constructor
_create: function () {
- var me = this;
- me.bLoadedEmpty = (me.options.current_image_url == '' || me.options.current_image_url == null);
+ this.element.addClass('ibo-input-image');
- var sMarkup = '';
- sMarkup += '';
-
- var sCssClasses = "view-image attribute-image";
- console.debug("edit_image", me.options.current_image_url);
- var sCssClassToAdd, sImageUrl;
- if (me.options.current_image_url === null)
- {
- sCssClassToAdd = "attribute-image-default";
- sImageUrl = me.options.default_image_url;
- }
- else
- {
- sCssClassToAdd = "attribute-image-custom";
- sImageUrl = me.options.current_image_url;
- }
- sCssClasses += ' '+sCssClassToAdd;
- sMarkup += '';
- sMarkup += '';
-
- sMarkup += '';
- sMarkup += '';
-
- this.element
- .addClass('edit-image')
- .append(sMarkup);
-
- $('#file_' + me.options.input_name).change(function () {
-
- $('#do_remove_' + me.options.input_name).val('0');
-
- me.previewImage(this, '#preview_' + me.options.input_name + ' img');
-
- var oImage = $('#preview_' + me.options.input_name + ' img');
- oImage.closest('.view-image').addClass('dirty');
-
- $('#reset_' + me.options.input_name).removeClass('disabled');
- $('#remove_' + me.options.input_name).removeClass('disabled');
- });
- $('#reset_' + me.options.input_name).click(function () {
-
- if ($(this).hasClass('disabled')) return;
-
- $('#do_remove_' + me.options.input_name).val('0');
-
- // Restore the image
- var oImage = $('#preview_' + me.options.input_name + ' img');
- oImage.attr('src', oImage.attr('data-original-src'));
- oImage.closest('.view-image').removeClass('dirty').removeClass('compat');
-
- // Reset the file input without losing events bound to it
- var oInput = $('#file_' + me.options.input_name);
- oInput.replaceWith(oInput.val('').clone(true));
-
- $('#reset_' + me.options.input_name).addClass('disabled');
- var oRemoveBtn = $('#remove_' + me.options.input_name);
- if (oRemoveBtn.attr('data-loaded-disabled') == 'yes') {
- oRemoveBtn.addClass('disabled');
- }
- else {
- oRemoveBtn.removeClass('disabled');
- }
- });
- $('#remove_' + me.options.input_name).click(function () {
-
- if ($(this).hasClass('disabled')) return;
-
- $('#do_remove_' + me.options.input_name).val('1');
-
- // Restore the default image
- var oImage = $('#preview_' + me.options.input_name + ' img');
- oImage.attr('src', oImage.attr('data-default-src'));
- oImage.closest('.view-image')
- .removeClass('compat')
- .addClass('dirty');
-
- // Reset the file input without losing events bound to it
- var oInput = $('#file_' + me.options.input_name);
- oInput.replaceWith(oInput.val('').clone(true));
-
- var oRemoveBtn = $('#remove_' + me.options.input_name);
- if (oRemoveBtn.attr('data-loaded-disabled') == 'yes') {
- $('#reset_' + me.options.input_name).addClass('disabled');
- }
- else {
- $('#reset_' + me.options.input_name).removeClass('disabled');
- }
- oRemoveBtn.addClass('disabled');
- });
+ this._buildMarkup();
+ this._bindEvents();
},
// called when created, and later when changing options
_refresh: function () {
@@ -136,7 +38,7 @@ $(function()
// events bound via _bind are removed automatically
// revert other modifications here
_destroy: function () {
- this.element.removeClass('edit-image');
+ this.element.removeClass('ibo-input-image');
},
// _setOptions is called with a hash of all options that are changing
_setOptions: function () {
@@ -146,6 +48,114 @@ $(function()
_setOption: function (key, value) {
this._superApply(arguments);
},
+
+ _buildMarkup: function () {
+ CombodoJSConsole.Debug('edit_image: '+this.options.current_image_url);
+
+ this.bLoadedEmpty = (this.options.current_image_url == '' || this.options.current_image_url == null);
+
+ const sDisabled = this.bLoadedEmpty ? 'disabled' : '';
+ const sLoadedDisabled = this.bLoadedEmpty ? 'yes' : 'no';
+ const sUploadButtonTooltipAttribute = this.options.labels.upload_button !== '' ? 'data-tooltip-content="'+this.options.labels.upload_button+'"' : '';
+ let sCssClasses = "ibo-input-image--image-view attribute-image";
+ let sCssClassToAdd, sImageUrl;
+
+ if (this.options.current_image_url === null) {
+ sCssClassToAdd = "attribute-image-default";
+ sImageUrl = this.options.default_image_url;
+ } else {
+ sCssClassToAdd = "attribute-image-custom";
+ sImageUrl = this.options.current_image_url;
+ }
+ sCssClasses += ' '+sCssClassToAdd;
+
+ let sMarkup = `
+
+
+
+
+
+
+
+ `;
+
+ this.element.append(sMarkup);
+
+ CombodoGlobalToolbox.InitAllNonInstantiatedTooltips(this.element);
+ },
+ _bindEvents: function () {
+ const me = this;
+
+ $('#file_'+me.options.input_name).change(function () {
+ $('#do_remove_'+me.options.input_name).val('0');
+
+ me.previewImage(this, '#preview_'+me.options.input_name+' img');
+
+ let oImage = $('#preview_'+me.options.input_name+' img');
+ oImage.closest('.ibo-input-image--image-view').addClass('dirty');
+
+ $('#reset_'+me.options.input_name).prop('disabled', false);
+ $('#remove_'+me.options.input_name).prop('disabled', false);
+ });
+
+ $('#reset_'+me.options.input_name).click(function () {
+ if ($(this).prop('disabled')) {
+ return;
+ }
+
+ $('#do_remove_'+me.options.input_name).val('0');
+
+ // Restore the image
+ let oImage = $('#preview_'+me.options.input_name+' img');
+ oImage.attr('src', oImage.attr('data-original-src'));
+ oImage.closest('.ibo-input-image--image-view').removeClass('dirty');
+
+ // Reset the file input without losing events bound to it
+ let oInput = $('#file_'+me.options.input_name);
+ oInput.replaceWith(oInput.val('').clone(true));
+
+ $('#reset_'+me.options.input_name).prop('disabled', true);
+ let oRemoveBtn = $('#remove_'+me.options.input_name);
+ if (oRemoveBtn.attr('data-loaded-disabled') == 'yes') {
+ oRemoveBtn.prop('disabled', true);
+ } else {
+ oRemoveBtn.prop('disabled', false);
+ }
+ });
+
+ $('#remove_'+me.options.input_name).click(function () {
+ if ($(this).prop('disabled')) {
+ return;
+ }
+
+ $('#do_remove_'+me.options.input_name).val('1');
+
+ // Restore the default image
+ let oImage = $('#preview_'+me.options.input_name+' img');
+ oImage.attr('src', oImage.attr('data-default-src'));
+ oImage.closest('.ibo-input-image--image-view')
+ .addClass('dirty');
+
+ // Reset the file input without losing events bound to it
+ let oInput = $('#file_'+me.options.input_name);
+ oInput.replaceWith(oInput.val('').clone(true));
+
+ let oRemoveBtn = $('#remove_'+me.options.input_name);
+ if (oRemoveBtn.attr('data-loaded-disabled') == 'yes') {
+ $('#reset_'+me.options.input_name).prop('disabled', true);
+ } else {
+ $('#reset_'+me.options.input_name).prop('disabled', false);
+ }
+ oRemoveBtn.prop('disabled', true);
+ });
+ },
previewImage: function (input, sImageSelector) {
if (input.files && input.files[0]) {
if (window.FileReader) {
@@ -157,12 +167,6 @@ $(function()
reader.readAsDataURL(input.files[0]);
}
- else {
- $(sImageSelector).closest('.view-image').addClass('compat');
- }
- }
- else {
- $(sImageSelector).closest('.view-image').addClass('compat');
}
}
});
diff --git a/sources/application/WebPage/iTopWebPage.php b/sources/application/WebPage/iTopWebPage.php
index bacb141a1..6d64a970d 100644
--- a/sources/application/WebPage/iTopWebPage.php
+++ b/sources/application/WebPage/iTopWebPage.php
@@ -343,7 +343,7 @@ JS
// Make image attributes zoomable
$this->add_ready_script(
<<