Automatically save the PDF of the impact analysis as an attachement to the ticket.

SVN:trunk[3582]
This commit is contained in:
Denis Flaven
2015-05-20 09:33:42 +00:00
parent 3949632339
commit 80c0312219
7 changed files with 162 additions and 38 deletions

View File

@@ -187,4 +187,10 @@ EOF
$this->flush();
echo $this->oPdf->Output($this->s_title.'.pdf', 'S');
}
public function get_pdf()
{
$this->flush();
return $this->oPdf->Output($this->s_title.'.pdf', 'S');
}
}

View File

@@ -879,8 +879,11 @@ class DisplayableGraph extends SimpleGraph
$fBreakMargin = $oPdf->getBreakMargin();
$oPdf->SetAutoPageBreak(false);
$fKeyWidth = $this->RenderKey($oPdf, $sComments, $xMin, $yMin, $xMax, $yMax);
$xMin += + $fKeyWidth;
$aRemainingArea = $this->RenderKey($oPdf, $sComments, $xMin, $yMin, $xMax, $yMax);
$xMin = $aRemainingArea['xmin'];
$xMax = $aRemainingArea['xmax'];
$yMin = $aRemainingArea['ymin'];
$yMax = $aRemainingArea['ymax'];
//$oPdf->Rect($xMin, $yMin, $xMax - $xMin, $yMax - $yMin, 'D', array(), array(225, 225, 225));
@@ -916,14 +919,15 @@ class DisplayableGraph extends SimpleGraph
}
/**
* Renders (in PDF) the key (legend) of the graphics vertically to the left of the specified zone (xmin,ymin, xmax,ymax). Returns the width used by the legend.
* Renders (in PDF) the key (legend) of the graphics vertically to the left of the specified zone (xmin,ymin, xmax,ymax),
* and the comment (if any) at the bottom of the page. Returns the position of remaining area.
* @param TCPDF $oPdf
* @param string $sComments
* @param float $xMin
* @param float $yMin
* @param float $xMax
* @param float $yMax
* @return number The width used by the legend
* @return hash An array ('xmin' => , 'xmax' => ,'ymin' => , 'ymax' => ) of the remaining available area to paint the graph
*/
protected function RenderKey(TCPDF $oPdf, $sComments, $xMin, $yMin, $xMax, $yMax)
{
@@ -963,15 +967,24 @@ class DisplayableGraph extends SimpleGraph
$oPdf->Image($aIcons[$sClass], $xMin+1, $yPos, $fIconSize, $fIconSize);
$yPos += $fIconSize + 2*$fPadding;
}
$oPdf->Rect($xMin, $yMin, $fMaxWidth + $fIconSize + 3*$fPadding, $yPos - $yMin, 'D');
$oPdf->Rect($xMin, $yPos, $fMaxWidth + $fIconSize + 3*$fPadding, $yMax - $yPos, 'D');
$yPos +=1;
$oPdf->SetXY($xMin + $fPadding, $yPos);
$oPdf->Cell($fIconSize + $fPadding + $fMaxWidth, $fIconSize + $fPadding, Dict::S('UI:Relation:Comments'), 0 /* border */, 1 /* ln */, 'C', true /* fill */);
$yPos += $fIconSize + 2*$fPadding;
$oPdf->SetX($xMin);
$oPdf->writeHTMLCell($fIconSize + 2*$fPadding + $fMaxWidth, $yMax - $yPos, $xMin, $yPos, '<p>'.str_replace("\n", '<br/>', htmlentities($sComments, ENT_QUOTES, 'UTF-8')).'</p>', 0 /* border */, 1 /* ln */);
return $fMaxWidth + $fIconSize + 4*$fPadding;
$oPdf->Rect($xMin, $yMin, $fMaxWidth + $fIconSize + 3*$fPadding, $yMax - $yMin, 'D');
if ($sComments != '')
{
// Draw the comment text (surrounded by a rectangle)
$xPos = $xMin + $fMaxWidth + $fIconSize + 4*$fPadding;
$w = $xMax - $xPos - 2*$fPadding;
$iNbLines = 1;
$sText = '<p>'.str_replace("\n", '<br/>', htmlentities($sComments, ENT_QUOTES, 'UTF-8'), $iNbLines).'</p>';
$fLineHeight = $oPdf->getStringHeight($w, $sText);
$h = (1+$iNbLines) * $fLineHeight;
$yPos = $yMax - 2*$fPadding - $h;
$oPdf->writeHTMLCell($w, $h, $xPos + $fPadding, $yPos + $fPadding, $sText, 0 /* border */, 1 /* ln */);
$oPdf->Rect($xPos, $yPos, $w + 2*$fPadding, $h + 2*$fPadding, 'D');
$yMax = $yPos - $fPadding;
}
return array('xmin' => $fMaxWidth + $fIconSize + 4*$fPadding, 'xmax' => $xMax, 'ymin' => $yMin, 'ymax' => $yMax);
}
/**
@@ -982,7 +995,7 @@ class DisplayableGraph extends SimpleGraph
* @param ApplicationContext $oAppContext
* @param array $aExcludedObjects
*/
function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects = array())
function Display(WebPage $oP, $aResults, $sRelation, ApplicationContext $oAppContext, $aExcludedObjects = array(), $sObjClass = null, $iObjKey = null)
{
$aExcludedByClass = array();
foreach($aExcludedObjects as $oObj)
@@ -1037,15 +1050,21 @@ EOF
{
$this->InitFromGraphviz();
$sExportAsPdfURL = '';
if (extension_loaded('gd'))
{
$sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_pdf&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
}
$sExportAsPdfURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_pdf&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$oAppcontext = new ApplicationContext();
$sContext = $oAppContext->GetForLink();
$sDrillDownURL = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=details&class=%1$s&id=%2$s&'.$sContext;
$sExportAsDocumentURL = '';
$sExportAsDocumentURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_attachment&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$sLoadFromURL = utils::GetAbsoluteUrlAppRoot().'pages/ajax.render.php?operation=relation_json&relation='.$sRelation.'&direction='.($this->bDirectionDown ? 'down' : 'up');
$sAttachmentExportTitle = '';
if (($sObjClass != null) && ($iObjKey != null))
{
$oTargetObj = MetaModel::GetObject($sObjClass, $iObjKey, false);
if ($oTargetObj)
{
$sAttachmentExportTitle = Dict::Format('UI:Relation:AttachmentExportOptions_Name', $oTargetObj->GetName());
}
}
$sId = 'graph';
$oP->add('<div id="'.$sId.'" class="simple-graph"></div>');
@@ -1055,13 +1074,15 @@ EOF
'excluded' => $aExcludedByClass,
'grouping_threshold' => $iGroupingThreshold,
'export_as_pdf' => array('url' => $sExportAsPdfURL, 'label' => Dict::S('UI:Relation:ExportAsPDF')),
//'export_as_document' => array('url' => $sExportAsDocumentURL, 'label' => Dict::S('UI:Relation:ExportAsDocument')),
'export_as_attachment' => array('url' => $sExportAsDocumentURL, 'label' => Dict::S('UI:Relation:ExportAsAttachment'), 'obj_class' => $sObjClass, 'obj_key' => $iObjKey),
'drill_down' => array('url' => $sDrillDownURL, 'label' => Dict::S('UI:Relation:DrillDown')),
'labels' => array(
'export_pdf_title' => Dict::S('UI:Relation:PDFExportOptions'),
'export_as_attachment_title' => $sAttachmentExportTitle,
'export' => Dict::S('UI:Button:Export'),
'cancel' => Dict::S('UI:Button:Cancel'),
'title' => Dict::S('UI:RelationOption:Title'),
'untitled' => Dict::S('UI:RelationOption:Untitled'),
'include_list' => Dict::S('UI:RelationOption:IncludeList'),
'comments' => Dict::S('UI:RelationOption:Comments'),
'grouping_threshold' => Dict::S('UI:RelationOption:GroupingThreshold'),
@@ -1083,6 +1104,17 @@ EOF
),
),
);
if (!extension_loaded('gd'))
{
// PDF export requires GD
unset($aParams['export_as_pdf']);
}
if (!extension_loaded('gd') || is_null($sObjClass) || is_null($iObjKey))
{
// PDF export requires GD AND a valid objclass/objkey couple
unset($aParams['export_as_pdf']);
unset($aParams['export_as_attachment']);
}
$oP->add_ready_script("$('#$sId').simple_graph(".json_encode($aParams).");");
}
catch(Exception $e)

View File

@@ -781,6 +781,8 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
'UI:Relation:ExportAsDocument' => 'Export as Document...~~',
'UI:Relation:DrillDown' => 'Details...~~',
'UI:Relation:PDFExportOptions' => 'PDF Export Options~~',
'UI:Relation:AttachmentExportOptions_Name' => 'Options for Attachment to %1$s~~',
'UI:RelationOption:Untitled' => 'Untitled~~',
'UI:Relation:Key' => 'Key~~',
'UI:Relation:Comments' => 'Comments~~',
'UI:RelationOption:Title' => 'Title~~',

View File

@@ -971,9 +971,11 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:RelationGroupNumber_N' => 'Group #%1$d',
'UI:Relation:ExportAsPDF' => 'Export as PDF...',
'UI:RelationOption:GroupingThreshold' => 'Grouping threshold',
'UI:Relation:ExportAsDocument' => 'Export as Document...',
'UI:Relation:ExportAsAttachment' => 'Export as Attachment...',
'UI:Relation:DrillDown' => 'Details...',
'UI:Relation:PDFExportOptions' => 'PDF Export Options',
'UI:Relation:AttachmentExportOptions_Name' => 'Options for Attachment to %1$s',
'UI:RelationOption:Untitled' => 'Untitled',
'UI:Relation:Key' => 'Key',
'UI:Relation:Comments' => 'Comments',
'UI:RelationOption:Title' => 'Title',

View File

@@ -814,9 +814,11 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:RelationGroupNumber_N' => 'Groupe n°%1$d',
'UI:Relation:ExportAsPDF' => 'Exporter en PDF...',
'UI:RelationOption:GroupingThreshold' => 'Seuil de groupage',
'UI:Relation:ExportAsDocument' => 'Exporter comme Document...',
'UI:Relation:ExportAsAttachment' => 'Exporter comme une Pièce Jointe...',
'UI:Relation:DrillDown' => 'Détails...',
'UI:Relation:PDFExportOptions' => 'Options de l\'export en PDF',
'UI:Relation:AttachmentExportOptions_Name' => 'Options pour la Pièce Jointe à %1$s',
'UI:RelationOption:Untitled' => 'Sans Titre',
'UI:Relation:Key' => 'Légende',
'UI:Relation:Comments' => 'Commentaires',
'UI:RelationOption:Title' => 'Titre',

View File

@@ -33,7 +33,9 @@ $(function()
export_as_document: null,
drill_down: null,
grouping_threshold: 10,
excluded_classes: []
excluded_classes: [],
attachment_obj_class: null,
attachment_obj_key: null
},
// the constructor
@@ -371,9 +373,9 @@ $(function()
{
sHtml += '<li><a href="#" id="'+sPopupMenuId+'_pdf">'+this.options.export_as_pdf.label+'</a></li>';
}
if (this.options.export_as_document != null)
if (this.options.export_as_attachment != null)
{
sHtml += '<li><a href="#" id="'+sPopupMenuId+'_document">'+this.options.export_as_document.label+'</a></li>';
sHtml += '<li><a href="#" id="'+sPopupMenuId+'_attachment">'+this.options.export_as_attachment.label+'</a></li>';
}
//sHtml += '<li><a href="#" id="'+sPopupMenuId+'_reload">Refresh</a></li>';
sHtml += '</ul></li></ul></div>';
@@ -385,7 +387,7 @@ $(function()
var me = this;
$('#'+sPopupMenuId+'_pdf').click(function() { me.export_as_pdf(); });
$('#'+sPopupMenuId+'_document').click(function() { me.export_as_document(); });
$('#'+sPopupMenuId+'_attachment').click(function() { me.export_as_attachment(); });
$('#'+sId+'_grouping_threshold').spinner({ min: 2});
$('#'+sId+'_refresh_btn').button().click(function() { me.reload(); });
},
@@ -449,13 +451,17 @@ $(function()
},
export_as_pdf: function()
{
this._export_dlg(this.options.labels.export_pdf_title, this.options.export_as_pdf.url, 'download_pdf');
},
_export_dlg: function(sTitle, sSubmitUrl, sOperation)
{
var oPositions = {};
for(k in this.aNodes)
{
oPositions[this.aNodes[k].id] = {x: this.aNodes[k].x, y: this.aNodes[k].y };
}
var sHtmlForm = '<div id="PDFExportDlg'+this.element.attr('id')+'"><form id="graph_'+this.element.attr('id')+'_export_as_pdf" target="_blank" action="'+this.options.export_as_pdf.url+'" method="post">';
var sHtmlForm = '<div id="GraphExportDlg'+this.element.attr('id')+'"><form id="graph_'+this.element.attr('id')+'_export_dlg" target="_blank" action="'+sSubmitUrl+'" method="post">';
sHtmlForm += '<input type="hidden" name="g" value="'+this.options.grouping_threshold+'">';
sHtmlForm += '<input type="hidden" name="positions" value="">';
for(k in this.options.excluded_classes)
@@ -476,6 +482,11 @@ $(function()
sHtmlForm += '<input type="hidden" name="excluded['+k1+'][]" value="'+this.options.excluded[k1][k2]+'">';
}
}
if (sOperation == 'attachment')
{
sHtmlForm += '<input type="hidden" name="obj_class" value="'+this.options.export_as_attachment.obj_class+'">';
sHtmlForm += '<input type="hidden" name="obj_key" value="'+this.options.export_as_attachment.obj_key+'">';
}
sHtmlForm += '<table>';
sHtmlForm += '<tr><td>'+this.options.page_format.label+'</td><td><select name="p">';
for(k in this.options.page_format.values)
@@ -491,26 +502,29 @@ $(function()
sHtmlForm += '<option value="'+k+'"'+sSelected+'>'+this.options.page_orientation.values[k]+'</option>';
}
sHtmlForm += '</select></td></tr>';
sHtmlForm += '<tr><td>'+this.options.labels.title+'</td><td><input name="title" style="width: 20em;"/></td></tr>';
sHtmlForm += '<tr><td>'+this.options.labels.title+'</td><td><input name="title" value="'+this.options.labels.untitled+'" style="width: 20em;"/></td></tr>';
sHtmlForm += '<tr><td>'+this.options.labels.comments+'</td><td><textarea style="width: 20em; height:5em;" name="comments"/></textarea></td></tr>';
sHtmlForm += '<tr><td colspan=2><input type="checkbox" checked id="include_list_checkbox" name="include_list" value="1"><label for="include_list_checkbox">&nbsp;'+this.options.labels.include_list+'</label></td></tr>';
sHtmlForm += '<table>';
sHtmlForm += '</form></div>';
$('body').append(sHtmlForm);
$('#graph_'+this.element.attr('id')+'_export_as_pdf input[name="positions"]').val(JSON.stringify(oPositions));
$('#graph_'+this.element.attr('id')+'_export_dlg input[name="positions"]').val(JSON.stringify(oPositions));
var me = this;
$('#PDFExportDlg'+this.element.attr('id')).dialog({
if (sOperation == 'attachment')
{
$('#GraphExportDlg'+this.element.attr('id')+' form').submit(function() { return me._on_export_as_attachment(); });
}
$('#GraphExportDlg'+this.element.attr('id')).dialog({
width: 'auto',
modal: true,
title: this.options.labels.export_pdf_title,
title: sTitle,
close: function() { $(this).remove(); },
buttons: [
{text: this.options.labels['cancel'], click: function() { $(this).dialog('close');} },
{text: this.options.labels['export'], click: function() { $('#graph_'+me.element.attr('id')+'_export_as_pdf').submit(); $(this).dialog('close');} },
{text: this.options.labels['export'], click: function() { $('#graph_'+me.element.attr('id')+'_export_dlg').submit(); $(this).dialog('close');} },
]
});
//$('#graph_'+this.element.attr('id')+'_export_as_pdf').submit();
});
},
_on_resize: function()
{
@@ -553,9 +567,54 @@ $(function()
me.load(data);
}, 'json');
},
export_as_document: function()
export_as_attachment: function()
{
alert('Export as document: not yet implemented');
this._export_dlg(this.options.labels.export_as_attachment_title, this.options.export_as_attachment.url, 'attachment');
},
_on_export_as_attachment: function()
{
var oParams = {};
var oPositions = {};
var jForm = $('#GraphExportDlg'+this.element.attr('id')+' form');
for(k in this.aNodes)
{
oPositions[this.aNodes[k].id] = {x: this.aNodes[k].x, y: this.aNodes[k].y };
}
oParams.positions = JSON.stringify(oPositions);
oParams.sources = this.options.sources;
oParams.excluded_classes = this.options.excluded_classes;
oParams.title = jForm.find(':input[name="title"]').val();
oParams.comments = jForm.find(':input[name="comments"]').val();
oParams.include_list = jForm.find(':input[name="include_list"]:checked').length;
oParams.o = jForm.find(':input[name="o"]').val();
oParams.p = jForm.find(':input[name="p"]').val();
oParams.obj_class = this.options.export_as_attachment.obj_class;
oParams.obj_key = this.options.export_as_attachment.obj_key;
var sUrl = jForm.attr('action');
var sTitle = oParams.title;
var jPanel = $('#attachments').closest('.ui-tabs-panel');
var jTab = null;
var sTabText = null;
if (jPanel.length > 0)
{
var sTabId = jPanel.attr('id');
jTab = $('li[aria-controls='+sTabId+']');
sTabText = jTab.find('span').html();
jTab.find('span').html(sTabText+' <img style="vertical-align:bottom" src="../images/indicator.gif">');
}
$.post(sUrl, oParams, function(data) {
var sDownloadLink = GetAbsoluteUrlAppRoot()+'pages/ajax.render.php?operation=download_document&class=Attachment&id='+data.att_id+'&field=contents';
var sIcon = GetAbsoluteUrlModulesRoot()+'itop-attachments/icons/pdf.png';
$('#attachments').append('<div class="attachment" id="display_attachment_'+data.att_id+'"><a data-preview="false" href="'+sDownloadLink+'"><img src="'+sIcon+'"><br/>'+sTitle+'.pdf<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="RemoveAttachment('+data.att_id+');"/></div>');
if (jTab != null)
{
var re = /^([^(]+)\(([0-9]+)\)(.*)$/;
var aParts = re.exec(sTabText);
var iPrevCount = parseInt(aParts[2], 10);
jTab.find('span').html(aParts[1]+'('+(1 + iPrevCount)+')'+aParts[3]);
}
}, 'json');
return false;
},
reload: function()
{

View File

@@ -1728,13 +1728,14 @@ EOF
break;
case 'relation_pdf':
case 'relation_attachment':
require_once(APPROOT.'core/simplegraph.class.inc.php');
require_once(APPROOT.'core/relationgraph.class.inc.php');
require_once(APPROOT.'core/displayablegraph.class.inc.php');
$sRelation = utils::ReadParam('relation', 'impacts');
$sDirection = utils::ReadParam('direction', 'down');
$iGroupingThreshold = utils::ReadParam('g', 5);
$iGroupingThreshold = utils::ReadParam('g', 5, false, 'integer');
$sPageFormat = utils::ReadParam('p', 'A4');
$sPageOrientation = utils::ReadParam('o', 'L');
$sTitle = utils::ReadParam('title', '', false, 'raw_data');
@@ -1876,6 +1877,26 @@ EOF
}
}
}
if ($operation == 'relation_attachment')
{
$sObjClass = utils::ReadParam('obj_class', '', false, 'class');
$iObjKey = (int)utils::ReadParam('obj_key', 0, false, 'integer');
// Save the generated PDF as an attachment
$sPDF = $oPage->get_pdf();
$oPage = new ajax_page('');
$oAttachment = new Attachment();
$oAttachment->Set('item_class', $sObjClass);
$oAttachment->Set('item_id', $iObjKey);
$oDoc = new ormDocument($sPDF, 'application/pdf', $sTitle.'.pdf');
$oAttachment->Set('contents', $oDoc);
$iAttachmentId = $oAttachment->DBInsert();
$aRet = array(
'status' => 'ok',
'att_id' => $iAttachmentId,
);
$oPage->add(json_encode($aRet));
}
break;
case 'relation_json':
@@ -2004,7 +2025,7 @@ EOF
$aResults = $oRelGraph->GetObjectsByClass();
$oGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down'));
$oAppContext = new ApplicationContext();
$oGraph->Display($oPage, $aResults, $sRelation, $oAppContext, $aExcludedObjects);
$oGraph->Display($oPage, $aResults, $sRelation, $oAppContext, $aExcludedObjects, $sClass, $iId);
break;
default: