mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-21 17:48:43 +02:00
N°330 Attachments display as table : console
This commit is contained in:
@@ -75,6 +75,29 @@ class ormDocument
|
||||
return $this->m_sMimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int size in bits
|
||||
* @uses strlen which returns the no of bits used
|
||||
* @since 2.7.0
|
||||
*/
|
||||
public function GetSize()
|
||||
{
|
||||
return strlen($this->m_data);
|
||||
}
|
||||
|
||||
public function GetFormatedSize($precision = 2)
|
||||
{
|
||||
$bytes = $this->GetSize();
|
||||
$units = array('B', 'KB', 'MB', 'GB', 'TB');
|
||||
|
||||
$bytes = max($bytes, 0);
|
||||
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
|
||||
$pow = min($pow, count($units) - 1);
|
||||
|
||||
$bytes /= pow(1024, $pow);
|
||||
|
||||
return round($bytes, $precision).' '.$units[$pow];
|
||||
}
|
||||
public function GetData()
|
||||
{
|
||||
return $this->m_data;
|
||||
|
||||
@@ -1,106 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2019 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
require_once('../../approot.inc.php');
|
||||
require_once(APPROOT.'/application/application.inc.php');
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/ajaxwebpage.class.inc.php');
|
||||
|
||||
try
|
||||
{
|
||||
require_once(APPROOT.'/application/startup.inc.php');
|
||||
// require_once(APPROOT.'/application/user.preferences.class.inc.php');
|
||||
|
||||
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
|
||||
LoginWebPage::DoLoginEx(null /* any portal */, false);
|
||||
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
|
||||
$sOperation = utils::ReadParam('operation', '');
|
||||
|
||||
switch($sOperation)
|
||||
{
|
||||
case 'add':
|
||||
$aResult = array(
|
||||
'error' => '',
|
||||
'att_id' => 0,
|
||||
'preview' => 'false',
|
||||
'msg' => ''
|
||||
);
|
||||
$sObjClass = stripslashes(utils::ReadParam('obj_class', '', false, 'class'));
|
||||
$sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id');
|
||||
if (empty($sObjClass))
|
||||
{
|
||||
$aResult['error'] = "Missing argument 'obj_class'";
|
||||
}
|
||||
elseif (empty($sTempId))
|
||||
{
|
||||
$aResult['error'] = "Missing argument 'temp_id'";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
$oDoc = utils::ReadPostedDocument('file');
|
||||
/** @var Attachment $oAttachment */
|
||||
$oAttachment = MetaModel::NewObject('Attachment');
|
||||
$oAttachment->Set('expire', time() + MetaModel::GetConfig()->Get('draft_attachments_lifetime'));
|
||||
$oAttachment->Set('temp_id', $sTempId);
|
||||
$oAttachment->Set('item_class', $sObjClass);
|
||||
$oAttachment->SetDefaultOrgId();
|
||||
$oAttachment->Set('contents', $oDoc);
|
||||
$iAttId = $oAttachment->DBInsert();
|
||||
|
||||
$aResult['msg'] = htmlentities($oDoc->GetFileName(), ENT_QUOTES, 'UTF-8');
|
||||
$aResult['icon'] = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($oDoc->GetFileName());
|
||||
$aResult['att_id'] = $iAttId;
|
||||
$aResult['preview'] = $oDoc->IsPreviewAvailable() ? 'true' : 'false';
|
||||
}
|
||||
catch (FileUploadException $e)
|
||||
{
|
||||
$aResult['error'] = $e->GetMessage();
|
||||
}
|
||||
}
|
||||
$oPage->add(json_encode($aResult));
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
$iAttachmentId = utils::ReadParam('att_id', '');
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE id = :id");
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('id' => $iAttachmentId));
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
$oAttachment->DBDelete();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$oPage->p("Missing argument 'operation'");
|
||||
}
|
||||
|
||||
$oPage->output();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// note: transform to cope with XSS attacks
|
||||
echo htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8');
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
?>
|
||||
140
datamodels/2.x/itop-attachments/ajax.itop-attachment.php
Normal file
140
datamodels/2.x/itop-attachments/ajax.itop-attachment.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* Copyright (C) 2013-2019 Combodo SARL
|
||||
*
|
||||
* This file is part of iTop.
|
||||
*
|
||||
* iTop is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* iTop is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
*/
|
||||
|
||||
require_once('../../approot.inc.php');
|
||||
require_once(APPROOT.'/application/application.inc.php');
|
||||
require_once(APPROOT.'/application/webpage.class.inc.php');
|
||||
require_once(APPROOT.'/application/ajaxwebpage.class.inc.php');
|
||||
|
||||
/**
|
||||
* @param \ajax_page $oPage
|
||||
* @param int $iTransactionId
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
function RenderAttachments(ajax_page $oPage, $iTransactionId)
|
||||
{
|
||||
$sClass = utils::ReadParam('objclass', '');
|
||||
$sId = utils::ReadParam('objkey', '');
|
||||
$oObject = MetaModel::GetObject($sClass, $sId, false);
|
||||
$bEditMode = utils::ReadParam('edit_mode', 0);
|
||||
$aAttachmentsDeleted = utils::ReadParam('attachments_deleted', array());
|
||||
|
||||
$oPage->SetContentType('text/html');
|
||||
$oAttachmentsRenderer = AttachmentsRendererFactory::GetInstance($oPage, $sClass, $sId, $iTransactionId);
|
||||
|
||||
$bIsReadOnlyState = (is_null($oObject))
|
||||
? false
|
||||
: AttachmentPlugIn::IsReadonlyState($oObject, $oObject->GetState(), AttachmentPlugIn::ENUM_GUI_BACKOFFICE);
|
||||
if ($bEditMode && !$bIsReadOnlyState)
|
||||
{
|
||||
$oAttachmentsRenderer->RenderEditAttachmentsList($aAttachmentsDeleted);
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttachmentsRenderer->RenderViewAttachmentsList();
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
require_once APPROOT.'/application/startup.inc.php';
|
||||
require_once APPROOT.'/application/loginwebpage.class.inc.php';
|
||||
LoginWebPage::DoLoginEx(null /* any portal */, false);
|
||||
|
||||
$oPage = new ajax_page("");
|
||||
$oPage->no_cache();
|
||||
|
||||
$sOperation = utils::ReadParam('operation', '');
|
||||
|
||||
switch ($sOperation)
|
||||
{
|
||||
case 'add':
|
||||
$aResult = array(
|
||||
'error' => '',
|
||||
'att_id' => 0,
|
||||
'preview' => 'false',
|
||||
'msg' => '',
|
||||
);
|
||||
$sClass = stripslashes(utils::ReadParam('obj_class', '', false, 'class'));
|
||||
$sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id');
|
||||
if (empty($sClass))
|
||||
{
|
||||
$aResult['error'] = "Missing argument 'obj_class'";
|
||||
}
|
||||
elseif (empty($sTempId))
|
||||
{
|
||||
$aResult['error'] = "Missing argument 'temp_id'";
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
$oDoc = utils::ReadPostedDocument('file');
|
||||
/** @var Attachment $oAttachment */
|
||||
$oAttachment = MetaModel::NewObject('Attachment');
|
||||
$oAttachment->Set('expire', time() + MetaModel::GetConfig()->Get('draft_attachments_lifetime'));
|
||||
$oAttachment->Set('temp_id', $sTempId);
|
||||
$oAttachment->Set('item_class', $sClass);
|
||||
$oAttachment->SetDefaultOrgId();
|
||||
$oAttachment->Set('contents', $oDoc);
|
||||
$iAttId = $oAttachment->DBInsert();
|
||||
|
||||
$aResult['msg'] = htmlentities($oDoc->GetFileName(), ENT_QUOTES, 'UTF-8');
|
||||
$aResult['icon'] = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($oDoc->GetFileName());
|
||||
$aResult['att_id'] = $iAttId;
|
||||
$aResult['preview'] = $oDoc->IsPreviewAvailable() ? 'true' : 'false';
|
||||
}
|
||||
catch (FileUploadException $e)
|
||||
{
|
||||
$aResult['error'] = $e->GetMessage();
|
||||
}
|
||||
}
|
||||
$oPage->add(json_encode($aResult));
|
||||
break;
|
||||
|
||||
case 'remove':
|
||||
$iAttachmentId = utils::ReadParam('att_id', '');
|
||||
$oSearch = DBObjectSearch::FromOQL("SELECT Attachment WHERE id = :id");
|
||||
$oSet = new DBObjectSet($oSearch, array(), array('id' => $iAttachmentId));
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
$oAttachment->DBDelete();
|
||||
}
|
||||
break;
|
||||
|
||||
case 'refresh_attachments_render':
|
||||
$sTempId = utils::ReadParam('temp_id', '', false, 'transaction_id');
|
||||
RenderAttachments($oPage, $sTempId);
|
||||
break;
|
||||
|
||||
default:
|
||||
$oPage->p("Missing argument 'operation'");
|
||||
}
|
||||
|
||||
$oPage->output();
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
// note: transform to cope with XSS attacks
|
||||
echo htmlentities($e->GetMessage(), ENT_QUOTES, 'utf-8');
|
||||
IssueLog::Error($e->getMessage());
|
||||
}
|
||||
@@ -37,6 +37,8 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
|
||||
'Attachments:NoAttachment' => 'Kein Attachment. ',
|
||||
'Attachments:PreviewNotAvailable' => 'Vorschau für diesen Attachment-Typ nicht verfügbar.',
|
||||
'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s~~',
|
||||
'Attachments:Render:Icons' => 'Display as icons~~',
|
||||
'Attachments:Render:Table' => 'Display as list~~',
|
||||
));
|
||||
|
||||
//
|
||||
|
||||
@@ -36,6 +36,8 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Attachments:NoAttachment' => 'No attachment. ',
|
||||
'Attachments:PreviewNotAvailable' => 'Preview not available for this type of attachment.',
|
||||
'Attachments:Error:FileTooLarge' => 'File is too large to be uploaded. %1$s',
|
||||
'Attachments:Render:Icons' => 'Display as icons',
|
||||
'Attachments:Render:Table' => 'Display as list',
|
||||
));
|
||||
|
||||
//
|
||||
@@ -58,3 +60,12 @@ Dict::Add('EN US', 'English', 'English', array(
|
||||
'Class:Attachment/Attribute:contents' => 'Contents',
|
||||
'Class:Attachment/Attribute:contents+' => '',
|
||||
));
|
||||
|
||||
|
||||
Dict::Add('EN US', 'English', 'English', array(
|
||||
'Attachments:File:Thumbnail' => 'Icon',
|
||||
'Attachments:File:Name' => 'File name',
|
||||
'Attachments:File:Date' => 'Date added',
|
||||
'Attachments:File:Size' => 'Size',
|
||||
'Attachments:File:MimeType' => 'Type',
|
||||
));
|
||||
@@ -36,6 +36,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
|
||||
'Attachments:NoAttachment' => 'Aucune pièce jointe.',
|
||||
'Attachments:PreviewNotAvailable' => 'Pas d\'aperçu pour ce type de pièce jointe.',
|
||||
'Attachments:Error:FileTooLarge' => 'Le fichier est trop gros pour être chargé. %1$s',
|
||||
'Attachments:Render:Icons' => 'Affichage en icônes',
|
||||
'Attachments:Render:Table' => 'Affichage en liste',
|
||||
));
|
||||
|
||||
//
|
||||
|
||||
@@ -1,900 +0,0 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
define('ATTACHMENT_DOWNLOAD_URL', 'pages/ajax.document.php?operation=download_document&class=Attachment&field=contents&id=');
|
||||
|
||||
class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExtension
|
||||
{
|
||||
const ENUM_GUI_ALL = 'all';
|
||||
const ENUM_GUI_BACKOFFICE = 'backoffice';
|
||||
const ENUM_GUI_PORTALS = 'portals';
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of "upload_max_filesize" in bytes if upload allowed, false otherwise.
|
||||
*
|
||||
* @since 2.6.1
|
||||
*
|
||||
* @return number|boolean
|
||||
*/
|
||||
public static function GetMaxUploadSize()
|
||||
{
|
||||
$sMaxUpload = ini_get('upload_max_filesize');
|
||||
if (!$sMaxUpload)
|
||||
{
|
||||
$result = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
$result = utils::ConvertToBytes($sMaxUpload);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \DBObject $oObject
|
||||
* @param \WebPage $oPage
|
||||
* @param bool $bEditMode
|
||||
*
|
||||
* @throws \CoreCannotSaveObjectException
|
||||
* @throws \CoreException
|
||||
* @throws \CoreUnexpectedValue
|
||||
* @throws \MissingQueryArgument
|
||||
* @throws \MySQLException
|
||||
* @throws \MySQLHasGoneAwayException
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function DisplayAttachments(DBObject $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()));
|
||||
|
||||
$iTransactionId = $oPage->GetTransactionId();
|
||||
$sTempId = utils::GetUploadTempId($iTransactionId);
|
||||
$oSearchTemp = DBObjectSearch::FromOQL("SELECT Attachment WHERE temp_id = :temp_id");
|
||||
$oSetTemp = new DBObjectSet($oSearchTemp, array(), array('temp_id' => $sTempId));
|
||||
|
||||
if ($this->GetAttachmentsPosition() == 'relations')
|
||||
{
|
||||
$iCount = $oSet->Count() + $oSetTemp->Count();
|
||||
$sTitle = ($iCount > 0)? Dict::Format('Attachments:TabTitle_Count', $iCount) : Dict::S('Attachments:EmptyTabTitle');
|
||||
$oPage->SetCurrentTab($sTitle);
|
||||
}
|
||||
$oPage->add_style(
|
||||
<<<EOF
|
||||
.attachment {
|
||||
display: inline-block;
|
||||
text-align:center;
|
||||
float:left;
|
||||
padding:5px;
|
||||
}
|
||||
.attachment:hover {
|
||||
background-color: #e0e0e0;
|
||||
}
|
||||
.attachment img {
|
||||
border: 0;
|
||||
}
|
||||
.attachment a {
|
||||
text-decoration: none;
|
||||
color: #1C94C4;
|
||||
}
|
||||
.btn_hidden {
|
||||
display: none;
|
||||
}
|
||||
.drag_in {
|
||||
-webkit-box-shadow:inset 0 0 10px 2px #1C94C4;
|
||||
box-shadow:inset 0 0 10px 2px #1C94C4;
|
||||
}
|
||||
#history .attachment-history-added {
|
||||
padding: 0;
|
||||
float: none;
|
||||
}
|
||||
.inline-image {
|
||||
cursor: zoom-in;
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$oPage->add('<fieldset>');
|
||||
$oPage->add('<legend>'.Dict::S('Attachments:FieldsetTitle').'</legend>');
|
||||
|
||||
if ($bEditMode && !static::IsReadonlyState($oObject, $oObject->GetState(), static::ENUM_GUI_BACKOFFICE) )
|
||||
{
|
||||
$sIsDeleteEnabled = $this->m_bDeleteEnabled ? 'true' : 'false';
|
||||
$sClass = get_class($oObject);
|
||||
$sDeleteBtn = Dict::S('Attachments:DeleteBtn');
|
||||
$oPage->add_script(
|
||||
<<<EOF
|
||||
function RemoveAttachment(att_id)
|
||||
{
|
||||
var bDelete = true;
|
||||
if ($('#display_attachment_'+att_id).hasClass('image-in-use'))
|
||||
{
|
||||
bDelete = window.confirm('This image is used in a description. Delete it anyway?');
|
||||
}
|
||||
if (bDelete)
|
||||
{
|
||||
$('#attachment_'+att_id).attr('name', 'removed_attachments[]');
|
||||
$('#display_attachment_'+att_id).hide();
|
||||
$('#attachment_plugin').trigger('remove_attachment', [att_id]);
|
||||
}
|
||||
return false; // Do not submit the form !
|
||||
}
|
||||
EOF
|
||||
);
|
||||
$oPage->add('<span id="attachments">');
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
$this->DisplayOneAttachment($oPage, $oAttachment);
|
||||
}
|
||||
|
||||
// Display Temporary attachments
|
||||
while ($oAttachment = $oSetTemp->Fetch())
|
||||
{
|
||||
$this->DisplayOneAttachment($oPage, $oAttachment, true);
|
||||
}
|
||||
|
||||
|
||||
// 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
|
||||
$this->DisplayOneAttachment($oPage, $oAttachment, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$oPage->add('</span>');
|
||||
$oPage->add('<div style="clear:both"></div>');
|
||||
$iMaxUploadInBytes = $this->GetMaxUploadSize();
|
||||
$sMaxUploadLabel = $this->GetMaxUpload();
|
||||
$sFileTooBigLabel = Dict::Format('Attachments:Error:FileTooLarge', $sMaxUploadLabel);
|
||||
$sFileTooBigLabelForJS = addslashes($sFileTooBigLabel);
|
||||
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file"><span style="display:none;" id="attachment_loading"> <img src="../images/indicator.gif"></span> '.$sMaxUploadLabel);
|
||||
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.iframe-transport.js');
|
||||
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'js/jquery.fileupload.js');
|
||||
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL;
|
||||
$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',
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sDownloadLink = '$sDownloadLink'+data.result.att_id;
|
||||
$('#attachments').append('<div class="attachment" id="display_attachment_'+data.result.att_id+'"><a data-preview="'+data.result.preview+'" href="'+sDownloadLink+'"><img src="'+data.result.icon+'"><br/>'+data.result.msg+'<input id="attachment_'+data.result.att_id+'" type="hidden" name="attachments[]" value="'+data.result.att_id+'"/></a><br/><input type="button" class="btn_hidden" value="{$sDeleteBtn}" onClick="RemoveAttachment('+data.result.att_id+');"/></div>');
|
||||
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.result.msg, false /* inline image */]);
|
||||
}
|
||||
}
|
||||
},
|
||||
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).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
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
// 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('<div class="image-in-use-wrapper" style="position:relative;display:inline-block;"></div>');
|
||||
}
|
||||
});
|
||||
$('.htmlEditor').each(function() {
|
||||
var oEditor = $(this).ckeditorGet();
|
||||
var sHtml = oEditor.getData();
|
||||
var jElement = $('<div/>').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('<div class="image-in-use-wrapper" style="position:relative;display:inline-block;"></div>');
|
||||
});
|
||||
});
|
||||
$('.image-in-use-wrapper').append('<div style="position:absolute;top:0;left:0;"><img src="../images/transp-lock.png"></div>');
|
||||
}, 200 );
|
||||
EOF
|
||||
);
|
||||
$oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>');
|
||||
$oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>');
|
||||
if ($this->m_bDeleteEnabled)
|
||||
{
|
||||
$oPage->add_ready_script('$(".attachment").hover( function() {$(this).children(":button").toggleClass("btn_hidden"); } );');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$oPage->add('<span id="attachments">');
|
||||
if ($oSet->Count() == 0)
|
||||
{
|
||||
$oPage->add(Dict::S('Attachments:NoAttachment'));
|
||||
}
|
||||
else
|
||||
{
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
$iAttId = $oAttachment->GetKey();
|
||||
$oDoc = $oAttachment->Get('contents');
|
||||
$sFileName = htmlentities($oDoc->GetFileName(), ENT_QUOTES, 'UTF-8');
|
||||
$sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
|
||||
$sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false';
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL.$iAttId;
|
||||
$oPage->add('<div class="attachment" id="attachment_'.$iAttId.'"><a data-preview="'.$sPreview.'" href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'</a><input type="hidden" name="attachments[]" value="'.$iAttId.'"/><br/> </div>');
|
||||
}
|
||||
}
|
||||
$oPage->add('</span>');
|
||||
}
|
||||
$oPage->add('</fieldset>');
|
||||
$sPreviewNotAvailable = addslashes(Dict::S('Attachments:PreviewNotAvailable'));
|
||||
$iMaxWidth = MetaModel::GetModuleSetting('itop-attachments', 'preview_max_width', 290);
|
||||
$oPage->add_ready_script(
|
||||
<<<EOF
|
||||
$(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('<img style=\"max-width:{$iMaxWidth}px\" src=\"'+$(this).attr('href')+'\"></img>');} else { return '$sPreviewNotAvailable'; }}
|
||||
});
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
$sTransactionId = utils::ReadParam('transaction_id', null, false, 'transaction_id');
|
||||
if (!is_null($sTransactionId))
|
||||
{
|
||||
$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::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);
|
||||
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::GetActionChangeOp($oAttachment, true /* true => creation */);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($aActions) > 0)
|
||||
{
|
||||
foreach($aActions as $oChangeOp)
|
||||
{
|
||||
self::RecordHistory($oChange, $oObject, $oChangeOp);
|
||||
}
|
||||
self::$m_bIsModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function CopyAttachments($oObject, $sTransactionId)
|
||||
{
|
||||
$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()));
|
||||
// Attach new (temporary) attachments
|
||||
$sTempId = utils::GetUploadTempId($sTransactionId);
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
$oTempAttachment = clone $oAttachment;
|
||||
$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 = 'document.png';
|
||||
}
|
||||
else
|
||||
{
|
||||
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($oChange, $oTargetObject, $oMyChangeOp)
|
||||
{
|
||||
if (!is_null($oChange))
|
||||
{
|
||||
$oMyChangeOp->Set("change", $oChange->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 = array(
|
||||
static::ENUM_GUI_ALL => array(
|
||||
'Ticket' => array('closed')
|
||||
)
|
||||
);
|
||||
|
||||
$bReadonly = false;
|
||||
$sClass = get_class($oObject);
|
||||
$aReadonlyStatus = MetaModel::GetModuleSetting('itop-attachments', 'readonly_states', $aParamDefaultValue);
|
||||
if(!empty($aReadonlyStatus))
|
||||
{
|
||||
// Merging GUIs entries
|
||||
$aEntries = array();
|
||||
// - All
|
||||
if( array_key_exists(static::ENUM_GUI_ALL, $aReadonlyStatus) )
|
||||
{
|
||||
$aEntries = array_merge_recursive($aEntries, $aReadonlyStatus[static::ENUM_GUI_ALL]);
|
||||
}
|
||||
// - Backoffice & Portals
|
||||
foreach( array(static::ENUM_GUI_BACKOFFICE, static::ENUM_GUI_PORTALS) as $sEnumGUI)
|
||||
{
|
||||
if( in_array($sGUI, array(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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param $oAttachment
|
||||
* @param bool $bIsTemporary
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected function DisplayOneAttachment(WebPage $oPage, $oAttachment, $bIsTemporary = false)
|
||||
{
|
||||
$iAttId = $oAttachment->GetKey();
|
||||
$oDoc = $oAttachment->Get('contents');
|
||||
$sFileName = htmlentities($oDoc->GetFileName(), ENT_QUOTES, 'UTF-8');
|
||||
$sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
|
||||
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().ATTACHMENT_DOWNLOAD_URL.$iAttId;
|
||||
$sPreview = $oDoc->IsPreviewAvailable() ? 'true' : 'false';
|
||||
$oPage->add('<div class="attachment" id="display_attachment_'.$iAttId.'"><a data-preview="'.$sPreview.'" href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'<input id="attachment_'.$iAttId.'" type="hidden" name="attachments[]" value="'.$iAttId.'"/></a><br/> <input id="btn_remove_'.$iAttId.'" type="button" class="btn_hidden" value="'.Dict::S('Attachments:DeleteBtn').'" onClick="RemoveAttachment('.$iAttId.');"/> </div>');
|
||||
if ($bIsTemporary)
|
||||
{
|
||||
$oPage->add_ready_script("$('#attachment_plugin').trigger('add_attachment', [$iAttId, '".addslashes($sFileName)."', false /* not an line image */]);");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"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", array("targetclass"=>"Attachment", "allowed_values"=>null, "sql"=>"attachment_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_SILENT, "depends_on"=>array())));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("filename", array("allowed_values"=>null, "sql"=>"filename", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('attachment_id')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('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 = htmlentities($this->Get('filename'), ENT_QUOTES, 'UTF-8');
|
||||
$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 = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"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", array("allowed_values"=>null, "sql"=>"filename", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('filename')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('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">'.htmlentities($this->Get('filename'), ENT_QUOTES, 'UTF-8').'</span>');
|
||||
return $sResult;
|
||||
}
|
||||
}
|
||||
|
||||
702
datamodels/2.x/itop-attachments/main.itop-attachments.php
Normal file
702
datamodels/2.x/itop-attachments/main.itop-attachments.php
Normal file
@@ -0,0 +1,702 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExtension
|
||||
{
|
||||
const ENUM_GUI_ALL = 'all';
|
||||
const ENUM_GUI_BACKOFFICE = 'backoffice';
|
||||
const ENUM_GUI_PORTALS = 'portals';
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
|
||||
if ($this->GetAttachmentsPosition() === 'relations')
|
||||
{
|
||||
$iCount = $oAttachmentsRenderer->GetAttachmentsSet()->Count() + $oAttachmentsRenderer->GetTempAttachmentsSet()->Count();
|
||||
$sTitle = ($iCount > 0) ? Dict::Format('Attachments:TabTitle_Count', $iCount) : Dict::S('Attachments:EmptyTabTitle');
|
||||
$oPage->SetCurrentTab($sTitle);
|
||||
}
|
||||
|
||||
$oPage->add('<fieldset>');
|
||||
$oPage->add('<legend>'.Dict::S('Attachments:FieldsetTitle').'</legend>');
|
||||
|
||||
$oPage->add('<div id="AttachmentsContent">');
|
||||
$bIsReadOnlyState = self::IsReadonlyState($oObject, $oObject->GetState(), AttachmentPlugIn::ENUM_GUI_BACKOFFICE);
|
||||
if ($bEditMode && !$bIsReadOnlyState)
|
||||
{
|
||||
$oAttachmentsRenderer->RenderEditAttachmentsList();
|
||||
}
|
||||
else
|
||||
{
|
||||
$oAttachmentsRenderer->RenderViewAttachmentsList();
|
||||
}
|
||||
$oPage->add('</div>');
|
||||
|
||||
$oPage->add('</fieldset>');
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
$sTransactionId = utils::ReadParam('transaction_id', null, false, 'transaction_id');
|
||||
if (!is_null($sTransactionId))
|
||||
{
|
||||
$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::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);
|
||||
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::GetActionChangeOp($oAttachment, true /* true => creation */);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (count($aActions) > 0)
|
||||
{
|
||||
foreach ($aActions as $oChangeOp)
|
||||
{
|
||||
self::RecordHistory($oChange, $oObject, $oChangeOp);
|
||||
}
|
||||
self::$m_bIsModified = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function CopyAttachments($oObject, $sTransactionId)
|
||||
{
|
||||
$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()));
|
||||
// Attach new (temporary) attachments
|
||||
$sTempId = utils::GetUploadTempId($sTransactionId);
|
||||
while ($oAttachment = $oSet->Fetch())
|
||||
{
|
||||
$oTempAttachment = clone $oAttachment;
|
||||
$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 = 'document.png';
|
||||
}
|
||||
else
|
||||
{
|
||||
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($oChange, $oTargetObject, $oMyChangeOp)
|
||||
{
|
||||
if (!is_null($oChange))
|
||||
{
|
||||
$oMyChangeOp->Set("change", $oChange->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 = array(
|
||||
static::ENUM_GUI_ALL => array(
|
||||
'Ticket' => array('closed'),
|
||||
),
|
||||
);
|
||||
|
||||
$bReadonly = false;
|
||||
$sClass = get_class($oObject);
|
||||
$aReadonlyStatus = MetaModel::GetModuleSetting('itop-attachments', 'readonly_states', $aParamDefaultValue);
|
||||
if (!empty($aReadonlyStatus))
|
||||
{
|
||||
// Merging GUIs entries
|
||||
$aEntries = array();
|
||||
// - All
|
||||
if (array_key_exists(static::ENUM_GUI_ALL, $aReadonlyStatus))
|
||||
{
|
||||
$aEntries = array_merge_recursive($aEntries, $aReadonlyStatus[static::ENUM_GUI_ALL]);
|
||||
}
|
||||
// - Backoffice & Portals
|
||||
foreach (array(static::ENUM_GUI_BACKOFFICE, static::ENUM_GUI_PORTALS) as $sEnumGUI)
|
||||
{
|
||||
if (in_array($sGUI, array(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 = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"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", array(
|
||||
"targetclass" => "Attachment",
|
||||
"allowed_values" => null,
|
||||
"sql" => "attachment_id",
|
||||
"is_null_allowed" => true,
|
||||
"on_target_delete" => DEL_SILENT,
|
||||
"depends_on" => array(),
|
||||
)));
|
||||
MetaModel::Init_AddAttribute(new AttributeString("filename", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => "filename",
|
||||
"default_value" => "",
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array(),
|
||||
)));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('attachment_id')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('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 = htmlentities($this->Get('filename'), ENT_QUOTES, 'UTF-8');
|
||||
$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 = array
|
||||
(
|
||||
"category" => "core/cmdb",
|
||||
"key_type" => "",
|
||||
"name_attcode" => "change",
|
||||
"state_attcode" => "",
|
||||
"reconc_keys" => array(),
|
||||
"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", array(
|
||||
"allowed_values" => null,
|
||||
"sql" => "filename",
|
||||
"default_value" => "",
|
||||
"is_null_allowed" => false,
|
||||
"depends_on" => array(),
|
||||
)));
|
||||
|
||||
// Display lists
|
||||
MetaModel::Init_SetZListItems('details', array('filename')); // Attributes to be displayed for the complete details
|
||||
MetaModel::Init_SetZListItems('list', array('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">'.htmlentities($this->Get('filename'), ENT_QUOTES, 'UTF-8').'</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 array();
|
||||
}
|
||||
$oSet = new DBObjectSet($oSearch);
|
||||
|
||||
try
|
||||
{
|
||||
$aAttachmentDates = array();
|
||||
while ($oChangeOpAttAdded = $oSet->Fetch())
|
||||
{
|
||||
$iAttachmentId = $oChangeOpAttAdded->Get('attachment_id');
|
||||
$sAttachmentDate = $oChangeOpAttAdded->Get('date');
|
||||
$aAttachmentDates[$iAttachmentId] = $sAttachmentDate;
|
||||
}
|
||||
}
|
||||
catch (Exception $e)
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
return $aAttachmentDates;
|
||||
}
|
||||
}
|
||||
9
datamodels/2.x/itop-attachments/module.attachments.php → datamodels/2.x/itop-attachments/module.itop-attachments.php
Executable file → Normal file
9
datamodels/2.x/itop-attachments/module.attachments.php → datamodels/2.x/itop-attachments/module.itop-attachments.php
Executable file → Normal file
@@ -19,7 +19,7 @@
|
||||
|
||||
SetupWebPage::AddModule(
|
||||
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
|
||||
'itop-attachments/2.6.2',
|
||||
'itop-attachments/2.7.0',
|
||||
array(
|
||||
// Identification
|
||||
//
|
||||
@@ -28,9 +28,7 @@ SetupWebPage::AddModule(
|
||||
|
||||
// Setup
|
||||
//
|
||||
'dependencies' => array(
|
||||
|
||||
),
|
||||
'dependencies' => array(),
|
||||
'mandatory' => false,
|
||||
'visible' => true,
|
||||
'installer' => 'AttachmentInstaller',
|
||||
@@ -39,7 +37,8 @@ SetupWebPage::AddModule(
|
||||
//
|
||||
'datamodel' => array(
|
||||
'model.itop-attachments.php',
|
||||
'main.attachments.php',
|
||||
'main.itop-attachments.php',
|
||||
'renderers.itop-attachments.php',
|
||||
),
|
||||
'webservice' => array(
|
||||
|
||||
478
datamodels/2.x/itop-attachments/renderers.itop-attachments.php
Normal file
478
datamodels/2.x/itop-attachments/renderers.itop-attachments.php
Normal file
@@ -0,0 +1,478 @@
|
||||
<?php
|
||||
// Copyright (C) 2010-2017 Combodo SARL
|
||||
//
|
||||
// This file is part of iTop.
|
||||
//
|
||||
// iTop is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// iTop is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with iTop. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
define('ATTACHMENT_DOWNLOAD_URL', 'pages/ajax.document.php?operation=download_document&class=Attachment&field=contents&id=');
|
||||
define('ATTACHMENTS_RENDERER', 'TableDetailsAttachmentsRenderer');
|
||||
|
||||
|
||||
class AttachmentsRendererFactory
|
||||
{
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param string $sObjClass class name of the objects holding the attachments
|
||||
* @param int $iObjKey key of the objects holding the attachments
|
||||
* @param string $sTransactionId CSRF token
|
||||
*
|
||||
* @return \AbstractAttachmentsRenderer rendering impl
|
||||
*/
|
||||
public static function GetInstance($oPage, $sObjClass, $iObjKey, $sTransactionId)
|
||||
{
|
||||
$sRendererClass = ATTACHMENTS_RENDERER;
|
||||
/** @var \AbstractAttachmentsRenderer $oAttachmentsRenderer */
|
||||
$oAttachmentsRenderer = new $sRendererClass($oPage, $sObjClass, $iObjKey, $sTransactionId);
|
||||
|
||||
return $oAttachmentsRenderer;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Common code for attachment rendering
|
||||
*
|
||||
* On each attachment you'll need to have :
|
||||
*
|
||||
* * an id on the attachment container (see GetAttachmentContainerId)
|
||||
* * an input hidden inside the container (see GetAttachmentHiddenInput)
|
||||
*
|
||||
* @see \AttachmentPlugIn::DisplayAttachments()
|
||||
*/
|
||||
abstract class AbstractAttachmentsRenderer
|
||||
{
|
||||
/**
|
||||
* If size (in bits) is above this, then we will display a file icon instead of preview
|
||||
*/
|
||||
const MAX_SIZE_FOR_PREVIEW = 500000;
|
||||
|
||||
/** @var \WebPage */
|
||||
protected $oPage;
|
||||
/**
|
||||
* @var string CSRF token, must be provided cause when getting content from AJAX we need the one from the original page, not the
|
||||
* ajaxpage
|
||||
*/
|
||||
private $sTransactionId;
|
||||
/** @var string */
|
||||
protected $sObjClass;
|
||||
/** @var int */
|
||||
protected $iObjKey;
|
||||
/** @var \DBObjectSet */
|
||||
protected $oTempAttachmentsSet;
|
||||
/** @var \DBObjectSet */
|
||||
protected $oAttachmentsSet;
|
||||
|
||||
/**
|
||||
* @param \WebPage $oPage
|
||||
* @param string $sObjClass class name of the objects holding the attachments
|
||||
* @param int $iObjKey key of the objects holding the attachments
|
||||
* @param string $sTransactionId CSRF token
|
||||
*
|
||||
* @throws \OQLException
|
||||
*/
|
||||
public function __construct(\WebPage $oPage, $sObjClass, $iObjKey, $sTransactionId)
|
||||
{
|
||||
$this->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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $aAttachmentsDeleted Attachments id that should be deleted after form submission
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function RenderEditAttachmentsList($aAttachmentsDeleted = array());
|
||||
|
||||
abstract public function RenderViewAttachmentsList();
|
||||
|
||||
protected function AddUploadButton()
|
||||
{
|
||||
$sClass = $this->sObjClass;
|
||||
$sId = $this->iObjKey;
|
||||
|
||||
$this->oPage->add('<div style="clear:both"></div>');
|
||||
$iMaxUploadInBytes = AttachmentPlugIn::GetMaxUploadSize();
|
||||
$sMaxUploadLabel = AttachmentPlugIn::GetMaxUpload();
|
||||
$sFileTooBigLabel = Dict::Format('Attachments:Error:FileTooLarge', $sMaxUploadLabel);
|
||||
$sFileTooBigLabelForJS = addslashes($sFileTooBigLabel);
|
||||
$this->oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file"><span style="display:none;" id="attachment_loading"> <img src="../images/indicator.gif"></span> '.$sMaxUploadLabel);
|
||||
|
||||
$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(
|
||||
<<<JS
|
||||
function RefreshAttachmentsDisplay()
|
||||
{
|
||||
var sContentNode = '#AttachmentsContent',
|
||||
aAttachmentsDeletedHiddenInputs = $('table.attachmentsList>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();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$('#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: RefreshAttachmentsDisplay,
|
||||
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).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
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
// 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('<div class="image-in-use-wrapper" style="position:relative;display:inline-block;"></div>');
|
||||
}
|
||||
});
|
||||
$('.htmlEditor').each(function() {
|
||||
var oEditor = $(this).ckeditorGet();
|
||||
var sHtml = oEditor.getData();
|
||||
var jElement = $('<div/>').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('<div class="image-in-use-wrapper" style="position:relative;display:inline-block;"></div>');
|
||||
});
|
||||
});
|
||||
$('.image-in-use-wrapper').append('<div style="position:absolute;top:0;left:0;"><img src="../images/transp-lock.png"></div>');
|
||||
}, 200 );
|
||||
JS
|
||||
);
|
||||
$this->oPage->p('<span style="display:none;" id="attachment_loading">Loading, please wait...</span>');
|
||||
$this->oPage->p('<input type="hidden" id="attachment_plugin" name="attachment_plugin"/>');
|
||||
|
||||
$this->oPage->add_style(<<<CSS
|
||||
.drag_in {
|
||||
-webkit-box-shadow:inset 0 0 10px 2px #1C94C4;
|
||||
box-shadow:inset 0 0 10px 2px #1C94C4;
|
||||
}
|
||||
CSS
|
||||
);
|
||||
}
|
||||
|
||||
protected function GetAttachmentContainerId($iAttachmentId)
|
||||
{
|
||||
return 'display_attachment_'.$iAttachmentId;
|
||||
}
|
||||
|
||||
protected function GetAttachmentHiddenInput($iAttachmentId, $bIsDeletedAttachment)
|
||||
{
|
||||
$sInputNamePrefix = $bIsDeletedAttachment ? 'removed_' : '';
|
||||
|
||||
return '<input id="attachment_'.$iAttachmentId.'" type="hidden" name="'.$sInputNamePrefix.'attachments[]" value="'.$iAttachmentId.'">';
|
||||
}
|
||||
|
||||
protected function GetDeleteAttachmentButton($iAttId)
|
||||
{
|
||||
return '<input id="btn_remove_'.$iAttId.'" type="button" class="btn_hidden" value="'.Dict::S('Attachments:DeleteBtn').'" onClick="RemoveAttachment('.$iAttId.');"/>';
|
||||
}
|
||||
|
||||
protected function GetDeleteAttachmentJs()
|
||||
{
|
||||
return <<<JS
|
||||
function RemoveAttachment(att_id)
|
||||
{
|
||||
var bDelete = true;
|
||||
if ($('#display_attachment_'+att_id).hasClass('image-in-use'))
|
||||
{
|
||||
bDelete = window.confirm('This image is used in a description. Delete it anyway?');
|
||||
}
|
||||
if (bDelete)
|
||||
{
|
||||
$('#attachment_'+att_id).attr('name', 'removed_attachments[]');
|
||||
$('#display_attachment_'+att_id).hide();
|
||||
$('#attachment_plugin').trigger('remove_attachment', [att_id]);
|
||||
}
|
||||
return false; // Do not submit the form !
|
||||
}
|
||||
JS;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TableDetailsAttachmentsRenderer extends AbstractAttachmentsRenderer
|
||||
{
|
||||
private function AddAttachmentsTable($bWithDeleteButton, $aAttachmentsDeleted = array())
|
||||
{
|
||||
if ($this->GetAttachmentsCount() === 0)
|
||||
{
|
||||
$this->oPage->add(Dict::S('Attachments:NoAttachment'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$this->oPage->add('<table class="listResults attachmentsList">'.PHP_EOL);
|
||||
$this->oPage->add('<thead>'.PHP_EOL);
|
||||
$this->oPage->add(' <th>'.Dict::S('Attachments:File:Thumbnail').'</th>'.PHP_EOL);
|
||||
$this->oPage->add(' <th>'.Dict::S('Attachments:File:Name').'</th>'.PHP_EOL);
|
||||
$this->oPage->add(' <th>'.Dict::S('Attachments:File:Size').'</th>'.PHP_EOL);
|
||||
$this->oPage->add(' <th>'.Dict::S('Attachments:File:Date').'</th>'.PHP_EOL);
|
||||
$this->oPage->add(' <th>'.Dict::S('Attachments:File:MimeType').'</th>'.PHP_EOL);
|
||||
if ($bWithDeleteButton)
|
||||
{
|
||||
$this->oPage->add(' <th></th>'.PHP_EOL);
|
||||
}
|
||||
$this->oPage->add('</thead>'.PHP_EOL);
|
||||
$this->oPage->add('<tbody>'.PHP_EOL);
|
||||
|
||||
|
||||
$iMaxWidth = MetaModel::GetModuleSetting('itop-attachments', 'preview_max_width', 290);
|
||||
$sPreviewNotAvailable = addslashes(Dict::S('Attachments:PreviewNotAvailable'));
|
||||
$this->oPage->add_ready_script(
|
||||
<<<JS
|
||||
$(document).tooltip({
|
||||
items: 'table.attachmentsList>tbody>tr>td a.trigger-preview',
|
||||
position: {
|
||||
my: 'left top', at: 'right top', using: function (position, feedback) {
|
||||
$(this).css(position);
|
||||
}
|
||||
},
|
||||
content: function () {
|
||||
if ($(this).hasClass("preview"))
|
||||
{
|
||||
return ('<img style=\"max-width:{$iMaxWidth}px\" src=\"'+$(this).attr('href')+'\"></img>');
|
||||
}
|
||||
else
|
||||
{
|
||||
return '$sPreviewNotAvailable';
|
||||
}
|
||||
}
|
||||
});
|
||||
JS
|
||||
);
|
||||
if ($bWithDeleteButton)
|
||||
{
|
||||
$this->oPage->add_script($this->GetDeleteAttachmentJs());
|
||||
}
|
||||
$this->oPage->add_style(
|
||||
<<<CSS
|
||||
table.attachmentsList>tbody>tr>td:first-child {
|
||||
text-align: center;
|
||||
}
|
||||
CSS
|
||||
);
|
||||
|
||||
$bIsEven = false;
|
||||
$aAttachmentsDate = AttachmentsHelper::GetAttachmentsDateAddedFromDb($this->sObjClass, $this->iObjKey);
|
||||
while ($oAttachment = $this->oAttachmentsSet->Fetch())
|
||||
{
|
||||
$bIsEven = ($bIsEven) ? false : true;
|
||||
$this->AddAttachmentsTableLine($bWithDeleteButton, $bIsEven, $oAttachment, $aAttachmentsDate, $aAttachmentsDeleted);
|
||||
}
|
||||
while ($oTempAttachment = $this->oTempAttachmentsSet->Fetch())
|
||||
{
|
||||
$bIsEven = ($bIsEven) ? false : true;
|
||||
$this->AddAttachmentsTableLine($bWithDeleteButton, $bIsEven, $oTempAttachment, $aAttachmentsDate, $aAttachmentsDeleted);
|
||||
}
|
||||
|
||||
$this->oPage->add('</tbody>'.PHP_EOL);
|
||||
$this->oPage->add('</table>'.PHP_EOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $bWithDeleteButton
|
||||
* @param $bIsEven
|
||||
* @param \DBObject $oAttachment
|
||||
* @param array $aAttachmentsDate
|
||||
* @param int[] $aAttachmentsDeleted
|
||||
*
|
||||
* @throws \ArchivedObjectException
|
||||
* @throws \CoreException
|
||||
*/
|
||||
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);
|
||||
$sFileSize = $oDoc->GetFormatedSize();
|
||||
$sAttachmentDate = array_key_exists($iAttachmentId, $aAttachmentsDate) ? $aAttachmentsDate[$iAttachmentId] : 'N/A';
|
||||
$sFileType = $oDoc->GetMimeType();
|
||||
|
||||
$sAttachmentThumbUrl = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
|
||||
$sIconClass = '';
|
||||
if ($oDoc->IsPreviewAvailable())
|
||||
{
|
||||
$sIconClass = ' preview';
|
||||
if ($oDoc->GetSize() <= self::MAX_SIZE_FOR_PREVIEW)
|
||||
{
|
||||
$sAttachmentThumbUrl = $sDocDownloadUrl;
|
||||
}
|
||||
}
|
||||
|
||||
$sDeleteColumn = '';
|
||||
if ($bWithDeleteButton)
|
||||
{
|
||||
$sDeleteButton = $this->GetDeleteAttachmentButton($iAttachmentId);
|
||||
$sDeleteColumn = "<td>$sDeleteButton</td>";
|
||||
}
|
||||
|
||||
$this->oPage->add(<<<HTML
|
||||
<tr id="$sTrId" $sLineClass $sLineStyle>
|
||||
<td><a href="$sDocDownloadUrl" target="_blank" class="trigger-preview $sIconClass"><img $sIconClass style="max-height: 48px;" src="$sAttachmentThumbUrl"></a></td>
|
||||
<td><a href="$sDocDownloadUrl" target="_blank" class="$sIconClass">$sFileName</a>$sAttachmentMeta</td>
|
||||
<td>$sFileSize</td>
|
||||
<td>$sAttachmentDate</td>
|
||||
<td>$sFileType</td>
|
||||
$sDeleteColumn
|
||||
</tr>
|
||||
HTML
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function RenderEditAttachmentsList($aAttachmentsDeleted = array())
|
||||
{
|
||||
$this->AddUploadButton();
|
||||
|
||||
$this->AddAttachmentsTable(true, $aAttachmentsDeleted);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function RenderViewAttachmentsList()
|
||||
{
|
||||
$this->AddAttachmentsTable(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user