diff --git a/datamodels/2.x/itop-attachments/renderers.itop-attachments.php b/datamodels/2.x/itop-attachments/renderers.itop-attachments.php index 075949a85..9c5e605b8 100644 --- a/datamodels/2.x/itop-attachments/renderers.itop-attachments.php +++ b/datamodels/2.x/itop-attachments/renderers.itop-attachments.php @@ -16,10 +16,20 @@ // You should have received a copy of the GNU Affero General Public License // along with iTop. If not, see +/** + * Attachments rendering for iTop console. + * + * For the user portal, see \Combodo\iTop\Renderer\Bootstrap\FieldRenderer\BsFileUploadFieldRenderer + */ + + define('ATTACHMENT_DOWNLOAD_URL', 'pages/ajax.document.php?operation=download_document&class=Attachment&field=contents&id='); define('ATTACHMENTS_RENDERER', 'TableDetailsAttachmentsRenderer'); +/** + * For now this factory is just a helper to instanciate the renderer + */ class AttachmentsRendererFactory { /** @@ -320,21 +330,32 @@ class TableDetailsAttachmentsRenderer extends AbstractAttachmentsRenderer return; } - $this->oPage->add(''.PHP_EOL); - $this->oPage->add(''.PHP_EOL); - $this->oPage->add(' '.PHP_EOL); - $this->oPage->add(' '.PHP_EOL); - $this->oPage->add(' '.PHP_EOL); - $this->oPage->add(' '.PHP_EOL); - $this->oPage->add(' '.PHP_EOL); - $this->oPage->add(' '.PHP_EOL); + + $sThumbnail = Dict::S('Attachments:File:Thumbnail'); + $sFileName = Dict::S('Attachments:File:Name'); + $sFileSize = Dict::S('Attachments:File:Size'); + $sFileDate = Dict::S('Attachments:File:Date'); + $sFileCreator = Dict::S('Attachments:File:Creator'); + $sFileType = Dict::S('Attachments:File:MimeType'); + $sDeleteColumn = ''; if ($bWithDeleteButton) { - $this->oPage->add(' '.PHP_EOL); + $sDeleteColumn = ''; } - $this->oPage->add(''.PHP_EOL); - $this->oPage->add(''.PHP_EOL); - + $this->oPage->add(<< + + + + + + + + $sDeleteColumn + + +HTML + ); $iMaxWidth = MetaModel::GetModuleSetting('itop-attachments', 'preview_max_width', 290); $sPreviewNotAvailable = addslashes(Dict::S('Attachments:PreviewNotAvailable')); diff --git a/datamodels/2.x/itop-portal-base/portal/public/css/portal.css b/datamodels/2.x/itop-portal-base/portal/public/css/portal.css index 4075fd873..e53e325ae 100644 --- a/datamodels/2.x/itop-portal-base/portal/public/css/portal.css +++ b/datamodels/2.x/itop-portal-base/portal/public/css/portal.css @@ -1111,13 +1111,15 @@ table .group-actions { .fileupload_field_content > div { margin-bottom: 15px; } +.attachments_container .attachmentsList thead > tr > th { + text-align: center; +} +.attachments_container .attachmentsList tbody > tr > td { + text-align: center; +} .attachments_container .attachment { height: 95px; overflow-x: hidden; - text-align: center; -} -.attachments_container .attachment:hover { - background-color: #e0e0e0; } .attachments_container .attachment .attachment_name { overflow-x: hidden; @@ -1127,6 +1129,9 @@ table .group-actions { .attachments_container .attachment .btn { margin-top: 3px; } +.attachments_container .attachmenthover { + background-color: #e0e0e0; +} .upload_container input { display: inline; } diff --git a/datamodels/2.x/itop-portal-base/portal/public/css/portal.scss b/datamodels/2.x/itop-portal-base/portal/public/css/portal.scss index cacb0b20c..2df2907ec 100644 --- a/datamodels/2.x/itop-portal-base/portal/public/css/portal.scss +++ b/datamodels/2.x/itop-portal-base/portal/public/css/portal.scss @@ -1194,21 +1194,35 @@ table .group-actions .item-action-wrapper .panel-body > p:last-child{ .fileupload_field_content > div{ margin-bottom: 15px; } -.attachments_container .attachment { - height: 95px; - overflow-x: hidden; - text-align: center; -} -.attachments_container .attachment:hover { - background-color: #e0e0e0; -} -.attachments_container .attachment .attachment_name{ - overflow-x: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} -.attachments_container .attachment .btn{ - margin-top: 3px; +.attachments_container { + .attachmentsList { + & thead > tr > th { + text-align: center; + } + + & tbody > tr > td { + text-align: center; + } + } + + .attachment { + height: 95px; + overflow-x: hidden; + + .attachment_name { + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .btn { + margin-top: 3px; + } + + &hover { + background-color: #e0e0e0; + } + } } .upload_container input{ display: inline; diff --git a/datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php b/datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php index 7b7b288e4..24af64e02 100644 --- a/datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php +++ b/datamodels/2.x/itop-portal-base/portal/src/Controller/ObjectController.php @@ -1152,6 +1152,8 @@ class ObjectController extends BrickController $oAttachment->Set('expire', time() + MetaModel::GetConfig()->Get('draft_attachments_lifetime')); // one hour... $oAttachment->Set('temp_id', $sTempId); $oAttachment->Set('item_class', $sObjectClass); + $oAttachment->Set('creation_date', time()); + $oAttachment->Set('user_id', UserRights::GetUserObject()); $oAttachment->SetDefaultOrgId(); $oAttachment->Set('contents', $oDocument); $iAttId = $oAttachment->DBInsert(); @@ -1162,6 +1164,10 @@ class ObjectController extends BrickController $aData['icon'] = utils::GetAbsoluteUrlAppRoot().'env-'.utils::GetCurrentEnvironment().'/itop-attachments/icons/image.png'; $aData['att_id'] = $iAttId; $aData['preview'] = $oDocument->IsPreviewAvailable() ? 'true' : 'false'; + $aData['file_size'] = $oDocument->GetFormatedSize(); + $aData['creation_date'] = $oAttachment->Get('creation_date'); + $aData['user_id_friendlyname'] = $oAttachment->Get('user_id_friendlyname'); + $aData['file_type'] = $oDocument->GetMimeType(); } catch (FileUploadException $e) { diff --git a/sources/renderer/bootstrap/fieldrenderer/bsfileuploadfieldrenderer.class.inc.php b/sources/renderer/bootstrap/fieldrenderer/bsfileuploadfieldrenderer.class.inc.php index 3801d2e29..a5153dde7 100644 --- a/sources/renderer/bootstrap/fieldrenderer/bsfileuploadfieldrenderer.class.inc.php +++ b/sources/renderer/bootstrap/fieldrenderer/bsfileuploadfieldrenderer.class.inc.php @@ -20,6 +20,7 @@ namespace Combodo\iTop\Renderer\Bootstrap\FieldRenderer; +use AbstractAttachmentsRenderer; use AttachmentPlugIn; use Combodo\iTop\Renderer\RenderingOutput; use DBObjectSearch; @@ -29,7 +30,11 @@ use InlineImage; use utils; /** - * Description of BsFileUploadFieldRenderer + * This is the class used to render attachments in the user portal. + * + * In the iTop console this is handled in the itop-attachments module. Most of the code here is a duplicate of this module. + * + * @see \AbstractAttachmentsRenderer and its implementations for the iTop console * * @author Guillaume Lajarige */ @@ -43,8 +48,7 @@ class BsFileUploadFieldRenderer extends BsFieldRenderer $oOutput = parent::Render(); $sObjectClass = get_class($this->oField->GetObject()); - $sIsDeleteAllowed = ($this->oField->GetAllowDelete() && !$this->oField->GetReadOnly()) ? 'true' : 'false'; - $sDeleteBtn = Dict::S('Portal:Button:Delete'); + $bIsDeleteAllowed = ($this->oField->GetAllowDelete() && !$this->oField->GetReadOnly()); $sTempId = utils::GetUploadTempId($this->oField->GetTransactionId()); $sUploadDropZoneLabel = Dict::S('Portal:Attachments:DropZone:Message'); @@ -68,7 +72,7 @@ class BsFileUploadFieldRenderer extends BsFieldRenderer $oOutput->AddHtml('
'); // Files list $oOutput->AddHtml('
'); - $this->PrepareExistingFiles($oOutput); + $this->PrepareExistingFiles($oOutput, $bIsDeleteAllowed); $oOutput->Addhtml('
'); // Removing upload input if in read only @@ -89,9 +93,24 @@ class BsFileUploadFieldRenderer extends BsFieldRenderer $sMaxUploadLabel = AttachmentPlugIn::GetMaxUpload(); $sFileTooBigLabel = Dict::Format('Attachments:Error:FileTooLarge', $sMaxUploadLabel); $sFileTooBigLabelForJS = addslashes($sFileTooBigLabel); - // Note : This is based on itop-attachement/main.attachments.php + // Note : This is based on itop-attachement/main.itop-attachments.php + $sAttachmentTableRowTemplate = json_encode(self::GetAttachmentTableRow( + '{{iAttId}}', + '{{sLineStyle}}', + '{{sDocDownloadUrl}}', + '{{sIconClass}}', + '{{sAttachmentThumbUrl}}', + '{{sFileName}}', + '{{sAttachmentMeta}}', + '{{sFileSize}}', + '{{sAttachmentDate}}', + '{{sAttachmentCreator}}', + '{{sFileType}}', + $bIsDeleteAllowed + )); $oOutput->AddJs( - <<'; - $(this).closest('.fileupload_field_content').find('.attachments_container').append( - '' - ); + var replaces = [ + {search: "{{iAttId}}", replace:iAttId }, + {search: "{{lineStyle}}", replace:'' }, + {search: "{{sDocDownloadUrl}}", replace:sDownloadLink }, + {search: "{{sIconClass}}", replace:sIconClass }, + {search: "{{sAttachmentThumbUrl}}", replace:data.result.icon }, + {search: "{{sFileName}}", replace: data.result.msg }, + {search: "{{sAttachmentMeta}}", replace:sAttachmentMeta }, + {search: "{{sFileSize}}", replace:data.result.file_size }, + {search: "{{sAttachmentDate}}", replace:data.result.creation_date }, + {search: "{{sAttachmentCreator}}", replace:data.result.contact_id_friendlyname }, + {search: "{{sFileType}}", replace:data.result.file_type } + ]; + var sAttachmentRow = attachmentRowTemplate; + $.each(replaces, function(indexInArray, value ) { + var re = new RegExp(value.search, 'gi'); + sAttachmentRow = sAttachmentRow.replace(re, value.replace); + }); + + $(this).closest('.fileupload_field_content').find('.attachments_container table.attachmentsList>tbody').append(sAttachmentRow); // Preview tooltip if(data.result.preview){ - $('#display_attachment_'+data.result.att_id).tooltip({ + $('#display_attachment_'+data.result.att_id +' a.trigger-preview').tooltip({ + container: 'body', html: true, - title: function(){ return '
'; } + title: function(){ + return '
'; + } }); } - // Showing remove button on hover - $('#display_attachment_'+data.result.att_id).hover( function(){ - $(this).children(':button').toggleClass('hidden'); - }); // Remove button handler $('#display_attachment_'+data.result.att_id+' :button').click(function(oEvent){ oEvent.preventDefault(); @@ -174,24 +205,18 @@ class BsFileUploadFieldRenderer extends BsFieldRenderer // Preview tooltip - $('.attachment [data-preview="true"]').each(function(iIndex, oElem){ + $('table.attachmentsList>tbody>tr>td a.trigger-preview').each(function(iIndex, oElem){ $(oElem).parent().tooltip({ + container: 'body', html: true, title: function(){ return '
'; } }); }); // Remove button handler - $('.attachments_container .attachment :button').click(function(oEvent){ + $('.attachments_container table.attachmentsList>tbody>tr>td :button').click(function(oEvent){ oEvent.preventDefault(); RemoveAttachment($(this).closest('.attachment').find(':input[name="attachments[]"]').val()); }); - // Remove button showing - if($sIsDeleteAllowed) - { - $('.attachment').hover( function(){ - $(this).find(':button').toggleClass('hidden'); - }); - } // Handles a drag / drop overlay if($('#drag_overlay').length === 0) @@ -236,8 +261,7 @@ class BsFileUploadFieldRenderer extends BsFieldRenderer oDropZone.removeClass('drag_in').addClass('drag_out'); }, 200); }); - -EOF +JS ); return $oOutput; @@ -246,12 +270,13 @@ EOF /** * * @param \Combodo\iTop\Renderer\RenderingOutput $oOutput + * @param boolean $bIsDeleteAllowed * * @throws \Exception * @throws \CoreException * @throws \OQLException */ - protected function PrepareExistingFiles(RenderingOutput &$oOutput) + protected function PrepareExistingFiles(RenderingOutput $oOutput, $bIsDeleteAllowed) { $sObjectClass = get_class($this->oField->GetObject()); $sDeleteBtn = Dict::S('Portal:Button:Delete'); @@ -268,29 +293,124 @@ EOF } else { + $sTitleThumbnail = Dict::S('Attachments:File:Thumbnail'); + $sTitleFileName = Dict::S('Attachments:File:Name'); + $sTitleFileSize = Dict::S('Attachments:File:Size'); + $sTitleFileDate = Dict::S('Attachments:File:Date'); + $sTitleFileCreator = Dict::S('Attachments:File:Creator'); + $sTitleFileType = Dict::S('Attachments:File:MimeType'); + $oOutput->Addhtml(<< +
+ + + + + + + + + +HTML + ); + while ($oAttachment = $oSet->Fetch()) { $iAttId = $oAttachment->GetKey(); + + $sLineStyle = ''; + + $sAttachmentMeta = ''; + $oDoc = $oAttachment->Get('contents'); $sFileName = htmlentities($oDoc->GetFileName(), ENT_QUOTES, 'UTF-8'); - $sIcon = utils::GetAbsoluteUrlAppRoot().'env-'.utils::GetCurrentEnvironment().'/itop-attachments/icons/image.png'; - $sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false'; - $sDownloadLink = str_replace('-sAttachmentId-', $iAttId, $this->oField->GetDownloadEndpoint()); - $oOutput->Addhtml( - << - -
-
{$sFileName}
- -
- - -EOF - ); + $sDocDownloadUrl = str_replace('-sAttachmentId-', $iAttId, $this->oField->GetDownloadEndpoint()); + + $sAttachmentThumbUrl = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName); + $sIconClass = ''; + if ($oDoc->IsPreviewAvailable()) + { + $sIconClass = ' preview'; + if ($oDoc->GetSize() <= AbstractAttachmentsRenderer::MAX_SIZE_FOR_PREVIEW) + { + $sAttachmentThumbUrl = $sDocDownloadUrl; + } + } + + $sFileSize = $oDoc->GetFormatedSize(); + $sFileType = $oDoc->GetMimeType(); + + $bIsTempAttachment = ($oAttachment->Get('item_id') === 0); + $sAttachmentDate = ''; + if (!$bIsTempAttachment) + { + $sAttachmentDate = $oAttachment->Get('creation_date'); + } + + $sAttachmentCreator = $oAttachment->Get('contact_id_friendlyname'); + + $oOutput->Addhtml(self::GetAttachmentTableRow( + $iAttId, + $sLineStyle, + $sDocDownloadUrl, + $sIconClass, + $sAttachmentThumbUrl, + $sFileName, + $sAttachmentMeta, + $sFileSize, + $sAttachmentDate, + $sAttachmentCreator, + $sFileType, + $bIsDeleteAllowed + )); } + + $oOutput->Addhtml(<< +
'.Dict::S('Attachments:File:Thumbnail').''.Dict::S('Attachments:File:Name').''.Dict::S('Attachments:File:Size').''.Dict::S('Attachments:File:Date').''.Dict::S('Attachments:File:Creator').''.Dict::S('Attachments:File:MimeType').'
$sThumbnail$sFileName$sFileSize$sFileDate$sFileCreator$sFileType
$sTitleThumbnail$sTitleFileName$sTitleFileSize$sTitleFileDate$sTitleFileCreator$sTitleFileType
+HTML + ); } } + /** + * @param $iAttId + * @param $sLineStyle + * @param $sDocDownloadUrl + * @param $sIconClass + * @param $sAttachmentThumbUrl + * @param $sFileName + * @param $sAttachmentMeta + * @param $sFileSize + * @param $sAttachmentDate + * @param $sAttachmentCreator + * @param $sFileType + * @param $bIsDeleteAllowed + * + * @return string + */ + protected static function GetAttachmentTableRow( + $iAttId, $sLineStyle, $sDocDownloadUrl, $sIconClass, $sAttachmentThumbUrl, $sFileName, $sAttachmentMeta, $sFileSize, + $sAttachmentDate, $sAttachmentCreator, $sFileType, $bIsDeleteAllowed + ) { + $sDeleteButton = ''; + if ($bIsDeleteAllowed) + { + $sDeleteBtnLabel = Dict::S('Portal:Button:Delete'); + $sDeleteButton = ''; + } + + return << + + $sFileName$sAttachmentMeta + $sFileSize + $sAttachmentDate + $sAttachmentCreator + $sFileType + $sDeleteButton + +HTML; + } }