oPage = $oPage; $this->sObjClass = $sObjClass; $this->iObjKey = $iObjKey; $this->sTransactionId = $sTransactionId; $oSearch = DBObjectSearch::FromOQL('SELECT Attachment WHERE item_class = :class AND item_id = :item_id'); $this->oAttachmentsSet = new DBObjectSet($oSearch, array(), array('class' => $sObjClass, 'item_id' => $iObjKey)); $oSearchTemp = DBObjectSearch::FromOQL('SELECT Attachment WHERE temp_id = :temp_id'); $this->oTempAttachmentsSet = new DBObjectSet($oSearchTemp, array(), array('temp_id' => $this->sTransactionId)); } /** * @return \DBObjectSet */ public function GetTempAttachmentsSet() { return $this->oTempAttachmentsSet; } /** * @return \DBObjectSet */ public function GetAttachmentsSet() { return $this->oAttachmentsSet; } public function GetAttachmentsCount() { return $this->GetAttachmentsSet()->Count() + $this->GetTempAttachmentsSet()->Count(); } /** * Can be overriden to change display order, but must generate an HTML container of ID {@link ATTACHMENTS_LIST_CONTAINER_ID} for JS refresh. * * @param int[] $aAttachmentsDeleted Attachments id that should be deleted after form submission * * @return void will print using {@link oPage} */ public function RenderEditAttachmentsList($aAttachmentsDeleted = array()) { $this->AddUploadButton(); $this->oPage->add('
'); $this->AddAttachmentsListContent(true, $aAttachmentsDeleted); $this->oPage->add('
'); } /** * Generates the attachments list content * * @param bool $bWithDeleteButton * @param array $aAttachmentsDeleted * * @throws \ArchivedObjectException * @throws \CoreException * @throws \CoreUnexpectedValue * @throws \MySQLException */ abstract public function AddAttachmentsListContent($bWithDeleteButton, $aAttachmentsDeleted = array()); public function RenderViewAttachmentsList() { $this->AddAttachmentsListContent(false, array()); } protected function AddUploadButton() { $sClass = $this->sObjClass; $sId = $this->iObjKey; $iMaxUploadInBytes = AttachmentPlugIn::GetMaxUploadSize(); $sMaxUploadLabel = AttachmentPlugIn::GetMaxUpload(); $sFileTooBigLabel = Dict::Format('Attachments:Error:FileTooLarge', $sMaxUploadLabel); $sFileTooBigLabelForJS = addslashes($sFileTooBigLabel); $this->oPage->add('
'); $this->oPage->add('
'); $this->oPage->add(Dict::S('Attachments:AddAttachment')); $oAddButton = FileSelectUIBlockFactory::MakeStandard('file', 'file'); $oAddButton->SetShowFilename(false); $this->oPage->AddUiBlock($oAddButton); $this->oPage->add(' '.$sMaxUploadLabel); $this->oPage->add('
'); $this->oPage->add('
'); $this->oPage->add(file_get_contents(APPROOT.'images/illustrations/undraw_upload.svg')); $this->oPage->add(Dict::S('UI:Attachments:DropYourFileHint').'
'); $this->oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js'); $this->oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js'); $this->oPage->add_ready_script( <<tbody>tr[id^="display_attachment_"]>td input[name="removed_attachments[]"]'), aAttachmentsDeletedIds = aAttachmentsDeletedHiddenInputs.map(function() { return $(this).val() }).toArray(); $(sContentNode).block(); $.post(GetAbsoluteUrlModulesRoot()+'itop-attachments/ajax.itop-attachment.php', { operation: 'refresh_attachments_render', objclass: '$sClass', objkey: $sId, temp_id: '$this->sTransactionId', edit_mode: 1, attachments_deleted: aAttachmentsDeletedIds }, function(data) { $(sContentNode).html(data); $(sContentNode).unblock(); $('#attachment_plugin').trigger('add_attachment', [dataUpload.result.att_id, dataUpload.result.msg, false]); } ) } $('#file').fileupload({ url: GetAbsoluteUrlModulesRoot()+'itop-attachments/ajax.itop-attachment.php', formData: { operation: 'add', temp_id: '$this->sTransactionId', obj_class: '$sClass' }, dataType: 'json', pasteZone: null, // Don't accept files via Chrome's copy/paste done: function(e, data) { if(typeof(data.result.error) != 'undefined') { if(data.result.error !== '') { alert(data.result.error); return; } } RefreshAttachmentsDisplay(data); }, send: function(e, data){ // Don't send attachment if size is greater than PHP post_max_size, otherwise it will break the request and all its parameters (\$_REQUEST, \$_POST, ...) // Note: We loop on the files as the data structures is an array but in this case, we only upload 1 file at a time. var iTotalSizeInBytes = 0; for(var i = 0; i < data.files.length; i++) { iTotalSizeInBytes += data.files[i].size; } if(iTotalSizeInBytes > $iMaxUploadInBytes) { alert('$sFileTooBigLabelForJS'); return false; } }, start: function() { $('#attachment_loading').show(); }, stop: function() { $('#attachment_loading').hide(); } }); $(document).on('drag dragstart dragend dragover dragenter dragleave drop', function(e) { e.preventDefault(); e.stopPropagation(); }) $(document).bind('dragover', function (e) { var bFiles = false; if (e.dataTransfer && e.dataTransfer.types) { for (var i = 0; i < e.dataTransfer.types.length; i++) { if (e.dataTransfer.types[i] == "application/x-moz-nativeimage") { bFiles = false; // mozilla contains "Files" in the types list when dragging images inside the page, but it also contains "application/x-moz-nativeimage" before break; } if (e.dataTransfer.types[i] == "Files") { bFiles = true; break; } } } if (!bFiles) return; // Not dragging files window.dropZone = $('#file').closest('.ibo-tab'); if (!CombodoGlobalToolbox.IsElementVisibleToTheUser(dropZone[0])) { // Hidden, but inside an inactive tab? Highlight the tab var sTabId = dropZone.closest('.ibo-tab-container--tab-container').attr('aria-labelledby'); dropZone = $('#'+sTabId).closest('li'); } window.dropZone.addClass('ibo-drag-in'); }); // Counter used to fix chrome firing dragenter/dragleave on each $(document) child it encounter window.dropZoneCnt = 0; $(document).on('dragenter', function(ev) { ev.preventDefault(); // needed for IE window.dropZoneCnt++; }); $(document).bind('dragend dragleave drop', function(event){ window.dropZoneCnt--; if(window.dropZone && window.dropZoneCnt === 0){ window.dropZone.removeClass('ibo-drag-in'); window.dropZone = null; } }); // check if the attachments are used by inline images window.setTimeout( function() { $('.attachment a').each(function() { var sUrl = $(this).attr('href'); if($('img[src="'+sUrl+'"]').length > 0) { $(this).addClass('image-in-use').find('img').wrap('
'); } }); $('.htmlEditor').each(function() { var oEditor = $(this).ckeditorGet(); var sHtml = oEditor.getData(); var jElement = $('
').html(sHtml).contents(); jElement.find('img').each(function() { var sSrc = $(this).attr('src'); $('.attachment a[href="'+sSrc+'"]').parent().addClass('image-in-use').find('img').wrap('
'); }); }); $('.image-in-use-wrapper').append('
'); }, 200 ); JS ); $this->oPage->p(''); $this->oPage->add('
'); } protected function GetAttachmentContainerId($iAttachmentId) { return 'display_attachment_'.$iAttachmentId; } protected function GetAttachmentHiddenInput($iAttachmentId, $bIsDeletedAttachment) { $sInputNamePrefix = $bIsDeletedAttachment ? 'removed_' : ''; return ''; } protected function GetDeleteAttachmentButton($iAttId) { $oButton = ButtonUIBlockFactory::MakeDestructiveIconLink('fas fa-trash', Dict::S('Attachments:DeleteBtn'), '', Dict::S('Attachments:DeleteBtn'), null, "btn_remove_".$iAttId); $oButton->AddCSSClass('btn_hidden') ->SetOnClickJsCode("RemoveAttachment(".$iAttId.");"); return $oButton; } protected function GetDeleteAttachmentJs() { return <<GetAttachmentsCount() === 0) { $this->oPage->add(Dict::S('Attachments:NoAttachment')); return; } $sThumbnail = Dict::S('Attachments:File:Thumbnail'); $sFileName = Dict::S('Attachments:File:Name'); $sFileSize = Dict::S('Attachments:File:Size'); $sFileDate = Dict::S('Attachments:File:Date'); $sFileUploader = Dict::S('Attachments:File:Uploader'); $sFileType = Dict::S('Attachments:File:MimeType'); $sDeleteColumn = ''; if ($bWithDeleteButton) { $this->oPage->add_script($this->GetDeleteAttachmentJs()); } $bIsEven = false; $aAttachmentsDate = AttachmentsHelper::GetAttachmentsDateAddedFromDb($this->sObjClass, $this->iObjKey); $aData = array(); while ($oAttachment = $this->oAttachmentsSet->Fetch()) { $bIsEven = ($bIsEven) ? false : true; $aData[] = $this->AddAttachmentsTableLine($bWithDeleteButton, $bIsEven, $oAttachment, $aAttachmentsDate, $aAttachmentsDeleted); } while ($oTempAttachment = $this->oTempAttachmentsSet->Fetch()) { $bIsEven = ($bIsEven) ? false : true; $aData[] = $this->AddAttachmentsTableLine($bWithDeleteButton, $bIsEven, $oTempAttachment, $aAttachmentsDate, $aAttachmentsDeleted); } $aAttribs = array( 'icon' => array('label' => $sThumbnail, 'description' => $sThumbnail), 'filename' => array('label' => $sFileName, 'description' => $sFileName), 'formatted-size' => array('label' => $sFileSize, 'description' => $sFileSize), 'upload-date' => array('label' => $sFileDate, 'description' => $sFileDate), 'uploader' => array('label' => $sFileUploader, 'description' => $sFileUploader), 'type' => array('label' => $sFileType, 'description' => $sFileType), ); if ($bWithDeleteButton) { $aAttribs['delete'] = array('label' => '', 'description' => ''); } $oAttachmentTableBlock = DataTableUIBlockFactory::MakeForStaticData('', $aAttribs, $aData); $oAttachmentTableBlock->AddCSSClass('ibo-attachment--datatable'); $this->oPage->AddUiBlock($oAttachmentTableBlock); $sTableId = $oAttachmentTableBlock->GetId(); } /** * @param bool $bWithDeleteButton * @param bool $bIsEven * @param \DBObject $oAttachment * @param array $aAttachmentsDate * @param int[] $aAttachmentsDeleted * * @return array * @throws \ArchivedObjectException * @throws \CoreException * @throws \Exception */ private function AddAttachmentsTableLine($bWithDeleteButton, $bIsEven, $oAttachment, $aAttachmentsDate, $aAttachmentsDeleted) { $iAttachmentId = $oAttachment->GetKey(); $sLineClass = ''; if ($bIsEven) { $sLineClass = 'class="even"'; } $sLineStyle = ''; $bIsDeletedAttachment = false; if (in_array($iAttachmentId, $aAttachmentsDeleted, true)) { $sLineStyle = 'style="display: none;"'; $bIsDeletedAttachment = true; } /** @var \ormDocument $oDoc */ $oDoc = $oAttachment->Get('contents'); $sDocDownloadUrl = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL.$iAttachmentId; $sFileName = utils::HtmlEntities($oDoc->GetFileName()); $sTrId = $this->GetAttachmentContainerId($iAttachmentId); $sAttachmentMeta = $this->GetAttachmentHiddenInput($iAttachmentId, $bIsDeletedAttachment); $iFileSize = $oDoc->GetSize(); $sFileFormattedSize = $oDoc->GetFormattedSize(); $bIsTempAttachment = ($oAttachment->Get('item_id') === 0); $sAttachmentDateFormatted = ''; $iAttachmentDateRaw = ''; if (!$bIsTempAttachment) { $sAttachmentDate = $oAttachment->Get('creation_date'); if (empty($sAttachmentDate) && array_key_exists($iAttachmentId, $aAttachmentsDate)) { $sAttachmentDate = $aAttachmentsDate[$iAttachmentId]; } $oAttachmentDate = DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $sAttachmentDate); $sAttachmentDateFormatted = AttributeDateTime::GetFormat()->Format($oAttachmentDate); $iAttachmentDateRaw = AttributeDateTime::GetAsUnixSeconds($sAttachmentDate); } $sAttachmentUploader = $oAttachment->Get('contact_id_friendlyname'); $sAttachmentUploaderForHtml = utils::HtmlEntities($sAttachmentUploader); $sFileType = $oDoc->GetMimeType(); $sAttachmentThumbUrl = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName); $sIconClass = ''; $iMaxWidth = MetaModel::GetModuleSetting('itop-attachments', 'preview_max_width', 290); $iMaxSizeForPreview = MetaModel::GetModuleSetting('itop-attachments', 'icon_preview_max_size', self::DEFAULT_MAX_SIZE_FOR_PREVIEW); $sPreviewNotAvailable = Dict::S('Attachments:PreviewNotAvailable'); $sPreviewMarkup = $sPreviewNotAvailable; if ($oDoc->IsPreviewAvailable()) { $sIconClass = ' preview'; if ($oDoc->GetSize() <= $iMaxSizeForPreview) { $sAttachmentThumbUrl = $sDocDownloadUrl; } $sPreviewMarkup = utils::HtmlEntities(''); } $aAttachmentLine = array( '@id' => $sTrId, '@meta' => 'data-file-type="'.utils::HtmlEntities($sFileType).'" data-file-size-raw="'.utils::HtmlEntities($iFileSize).'" data-file-size-formatted="'.utils::HtmlEntities($sFileFormattedSize).'" data-file-uploader="'.utils::HtmlEntities($sAttachmentUploader).'"', 'icon' => '', 'filename' => ''.$sFileName.''.$sAttachmentMeta, 'formatted-size' => $sFileFormattedSize, 'upload-date' => $sAttachmentDateFormatted, 'uploader' => $sAttachmentUploader, 'type' => $sFileType, ); if ($bWithDeleteButton) { $sDeleteButton = $this->GetDeleteAttachmentButton($iAttachmentId); $oBlockRenderer = new BlockRenderer($sDeleteButton); $this->oPage->add_ready_script($oBlockRenderer->RenderJsInline($sDeleteButton::ENUM_JS_TYPE_ON_INIT)); $aAttachmentLine['delete'] = $oBlockRenderer->RenderHtml(); } return $aAttachmentLine; } }