More options for the PDF export of the 'impact' graph.

SVN:trunk[3577]
This commit is contained in:
Denis Flaven
2015-05-10 09:07:19 +00:00
parent 3e6896b8e6
commit d1a74589b1
18 changed files with 523 additions and 52 deletions

View File

@@ -705,7 +705,45 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
public static function DisplaySet(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array())
{
$oPage->add(self::GetDisplaySet($oPage, $oSet, $aExtraParams));
if ($oPage->is_pdf())
{
$oPage->add(self::DisplaySetForPrinting($oPage, $oSet, $aExtraParams));
}
else
{
$oPage->add(self::GetDisplaySet($oPage, $oSet, $aExtraParams));
}
}
/**
* Simplifed version of GetDisplaySet() with less "decoration" around the table (and no paging)
* that fits better into a printed document (like a PDF)
* @param PDFPage $oPage
* @param DBObjectSet $oSet
* @param hash $aExtraParams
* @return string The HTML representation of the table
*/
public static function DisplaySetForPrinting(PDFPage $oPage, DBObjectSet $oSet, $aExtraParams = array())
{
$iListId = empty($aExtraParams['currentId']) ? $oPage->GetUniqueId() : $aExtraParams['currentId'];
$sTableId = isset($aExtraParams['table_id']) ? $aExtraParams['table_id'] : null;
$bViewLink = true;
$sSelectMode = 'none';
$iListId = $sTableId;
$sClassAlias = $oSet->GetClassAlias();
$sClassName = $oSet->GetClass();
$sZListName = 'list';
$aClassAliases = array( $sClassAlias => $sClassName);
$aList = cmdbAbstractObject::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName));
$oDataTable = new PrintableDataTable($iListId, $oSet, $aClassAliases, $sTableId);
$oSettings = DataTableSettings::GetDataModelSettings($aClassAliases, $bViewLink, array($sClassAlias => $aList));
$oSettings->iDefaultPageSize = 0;
$oSettings->aSortOrder = MetaModel::GetOrderByDefault($sClassName);
return $oDataTable->Display($oPage, $oSettings, false /* $bDisplayMenu */, $sSelectMode, $bViewLink, $aExtraParams);
}
/**

View File

@@ -606,6 +606,34 @@ EOF
}
}
/**
* Simplified version of the data table with less "decoration" (and no paging)
* which is optimized for printing
*/
class PrintableDataTable extends DataTable
{
public function GetAsHTML(WebPage $oPage, $iPageSize, $iDefaultPageSize, $iPageIndex, $aColumns, $bActionsMenu, $bToolkitMenu, $sSelectMode, $bViewLink, $aExtraParams)
{
return $this->GetHTMLTable($oPage, $aColumns, $sSelectMode, -1, $bViewLink, $aExtraParams);
}
public function GetHTMLTable(WebPage $oPage, $aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams)
{
$iNbPages = ($iPageSize < 1) ? 1 : ceil($this->iNbObjects / $iPageSize);
if ($iPageSize < 1)
{
$iPageSize = -1; // convention: no pagination
}
$aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink);
$aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams);
$sHtml = $oPage->GetTable($aAttribs, $aValues);
return $sHtml;
}
}
class DataTableSettings implements Serializable
{
public $aClassAliases;

View File

@@ -0,0 +1,190 @@
<?php
require_once(APPROOT.'lib/tcpdf/tcpdf.php');
/**
* Custom class derived from TCPDF for providing custom headers and footers
* @author denis
*
*/
class iTopPDF extends TCPDF
{
protected $sDocumentTitle;
public function SetDocumentTitle($sDocumentTitle)
{
$this->sDocumentTitle = $sDocumentTitle;
}
/**
* Builds the custom header. Called for each new page.
* @see TCPDF::Header()
*/
public function Header()
{
// Title
// Set font
$this->SetFont('dejavusans', 'B', 10);
$iPageNumberWidth = 25;
$aMargins = $this->getMargins();
// Display the title (centered)
$this->SetY(0);
$this->MultiCell($this->getPageWidth() - $aMargins['left'] - $aMargins['right'] - $iPageNumberWidth, 15, $this->sDocumentTitle, 0, 'C', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
$this->SetFont('dejavusans', '', 10);
// Display the page number (right aligned)
// Warning: the 'R'ight alignment does not work when using placeholders like $this->getAliasNumPage() or $this->getAliasNbPages()
$this->MultiCell($iPageNumberWidth, 15, 'Page '.$this->page, 0, 'R', false, 0 /* $ln */, '', '', true, 0, false, true, 15, 'M' /* $valign */);
// Branding logo
$sBrandingIcon = APPROOT.'images/itop-logo.png';
if (file_exists(MODULESROOT.'branding/main-logo.png'))
{
$sBrandingIcon = MODULESROOT.'branding/main-logo.png';
}
$this->Image($sBrandingIcon, $aMargins['left'], 5, 0, 10);
}
// Page footer
public function Footer()
{
// No footer
}
}
/**
* Special class of WebPage for printing into a PDF document
*/
class PDFPage extends WebPage
{
/**
* Instance of the TCPDF object for creating the PDF
* @var TCPDF
*/
protected $oPdf;
public function __construct($s_title, $sPageFormat = 'A4', $sPageOrientation = 'L')
{
parent::__construct($s_title);
define(K_PATH_FONTS, APPROOT.'lib/tcpdf/fonts');
$this->oPdf = new iTopPDF($sPageOrientation, 'mm', $sPageFormat, true, 'UTF-8', false);
// set document information
$this->oPdf->SetCreator(PDF_CREATOR);
$this->oPdf->SetAuthor('iTop');
$this->oPdf->SetTitle($s_title);
$this->oPdf->SetDocumentTitle($s_title);
$this->oPdf->setFontSubsetting(true);
// Set font
// dejavusans is a UTF-8 Unicode font. Standard PDF fonts like helvetica or times new roman are NOT UTF-8
$this->oPdf->SetFont('dejavusans', '', 10, '', true);
// set auto page breaks
$this->oPdf->SetAutoPageBreak(true, 15); // 15 mm break margin at the bottom
$this->oPdf->SetTopMargin(15);
// Add a page, we're ready to start
$this->oPdf->AddPage();
$this->SetContentType('application/pdf');
$this->SetContentDisposition('inline', $s_title.'.pdf');
$this->SetDefaultStyle();
}
/**
* Sets a default style (suitable for printing) to be included each time $this->oPdf->writeHTML() is called
*/
protected function SetDefaultStyle()
{
$this->add_style(
<<<EOF
table.listResults td {
border: 0.5pt solid #000 ;
padding: 0.5pt;
}
table.listResults th {
background-color: #eee;
border: 0.5pt solid #000 ;
}
a {
text-decoration: none;
color: #000;
}
table.section td {
vertical-align: middle;
font-size: 10pt;
background-color:#eee;
}
td.icon {
width: 30px;
}
EOF
);
}
/**
* Get access to the underlying TCPDF object
* @return TCPDF
*/
public function get_tcpdf()
{
$this->flush();
return $this->oPdf;
}
/**
* Writes the currently buffered HTML content into the PDF. This can be useful:
* - to sync the flow in case you want to access the underlying TCPDF object for some specific/graphic output
* - to process the HTML by smaller chunks instead of processing the whole page at once for performance reasons
*/
public function flush()
{
if (strlen($this->s_content) > 0)
{
$sHtml = '';
if (count($this->a_styles) > 0)
{
$sHtml .= "<style>\n".implode("\n", $this->a_styles)."\n</style>\n";
}
$sHtml .= $this->s_content;
$this->oPdf->writeHTML($sHtml); // The style(s) must be supplied each time we call writeHtml
$this->s_content = '';
}
}
/**
* Whether or not the page is a PDF page
* @return boolean
*/
public function is_pdf()
{
return true;
}
/**
* Generates the PDF document and returns the PDF content as a string
* @return string
* @see WebPage::output()
*/
public function output()
{
if (!empty($this->sContentType))
{
$this->add_header('Content-type: '.$this->sContentType);
}
if (!empty($this->sContentDisposition))
{
$this->add_header('Content-Disposition: '.$this->sContentDisposition.'; filename="'.$this->sContentFileName.'"');
}
foreach($this->a_headers as $s_header)
{
header($s_header);
}
$this->flush();
echo $this->oPdf->Output($this->s_title.'.pdf', 'S');
}
}

View File

@@ -268,7 +268,27 @@ class WebPage implements Page
{
$this->a_linked_stylesheets[] = array( 'link' => $s_linked_stylesheet, 'condition' => $s_condition);
}
public function add_saas($sSaasRelPath)
{
$sSaasPath = APPROOT.$sSaasRelPath;
$sCssRelPath = preg_replace('/\.scss$/', '.css', $sSaasRelPath);
$sCssPath = APPROOT.$sCssRelPath;
clearstatcache();
if (!file_exists($sCssPath) || (is_writable($sCssPath) && (filemtime($sCssPath) < filemtime($sSaasPath))))
{
// Rebuild the CSS file from the Saas file
if (file_exists(APPROOT.'lib/sass/sass/SassParser.php'))
{
require_once(APPROOT.'lib/sass/sass/SassParser.php'); //including Sass libary (Syntactically Awesome Stylesheets)
$oParser = new SassParser(array('style'=>'expanded'));
$sCss = $oParser->toCss($sSaasPath);
file_put_contents($sCssPath, $sCss);
}
}
$sCSSUrl = utils::GetAbsoluteUrlAppRoot().$sCssRelPath;
$this->add_linked_stylesheet($sCSSUrl);
}
/**
* Add some custom header to the page
*/
@@ -294,6 +314,15 @@ class WebPage implements Page
$this->add($this->GetDetails($aFields));
}
/**
* Whether or not the page is a PDF page
* @return boolean
*/
public function is_pdf()
{
return false;
}
/**
* Records the current state of the 'html' part of the page output

View File

@@ -892,55 +892,49 @@ class DisplayableGraph extends SimpleGraph
}
/**
* Renders the graph as a PDF file
* @param WebPage $oP The page for the ouput of the PDF
* @param string $sTitle The title of the PDF
* @param string $sPageFormat The page format: A4, A3, Letter...
* @param string $sPageOrientation The orientation of the page (L = Landscape, P = Portrait)
* Renders the graph in a PDF document: centered in the current page
* @param PDFPage $oPage The PDFPage representing the PDF document to draw into
* @param string $sComments An optional comment to display next to the graph (HTML entities will be escaped, \n replaced by <br/>)
* @param float $xMin Left coordinate of the bounding box to display the graph
* @param float $xMax Right coordinate of the bounding box to display the graph
* @param float $yMin Top coordinate of the bounding box to display the graph
* @param float $yMax Bottom coordinate of the bounding box to display the graph
*/
function RenderAsPDF(WebPage $oP, $sTitle = 'Untitled', $sPageFormat = 'A4', $sPageOrientation = 'P')
function RenderAsPDF(PDFPage $oPage, $sComments = '', $xMin = -1, $xMax = -1, $yMin = -1, $yMax = -1)
{
require_once(APPROOT.'lib/tcpdf/tcpdf.php');
$oPdf = new TCPDF($sPageOrientation, 'mm', $sPageFormat, true, 'UTF-8', false);
// set document information
$oPdf->SetCreator(PDF_CREATOR);
$oPdf->SetAuthor('iTop');
$oPdf->SetTitle($sTitle);
$oPdf->setFontSubsetting(true);
// Set font
// dejavusans is a UTF-8 Unicode font, if you only need to
// print standard ASCII chars, you can use core fonts like
// helvetica or times to reduce file size.
$oPdf->SetFont('dejavusans', '', 14, '', true);
// set auto page breaks
$oPdf->SetAutoPageBreak(false);
// Add a page
// This method has several options, check the source code documentation for more information.
$oPdf->AddPage();
$oPdf = $oPage->get_tcpdf();
$aBB = $this->GetBoundingBox();
$this->Translate(-$aBB['xmin'], -$aBB['ymin']);
if ($sPageOrientation == 'P')
$aMargins = $oPdf->getMargins();
if ($xMin == -1)
{
// Portrait mode
$fHMargin = 10; // mm
$fVMargin = 15; // mm
$xMin = $aMargins['left'];
}
else
if ($xMax == -1)
{
// Landscape mode
$fHMargin = 15; // mm
$fVMargin = 10; // mm
$xMax = $oPdf->getPageWidth() - $aMargins['right'];
}
if ($yMin == -1)
{
$yMin = $aMargins['top'];
}
if ($yMax == -1)
{
$yMax = $oPdf->getPageHeight() - $aMargins['bottom'];
}
$fPageW = $oPdf->getPageWidth() - 2 * $fHMargin;
$fPageH = $oPdf->getPageHeight() - 2 * $fVMargin;
$fBreakMargin = $oPdf->getBreakMargin();
$oPdf->SetAutoPageBreak(false);
$fKeyWidth = $this->RenderKey($oPdf, $sComments, $xMin, $yMin, $xMax, $yMax);
$xMin += + $fKeyWidth;
//$oPdf->Rect($xMin, $yMin, $xMax - $xMin, $yMax - $yMin, 'D', array(), array(225, 225, 225));
$fPageW = $xMax - $xMin;
$fPageH = $yMax - $yMin;
$w = $aBB['xmax'] - $aBB['xmin'];
$h = $aBB['ymax'] - $aBB['ymin'] + 10; // Extra space for the labels which may appear "below" the icons
@@ -949,21 +943,70 @@ class DisplayableGraph extends SimpleGraph
$dx = ($fPageW - $fScale * $w) / 2;
$dy = ($fPageH - $fScale * $h) / 2;
$this->Translate(($fHMargin + $dx)/$fScale, ($fVMargin + $dy)/$fScale);
$this->Translate(($xMin + $dx)/$fScale, ($yMin + $dy)/$fScale);
$oIterator = new RelationTypeIterator($this, 'Edge');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
foreach($oIterator as $sId => $oEdge)
{
set_time_limit($iLoopTimeLimit);
$oEdge->RenderAsPDF($oPdf, $this, $fScale);
}
$oIterator = new RelationTypeIterator($this, 'Node');
foreach($oIterator as $sId => $oNode)
{
set_time_limit($iLoopTimeLimit);
$oNode->RenderAsPDF($oPdf, $this, $fScale);
}
$oP->add($oPdf->Output('iTop.pdf', 'S'));
$oIterator = new RelationTypeIterator($this, 'Node');
$oPdf->SetAutoPageBreak(true, $fBreakMargin);
$oPdf->SetAlpha(1);
}
protected function RenderKey(TCPDF $oPdf, $sComments, $xMin, $yMin, $xMax, $yMax)
{
$oIterator = new RelationTypeIterator($this, 'Node');
$fMaxWidth = max($oPdf->GetStringWidth(Dict::S('UI:Relation:Key')) - 6, $oPdf->GetStringWidth(Dict::S('UI:Relation:Comments')) - 6);
$aClasses = array();
$aIcons = array();
$oPdf->SetFont('dejavusans', '', 8, '', true);
foreach($oIterator as $sId => $oNode)
{
if ($sClass = $oNode->GetProperty('class'))
{
if (!array_key_exists($sClass, $aClasses))
{
$sClassLabel = MetaModel::GetName($sClass);
$width = $oPdf->GetStringWidth($sClassLabel);
$fMaxWidth = max($width, $fMaxWidth);
$aClasses[$sClass] = $sClassLabel;
$sIconUrl = $oNode->GetProperty('icon_url');
$sIconPath = str_replace(utils::GetAbsoluteUrlModulesRoot(), APPROOT.'env-production/', $sIconUrl);
$aIcons[$sClass] = $sIconPath;
}
}
}
$oPdf->SetXY($xMin + 1, $yMin +1);
$yPos = $yMin + 1;
$oPdf->SetFillColor(225, 225, 225);
$oPdf->Cell(7 + $fMaxWidth, 7, Dict::S('UI:Relation:Key'), 0 /* border */, 1 /* ln */, 'C', true /* fill */);
$yPos += 8;
foreach($aClasses as $sClass => $sLabel)
{
$oPdf->SetX($xMin + 7);
$oPdf->Cell(0, 8, $sLabel, 0 /* border */, 1 /* ln */);
$oPdf->Image($aIcons[$sClass], $xMin+1, $yPos, 6, 6);
$yPos += 8;
}
$oPdf->Rect($xMin, $yMin, $fMaxWidth+9, $yPos - $yMin, 'D');
$oPdf->Rect($xMin, $yPos, $fMaxWidth+9, $yMax - $yPos, 'D');
$yPos +=1;
$oPdf->SetXY($xMin + 1, $yPos);
$oPdf->Cell(7 + $fMaxWidth, 7, Dict::S('UI:Relation:Comments'), 0 /* border */, 1 /* ln */, 'C', true /* fill */);
$yPos += 8;
$oPdf->SetX($xMin);
$oPdf->writeHTMLCell(8 + $fMaxWidth, $yMax - $yPos, $xMin, $yPos, '<p>'.str_replace("\n", '<br/>', htmlentities($sComments, ENT_QUOTES, 'UTF-8')).'</p>', 0 /* border */, 1 /* ln */);
return $fMaxWidth + 10;
}
}

View File

@@ -774,6 +774,25 @@ Wenn Aktionen mit Trigger verknüpft sind, bekommt jede Aktion eine Auftragsnumm
'UI:DisplayThisMessageAtStartup' => 'Diese Meldung beim Start immer anzeigen',
'UI:RelationshipGraph' => 'Grafische Ansicht',
'UI:RelationshipList' => 'Liste',
'UI:RelationGroups' => 'Gruppen',
'UI:RelationGroupNumber_N' => 'Gruppe #%1$d~~',
'UI:Relation:ExportAsPDF' => 'Export as PDF...~~',
'UI:Relation:ExportAsDocument' => 'Export as Document...~~',
'UI:Relation:DrillDown' => 'Details...~~',
'UI:Relation:PDFExportOptions' => 'PDF Export Options~~',
'UI:Relation:Key' => 'Key~~',
'UI:Relation:Comments' => 'Comments~~',
'UI:RelationOption:Title' => 'Title~~',
'UI:RelationOption:IncludeList' => 'Include the list of objects~~',
'UI:RelationOption:Comments' => 'Comments~~',
'UI:Button:Export' => 'Export~~',
'UI:Relation:PDFExportPageFormat' => 'Page format~~',
'UI:PageFormat_A3' => 'A3~~',
'UI:PageFormat_A4' => 'A4~~',
'UI:PageFormat_Letter' => 'Letter~~',
'UI:Relation:PDFExportPageOrientation' => 'Page orientation~~',
'UI:PageOrientation_Portrait' => 'Portrait~~',
'UI:PageOrientation_Landscape' => 'Landscape~~',
'UI:OperationCancelled' => 'Operation abgebrochen',
'UI:ElementsDisplayed' => 'Filtere',
'Portal:Title' => 'iTop-Benutzerportal',

View File

@@ -973,6 +973,11 @@ When associated with a trigger, each action is given an "order" number, specifyi
'UI:Relation:ExportAsDocument' => 'Export as Document...',
'UI:Relation:DrillDown' => 'Details...',
'UI:Relation:PDFExportOptions' => 'PDF Export Options',
'UI:Relation:Key' => 'Key',
'UI:Relation:Comments' => 'Comments',
'UI:RelationOption:Title' => 'Title',
'UI:RelationOption:IncludeList' => 'Include the list of objects',
'UI:RelationOption:Comments' => 'Comments',
'UI:Button:Export' => 'Export',
'UI:Relation:PDFExportPageFormat' => 'Page format',
'UI:PageFormat_A3' => 'A3',

View File

@@ -816,6 +816,11 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé
'UI:Relation:ExportAsDocument' => 'Exportation comme Document...',
'UI:Relation:DrillDown' => 'Détails...',
'UI:Relation:PDFExportOptions' => 'Options de l\'Exportation PDF',
'UI:Relation:Key' => 'Légende',
'UI:Relation:Comments' => 'Commentaires',
'UI:RelationOption:Title' => 'Titre',
'UI:RelationOption:IncludeList' => 'Inclure la liste des objets',
'UI:RelationOption:Comments' => 'Commentaires',
'UI:Button:Export' => 'Exporter',
'UI:Relation:PDFExportPageFormat' => 'Format de page',
'UI:PageFormat_A3' => 'A3',

View File

@@ -19,7 +19,7 @@ $(function()
export_as_pdf: null,
page_format: { label: 'Page Format:', values: { A3: 'A3', A4: 'A4', Letter: 'Letter' }, 'default': 'A4'},
page_orientation: { label: 'Page Orientation:', values: { P: 'Portait', L: 'Landscape' }, 'default': 'L' },
labels: { export_pdf_title: 'PDF Export Options', cancel: 'Cancel', 'export': 'Export' },
labels: { export_pdf_title: 'PDF Export Options', cancel: 'Cancel', 'export': 'Export', title: 'Document Title', include_list: 'Include the list of objects', comments: 'Comments' },
export_as_document: null,
drill_down: null,
excluded: []
@@ -458,6 +458,9 @@ $(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.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>';
@@ -465,6 +468,7 @@ $(function()
$('#graph_'+this.element.attr('id')+'_export_as_pdf input[name="positions"]').val(JSON.stringify(oPositions));
var me = this;
$('#PDFExportDlg'+this.element.attr('id')).dialog({
width: 'auto',
modal: true,
title: this.options.labels.export_pdf_title,
close: function() { $(this).remove(); },

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -330,6 +330,9 @@ EOF
'export_pdf_title' => Dict::S('UI:Relation:PDFExportOptions'),
'export' => Dict::S('UI:Button:Export'),
'cancel' => Dict::S('UI:Button:Cancel'),
'title' => Dict::S('UI:RelationOption:Title'),
'include_list' => Dict::S('UI:RelationOption:IncludeList'),
'comments' => Dict::S('UI:RelationOption:Comments'),
),
'page_format' => array(
'label' => Dict::S('UI:Relation:PDFExportPageFormat'),
@@ -1615,7 +1618,7 @@ EOF
if ($oObj)
{
$sObjClass = get_class($oObj);
if (!array_key_exists($sClass, $aResults))
if (!array_key_exists($sObjClass, $aResults))
{
$aResults[$sObjClass] = array();
}

View File

@@ -28,6 +28,7 @@ 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');
require_once(APPROOT.'/application/pdfpage.class.inc.php');
require_once(APPROOT.'/application/wizardhelper.class.inc.php');
require_once(APPROOT.'/application/ui.linkswidget.class.inc.php');
require_once(APPROOT.'/application/ui.extkeywidget.class.inc.php');
@@ -1731,7 +1732,7 @@ EOF
require_once(APPROOT.'core/relationgraph.class.inc.php');
require_once(APPROOT.'core/displayablegraph.class.inc.php');
$sClass = utils::ReadParam('class', '', false, 'class');
$id = utils::ReadParam('id', 0);
$id = (int)utils::ReadParam('id', 0);
$sRelation = utils::ReadParam('relation', 'impact');
$sDirection = utils::ReadParam('direction', 'down');
@@ -1741,6 +1742,8 @@ EOF
$sTitle = utils::ReadParam('title', '', false, 'raw_data');
$sPositions = utils::ReadParam('positions', null, false, 'raw_data');
$aExcluded = utils::ReadParam('excluded', array(), false, 'raw_data');
$bIncludeList = (bool)utils::ReadParam('include_list', false);
$sComments = utils::ReadParam('comments', '', false, 'raw_data');
$aPositions = null;
if ($sPositions != null)
{
@@ -1773,16 +1776,88 @@ EOF
}
}
$oPage = new PDFPage($sTitle, $sPageFormat, $sPageOrientation);
$oGraph = DisplayableGraph::FromRelationGraph($oRelGraph, $iGroupingThreshold, ($sDirection == 'down'));
$oGraph->InitFromGraphviz();
if ($aPositions != null)
{
$oGraph->UpdatePositions($aPositions);
}
$oGraph->RenderAsPDF($oPage, $sTitle, $sPageFormat, $sPageOrientation);
$oPage->SetContentType('application/pdf');
$oPage->SetContentDisposition('inline', 'iTop.pdf');
$iGroupIdx = 0;
$aGroups = array();
$oIterator = new RelationTypeIterator($oGraph, 'Node');
foreach($oIterator as $oNode)
{
if ($oNode instanceof DisplayableGroupNode)
{
$aGroups[] = $oNode->GetObjects();
$oNode->SetProperty('group_index', $iGroupIdx);
$iGroupIdx++;
}
}
// First page is the graph
$oGraph->RenderAsPDF($oPage, $sComments);
/*
// Experimental QR code at the bottom left of the page...
$sUrl = "r=$sRelation&d=$sDirection&c=$sClass&id=$id";
$oPdf = $oPage->get_tcpdf();
$aMargins = $oPdf->getMargins();
$oPdf->write2DBarcode($sUrl, 'QRCODE,H', $aMargins['left'], $oPdf->getPageHeight() - $aMargins['bottom'] - 35, 25, 25, array(), 'N');
*/
if ($bIncludeList)
{
// Then the lists of objects (one table per finalclass)
$aResults = array();
$oIterator = new RelationTypeIterator($oRelGraph, 'Node');
foreach($oIterator as $oNode)
{
$oObj = $oNode->GetProperty('object'); // Some nodes (Redundancy Nodes and Group) do not contain an object
if ($oObj)
{
$sObjClass = get_class($oObj);
if (!array_key_exists($sObjClass, $aResults))
{
$aResults[$sObjClass] = array();
}
$aResults[$sObjClass][] = $oObj;
}
}
$oPage->get_tcpdf()->AddPage();
$oPage->add('<div class="page_header"><h1>'.Dict::S('UI:RelationshipList').'</h1></div>');
$iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop');
foreach($aResults as $sListClass => $aObjects)
{
set_time_limit($iLoopTimeLimit);
$oSet = CMDBObjectSet::FromArray($sListClass, $aObjects);
$sHtml = "<div class=\"page_header\">\n";
$sHtml .= "<table class=\"section\"><tr><td>".MetaModel::GetClassIcon($sListClass, true, 'width: 24px; height: 24px;')." ".Dict::Format('UI:Search:Count_ObjectsOf_Class_Found', $oSet->Count(), Metamodel::GetName($sListClass))."</td></tr></table>\n";
$sHtml .= "</div>\n";
$oPage->add($sHtml);
cmdbAbstractObject::DisplaySet($oPage, $oSet);
$oPage->p(''); // Some space
}
// Then the content of the groups (one table per group)
if (count($aGroups) > 0)
{
$oPage->get_tcpdf()->AddPage();
$oPage->add('<div class="page_header"><h1>'.Dict::S('UI:RelationGroups').'</h1></div>');
foreach($aGroups as $idx => $aObjects)
{
set_time_limit($iLoopTimeLimit);
$sListClass = get_class(current($aObjects));
$oSet = CMDBObjectSet::FromArray($sListClass, $aObjects);
$sHtml = "<div class=\"page_header\">\n";
$sHtml .= "<table class=\"section\"><tr><td>".MetaModel::GetClassIcon($sListClass, true, 'width: 24px; height: 24px;')." ".Dict::Format('UI:RelationGroupNumber_N', (1+$idx))."</td></tr></table>\n";
$sHtml .= "</div>\n";
$oPage->add($sHtml);
cmdbAbstractObject::DisplaySet($oPage, $oSet);
$oPage->p(''); // Some space
}
}
}
break;
case 'relation_json':