New module to easily manage attachments

SVN:trunk[1417]
This commit is contained in:
Denis Flaven
2011-08-02 10:29:59 +00:00
parent c2d02a2394
commit 768dbd8946
19 changed files with 982 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Handles various ajax requests
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
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::DoLogin(false /* bMustBeAdmin */, true /* IsAllowedToPortalUsers */); // Check user rights and prompt if needed
$oPage = new ajax_page("");
$oPage->no_cache();
$sOperation = utils::ReadParam('operation', '');
switch($sOperation)
{
case 'add':
$aResult = array(
'error' => '',
'att_id' => 0,
'msg' => ''
);
$sObjClass = stripslashes(utils::ReadParam('obj_class', ''));
$sTempId = utils::ReadParam('temp_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');
$oAttachment = MetaModel::NewObject('Attachment');
$oAttachment->Set('expire', time() + 3600); // one hour...
$oAttachment->Set('temp_id', $sTempId);
$oAttachment->Set('item_class', $sObjClass);
$oAttachment->Set('item_id', 0);
$oAttachment->Set('contents', $oDoc);
$iAttId = $oAttachment->DBInsert();
$aResult['msg'] = $oDoc->GetFileName();
$aResult['icon'] = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($oDoc->GetFileName());
$aResult['att_id'] = $iAttId;
}
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)
{
echo $e->GetMessage();
IssueLog::Error($e->getMessage());
}
?>

View File

@@ -0,0 +1,191 @@
jQuery.extend(
{
createUploadIframe: function(id, uri)
{
//create frame
var frameId = 'jUploadFrame' + id;
var iframeHtml = '<iframe id="' + frameId + '" name="' + frameId + '" style="position:absolute; top:-9999px; left:-9999px"';
if(window.ActiveXObject)
{
if(typeof uri== 'boolean')
{
iframeHtml += ' src="' + 'javascript:false' + '"';
}
else if(typeof uri== 'string')
{
iframeHtml += ' src="' + uri + '"';
}
}
iframeHtml += ' />';
jQuery(iframeHtml).appendTo(document.body);
return jQuery('#' + frameId).get(0);
},
createUploadForm: function(id, fileElementId)
{
//create form
var formId = 'jUploadForm' + id;
var fileId = 'jUploadFile' + id;
var form = jQuery('<form action="" method="POST" name="' + formId + '" id="' + formId + '" enctype="multipart/form-data"></form>');
var oldElement = jQuery('#' + fileElementId);
var newElement = jQuery(oldElement).clone();
jQuery(oldElement).attr('id', fileId);
jQuery(oldElement).before(newElement);
jQuery(oldElement).appendTo(form);
//set attributes
jQuery(form).css('position', 'absolute');
jQuery(form).css('top', '-1200px');
jQuery(form).css('left', '-1200px');
jQuery(form).appendTo('body');
return form;
},
ajaxFileUpload: function(s) {
// TODO introduce global settings, allowing the client to modify them for all requests, not only timeout
s = jQuery.extend({}, jQuery.ajaxSettings, s);
var id = new Date().getTime();
var form = jQuery.createUploadForm(id, s.fileElementId);
var io = jQuery.createUploadIframe(id, s.secureuri);
var frameId = 'jUploadFrame' + id;
var formId = 'jUploadForm' + id;
// Watch for a new set of requests
if ( s.global && ! jQuery.active++ )
{
jQuery.event.trigger( "ajaxStart" );
}
var requestDone = false;
// Create the request object
var xml = {};
if ( s.global )
jQuery.event.trigger("ajaxSend", [xml, s]);
// Wait for a response to come back
var uploadCallback = function(isTimeout)
{
var io = document.getElementById(frameId);
try
{
if(io.contentWindow)
{
xml.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:null;
xml.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
}else if(io.contentDocument)
{
xml.responseText = io.contentDocument.document.body?io.contentDocument.document.body.innerHTML:null;
xml.responseXML = io.contentDocument.document.XMLDocument?io.contentDocument.document.XMLDocument:io.contentDocument.document;
}
}
catch(e)
{
jQuery.handleError(s, xml, null, e);
}
if ( xml || isTimeout == "timeout")
{
requestDone = true;
var status;
try {
status = isTimeout != "timeout" ? "success" : "error";
// Make sure that the request was successful or notmodified
if ( status != "error" )
{
// process the data (runs the xml through httpData regardless of callback)
var data = jQuery.uploadHttpData( xml, s.dataType );
// If a local callback was specified, fire it and pass it the data
if ( s.success )
s.success( data, status );
// Fire the global callback
if( s.global )
jQuery.event.trigger( "ajaxSuccess", [xml, s] );
} else
jQuery.handleError(s, xml, status);
} catch(e)
{
status = "error";
jQuery.handleError(s, xml, status, e);
}
// The request was completed
if( s.global )
jQuery.event.trigger( "ajaxComplete", [xml, s] );
// Handle the global AJAX counter
if ( s.global && ! --jQuery.active )
jQuery.event.trigger( "ajaxStop" );
// Process result
if ( s.complete )
s.complete(xml, status);
jQuery(io).unbind();
setTimeout(function()
{ try
{
jQuery(io).remove();
jQuery(form).remove();
} catch(e)
{
jQuery.handleError(s, xml, null, e);
}
}, 100)
xml = null
}
}
// Timeout checker
if ( s.timeout > 0 )
{
setTimeout(function(){
// Check to see if the request is still happening
if( !requestDone ) uploadCallback( "timeout" );
}, s.timeout);
}
try
{
var form = jQuery('#' + formId);
jQuery(form).attr('action', s.url);
jQuery(form).attr('method', 'POST');
jQuery(form).attr('target', frameId);
if(form.encoding)
{
jQuery(form).attr('encoding', 'multipart/form-data');
}
else
{
jQuery(form).attr('enctype', 'multipart/form-data');
}
jQuery(form).submit();
} catch(e)
{
jQuery.handleError(s, xml, null, e);
}
jQuery('#' + frameId).load(uploadCallback );
return {abort: function () {}};
},
uploadHttpData: function( r, type ) {
var data = !type;
data = type == "xml" || data ? r.responseXML : r.responseText;
// If the type is "script", eval it in global context
if ( type == "script" )
jQuery.globalEval( data );
// Get the JavaScript object, if JSON is used.
if ( type == "json" )
eval( "data = " + data );
// evaluate scripts within html
if ( type == "html" )
jQuery("<div>").html(data).evalScripts();
return data;
}
})

View File

@@ -0,0 +1,39 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Localized data
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
Dict::Add('EN US', 'English', 'English', array(
'Attachments:TabTitle_Count' => 'Attachments (%1$d)',
'Attachments:EmptyTabTitle' => 'Attachments',
'Attachments:FieldsetTitle' => 'Attachments',
'Attachments:DeleteBtn' => 'Delete',
'Attachments:History_File_Added' => 'Attachment %1$s added.',
'Attachments:History_File_Removed' => 'Attachment %1$s removed.',
'Attachments:AddAttachment' => 'Add attachment: ',
'Attachments:UploadNotAllowedOnThisSystem' => 'File upload in NOT allowed on this system.',
'Attachment:Max_Go' => '(Maximum file size: %1$s Go)',
'Attachment:Max_Mo' => '(Maximum file size: %1$s Mo)',
'Attachment:Max_Ko' => '(Maximum file size: %1$s Ko)',
));
?>

View File

@@ -0,0 +1,39 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Localized data
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
Dict::Add('FR FR', 'French', 'Français', array(
'Attachments:TabTitle_Count' => 'Pièces jointes (%1$d)',
'Attachments:EmptyTabTitle' => 'Pièces jointes',
'Attachments:FieldsetTitle' => 'Pièces jointes',
'Attachments:DeleteBtn' => 'Supprimer',
'Attachments:History_File_Added' => 'Ajout de la pièce jointe: %1$s.',
'Attachments:History_File_Removed' => 'Suppression de la pièce jointe: %1$s.',
'Attachments:AddAttachment' => 'Ajouter une pièce jointe: ',
'Attachments:UploadNotAllowedOnThisSystem' => 'Le téléchargement de fichiers est interdit sur ce système.',
'Attachment:Max_Go' => '(Taille de fichier max.: %1$s Gb)',
'Attachment:Max_Mo' => '(Taille de fichier max.: %1$s Mb)',
'Attachment:Max_Ko' => '(Taille de fichier max.: %1$s Kb)',
));
?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,540 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
/**
* Module attachments
*
* A quick and easy way to upload and attach files to *any* (see Configuration below) object in the CMBD in one click
*
* Configuration: the list of classes for which the "Attachments" tab is visible is defined via the module's 'allowed_classes'
* configuration parameter. By default the tab is active for all kind of Tickets.
*
* @author Erwan Taloc <erwan.taloc@combodo.com>
* @author Romain Quetiez <romain.quetiez@combodo.com>
* @author Denis Flaven <denis.flaven@combodo.com>
* @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
*/
class Attachment extends DBObject
{
public static function Init()
{
$aParams = array
(
"category" => "addon",
"key_type" => "autoincrement",
"name_attcode" => array('item_class', 'temp_id'),
"state_attcode" => "",
"reconc_keys" => array(),
"db_table" => "attachment",
"db_key_field" => "id",
"db_finalclass_field" => "",
);
MetaModel::Init_Params($aParams);
MetaModel::Init_InheritAttributes();
MetaModel::Init_AddAttribute(new AttributeDateTime("expire", array("allowed_values"=>null, "sql"=>"expire", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("temp_id", array("allowed_values"=>null, "sql"=>"temp_id", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("item_class", array("allowed_values"=>null, "sql"=>"item_class", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("item_id", array("allowed_values"=>null, "sql"=>"item_id", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeBlob("contents", array("depends_on"=>array())));
MetaModel::Init_SetZListItems('details', array('temp_id', 'item_class', 'item_id'));
MetaModel::Init_SetZListItems('advanced_search', array('temp_id', 'item_class', 'item_id'));
MetaModel::Init_SetZListItems('standard_search', array('temp_id', 'item_class', 'item_id'));
MetaModel::Init_SetZListItems('list', array('temp_id', 'item_class', 'item_id'));
}
// Todo - implement a cleanup function (see a way to do that generic !)
}
class AttachmentPlugIn implements iApplicationUIExtension, iApplicationObjectExtension
{
public function OnDisplayProperties($oObject, WebPage $oPage, $bEditMode = false)
{
}
public function OnDisplayRelations($oObject, WebPage $oPage, $bEditMode = false)
{
$bDisplayTab = $this->IsTargetObject($oObject);
// Exit here if the class is not allowed
if (!$bDisplayTab) 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()));
$sTitle = ($oSet->Count() > 0)? Dict::Format('Attachments:TabTitle_Count', $oSet->Count()) : 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;
}
EOF
);
$oPage->add('<fieldset>');
$oPage->add('<legend>'.Dict::S('Attachments:FieldsetTitle').'</legend>');
if ($bEditMode)
{
$sIsDeleteEnabled = $this->m_bDeleteEnabled ? 'true' : 'false';
$iTransactionId = $oPage->GetTransactionId();
$oPage->add_linked_script(utils::GetAbsoluteUrlAppRoot().'modules/itop-attachments/ajaxfileupload.js');
$sClass = get_class($oObject);
$sTempId = session_id().'_'.$iTransactionId;
$sDeleteBtn = Dict::S('Attachments:DeleteBtn');
$oPage->add_script(
<<<EOF
function RemoveNewAttachment(att_id)
{
$('#attachment_'+att_id).attr('name', 'removed_attachments[]');
$('#display_attachment_'+att_id).hide();
return false; // Do not submit the form !
}
function ajaxFileUpload()
{
//starting setting some animation when the ajax starts and completes
$("#loading").ajaxStart(function(){
$(this).show();
}).ajaxComplete(function(){
$(this).hide();
});
/*
prepareing ajax file upload
url: the url of script file handling the uploaded files
fileElementId: the file type of input element id and it will be the index of $_FILES Array()
dataType: it support json, xml
secureuri:use secure protocol
success: call back function when the ajax complete
error: callback function when the ajax failed
*/
$.ajaxFileUpload
(
{
url: GetAbsoluteUrlAppRoot()+'modules/itop-attachments/ajax.attachment.php?obj_class={$sClass}&temp_id={$sTempId}&operation=add',
secureuri:false,
fileElementId:'file',
dataType: 'json',
success: function (data, status)
{
if(typeof(data.error) != 'undefined')
{
if(data.error != '')
{
alert(data.error);
}
else
{
var sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php/?operation=download_document&class=Attachment&id='+data.att_id+'&field=contents';
$('#attachments').append('<div class="attachment" id="display_attachment_'+data.att_id+'"><a href="'+sDownloadLink+'"><img src="'+data.icon+'"><br/>'+data.msg+'<input id="attachment_'+data.att_id+'" type="hidden" name="attachments[]" value="'+data.att_id+'"/></a><br/><input type="button" class="btn_hidden" value="{$sDeleteBtn}" onClick="RemoveNewAttachment('+data.att_id+');"/></div>');
if($sIsDeleteEnabled)
{
$('#display_attachment_'+data.att_id).hover( function() { $(this).children(':button').toggleClass('btn_hidden'); } );
}
//alert(data.msg);
}
}
},
error: function (data, status, e)
{
alert(e);
}
}
)
return false;
}
EOF
);
$oPage->add('<span id="attachments">');
while ($oAttachment = $oSet->Fetch())
{
$iAttId = $oAttachment->GetKey();
$oDoc = $oAttachment->Get('contents');
$sFileName = $oDoc->GetFileName();
$sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php/?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents';
$oPage->add('<div class="attachment" id="attachment_'.$iAttId.'"><a href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'<input type="hidden" name="attachments[]" value="'.$iAttId.'"/></a><br/>&nbsp;<input id="btn_remove_'.$iAttId.'" type="button" class="btn_hidden" value="Delete" onClick="$(\'#attachment_'.$iAttId.'\').remove();"/>&nbsp;</div>');
}
$oPage->add('</span>');
$oPage->add('<div style="clear:both"></div>');
$sMaxUpload = $this->GetMaxUpload();
$oPage->p(Dict::S('Attachments:AddAttachment').'<input type="file" name="file" id="file" onChange="ajaxFileUpload();"><span style="display:none;" id="loading">&nbsp;<img src="../images/indicator.gif"></span> '.$sMaxUpload);
//$oPage->p('<input type="button" onClick="ajaxFileUpload();" value=" Upload !">');
$oPage->p('<span style="display:none;" id="loading">Loading, please wait...</span>');
$oPage->add('</fieldset>');
if ($this->m_bDeleteEnabled)
{
$oPage->add_ready_script('$(".attachment").hover( function() {$(this).children(":button").toggleClass("btn_hidden"); } );');
}
}
else
{
$oPage->add('<span id="attachments">');
while ($oAttachment = $oSet->Fetch())
{
$iAttId = $oAttachment->GetKey();
$oDoc = $oAttachment->Get('contents');
$sFileName = $oDoc->GetFileName();
$sIcon = utils::GetAbsoluteUrlAppRoot().AttachmentPlugIn::GetFileIcon($sFileName);
$sDownloadLink = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php/?operation=download_document&class=Attachment&id='.$iAttId.'&field=contents';
$oPage->add('<div class="attachment" id="attachment_'.$iAttId.'"><a href="'.$sDownloadLink.'"><img src="'.$sIcon.'"><br/>'.$sFileName.'</a><input type="hidden" name="attachments[]" value="'.$iAttId.'"/><br/>&nbsp;&nbsp;</div>');
}
}
}
public function OnFormSubmit($oObject, $sFormPrefix = '')
{
if ($this->IsTargetObject($oObject))
{
// For new objects attachments are processed in OnDBInsert
if (!$oObject->IsNew())
{
self::UpdateAttachments($oObject);
}
}
}
protected function GetMaxUpload()
{
$iMaxUpload = ini_get('upload_max_filesize');
if (!$iMaxUpload)
{
$sRet = Dict::S('Attachments:UploadNotAllowedOnThisSystem');
}
else
{
$iMaxUpload = utils::ConvertToBytes($iMaxUpload);
if ($iMaxUpload > 1024*1024*1024)
{
$sRet = Dict::Format('Attachment:Max_Go', sprintf('%0.2f', $iMaxUpload/(1024*1024*1024)));
}
else if ($iMaxUpload > 1024*1024)
{
$sRet = Dict::Format('Attachment:Max_Mo', sprintf('%0.2f', $iMaxUpload/(1024*1024)));
}
else
{
$sRet = Dict::Format('Attachment:Max_Ko', sprintf('%0.2f', $iMaxUpload/(1024)));
}
}
return $sRet;
}
public function OnFormCancel($sTempId)
{
// Delete all "pending" attachments for this form
$sOQL = 'SELECT Attachment WHERE temp_id = :temp_id';
$oSearch = DBObjectSearch::FromOQL($sOQL);
$oSet = new DBObjectSet($oSearch, array(), array('temp_id' => $sTempId));
while($oAttachment = $oSet->Fetch())
{
$oAttachment->DBDelete();
// Pending attachment, don't mention it in the history
}
}
public function EnumUsedAttributes($oObject)
{
return array();
}
public function GetIcon($oObject)
{
return '';
}
public function GetHilightClass($oObject)
{
// Possible return values are:
// HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE
return HILIGHT_CLASS_NONE;
}
public function EnumAllowedActions(DBObjectSet $oSet)
{
// No action
return array();
}
public function OnIsModified($oObject)
{
if ($this->IsTargetObject($oObject))
{
$aAttachmentIds = utils::ReadParam('attachments', array());
$aRemovedAttachmentIds = utils::ReadParam('removed_attachments', array());
if ( (count($aAttachmentIds) > 0) || (count($aRemovedAttachmentIds) > 0) )
{
return true;
}
}
return false;
}
public function OnCheckToWrite($oObject)
{
return array();
}
public function OnCheckToDelete($oObject)
{
return array();
}
public function OnDBUpdate($oObject, $oChange = null)
{
}
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('attachments', 'allowed_classes', array('Ticket'));
foreach($aAllowedClasses as $sAllowedClass)
{
if ($oObject instanceof $sAllowedClass)
{
return true;
}
}
return false;
}
var $m_bDeleteEnabled = true;
public function EnableDelete($bEnabled)
{
$this->m_bDeleteEnabled = $bEnabled;
}
protected static function UpdateAttachments($oObject, $oChange = null)
{
$iTransactionId = utils::ReadParam('transaction_id', null);
if (!is_null($iTransactionId))
{
$aActions = array();
$aAttachmentIds = utils::ReadParam('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(), $aAttachmentIds))
{
$oAttachment->DBDelete();
$aActions[] = self::GetActionDescription($oAttachment, false /* false => deletion */);
}
}
// Attach new (temporary) attachements
$sTempId = session_id().'_'.$iTransactionId;
// The object is being created from a form, check if there are pending attachments
// for this object, but deleting the "new" ones that were already removed from the form
$aRemovedAttachmentIds = utils::ReadParam('removed_attachments', array());
$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->Set('item_id', $oObject->GetKey());
$oAttachment->Set('temp_id', '');
$oAttachment->DBUpdate();
// temporary attachment confirmed, list it in the history
$aActions[] = self::GetActionDescription($oAttachment, true /* true => creation */);
}
}
}
if (count($aActions) > 0)
{
if ($oChange == null)
{
// Let's create a change if non is supplied
$oChange = MetaModel::NewObject("CMDBChange");
$oChange->Set("date", time());
$sUserString = CMDBChange::GetCurrentUserName();
$oChange->Set("userinfo", $sUserString);
$iChangeId = $oChange->DBInsert();
}
foreach($aActions as $sActionDescription)
{
self::RecordHistory($oChange, $oObject, $sActionDescription);
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
public static function GetFileIcon($sFileName)
{
$aPathParts = pathinfo($sFileName);
switch($aPathParts['extension'])
{
case 'doc':
case 'docx':
$sIcon = 'doc.png';
break;
case 'xls':
case 'xlsx':
$sIcon = 'xls.png';
break;
case 'ppt':
case 'pptx':
$sIcon = 'ppt.png';
break;
case 'pdf':
$sIcon = 'pdf.png';
break;
case 'txt':
case 'text':
$sIcon = 'txt.png';
break;
case 'rtf':
$sIcon = 'rtf.png';
break;
case 'odt':
$sIcon = 'odt.png';
break;
case 'ods':
$sIcon = 'ods.png';
break;
case 'odp':
$sIcon = 'odp.png';
break;
case 'html':
case 'htm':
$sIcon = 'html.png';
break;
case 'png':
case 'gif':
case 'jpg':
case 'jpeg':
case 'tiff':
case 'tif':
case 'bmp':
$sIcon = 'image.png';
break;
case 'zip':
case 'gz':
case 'tgz':
case 'rar':
$sIcon = 'zip.png';
break;
default:
$sIcon = 'document.png';
break;
}
return "modules/itop-attachments/icons/$sIcon";
}
/////////////////////////////////////////////////////////////////////////
private static function RecordHistory(CMDBChange $oChange, $oTargetObject, $sDescription)
{
$oMyChangeOp = MetaModel::NewObject("CMDBChangeOpPlugin");
$oMyChangeOp->Set("change", $oChange->GetKey());
$oMyChangeOp->Set("objclass", get_class($oTargetObject));
$oMyChangeOp->Set("objkey", $oTargetObject->GetKey());
$oMyChangeOp->Set("description", $sDescription);
$iId = $oMyChangeOp->DBInsertNoReload();
}
/////////////////////////////////////////////////////////////////////////
private static function GetActionDescription($oAttachment, $bCreate = true)
{
$oBlob = $oAttachment->Get('contents');
$sFileName = $oBlob->GetFileName();
if ($bCreate)
{
$sDescription = Dict::Format('Attachments:History_File_Added', $sFileName);
}
else
{
$sDescription = Dict::Format('Attachments:History_File_Removed', $sFileName);
}
return $sDescription;
}
}
?>

View File

@@ -0,0 +1,65 @@
<?php
// Copyright (C) 2010 Combodo SARL
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; version 3 of the License.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
SetupWebPage::AddModule(
__FILE__, // Path to the current file, all other file names are relative to the directory containing this file
'itop-attachments/1.0.0',
array(
// Identification
//
'label' => 'Tickets attachments',
'category' => 'business',
// Setup
//
'dependencies' => array(
),
'mandatory' => false,
'visible' => true,
// Components
//
'datamodel' => array(
'model.attachments.php'
),
'webservice' => array(
),
'dictionary' => array(
'en.dict.attachments.php',
'fr.dict.attachments.php',
),
'data.struct' => array(
// add your 'structure' definition XML files here,
),
'data.sample' => array(
// add your sample data XML files here,
),
// Documentation
//
'doc.manual_setup' => '', // hyperlink to manual setup documentation, if any
'doc.more_information' => '', // hyperlink to more information, if any
// Default settings
//
'settings' => array(
'allowed_classes' => array('Ticket'), // List of classes for which the "Attachments" tab is visible
),
)
);
?>