New attribute: ImageAttribute

SVN:trunk[4129]
This commit is contained in:
Romain Quetiez
2016-05-24 15:29:44 +00:00
parent 2299d23099
commit 1e719b97d8
20 changed files with 730 additions and 15 deletions

View File

@@ -1870,6 +1870,41 @@ EOF
$sHTMLValue .= "<input title=\"$sHelpText\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}[fcontents]\" type=\"file\" id=\"file_$iId\" onChange=\"UpdateFileName('$iId', this.value)\"/>&nbsp;{$sValidationSpan}{$sReloadSpan}\n";
break;
case 'Image':
$aEventsList[] ='validate';
$aEventsList[] ='change';
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/edit_image.js');
$oDocument = $value; // Value is an ormDocument object
$sDefaultUrl = $oAttDef->Get('default_image');
if (is_object($oDocument) && !$oDocument->IsEmpty())
{
$sUrl = 'data:'.$oDocument->GetMimeType().';base64,'.base64_encode($oDocument->GetData());
}
else
{
$sUrl = $sDefaultUrl;
}
$sHTMLValue = "<div id=\"edit_$iInputId\" class=\"edit-image\"></div>";
$sHTMLValue .= "&nbsp;{$sValidationSpan}{$sReloadSpan}\n";
$aEditImage = array(
'input_name' => 'attr_'.$sFieldPrefix.$sAttCode.$sNameSuffix,
'max_file_size' => utils::ConvertToBytes(ini_get('upload_max_filesize')),
'max_width_px' => $oAttDef->Get('display_max_width'),
'max_height_px' => $oAttDef->Get('display_max_height'),
'current_image_url' => $sUrl,
'default_image_url' => $sDefaultUrl,
'labels' => array(
'reset_button' => htmlentities(Dict::S('UI:Button:ResetImage'), ENT_QUOTES, 'UTF-8'),
'remove_button' => htmlentities(Dict::S('UI:Button:RemoveImage'), ENT_QUOTES, 'UTF-8'),
'upload_button' => $sHelpText
)
);
$sEditImageOptions = json_encode($aEditImage);
$oPage->add_ready_script("$('#edit_$iInputId').edit_image($sEditImageOptions);");
break;
case 'StopWatch':
$sHTMLValue = "The edition of a stopwatch is not allowed!!!";
break;
@@ -3008,6 +3043,23 @@ EOF
$this->Set($sAttCode, $oDocument);
}
}
elseif ($oAttDef->GetEditClass() == 'Image')
{
// There should be an uploaded file with the named attr_<attCode>
if ($value['remove'])
{
$this->Set($sAttCode, null);
}
else
{
$oDocument = $value['fcontents'];
if (!$oDocument->IsEmpty())
{
// A new file has been uploaded
$this->Set($sAttCode, $oDocument);
}
}
}
elseif ($oAttDef->GetEditClass() == 'One Way Password')
{
// Check if the password was typed/changed
@@ -3143,6 +3195,14 @@ EOF
{
$value = array('fcontents' => utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents'));
}
elseif ($oAttDef->GetEditClass() == 'Image')
{
$oImage = utils::ReadPostedDocument("attr_{$sFormPrefix}{$sAttCode}", 'fcontents');
$aSize = utils::GetImageSize($oImage->GetData());
$oImage = utils::ResizeImageToFit($oImage, $aSize[0], $aSize[1], $oAttDef->Get('storage_max_width'), $oAttDef->Get('storage_max_height'));
$aOtherData = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", array(), 'raw_data');
$value = array('fcontents' => $oImage, 'remove' => $aOtherData['remove']);
}
elseif ($oAttDef->GetEditClass() == 'RedundancySetting')
{
$value = $oAttDef->ReadValueFromPostedForm($sFormPrefix);

View File

@@ -1223,4 +1223,108 @@ class utils
}
return $sCssRelPath;
}
static public function GetImageSize($sImageData)
{
if (function_exists('getimagesizefromstring')) // PHP 5.4.0 or higher
{
$aRet = @getimagesizefromstring($sImageData);
}
else if(ini_get('allow_url_fopen'))
{
// work around to avoid creating a tmp file
$sUri = 'data://application/octet-stream;base64,'.base64_encode($sImageData);
$aRet = @getimagesize($sUri);
}
else
{
// Damned, need to create a tmp file
$sTempFile = tempnam(SetupUtils::GetTmpDir(), 'img-');
@file_put_contents($sTempFile, $sImageData);
$aRet = @getimagesize($sTempFile);
@unlink($sTempFile);
}
return $aRet;
}
/**
* Resize an image attachment so that it fits in the given dimensions
* @param ormDocument $oImage The original image stored as an ormDocument
* @param int $iWidth Image's original width
* @param int $iHeight Image's original height
* @param int $iMaxImageWidth Maximum width for the resized image
* @param int $iMaxImageHeight Maximum height for the resized image
* @return ormDocument The resampled image
*/
public static function ResizeImageToFit(ormDocument $oImage, $iWidth, $iHeight, $iMaxImageWidth, $iMaxImageHeight)
{
if (($iWidth <= $iMaxImageWidth) && ($iHeight <= $iMaxImageHeight))
{
return $oImage;
}
switch($oImage->GetMimeType())
{
case 'image/gif':
case 'image/jpeg':
case 'image/png':
$img = @imagecreatefromstring($oImage->GetData());
break;
default:
// Unsupported image type, return the image as-is
//throw new Exception("Unsupported image type: '".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
return $oImage;
}
if ($img === false)
{
//throw new Exception("Warning: corrupted image: '".$oImage->GetFileName()." / ".$oImage->GetMimeType()."'. Cannot resize the image, original image will be used.");
return $oImage;
}
else
{
// Let's scale the image, preserving the transparency for GIFs and PNGs
$fScale = min($iMaxImageWidth / $iWidth, $iMaxImageHeight / $iHeight);
$iNewWidth = $iWidth * $fScale;
$iNewHeight = $iHeight * $fScale;
$new = imagecreatetruecolor($iNewWidth, $iNewHeight);
// Preserve transparency
if(($oImage->GetMimeType() == "image/gif") || ($oImage->GetMimeType() == "image/png"))
{
imagecolortransparent($new, imagecolorallocatealpha($new, 0, 0, 0, 127));
imagealphablending($new, false);
imagesavealpha($new, true);
}
imagecopyresampled($new, $img, 0, 0, 0, 0, $iNewWidth, $iNewHeight, $iWidth, $iHeight);
ob_start();
switch ($oImage->GetMimeType())
{
case 'image/gif':
imagegif($new); // send image to output buffer
break;
case 'image/jpeg':
imagejpeg($new, null, 80); // null = send image to output buffer, 80 = good quality
break;
case 'image/png':
imagepng($new, null, 5); // null = send image to output buffer, 5 = medium compression
break;
}
$oResampledImage = new ormDocument(ob_get_contents(), $oImage->GetMimeType(), $oImage->GetFileName());
@ob_end_clean();
imagedestroy($img);
imagedestroy($new);
return $oResampledImage;
}
}
}

View File

@@ -109,6 +109,20 @@ class WizardHelper
$oObj->Set($sAttCode, $oDocument);
}
}
else if ( $oAttDef->GetEditClass() == 'Image' )
{
if ($bReadUploadedFiles)
{
$oDocument = utils::ReadPostedDocument('attr_'.$sAttCode, 'fcontents');
$oObj->Set($sAttCode, $oDocument);
}
else
{
// Create a new empty document, just for displaying the file name
$oDocument = new ormDocument(null, '', $value);
$oObj->Set($sAttCode, $oDocument);
}
}
else if (($oAttDef->IsExternalKey()) && (!empty($value)) && ($value > 0) )
{
// For external keys: load the target object so that external fields

View File

@@ -4880,7 +4880,7 @@ class AttributeBlob extends AttributeDefinition
// (temporary tables created on disk)
// We will have to remove the blobs from the list of attributes when doing the select
// then the use of Get() should finalize the load
if ($value instanceOf ormDocument)
if ($value instanceOf ormDocument && !$value->IsEmpty())
{
$aValues = array();
$aValues[$this->GetCode().'_data'] = $value->GetData();
@@ -4946,7 +4946,17 @@ class AttributeBlob extends AttributeDefinition
public function GetAsXML($value, $oHostObject = null, $bLocalize = true)
{
return ''; // Not exportable in XML, or as CDATA + some subtags ??
$sRet = '';
if (is_object($value))
{
if (!$value->IsEmpty())
{
$sRet = '<mimetype>'.$value->GetMimeType().'</mimetype>';
$sRet .= '<filename>'.$value->GetFileName().'</filename>';
$sRet .= '<data>'.base64_encode($value->GetData()).'</data>';
}
}
return $sRet;
}
/**
@@ -4998,6 +5008,58 @@ class AttributeBlob extends AttributeDefinition
}
}
/**
* An image is a specific type of document, it is stored as several columns in the database
*
* @package iTopORM
*/
class AttributeImage extends AttributeBlob
{
public function GetEditClass() {return "Image";}
// Facilitate things: allow administrators to upload a document
// from a CSV by specifying its path/URL
public function MakeRealValue($proposedValue, $oHostObj)
{
if (!is_object($proposedValue))
{
if (file_exists($proposedValue) && UserRights::IsAdministrator())
{
$sContent = file_get_contents($proposedValue);
$sExtension = strtolower(pathinfo($proposedValue, PATHINFO_EXTENSION));
$sMimeType = "application/x-octet-stream";
$aKnownExtensions = array(
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'gif' => 'image/gif',
'png' => 'image/png'
);
if (!array_key_exists($sExtension, $aKnownExtensions) && extension_loaded('fileinfo'))
{
$finfo = new finfo(FILEINFO_MIME);
$sMimeType = $finfo->file($proposedValue);
}
return new ormDocument($sContent, $sMimeType);
}
}
return $proposedValue;
}
public function GetAsHTML($value, $oHostObject = null, $bLocalize = true)
{
$iMaxWidthPx = $this->Get('display_max_width');
$iMaxHeightPx = $this->Get('display_max_height');
$sUrl = $this->Get('default_image');
$sRet = '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'px; max-height: '.$iMaxHeightPx.'px">';
if (is_object($value) && !$value->IsEmpty())
{
$sUrl = $value->GetDownloadURL(get_class($oHostObject), $oHostObject->GetKey(), $this->GetCode());
$sRet = '<img src="'.$sUrl.'" style="max-width: '.$iMaxWidthPx.'px; max-height: '.$iMaxHeightPx.'px">';
}
return '<div class="view-image" style="width: '.$iMaxWidthPx.'px; height: '.$iMaxHeightPx.'px;"><span class="helper-middle"></span>'.$sRet.'</div>';
}
}
/**
* A stop watch is an ormStopWatch object, it is stored as several columns in the database
*

View File

@@ -355,12 +355,20 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute
$sAttName = $this->Get('attcode');
}
$oPrevDoc = $this->Get('prevdata');
if ($oPrevDoc->IsEmpty())
{
$sPrevious = '';
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sPrevious);
}
else
{
$sDocView = $oPrevDoc->GetAsHtml();
$sDocView .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_',$oPrevDoc->GetDisplayLink(get_class($this), $this->GetKey(), 'prevdata')).", \n";
$sDocView .= "<br/>".Dict::Format('UI:OpenDocumentInNewWindow_', $oPrevDoc->GetDisplayLink(get_class($this), $this->GetKey(), 'prevdata')).", \n";
$sDocView .= Dict::Format('UI:DownloadDocument_', $oPrevDoc->GetDownloadLink(get_class($this), $this->GetKey(), 'prevdata'))."\n";
//$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata');
$sResult = Dict::Format('Change:AttName_Changed_PreviousValue_OldValue', $sAttName, $sDocView);
}
}
return $sResult;
}
}

View File

@@ -187,6 +187,55 @@ EOF
return $sPDF;
}
protected function GetValue($oObj, $sAttCode)
{
switch($sAttCode)
{
case 'id':
$sRet = parent::GetValue($oObj, $sAttCode);
break;
default:
$value = $oObj->Get($sAttCode);
if ($value instanceof ormDocument)
{
$oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode);
if ($oAttDef instanceof AttributeImage)
{
// To limit the image size in the PDF output, we have to enforce the size as height/width because max-width/max-height have no effect
//
list($iWidth, $iHeight) = utils::GetImageSize($value->GetData());
$iMaxWidthPx = min(48, $oAttDef->Get('display_max_width'));
$iMaxHeightPx = min(48, $oAttDef->Get('display_max_height'));
$fScale = min($iMaxWidthPx / $iWidth, $iMaxHeightPx / $iHeight);
$iNewWidth = $iWidth * $fScale;
$iNewHeight = $iHeight * $fScale;
if ($value->IsEmpty())
{
$sUrl = $oAttDef->Get('default_image');
$sRet = '<img src="'.$sUrl.'" style="width: '.$iNewWidth.'px; height: '.$iNewHeight.'px">';
}
else
{
$sUrl = 'data:'.$value->GetMimeType().';base64,'.base64_encode($value->GetData());
$sRet = '<img src="'.$sUrl.'" style="width: '.$iNewWidth.'px; height: '.$iNewHeight.'px">';
}
$sRet = '<div class="view-image">'.$sRet.'</div>';
}
else
{
$sRet = parent::GetValue($oObj, $sAttCode);
}
}
else
{
$sRet = parent::GetValue($oObj, $sAttCode);
}
}
return $sRet;
}
public function GetSupportedFormats()
{
return array('pdf' => Dict::S('Core:BulkExport:PDFFormat'));

View File

@@ -143,6 +143,10 @@ EOF
{
$sRet = $value->GetTimeSpent();
}
elseif ($value instanceof ormDocument)
{
$sRet = '';
}
elseif ($oAttDef instanceof AttributeString)
{
$sRet = $oObj->GetAsHTML($sAttCode);

View File

@@ -107,6 +107,78 @@ table.listResults td {
}
table.listResults td .view-image {
width: inherit !important;
height: inherit !important;
}
table.listResults td .view-image img {
max-width: 48px !important;
max-height: 48px !important;
display: block;
margin-left: auto;
margin-right: auto;
}
.edit-image .view-image {
display: inline-block;
}
.edit-image .view-image.dirty.compat {
background-image: url("ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png");
}
.edit-image .view-image.dirty.compat img {
opacity: 0.3;
}
.edit-image .edit-buttons {
display: inline-block;
vertical-align: top;
margin-top: 4px;
margin-left: 3px;
}
.edit-image .edit-buttons .button {
cursor: pointer;
margin-bottom: 3px;
padding: 2px;
background-color: #e87c1e;
}
.edit-image .edit-buttons .button.disabled {
cursor: default;
background-color: #555555;
opacity: 0.3;
}
.edit-image .edit-buttons .button .ui-icon {
background-image: url("ui-lightness/images/ui-icons_ffffff_256x240.png");
}
.edit-image .file-input {
display: block;
}
/*
* 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;
padding: 2px;
border: 2px solid #dddddd;
border-radius: 6px;
}
.details .view-image img {
display: inline-block;
vertical-align: middle;
}
.details .view-image .helper-middle {
display: inline-block;
height: 100%;
vertical-align: middle;
}
table.listContainer {
border: 0;
padding: 0;
@@ -2057,6 +2129,22 @@ table.export_parameters td {
}
/*
* Format for the PDF output
*/
.table_preview .view-image {
width: inherit !important;
height: inherit !important;
text-align: center;
}
.table_preview .view-image img {
max-width: 48px !important;
max-height: 48px !important;
display: inline-block;
}
.graph_zoom {
display: inline-block;
float: right;

View File

@@ -90,6 +90,83 @@ table.listResults td {
padding: 2px;
}
table.listResults td .view-image {
// Counteract the forced dimensions (usefull for displaying in the details view)
width: inherit !important;
height: inherit !important;
img {
max-width: 48px !important;
max-height: 48px !important;
display: block;
margin-left: auto;
margin-right: auto;
}
}
.edit-image {
.view-image {
display: inline-block;
&.dirty {
// The image will be modified when saving the changes
&.compat {
// Browser not supporting FileReader
background-image: url("ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png");
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("ui-lightness/images/ui-icons_ffffff_256x240.png");
}
}
}
.file-input {
display: block;
}
}
/* 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;
padding: 2px;
border: 2px solid #DDDDDD;
border-radius: 6px;
img {
display: inline-block;
vertical-align: middle;
}
.helper-middle {
// Helper to center the image (requires a span dedicated to this)
display: inline-block;
height: 100%;
vertical-align: middle;
}
}
table.listContainer {
border: 0;
padding: 0;
@@ -1520,6 +1597,18 @@ table.export_parameters td {
.table_preview div.text-preview {
white-space: pre-wrap;
}
/* Format for the PDF output */
.table_preview .view-image {
// Counteract the forced dimensions (usefull for displaying in the details view)
width: inherit !important;
height: inherit !important;
text-align: center;
img {
max-width: 48px !important;
max-height: 48px !important;
display: inline-block;
}
}
.graph_zoom {
display: inline-block;
float: right;

File diff suppressed because one or more lines are too long

View File

@@ -458,6 +458,14 @@
</reconciliation>
</properties>
<fields>
<field id="picture" xsi:type="AttributeImage">
<display_max_width>96</display_max_width>
<display_max_height>96</display_max_height>
<storage_max_width>128</storage_max_width>
<storage_max_height>128</storage_max_height>
<default_image>images/silhouette.png</default_image>
<is_null_allowed>true</is_null_allowed>
</field>
<field id="first_name" xsi:type="AttributeString">
<sql>first_name</sql>
<default_value/>
@@ -568,6 +576,14 @@
<item id="col:col2">
<rank>50</rank>
<items>
<item id="fieldset:Person:personal_info">
<rank>5</rank>
<items>
<item id="picture">
<rank>10</rank>
</item>
</items>
</item>
<item id="fieldset:Person:notifiy">
<rank>10</rank>
<items>

View File

@@ -201,6 +201,8 @@ Dict::Add('EN US', 'English', 'English', array(
'Class:Person/Attribute:tickets_list+' => 'All the tickets this person is the caller',
'Class:Person/Attribute:manager_id_friendlyname' => 'Manager friendly name',
'Class:Person/Attribute:manager_id_friendlyname+' => '',
'Class:Person/Attribute:picture' => 'Picture',
'Class:Person/Attribute:picture+' => '',
));
//
@@ -1880,6 +1882,7 @@ Dict::Add('EN US', 'English', 'English', array(
'Server:otherinfo' => 'Other information',
'Server:power' => 'Power supply',
'Person:info' => 'General information',
'Person:personal_info' => 'Personal information',
'Person:notifiy' => 'Notification',
'Class:Subnet/Tab:IPUsage' => 'IP Usage',
'Class:Subnet/Tab:IPUsage-explain' => 'Interfaces having an IP in the range: <em>%1$s</em> to <em>%2$s</em>',

View File

@@ -146,6 +146,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Class:Person/Attribute:tickets_list+' => '',
'Class:Person/Attribute:manager_id_friendlyname' => 'Manager friendly name',
'Class:Person/Attribute:manager_id_friendlyname+' => '',
'Class:Person/Attribute:picture' => 'Photo',
'Class:Person/Attribute:picture+' => '',
));
//
@@ -1850,6 +1852,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
'Server:otherinfo' => 'Autres informations',
'Server:power' => 'Alimentation électrique',
'Person:info' => 'Informations générales',
'Person:personal_info' => 'Informations personnelles',
'Person:notifiy' => 'Notification',
'Class:Subnet/Tab:IPUsage' => 'IP utilisées',
'Class:Subnet/Tab:IPUsage-explain' => 'Interfaces ayant une IP dans la plage: <em>%1$s</em> à <em>%2$s</em>',

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -424,6 +424,25 @@ class ApplicationHelper
}
$oApp['combodo.current_user'] = $oUser;
$sUrl = $oApp['combodo.portal.base.absolute_url'].'img/user-profile-default-256px.png';
$oContact = UserRights::GetContactObject();
if ($oContact)
{
if (MetaModel::IsValidAttCode(get_class($oContact), 'picture'))
{
$oImage = $oContact->Get('picture');
if (is_object($oImage) && !$oImage->IsEmpty())
{
$sUrl = $oImage->GetDownloadURL(get_class($oContact), $oContact->GetKey(), 'picture');
}
else
{
$sUrl = MetaModel::GetAttributeDef(get_class($oContact), 'picture')->Get('default_image');
}
}
}
$oApp['combodo.current_user_img'] = $sUrl;
}
/**

View File

@@ -5,8 +5,7 @@
{% set bUserConnected = true %}
{% set sUserFullname = app['combodo.current_user'].Get('first_name') ~ ' ' ~ app['combodo.current_user'].Get('last_name') %}
{% set sUserEmail = app['combodo.current_user'].Get('email') %}
{% set sUserPhotoUrl = app['combodo.portal.base.absolute_url'] ~ 'img/user-profile-default-256px.png' %}
{% set sUserPhotoUrl = 'https://scontent-fra3-1.xx.fbcdn.net/v/t1.0-9/11050099_10153305298138954_7206181025917413544_n.jpg?oh=728b8e7b1f073b81a2e6b43858c795f8&oe=57E2B0D3' %}
{% set sUserPhotoUrl = app['combodo.current_user_img'] %}
{% else %}
{% set bUserConnected = false %}
{% set sUserFullname = '' %}

View File

@@ -1326,5 +1326,6 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:NoInlineImage' => 'There is no image available on the server. Use the "Browse" button above to select an image from your computer and upload it to the server.',
'UI:ToggleFullScreen' => 'Toggle Maximize / Minimize',
'UI:Button:ResetImage' => 'Recover the previous image',
'UI:Button:RemoveImage' => 'Remove the image',
));
?>

View File

@@ -1168,4 +1168,6 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:NoInlineImage' => 'Il n\'y a aucune image de disponible sur le serveur. Utilisez le bouton "Parcourir" (ci-dessus) pour sélectionner une image sur votre ordinateur et la télécharger sur le serveur.',
'UI:ToggleFullScreen' => 'Agrandir / Minimiser',
'UI:Button:ResetImage' => 'Récupérer l\'image initiale',
'UI:Button:RemoveImage' => 'Supprimer l\'image',
));

153
js/edit_image.js Normal file
View File

@@ -0,0 +1,153 @@
// jQuery UI style "widget" for editing an image (file upload)
////////////////////////////////////////////////////////////////////////////////
//
// graph
//
$(function()
{
// the widget definition, where "itop" is the namespace,
// "dashboard" the widget name
$.widget( "itop.edit_image",
{
// default options
options: {
input_name: '_image_input_',
max_file_size: 0,
max_width_px: 32,
max_height_px: 32,
current_image_url: '',
default_image_url: '',
labels: {
reset_button: 'Reset',
remove_button: 'Remove',
upload_button: 'Upload'
}
},
// the constructor
_create: function () {
var me = this;
me.bLoadedEmpty = (me.options.current_image_url == '');
var sMarkup = '';
sMarkup += '<input type="hidden" id="do_remove_' + me.options.input_name + '" name="' + me.options.input_name + '[remove]" value="0"/>';
sMarkup += '<div id="preview_' + me.options.input_name + '" class="view-image" style="width: ' + me.options.max_width_px + 'px; height: ' + me.options.max_height_px + 'px;">';
sMarkup += '<span class="helper-middle"></span>';
sMarkup += '<img src="' + me.options.current_image_url + '" data-original-src="' + me.options.current_image_url + '" data-default-src="' + me.options.default_image_url + '" style="max-width: ' + me.options.max_width_px + 'px; max-height: ' + me.options.max_height_px + 'px">';
sMarkup += '</div>';
sMarkup += '<div id="buttons_' + me.options.input_name + '" class="edit-buttons">';
sMarkup += '<div title="' + me.options.labels.reset_button + '" id="reset_' + me.options.input_name + '" class="button disabled"><div class="ui-icon ui-icon-arrowreturnthick-1-w"></div></div>';
var sDisabled = me.bLoadedEmpty ? 'disabled' : '';
var sLoadedDisabled = me.bLoadedEmpty ? 'yes' : 'no';
sMarkup += '<div title="' + me.options.labels.remove_button + '" id="remove_' + me.options.input_name + '" data-loaded-disabled="' + sLoadedDisabled + '" class="button ' + sDisabled + '"><div class="ui-icon ui-icon-trash"></div></div>';
sMarkup += '</div>';
sMarkup += '<input type="hidden" name="MAX_FILE_SIZE" value="'+me.options.max_file_size+'" />';
sMarkup += '<input class="file-input" title="' + me.options.labels.upload_button + '" name="' + me.options.input_name + '[fcontents]" type="file" id="file_' + me.options.input_name + '" />';
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');
});
},
// called when created, and later when changing options
_refresh: function () {
},
// events bound via _bind are removed automatically
// revert other modifications here
_destroy: function () {
this.element.removeClass('edit-image');
},
// _setOptions is called with a hash of all options that are changing
_setOptions: function () {
this._superApply(arguments);
},
// _setOption is called for each individual option that is changing
_setOption: function (key, value) {
this._superApply(arguments);
},
previewImage: function (input, sImageSelector) {
if (input.files && input.files[0]) {
if (window.FileReader) {
var reader = new FileReader();
reader.onload = function (e) {
$(sImageSelector).attr('src', e.target.result);
}
reader.readAsDataURL(input.files[0]);
}
else {
$(sImageSelector).closest('.view-image').addClass('compat');
}
}
else {
$(sImageSelector).closest('.view-image').addClass('compat');
}
}
});
});

View File

@@ -1158,6 +1158,20 @@ EOF
$aParameters['is_null_allowed'] = $this->GetPropBoolean($oField, 'is_null_allowed', false);
$aParameters['depends_on'] = $sDependencies;
}
elseif ($sAttType == 'AttributeImage')
{
$aParameters['is_null_allowed'] = $this->GetPropBoolean($oField, 'is_null_allowed', false);
$aParameters['depends_on'] = $sDependencies;
$aParameters['display_max_width'] = $this->GetPropNumber($oField, 'display_max_width', 128);
$aParameters['display_max_height'] = $this->GetPropNumber($oField, 'display_max_height', 128);
$aParameters['storage_max_width'] = $this->GetPropNumber($oField, 'storage_max_width', 256);
$aParameters['storage_max_height'] = $this->GetPropNumber($oField, 'storage_max_height', 256);
if (($sDefault = $oField->GetChildText('default_image')) && (strlen($sDefault) > 0))
{
$aParameters['default_image'] = "utils::GetAbsoluteUrlModulesRoot().'$sModuleRelativeDir/$sDefault'";
}
}
elseif ($sAttType == 'AttributeStopWatch')
{
$oStates = $oField->GetUniqueElement('states');