class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExtension { protected static $m_bIsModified = false; public function OnDisplayProperties($oObject, WebPage $oPage, $bEditMode = false) { if ($this->GetAttachmentsPosition() == 'properties') { $this->DisplayAttachments($oObject, $oPage, $bEditMode); } } public function OnDisplayRelations($oObject, WebPage $oPage, $bEditMode = false) { if ($this->GetAttachmentsPosition() == 'relations') { $this->DisplayAttachments($oObject, $oPage, $bEditMode); } } public function OnFormSubmit($oObject, $sFormPrefix = '') { if ($this->IsTargetObject($oObject)) { // For new objects attachments are processed in OnDBInsert if (!$oObject->IsNew()) { self::UpdateAttachments($oObject); } } } protected function GetMaxUpload() { $iMaxUpload = ini_get('upload_max_filesize'); if (!$iMaxUpload) { $sRet = Dict::S('Attachments:UploadNotAllowedOnThisSystem'); } else { $iMaxUpload = utils::ConvertToBytes($iMaxUpload); if ($iMaxUpload > 1024*1024*1024) { $sRet = Dict::Format('Attachment:Max_Go', sprintf('%0.2f', $iMaxUpload/(1024*1024*1024))); } else if ($iMaxUpload > 1024*1024) { $sRet = Dict::Format('Attachment:Max_Mo', sprintf('%0.2f', $iMaxUpload/(1024*1024))); } else { $sRet = Dict::Format('Attachment:Max_Ko', sprintf('%0.2f', $iMaxUpload/(1024))); } } return $sRet; } public function OnFormCancel($sTempId) { // Delete all "pending" attachments for this form $sOQL = 'SELECT Attachment WHERE temp_id = :temp_id'; $oSearch = DBObjectSearch::FromOQL($sOQL); $oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId)); while($oAttachment = $oSet->Fetch()) { $oAttachment->DBDelete(); // Pending attachment, don't mention it in the history } } public function EnumUsedAttributes($oObject) { return array(); } public function GetIcon($oObject) { return ''; } public function GetHilightClass($oObject) { // Possible return values are: // HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE return HILIGHT_CLASS_NONE; } public function EnumAllowedActions(DBObjectSet $oSet) { // No action return array(); } public function OnIsModified($oObject) { return self::$m_bIsModified; } public function OnCheckToWrite($oObject) { return array(); } public function OnCheckToDelete($oObject) { return array(); } public function OnDBUpdate($oObject, $oChange = null) { if ($this->IsTargetObject($oObject)) { // Get all current attachments $oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id"); $oSet = new DBObjectSet($oSearch, array(), array('class' => get_class($oObject), 'item_id' => $oObject->GetKey())); while ($oAttachment = $oSet->Fetch()) { $oAttachment->SetItem($oObject, true /*updateonchange*/); } } } public function OnDBInsert($oObject, $oChange = null) { if ($this->IsTargetObject($oObject)) { self::UpdateAttachments($oObject, $oChange); } } public function OnDBDelete($oObject, $oChange = null) { if ($this->IsTargetObject($oObject)) { $oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id"); $oSet = new DBObjectSet($oSearch, array(), array('class' => get_class($oObject), 'item_id' => $oObject->GetKey())); while ($oAttachment = $oSet->Fetch()) { $oAttachment->DBDelete(); } } } /////////////////////////////////////////////////////////////////////////////////////////////////////// // // Plug-ins specific functions // /////////////////////////////////////////////////////////////////////////////////////////////////////// protected function IsTargetObject($oObject) { $aAllowedClasses = MetaModel::GetModuleSetting('itop-attachments', 'allowed_classes', array('Ticket')); foreach($aAllowedClasses as $sAllowedClass) { if ($oObject instanceof $sAllowedClass) { return true; } } return false; } protected function GetAttachmentsPosition() { return MetaModel::GetModuleSetting('itop-attachments', 'position', 'relations'); } var $m_bDeleteEnabled = true; public function EnableDelete($bEnabled) { $this->m_bDeleteEnabled = $bEnabled; } public function DisplayAttachments($oObject, WebPage $oPage, $bEditMode = false) { // Exit here if the class is not allowed if (!$this->IsTargetObject($oObject)) return; $oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id"); $oSet = new DBObjectSet($oSearch, array(), array('class' => get_class($oObject), 'item_id' => $oObject->GetKey())); if ($this->GetAttachmentsPosition() == 'relations') { $sTitle = ($oSet->Count() > 0)? Dict::Format('Attachments:TabTitle_Count', $oSet->Count()) : Dict::S('Attachments:EmptyTabTitle'); $oPage->SetCurrentTab($sTitle); } $oPage->add_style( <<add('
'); $oPage->add(''.Dict::S('Attachments:FieldsetTitle').''); if ($bEditMode) { $sIsDeleteEnabled = $this->m_bDeleteEnabled ? 'true' : 'false'; $iTransactionId = $oPage->GetTransactionId(); $sClass = get_class($oObject); $sTempId = session_id().'_'.$iTransactionId; $sDeleteBtn = Dict::S('Attachments:DeleteBtn'); $oPage->add_script( <<
'+data.msg+'

'); if($sIsDeleteEnabled) { $('#display_attachment_'+data.att_id).hover( function() { $(this).children(':button').toggleClass('btn_hidden'); } ); } $('#attachment_plugin').trigger('add_attachment', [data.att_id, data.msg]); //alert(data.msg); } } }, error: function (data, status, e) { alert(e); } } ) return false; } EOF ); $oPage->add(''); while ($oAttachment = $oSet->Fetch()) { $iAttId = $oAttachment->GetKey(); $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName); $sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false'; $sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents'; $oPage->add(''); } // Suggested attachments are listed here but treated as temporary $aDefault = utils::ReadParam('default', array(), false, 'raw_data'); if (array_key_exists('suggested_attachments', $aDefault)) { $sSuggestedAttachements = $aDefault['suggested_attachments']; if (is_array($sSuggestedAttachements)) { $sSuggestedAttachements = implode(',', $sSuggestedAttachements); } $oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE id IN($sSuggestedAttachements)"); $oSet = new DBObjectSet($oSearch, array()); if ($oSet->Count() > 0) { while ($oAttachment = $oSet->Fetch()) { // Mark the attachments as temporary attachments for the current object/form $oAttachment->Set('temp_id', $sTempId); $oAttachment->DBUpdate(); // Display them $iAttId = $oAttachment->GetKey(); $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName); $sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents'; $sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false'; $oPage->add(''); $oPage->add_ready_script("$('#attachment_plugin').trigger('add_attachment', [$iAttId, '".addslashes($sFileName)."']);"); } } } $oPage->add(''); $oPage->add('
'); $sMaxUpload = $this->GetMaxUpload(); // $oPage->p(Dict::S('Attachments:AddAttachment').' '.$sMaxUpload); $oPage->p(Dict::S('Attachments:AddAttachment').' '.$sMaxUpload); $oPage->add_linked_script('../js/jquery.iframe-transport.js'); $oPage->add_linked_script('../js/jquery.fileupload.js'); $oPage->add_ready_script( <<< EOF $('#file').fileupload({ url: GetAbsoluteUrlModulesRoot()+'itop-attachments/ajax.attachment.php', formData: { operation: 'add', temp_id: '$sTempId', obj_class: '$sClass' }, dataType: 'json', done: function (e, data) { if(typeof(data.result.error) != 'undefined') { if(data.result.error != '') { alert(data.result.error); } else { var sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?operation=download_document&class=Attachment&id='+data.result.att_id+'&field=contents'; $('#attachments').append(''); if($sIsDeleteEnabled) { $('#display_attachment_'+data.result.att_id).hover( function() { $(this).children(':button').toggleClass('btn_hidden'); } ); } $('#attachment_plugin').trigger('add_attachment', [data.result.att_id, data.msg]); } } }, start: function() { $('#attachment_loading').show(); }, stop: function() { $('#attachment_loading').hide(); } }); $(document).bind('dragover', function (e) { var bFiles = false; if (e.dataTransfer.types) { for (var i = 0; i < e.dataTransfer.types.length; i++) { if (e.dataTransfer.types[i] == "text/plain") { bFiles = false; // mozilla contains "Files" in the types list when dragging images inside the page, but it also contains "text/plain" before break; } if (e.dataTransfer.types[i] == "Files") { bFiles = true; break; } } } if (!bFiles) return; // Not dragging files var dropZone = $('#file').closest('fieldset'); if (!dropZone.is(':visible')) { // Hidden, but inside an inactive tab? Higlight the tab var sTabId = dropZone.closest('.ui-tabs-panel').attr('aria-labelledby'); dropZone = $('#'+sTabId).closest('li'); } timeout = window.dropZoneTimeout; if (!timeout) { dropZone.addClass('drag_in'); } else { clearTimeout(timeout); } window.dropZoneTimeout = setTimeout(function () { window.dropZoneTimeout = null; dropZone.removeClass('drag_in'); }, 300); }); EOF ); $oPage->p(''); $oPage->p(''); $oPage->add('
'); if ($this->m_bDeleteEnabled) { $oPage->add_ready_script('$(".attachment").hover( function() {$(this).children(":button").toggleClass("btn_hidden"); } );'); } } else { $oPage->add(''); if ($oSet->Count() == 0) { $oPage->add(Dict::S('Attachments:NoAttachment')); } else { while ($oAttachment = $oSet->Fetch()) { $iAttId = $oAttachment->GetKey(); $oDoc = $oAttachment->Get('contents'); $sFileName = $oDoc->GetFileName(); $sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName); $sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false'; $sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents'; $oPage->add(''); } } } $sPreviewNotAvailable = addslashes(Dict::S('Attachments:PreviewNotAvailable')); $iMaxWidth = MetaModel::GetModuleSetting('itop-attachments', 'preview_max_width', 290); $oPage->add_ready_script("$(document).tooltip({ items: '.attachment a', position: { my: 'left top', at: 'right top', using: function( position, feedback ) { $( this ).css( position ); }}, content: function() { if ($(this).attr('data-preview') == 'true') { return('');} else { return '$sPreviewNotAvailable'; }}});"); } protected static function UpdateAttachments($oObject, $oChange = null) { self::$m_bIsModified = false; if (utils::ReadParam('attachment_plugin', 'not-in-form') == 'not-in-form') { // Workaround to an issue in iTop < 2.0 // Leave silently if there is no trace of the attachment form return; } $iTransactionId = utils::ReadParam('transaction_id', null); if (!is_null($iTransactionId)) { $aActions = array(); $aAttachmentIds = utils::ReadParam('attachments', array()); $aRemovedAttachmentIds = utils::ReadParam('removed_attachments', array()); // Get all current attachments $oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id"); $oSet = new DBObjectSet($oSearch, array(), array('class' => get_class($oObject), 'item_id' => $oObject->GetKey())); while ($oAttachment = $oSet->Fetch()) { // Remove attachments that are no longer attached to the current object if (in_array($oAttachment->GetKey(), $aRemovedAttachmentIds)) { $oAttachment->DBDelete(); $aActions[] = self::GetActionDescription($oAttachment, false /* false => deletion */); } } // Attach new (temporary) attachements $sTempId = session_id().'_'.$iTransactionId; // The object is being created from a form, check if there are pending attachments // for this object, but deleting the "new" ones that were already removed from the form $sOQL = 'SELECT Attachment WHERE temp_id = :temp_id'; $oSearch = DBObjectSearch::FromOQL($sOQL); foreach($aAttachmentIds as $iAttachmentId) { $oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId)); while($oAttachment = $oSet->Fetch()) { if (in_array($oAttachment->GetKey(),$aRemovedAttachmentIds)) { $oAttachment->DBDelete(); // temporary attachment removed, don't even mention it in the history } else { $oAttachment->SetItem($oObject); $oAttachment->Set('temp_id', ''); $oAttachment->DBUpdate(); // temporary attachment confirmed, list it in the history $aActions[] = self::GetActionDescription($oAttachment, true /* true => creation */); } } } if (count($aActions) > 0) { if ($oChange == null) { // Let's create a change if non is supplied $oChange = MetaModel::NewObject("CMDBChange"); $oChange->Set("date", time()); $sUserString = CMDBChange::GetCurrentUserName(); $oChange->Set("userinfo", $sUserString); $iChangeId = $oChange->DBInsert(); } foreach($aActions as $sActionDescription) { self::RecordHistory($oChange, $oObject, $sActionDescription); } self::$m_bIsModified = true; } } } ///////////////////////////////////////////////////////////////////////////////////////// public static function GetFileIcon($sFileName) { $aPathParts = pathinfo($sFileName); switch($aPathParts['extension']) { case 'doc': case 'docx': $sIcon = 'doc.png'; break; case 'xls': case 'xlsx': $sIcon = 'xls.png'; break; case 'ppt': case 'pptx': $sIcon = 'ppt.png'; break; case 'pdf': $sIcon = 'pdf.png'; break; case 'txt': case 'text': $sIcon = 'txt.png'; break; case 'rtf': $sIcon = 'rtf.png'; break; case 'odt': $sIcon = 'odt.png'; break; case 'ods': $sIcon = 'ods.png'; break; case 'odp': $sIcon = 'odp.png'; break; case 'html': case 'htm': $sIcon = 'html.png'; break; case 'png': case 'gif': case 'jpg': case 'jpeg': case 'tiff': case 'tif': case 'bmp': $sIcon = 'image.png'; break; case 'zip': case 'gz': case 'tgz': case 'rar': $sIcon = 'zip.png'; break; default: $sIcon = 'document.png'; break; } return 'env-'.utils::GetCurrentEnvironment()."/itop-attachments/icons/$sIcon"; } ///////////////////////////////////////////////////////////////////////// private static function RecordHistory(CMDBChange $oChange, $oTargetObject, $sDescription) { $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpPlugin"); $oMyChangeOp->Set("change", $oChange->GetKey()); $oMyChangeOp->Set("objclass", get_class($oTargetObject)); $oMyChangeOp->Set("objkey", $oTargetObject->GetKey()); $oMyChangeOp->Set("description", $sDescription); $iId = $oMyChangeOp->DBInsertNoReload(); } ///////////////////////////////////////////////////////////////////////// private static function GetActionDescription($oAttachment, $bCreate = true) { $oBlob = $oAttachment->Get('contents'); $sFileName = $oBlob->GetFileName(); if ($bCreate) { $sDescription = Dict::Format('Attachments:History_File_Added', $sFileName); } else { $sDescription = Dict::Format('Attachments:History_File_Removed', $sFileName); } return $sDescription; } } ?>