diff --git a/application/utils.inc.php b/application/utils.inc.php index eddb7a46e..e4a8357e1 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -437,6 +437,45 @@ class utils return $iReturn; } + /** + * Format a value into a more friendly format (KB, MB, GB, TB) instead a juste a Bytes amount. + * + * @param type $value + * @return string + */ + public static function BytesToFriendlyFormat($value) + { + $sReturn = ''; + // Kilobytes + if ($value >= 1024) + { + $sReturn = 'K'; + $value = $value / 1024; + } + // Megabytes + if ($value >= 1024) + { + $sReturn = 'M'; + $value = $value / 1024; + } + // Gigabytes + if ($value >= 1024) + { + $sReturn = 'G'; + $value = $value / 1024; + } + // Terabytes + if ($value >= 1024) + { + $sReturn = 'T'; + $value = $value / 1024; + } + + $value = round($value, 1); + + return $value . '' . $sReturn . 'B'; + } + /** * Helper function to convert a string to a date, given a format specification. It replaces strtotime which does not allow for specifying a date in a french format (for instance) * Example: StringToTime('01/05/11 12:03:45', '%d/%m/%y %H:%i:%s') diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 16712c0f1..fd48cb02a 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -5113,6 +5113,33 @@ class AttributeBlob extends AttributeDefinition } return $sFingerprint; } + + static public function GetFormFieldClass() + { + return '\\Combodo\\iTop\\Form\\Field\\BlobField'; + } + + public function MakeFormField(DBObject $oObject, $oFormField = null) + { + if ($oFormField === null) + { + $sFormFieldClass = static::GetFormFieldClass(); + $oFormField = new $sFormFieldClass($this->GetCode()); + } + + // Note: As of today we want this field to always be read-only + $oFormField->SetReadOnly(true); + + // Generating urls + $value = $oObject->Get($this->GetCode()); + $oFormField->SetDownloadUrl($value->GetDownloadURL(get_class($oObject), $oObject->GetKey(), $this->GetCode())); + $oFormField->SetDisplayUrl($value->GetDisplayURL(get_class($oObject), $oObject->GetKey(), $this->GetCode())); + + parent::MakeFormField($oObject, $oFormField); + + return $oFormField; + } + } /** diff --git a/core/ormdocument.class.inc.php b/core/ormdocument.class.inc.php index 174a70bcd..7d144bcd8 100644 --- a/core/ormdocument.class.inc.php +++ b/core/ormdocument.class.inc.php @@ -118,18 +118,26 @@ class ormDocument return "".htmlentities($this->GetFileName(), ENT_QUOTES, 'UTF-8')."\n"; } + /** + * Returns an URL to display a document like an image + * @return string + */ + public function GetDisplayURL($sClass, $Id, $sAttCode) + { + return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.render.php?operation=display_document&class=$sClass&id=$Id&field=$sAttCode"; + } + /** * Returns an URL to download a document like an image (uses HTTP caching) * @return string - */ + */ public function GetDownloadURL($sClass, $Id, $sAttCode) { // Compute a signature to reset the cache anytime the data changes (this is acceptable if used only with icon files) $sSignature = md5($this->GetData()); - return utils::GetAbsoluteUrlAppRoot()."pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400"; + return utils::GetAbsoluteUrlAppRoot() . "pages/ajax.document.php?operation=download_document&class=$sClass&id=$Id&field=$sAttCode&s=$sSignature&cache=86400"; } - public function IsPreviewAvailable() { $bRet = false; diff --git a/datamodels/2.x/itop-portal-base/cs.dict.itop-portal-base.php b/datamodels/2.x/itop-portal-base/cs.dict.itop-portal-base.php index 2306e5212..a22596d0f 100644 --- a/datamodels/2.x/itop-portal-base/cs.dict.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/cs.dict.itop-portal-base.php @@ -61,6 +61,9 @@ Dict::Add('CS CZ', 'Czech', 'Čeština', array( 'Portal:Datatables:Language:Sort:Descending' => 'řadit sestupně', 'Portal:Autocomplete:NoResult' => 'Žádná data', 'Portal:Attachments:DropZone:Message' => 'Přesuňte soubory myší pro vložení', + 'Portal:File:None' => 'No file', + 'Portal:File:DisplayInfo' => '%1$s', + 'Portal:File:DisplayInfo+' => '%1$s (%2$s) Open / Download', )); // UserProfile brick diff --git a/datamodels/2.x/itop-portal-base/en.dict.itop-portal-base.php b/datamodels/2.x/itop-portal-base/en.dict.itop-portal-base.php index bd2741334..c671c5547 100644 --- a/datamodels/2.x/itop-portal-base/en.dict.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/en.dict.itop-portal-base.php @@ -57,6 +57,9 @@ Dict::Add('EN US', 'English', 'English', array( 'Portal:Datatables:Language:Sort:Descending' => 'enable for a descending sort', 'Portal:Autocomplete:NoResult' => 'No data', 'Portal:Attachments:DropZone:Message' => 'Drop your files to add them as attachments', + 'Portal:File:None' => 'No file', + 'Portal:File:DisplayInfo' => '%1$s', + 'Portal:File:DisplayInfo+' => '%1$s (%2$s) Open / Download', )); // UserProfile brick diff --git a/datamodels/2.x/itop-portal-base/es_cr.dict.itop-portal-base.php b/datamodels/2.x/itop-portal-base/es_cr.dict.itop-portal-base.php index 57f680461..d271b1fb0 100644 --- a/datamodels/2.x/itop-portal-base/es_cr.dict.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/es_cr.dict.itop-portal-base.php @@ -57,6 +57,9 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array( 'Portal:Datatables:Language:Sort:Descending' => 'Habilitar para un tipo descendente', 'Portal:Autocomplete:NoResult' => 'Sin datos', 'Portal:Attachments:DropZone:Message' => 'Agrega tus archivos para agregarlos como documentos adjuntos', + 'Portal:File:None' => 'No file', + 'Portal:File:DisplayInfo' => '%1$s', + 'Portal:File:DisplayInfo+' => '%1$s (%2$s) Open / Download', )); // UserProfile brick diff --git a/datamodels/2.x/itop-portal-base/fr.dict.itop-portal-base.php b/datamodels/2.x/itop-portal-base/fr.dict.itop-portal-base.php index 5d6969039..3cbfb8650 100644 --- a/datamodels/2.x/itop-portal-base/fr.dict.itop-portal-base.php +++ b/datamodels/2.x/itop-portal-base/fr.dict.itop-portal-base.php @@ -57,6 +57,9 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Portal:Datatables:Language:Sort:Descending' => 'activer pour trier la colonne par ordre décroissant', 'Portal:Autocomplete:NoResult' => 'Aucun résultat', 'Portal:Attachments:DropZone:Message' => 'Déposez vos fichiers pour les ajouter en pièces jointes', + 'Portal:File:None' => 'Aucun fichier', + 'Portal:File:DisplayInfo' => '%1$s', + 'Portal:File:DisplayInfo+' => '%1$s (%2$s) Ouvrir / Télécharger', )); // UserProfile brick diff --git a/datamodels/2.x/itop-portal-base/portal/web/css/portal.css b/datamodels/2.x/itop-portal-base/portal/web/css/portal.css index 2b4505acd..26b941abe 100644 --- a/datamodels/2.x/itop-portal-base/portal/web/css/portal.css +++ b/datamodels/2.x/itop-portal-base/portal/web/css/portal.css @@ -810,6 +810,10 @@ table .group-actions { } } } +/* BlobField */ +.form_fields .file_open_link { + margin-left: 10px; +} .form_field .form-control-static img { max-width: 100% !important; height: initial !important; diff --git a/datamodels/2.x/itop-portal-base/portal/web/css/portal.scss b/datamodels/2.x/itop-portal-base/portal/web/css/portal.scss index 6ddb4599e..bf241b202 100644 --- a/datamodels/2.x/itop-portal-base/portal/web/css/portal.scss +++ b/datamodels/2.x/itop-portal-base/portal/web/css/portal.scss @@ -850,6 +850,10 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{ } } } +/* BlobField */ +.form_fields .file_open_link{ + margin-left: 10px; +} .form_field .form-control-static img{ max-width: 100% !important; diff --git a/sources/autoload.php b/sources/autoload.php index 757c4e066..1e4e84536 100644 --- a/sources/autoload.php +++ b/sources/autoload.php @@ -24,6 +24,7 @@ require_once APPROOT . 'sources/form/form.class.inc.php'; require_once APPROOT . 'sources/form/formmanager.class.inc.php'; require_once APPROOT . 'sources/form/field/field.class.inc.php'; require_once APPROOT . 'sources/form/field/fileuploadfield.class.inc.php'; +require_once APPROOT . 'sources/form/field/blobfield.class.inc.php'; require_once APPROOT . 'sources/form/field/subformfield.class.inc.php'; require_once APPROOT . 'sources/form/field/textfield.class.inc.php'; require_once APPROOT . 'sources/form/field/hiddenfield.class.inc.php'; diff --git a/sources/form/field/blobfield.class.inc.php b/sources/form/field/blobfield.class.inc.php new file mode 100644 index 000000000..ec6ad43e4 --- /dev/null +++ b/sources/form/field/blobfield.class.inc.php @@ -0,0 +1,83 @@ + + +namespace Combodo\iTop\Form\Field; + +use \utils; +use \Dict; +use \ormDocument; +use \Combodo\iTop\Form\Field\Field; + +/** + * Description of BlobField + * + * @author Guillaume Lajarige + */ +class BlobField extends Field +{ + protected $sDownloadUrl; + protected $sDisplayUrl; + + public function GetDownloadUrl() + { + return $this->sDownloadUrl; + } + + public function GetDisplayUrl() + { + return $this->sDisplayUrl; + } + + public function SetDownloadUrl($sDownloadUrl) + { + $this->sDownloadUrl = $sDownloadUrl; + return $this; + } + + public function SetDisplayUrl($sDisplayUrl) + { + $this->sDisplayUrl = $sDisplayUrl; + return $this; + } + + public function GetCurrentValue() + { + return $this->currentValue->GetFileName(); + } + + public function GetDisplayValue() + { + if ($this->currentValue->IsEmpty()) + { + $sValue = Dict::S('Portal:File:None'); + } + else + { + $sFilename = $this->currentValue->GetFileName(); + $iSize = utils::BytesToFriendlyFormat(strlen($this->currentValue->GetData())); + $sOpenLink = $this->GetDisplayUrl(); + $sDownloadLink = $this->GetDownloadUrl(); + + $sValue = Dict::Format('Portal:File:DisplayInfo+', $sFilename, $iSize, $sOpenLink, $sDownloadLink); + } + + return $sValue; + } + +} diff --git a/sources/renderer/bootstrap/bsformrenderer.class.inc.php b/sources/renderer/bootstrap/bsformrenderer.class.inc.php index 8d05b6d99..5b72f8bb9 100644 --- a/sources/renderer/bootstrap/bsformrenderer.class.inc.php +++ b/sources/renderer/bootstrap/bsformrenderer.class.inc.php @@ -56,6 +56,7 @@ class BsFormRenderer extends FormRenderer $this->AddSupportedField('DateTimeField', 'BsSimpleFieldRenderer'); $this->AddSupportedField('DurationField', 'BsSimpleFieldRenderer'); $this->AddSupportedField('FileUploadField', 'BsFileUploadFieldRenderer'); + $this->AddSupportedField('BlobField', 'BsSimpleFieldRenderer'); } } diff --git a/sources/renderer/bootstrap/fieldrenderer/bssimplefieldrenderer.class.inc.php b/sources/renderer/bootstrap/fieldrenderer/bssimplefieldrenderer.class.inc.php index c274e4791..8f9b42fc4 100644 --- a/sources/renderer/bootstrap/fieldrenderer/bssimplefieldrenderer.class.inc.php +++ b/sources/renderer/bootstrap/fieldrenderer/bssimplefieldrenderer.class.inc.php @@ -284,6 +284,21 @@ EOF $oOutput->AddHtml(''); break; + case 'Combodo\\iTop\\Form\\Field\\BlobField': + $oOutput->AddHtml('
'); + // Showing label / value only if read-only but not hidden + if (!$this->oField->GetHidden()) + { + if ($this->oField->GetLabel() !== '') + { + $oOutput->AddHtml(''); + } + $oOutput->AddHtml('
')->AddHtml($this->oField->GetDisplayValue(), false)->AddHtml('
'); + } + $oOutput->AddHtml(''); + $oOutput->AddHtml('
'); + break; + case 'Combodo\\iTop\\Form\\Field\\RadioField': case 'Combodo\\iTop\\Form\\Field\\SelectField': case 'Combodo\\iTop\\Form\\Field\\MultipleSelectField':