mirror of
https://github.com/Combodo/iTop.git
synced 2026-02-13 07:24:13 +01:00
754 lines
21 KiB
PHP
754 lines
21 KiB
PHP
<?php
|
|
|
|
/*
|
|
* @copyright Copyright (C) 2010-2024 Combodo SAS
|
|
* @license http://opensource.org/licenses/AGPL-3.0
|
|
*/
|
|
|
|
use Combodo\iTop\Application\UI\Base\Component\FieldSet\FieldSetUIBlockFactory;
|
|
use Combodo\iTop\Application\WebPage\WebPage;
|
|
use Combodo\iTop\Service\Events\EventData;
|
|
use Combodo\iTop\Service\Events\EventService;
|
|
use Combodo\iTop\Service\Events\iEventServiceSetup;
|
|
use Combodo\iTop\Application\UI\Base\Component\Html\Html;
|
|
|
|
class AttachmentPlugIn implements iApplicationUIExtension, iEventServiceSetup
|
|
{
|
|
public const ENUM_GUI_ALL = 'all';
|
|
public const ENUM_GUI_BACKOFFICE = 'backoffice';
|
|
public const ENUM_GUI_PORTALS = 'portals';
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the value of "upload_max_filesize" in bytes if upload allowed, false otherwise.
|
|
*
|
|
* @return number|boolean
|
|
* @since 2.6.1
|
|
*
|
|
*/
|
|
public static function GetMaxUploadSize()
|
|
{
|
|
$sMaxUpload = ini_get('upload_max_filesize');
|
|
if (!$sMaxUpload) {
|
|
$result = false;
|
|
} else {
|
|
$result = utils::ConvertToBytes($sMaxUpload);
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param cmdbAbstractObject $oObject
|
|
*
|
|
* @return bool
|
|
* @since 3.2.1 N°7534
|
|
*/
|
|
public static function IsAttachmentAllowedForObject(cmdbAbstractObject $oObject): bool
|
|
{
|
|
$aAllowedClasses = MetaModel::GetModuleSetting('itop-attachments', 'allowed_classes', ['Ticket']);
|
|
foreach ($aAllowedClasses as $sAllowedClass) {
|
|
if ($oObject instanceof $sAllowedClass) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns the max. file upload size allowed as a dictionary entry
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function GetMaxUpload()
|
|
{
|
|
$iMaxUpload = static::GetMaxUploadSize();
|
|
if (!$iMaxUpload) {
|
|
$sRet = Dict::S('Attachments:UploadNotAllowedOnThisSystem');
|
|
} else {
|
|
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)
|
|
{
|
|
// Protection against unfortunate massive delete of attachments when a null temp ID is passed
|
|
if (strlen($sTempId) === 0) {
|
|
return;
|
|
}
|
|
|
|
// Delete all "pending" attachments for this form
|
|
$sOQL = 'SELECT Attachment WHERE temp_id = :temp_id';
|
|
$oSearch = DBObjectSearch::FromOQL($sOQL);
|
|
$oSet = new DBObjectSet($oSearch, [], ['temp_id' => $sTempId]);
|
|
while ($oAttachment = $oSet->Fetch()) {
|
|
$oAttachment->DBDelete();
|
|
// Pending attachment, don't mention it in the history
|
|
}
|
|
}
|
|
|
|
public function EnumUsedAttributes($oObject)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
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 [];
|
|
}
|
|
|
|
public function RegisterEventsAndListeners(): void
|
|
{
|
|
EventService::RegisterListener(EVENT_DB_AFTER_WRITE, [$this, 'OnDBAfterWrite']);
|
|
EventService::RegisterListener(EVENT_DB_AFTER_DELETE, [$this, 'OnDBAfterDelete']);
|
|
}
|
|
|
|
public function OnDBAfterWrite(EventData $oEventData)
|
|
{
|
|
$oObject = $oEventData->Get('object');
|
|
$oCMDBChange = $oEventData->Get('cmdb_change');
|
|
$bIsNew = $oEventData->Get('is_new');
|
|
|
|
if ($this->IsTargetObject($oObject)) {
|
|
if ($bIsNew) {
|
|
self::UpdateAttachments($oObject, $oCMDBChange);
|
|
} else {
|
|
// Get all current attachments
|
|
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id");
|
|
$oSet = new DBObjectSet($oSearch, [], ['class' => get_class($oObject), 'item_id' => $oObject->GetKey()]);
|
|
while ($oAttachment = $oSet->Fetch()) {
|
|
$oAttachment->SetItem($oObject, true /*updateonchange*/);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public function OnDBAfterDelete(EventData $oEventData)
|
|
{
|
|
$oObject = $oEventData->Get('object');
|
|
|
|
if ($this->IsTargetObject($oObject)) {
|
|
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id");
|
|
$oSet = new DBObjectSet($oSearch, [], ['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', ['Ticket']);
|
|
foreach ($aAllowedClasses as $sAllowedClass) {
|
|
if ($oObject instanceof $sAllowedClass) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
protected function GetAttachmentsPosition()
|
|
{
|
|
return MetaModel::GetModuleSetting('itop-attachments', 'position', 'relations');
|
|
}
|
|
|
|
public $m_bDeleteEnabled = true;
|
|
|
|
public function EnableDelete($bEnabled)
|
|
{
|
|
$this->m_bDeleteEnabled = $bEnabled;
|
|
}
|
|
|
|
/**
|
|
* @param \DBObject $oObject
|
|
* @param WebPage $oPage
|
|
* @param bool $bEditMode
|
|
*
|
|
* @throws \CoreException
|
|
* @throws \MissingQueryArgument
|
|
* @throws \MySQLException
|
|
* @throws \MySQLHasGoneAwayException
|
|
* @throws \InvalidParameterException
|
|
*/
|
|
public function DisplayAttachments(DBObject $oObject, WebPage $oPage, $bEditMode = false)
|
|
{
|
|
// Exit here if the class is not allowed
|
|
if (!$this->IsTargetObject($oObject)) {
|
|
return;
|
|
}
|
|
|
|
$sObjClass = get_class($oObject);
|
|
$iObjKey = $oObject->GetKey();
|
|
$sTransactionId = $oPage->GetTransactionId();
|
|
if ($bEditMode && empty($sTransactionId)) {
|
|
throw new InvalidParameterException('Attachments renderer : invalid transaction id');
|
|
}
|
|
$oAttachmentsRenderer = AttachmentsRendererFactory::GetInstance($oPage, $sObjClass, $iObjKey, $sTransactionId);
|
|
|
|
$iCount = $oAttachmentsRenderer->GetAttachmentsSet()->Count() + $oAttachmentsRenderer->GetTempAttachmentsSet()->Count();
|
|
$sTitle = ($iCount > 0) ? Dict::Format('Attachments:TabTitle_Count', $iCount) : Dict::S('Attachments:EmptyTabTitle');
|
|
if ($this->GetAttachmentsPosition() === 'relations') {
|
|
$oPage->SetCurrentTab('Attachments:Tab', $sTitle);
|
|
} else {
|
|
$oBlock = FieldSetUIBlockFactory::MakeStandard($sTitle);
|
|
$oBlock->AddSubBlock(new Html(''));
|
|
$oPage->AddUiBlock($oBlock);
|
|
}
|
|
|
|
$bIsReadOnlyState = self::IsReadonlyState($oObject, $oObject->GetState(), AttachmentPlugIn::ENUM_GUI_BACKOFFICE);
|
|
if ($bEditMode && !$bIsReadOnlyState) {
|
|
$oAttachmentsRenderer->RenderEditAttachmentsList();
|
|
} else {
|
|
$oAttachmentsRenderer->RenderViewAttachmentsList();
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @see ObjectFormManager::FinalizeAttachments() for the portal version
|
|
*
|
|
* @param $oObject
|
|
* @param $oCMDBChange
|
|
*
|
|
* @return void
|
|
* @throws \ArchivedObjectException
|
|
* @throws \CoreCannotSaveObjectException
|
|
* @throws \CoreException
|
|
* @throws \CoreUnexpectedValue
|
|
* @throws \DeleteException
|
|
* @throws \MySQLException
|
|
* @throws \MySQLHasGoneAwayException
|
|
* @throws \OQLException
|
|
*/
|
|
protected static function UpdateAttachments($oObject, $oCMDBChange = null)
|
|
{
|
|
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;
|
|
}
|
|
$sTransactionId = utils::ReadParam('transaction_id', null, false, 'transaction_id');
|
|
if (!is_null($sTransactionId)) {
|
|
$aActions = [];
|
|
$aRemovedAttachmentIds = utils::ReadParam('removed_attachments', []);
|
|
|
|
// Get all current attachments
|
|
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id");
|
|
$oSearch->AllowAllData();
|
|
$oSet = new DBObjectSet($oSearch, [], ['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)) {
|
|
$aData = ['attachment' => $oAttachment];
|
|
$oObject->FireEvent(EVENT_REMOVE_ATTACHMENT_FROM_OBJECT, $aData);
|
|
$oAttachment->DBDelete();
|
|
$aActions[] = self::GetActionChangeOp($oAttachment, false /* false => deletion */);
|
|
}
|
|
}
|
|
|
|
// Attach new (temporary) attachments
|
|
$sTempId = utils::GetUploadTempId($sTransactionId);
|
|
// 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);
|
|
$oSearch->AllowAllData();
|
|
$oSet = new DBObjectSet($oSearch, [], ['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::GetActionChangeOp($oAttachment, true /* true => creation */);
|
|
$aData = ['attachment' => $oAttachment];
|
|
$oObject->FireEvent(EVENT_ADD_ATTACHMENT_TO_OBJECT, $aData);
|
|
}
|
|
}
|
|
if (count($aActions) > 0) {
|
|
foreach ($aActions as $oChangeOp) {
|
|
self::RecordHistory($oCMDBChange, $oObject, $oChangeOp);
|
|
}
|
|
|
|
$oObject->MarkObjectAsModified();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static function CopyAttachments($oObject, $sTransactionId)
|
|
{
|
|
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE item_class = :class AND item_id = :item_id");
|
|
$oSet = new DBObjectSet($oSearch, [], ['class' => get_class($oObject), 'item_id' => $oObject->GetKey()]);
|
|
// Attach new (temporary) attachments
|
|
$sTempId = utils::GetUploadTempId($sTransactionId);
|
|
while ($oAttachment = $oSet->Fetch()) {
|
|
$oTempAttachment = clone $oAttachment;
|
|
$oTempAttachment->Set('expire', time() + utils::GetConfig()->Get('draft_attachments_lifetime'));
|
|
$oTempAttachment->Set('item_id', null);
|
|
$oTempAttachment->Set('temp_id', $sTempId);
|
|
$oTempAttachment->DBInsert();
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
public static function GetFileIcon($sFileName)
|
|
{
|
|
$aPathParts = pathinfo($sFileName);
|
|
if (!array_key_exists('extension', $aPathParts)) {
|
|
// No extension: use the default icon
|
|
$sIcon = 'icons8-file.svg';
|
|
} else {
|
|
switch (strtolower($aPathParts['extension'])) {
|
|
case 'doc':
|
|
case 'docx':
|
|
$sIcon = 'icons8-word.svg';
|
|
break;
|
|
|
|
case 'xls':
|
|
case 'xlsx':
|
|
case 'xlsm':
|
|
$sIcon = 'icons8-xls.svg';
|
|
break;
|
|
|
|
case 'ppt':
|
|
case 'pptx':
|
|
case 'pps':
|
|
$sIcon = 'icons8-ppt.svg';
|
|
break;
|
|
|
|
case 'c':
|
|
case 'cgi':
|
|
case 'pl':
|
|
case 'class':
|
|
case 'cpp':
|
|
case 'cs':
|
|
case 'h':
|
|
case 'java':
|
|
case 'py':
|
|
case 'php':
|
|
case 'sh':
|
|
case 'swift':
|
|
case 'vb':
|
|
$sIcon = 'icons8-code-file.svg';
|
|
break;
|
|
|
|
case 'pdf':
|
|
$sIcon = 'icons8-pdf.svg';
|
|
break;
|
|
|
|
case 'txt':
|
|
case 'text':
|
|
$sIcon = 'icons8-txt.svg';
|
|
break;
|
|
|
|
case 'rtf':
|
|
$sIcon = 'rtf.png';
|
|
break;
|
|
|
|
case 'odt':
|
|
$sIcon = 'icons8-libre-office-writer.svg';
|
|
break;
|
|
|
|
case 'ods':
|
|
$sIcon = 'icons8-libre-office-calc.svg';
|
|
break;
|
|
|
|
case 'odp':
|
|
$sIcon = 'icons8-libre-office-impress.svg';
|
|
break;
|
|
|
|
case 'odb':
|
|
$sIcon = 'icons8-libre-office-base.svg';
|
|
break;
|
|
|
|
case 'odg':
|
|
$sIcon = 'icons8-libre-office-draw.svg';
|
|
break;
|
|
|
|
case 'xhtml':
|
|
case 'html':
|
|
case 'htm':
|
|
$sIcon = 'icons8-html-filetype.svg';
|
|
break;
|
|
|
|
case 'png':
|
|
case 'gif':
|
|
case 'jpg':
|
|
case 'jpeg':
|
|
case 'tiff':
|
|
case 'tif':
|
|
case 'bmp':
|
|
case 'ico':
|
|
case 'psd':
|
|
case 'svg':
|
|
case 'ai':
|
|
$sIcon = 'icons8-image-file.svg';
|
|
break;
|
|
|
|
case 'zip':
|
|
case 'gz':
|
|
case 'tgz':
|
|
case 'rar':
|
|
case '7z':
|
|
case 'pkg':
|
|
case 'tar':
|
|
$sIcon = 'icons8-archive-folder.svg';
|
|
break;
|
|
|
|
case 'avi':
|
|
case 'mp4':
|
|
case 'mpeg':
|
|
case 'mpg':
|
|
case 'h264':
|
|
case 'mkv':
|
|
case 'mov':
|
|
case 'm4v':
|
|
case 'wmv':
|
|
$sIcon = 'icons8-video-file.svg';
|
|
break;
|
|
|
|
case 'aif':
|
|
case 'cda':
|
|
case 'mid':
|
|
case 'midi':
|
|
case 'mp3':
|
|
case 'mpa':
|
|
case 'ogg':
|
|
case 'wav':
|
|
case 'wma':
|
|
$sIcon = 'icons8-audio-file.svg';
|
|
break;
|
|
|
|
case 'csv':
|
|
$sIcon = 'icons8-csv.svg';
|
|
break;
|
|
|
|
case 'log':
|
|
$sIcon = 'icons8-event-log.svg';
|
|
break;
|
|
|
|
case 'sql':
|
|
$sIcon = 'icons8-sql.svg';
|
|
break;
|
|
|
|
case 'xml':
|
|
$sIcon = 'icons8-xml-file.svg';
|
|
break;
|
|
|
|
case 'email':
|
|
case 'eml':
|
|
case 'emlx':
|
|
case 'msg':
|
|
$sIcon = 'icons8-mail.svg';
|
|
break;
|
|
|
|
case 'patch':
|
|
$sIcon = 'icons8-bandage.svg';
|
|
break;
|
|
|
|
default:
|
|
$sIcon = 'icons8-file.svg';
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 'env-'.utils::GetCurrentEnvironment()."/itop-attachments/icons/$sIcon";
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
private static function RecordHistory($oCMDBChange, $oTargetObject, $oMyChangeOp)
|
|
{
|
|
if (!is_null($oCMDBChange)) {
|
|
$oMyChangeOp->Set("change", $oCMDBChange->GetKey());
|
|
}
|
|
$oMyChangeOp->Set("objclass", get_class($oTargetObject));
|
|
$oMyChangeOp->Set("objkey", $oTargetObject->GetKey());
|
|
$oMyChangeOp->DBInsertNoReload();
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
private static function GetActionChangeOp($oAttachment, $bCreate = true)
|
|
{
|
|
$oBlob = $oAttachment->Get('contents');
|
|
$sFileName = $oBlob->GetFileName();
|
|
if ($bCreate) {
|
|
$oChangeOp = new CMDBChangeOpAttachmentAdded();
|
|
$oChangeOp->Set('attachment_id', $oAttachment->GetKey());
|
|
$oChangeOp->Set('filename', $sFileName);
|
|
} else {
|
|
$oChangeOp = new CMDBChangeOpAttachmentRemoved();
|
|
$oChangeOp->Set('filename', $sFileName);
|
|
}
|
|
|
|
return $oChangeOp;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
* Returns if Attachments should be readonly for $oObject in the $sState state for the $sGUI GUI
|
|
*
|
|
* @param DBObject $oObject
|
|
* @param string $sState
|
|
* @param string $sGUI
|
|
*
|
|
* @return bool
|
|
* @throws \CoreException
|
|
*/
|
|
public static function IsReadonlyState(DBObject $oObject, $sState, $sGUI = self::ENUM_GUI_ALL)
|
|
{
|
|
$aParamDefaultValue = [
|
|
static::ENUM_GUI_ALL => [
|
|
'Ticket' => ['closed'],
|
|
],
|
|
];
|
|
|
|
$bReadonly = false;
|
|
$sClass = get_class($oObject);
|
|
$aReadonlyStatus = MetaModel::GetModuleSetting('itop-attachments', 'readonly_states', $aParamDefaultValue);
|
|
if (!empty($aReadonlyStatus)) {
|
|
// Merging GUIs entries
|
|
$aEntries = [];
|
|
// - All
|
|
if (array_key_exists(static::ENUM_GUI_ALL, $aReadonlyStatus)) {
|
|
$aEntries = array_merge_recursive($aEntries, $aReadonlyStatus[static::ENUM_GUI_ALL]);
|
|
}
|
|
// - Backoffice & Portals
|
|
foreach ([static::ENUM_GUI_BACKOFFICE, static::ENUM_GUI_PORTALS] as $sEnumGUI) {
|
|
if (in_array($sGUI, [static::ENUM_GUI_ALL, $sEnumGUI])) {
|
|
if (array_key_exists($sEnumGUI, $aReadonlyStatus)) {
|
|
$aEntries = array_merge_recursive($aEntries, $aReadonlyStatus[$sEnumGUI]);
|
|
}
|
|
}
|
|
}
|
|
|
|
$aParentClasses = array_reverse(MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL));
|
|
foreach ($aParentClasses as $sParentClass) {
|
|
if (array_key_exists($sParentClass, $aEntries)) {
|
|
// If we found an ancestor of the object's class, we stop looking event if the current state is not specified
|
|
if (in_array($oObject->GetState(), $aEntries[$sParentClass])) {
|
|
$bReadonly = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $bReadonly;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Record the modification of a caselog (text)
|
|
* since the caselog itself stores the history
|
|
* of its entries, there is no need to duplicate
|
|
* the text here
|
|
*
|
|
* @package iTopORM
|
|
*/
|
|
class CMDBChangeOpAttachmentAdded extends CMDBChangeOp
|
|
{
|
|
public static function Init()
|
|
{
|
|
$aParams =
|
|
[
|
|
"category" => "core/cmdb, grant_by_profile",
|
|
"key_type" => "",
|
|
"name_attcode" => "change",
|
|
"state_attcode" => "",
|
|
"reconc_keys" => [],
|
|
"db_table" => "priv_changeop_attachment_added",
|
|
"db_key_field" => "id",
|
|
"db_finalclass_field" => "",
|
|
];
|
|
MetaModel::Init_Params($aParams);
|
|
MetaModel::Init_InheritAttributes();
|
|
MetaModel::Init_AddAttribute(new AttributeExternalKey("attachment_id", [
|
|
"targetclass" => "Attachment",
|
|
"allowed_values" => null,
|
|
"sql" => "attachment_id",
|
|
"is_null_allowed" => true,
|
|
"on_target_delete" => DEL_SILENT,
|
|
"depends_on" => [],
|
|
]));
|
|
MetaModel::Init_AddAttribute(new AttributeString("filename", [
|
|
"allowed_values" => null,
|
|
"sql" => "filename",
|
|
"default_value" => "",
|
|
"is_null_allowed" => false,
|
|
"depends_on" => [],
|
|
]));
|
|
|
|
// Display lists
|
|
MetaModel::Init_SetZListItems('details', ['attachment_id']); // Attributes to be displayed for the complete details
|
|
MetaModel::Init_SetZListItems('list', ['attachment_id']); // Attributes to be displayed for a list
|
|
}
|
|
|
|
/**
|
|
* Describe (as a text string) the modifications corresponding to this change
|
|
*/
|
|
public function GetDescription()
|
|
{
|
|
// Temporary, until we change the options of GetDescription() -needs a more global revision
|
|
$sTargetObjectClass = 'Attachment';
|
|
$iTargetObjectKey = $this->Get('attachment_id');
|
|
$sFilename = utils::EscapeHtml($this->Get('filename'));
|
|
$oTargetSearch = new DBObjectSearch($sTargetObjectClass);
|
|
$oTargetSearch->AddCondition('id', $iTargetObjectKey, '=');
|
|
|
|
$oMonoObjectSet = new DBObjectSet($oTargetSearch);
|
|
if ($oMonoObjectSet->Count() > 0) {
|
|
$oAttachment = $oMonoObjectSet->Fetch();
|
|
$oDoc = $oAttachment->Get('contents');
|
|
$sPreview = $oDoc->IsPreviewAvailable() ? 'data-preview="true"' : '';
|
|
$sResult = Dict::Format(
|
|
'Attachments:History_File_Added',
|
|
'<span class="attachment-history-added attachment"><a '.$sPreview.' target="_blank" href="'.$oDoc->GetDownloadURL(
|
|
$sTargetObjectClass,
|
|
$iTargetObjectKey,
|
|
'contents'
|
|
).'">'.$sFilename.'</a></span>'
|
|
);
|
|
} else {
|
|
$sResult = Dict::Format('Attachments:History_File_Added', '<span class="attachment-history-deleted">'.$sFilename.'</span>');
|
|
}
|
|
|
|
return $sResult;
|
|
}
|
|
}
|
|
|
|
class CMDBChangeOpAttachmentRemoved extends CMDBChangeOp
|
|
{
|
|
public static function Init()
|
|
{
|
|
$aParams =
|
|
[
|
|
"category" => "core/cmdb, grant_by_profile",
|
|
"key_type" => "",
|
|
"name_attcode" => "change",
|
|
"state_attcode" => "",
|
|
"reconc_keys" => [],
|
|
"db_table" => "priv_changeop_attachment_removed",
|
|
"db_key_field" => "id",
|
|
"db_finalclass_field" => "",
|
|
];
|
|
MetaModel::Init_Params($aParams);
|
|
MetaModel::Init_InheritAttributes();
|
|
MetaModel::Init_AddAttribute(new AttributeString("filename", [
|
|
"allowed_values" => null,
|
|
"sql" => "filename",
|
|
"default_value" => "",
|
|
"is_null_allowed" => false,
|
|
"depends_on" => [],
|
|
]));
|
|
|
|
// Display lists
|
|
MetaModel::Init_SetZListItems('details', ['filename']); // Attributes to be displayed for the complete details
|
|
MetaModel::Init_SetZListItems('list', ['filename']); // Attributes to be displayed for a list
|
|
}
|
|
|
|
/**
|
|
* Describe (as a text string) the modifications corresponding to this change
|
|
*/
|
|
public function GetDescription()
|
|
{
|
|
// Temporary, until we change the options of GetDescription() -needs a more global revision
|
|
$sResult = Dict::Format(
|
|
'Attachments:History_File_Removed',
|
|
'<span class="attachment-history-deleted">'.utils::EscapeHtml($this->Get('filename')).'</span>'
|
|
);
|
|
|
|
return $sResult;
|
|
}
|
|
}
|
|
|
|
class AttachmentsHelper
|
|
{
|
|
/**
|
|
* @param string $sObjClass class name of the objects holding the attachments
|
|
* @param int $iObjKey key of the objects holding the attachments
|
|
*
|
|
* @return array containing attachment_id as key and date as value
|
|
*/
|
|
public static function GetAttachmentsDateAddedFromDb($sObjClass, $iObjKey)
|
|
{
|
|
$sQuery = "SELECT CMDBChangeOpAttachmentAdded WHERE objclass='$sObjClass' AND objkey=$iObjKey";
|
|
try {
|
|
$oSearch = DBObjectSearch::FromOQL($sQuery);
|
|
} catch (OQLException $e) {
|
|
return [];
|
|
}
|
|
$oSet = new DBObjectSet($oSearch);
|
|
|
|
try {
|
|
$aAttachmentDates = [];
|
|
while ($oChangeOpAttAdded = $oSet->Fetch()) {
|
|
$iAttachmentId = $oChangeOpAttAdded->Get('attachment_id');
|
|
$sAttachmentDate = $oChangeOpAttAdded->Get('date');
|
|
$aAttachmentDates[$iAttachmentId] = $sAttachmentDate;
|
|
}
|
|
} catch (Exception $e) {
|
|
return [];
|
|
}
|
|
|
|
return $aAttachmentDates;
|
|
}
|
|
}
|