dashlet.class.inc.php 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353
  1. <?php
  2. // Copyright (C) 2012 Combodo SARL
  3. //
  4. // This program is free software; you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation; version 3 of the License.
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  16. require_once(APPROOT.'application/forms.class.inc.php');
  17. /**
  18. * Base class for all 'dashlets' (i.e. widgets to be inserted into a dashboard)
  19. *
  20. */
  21. abstract class Dashlet
  22. {
  23. protected $sId;
  24. protected $bRedrawNeeded;
  25. protected $bFormRedrawNeeded;
  26. protected $aProperties; // array of {property => value}
  27. protected $aCSSClasses;
  28. public function __construct($sId)
  29. {
  30. $this->sId = $sId;
  31. $this->bRedrawNeeded = true; // By default: redraw each time a property changes
  32. $this->bFormRedrawNeeded = false; // By default: no need to redraw the form (independent fields)
  33. $this->aProperties = array(); // By default: there is no property
  34. $this->aCSSClasses = array('dashlet');
  35. }
  36. // Assuming that a property has the type of its default value, set in the constructor
  37. //
  38. public function Str2Prop($sProperty, $sValue)
  39. {
  40. $refValue = $this->aProperties[$sProperty];
  41. $sRefType = gettype($refValue);
  42. if ($sRefType == 'boolean')
  43. {
  44. $ret = ($sValue == 'true');
  45. }
  46. else
  47. {
  48. $ret = $sValue;
  49. settype($ret, $sRefType);
  50. }
  51. return $ret;
  52. }
  53. public function Prop2Str($value)
  54. {
  55. if (gettype($value) == 'boolean')
  56. {
  57. $sRet = $value ? 'true' : 'false';
  58. }
  59. else
  60. {
  61. $sRet = (string) $value;
  62. }
  63. return $sRet;
  64. }
  65. public function FromDOMNode($oDOMNode)
  66. {
  67. foreach ($this->aProperties as $sProperty => $value)
  68. {
  69. $this->oDOMNode = $oDOMNode->getElementsByTagName($sProperty)->item(0);
  70. if ($this->oDOMNode != null)
  71. {
  72. $newvalue = $this->Str2Prop($sProperty, $this->oDOMNode->textContent);
  73. $this->aProperties[$sProperty] = $newvalue;
  74. }
  75. }
  76. }
  77. public function ToDOMNode($oDOMNode)
  78. {
  79. foreach ($this->aProperties as $sProperty => $value)
  80. {
  81. $sXmlValue = $this->Prop2Str($value);
  82. $oPropNode = $oDOMNode->ownerDocument->createElement($sProperty, $sXmlValue);
  83. $oDOMNode->appendChild($oPropNode);
  84. }
  85. }
  86. public function FromXml($sXml)
  87. {
  88. $oDomDoc = new DOMDocument('1.0', 'UTF-8');
  89. $oDomDoc->loadXml($sXml);
  90. $this->FromDOMNode($oDomDoc->firstChild);
  91. }
  92. public function FromParams($aParams)
  93. {
  94. foreach ($this->aProperties as $sProperty => $value)
  95. {
  96. if (array_key_exists($sProperty, $aParams))
  97. {
  98. $this->aProperties[$sProperty] = $aParams[$sProperty];
  99. }
  100. }
  101. }
  102. public function DoRender($oPage, $bEditMode = false, $bEnclosingDiv = true, $aExtraParams = array())
  103. {
  104. $sCSSClasses = implode(' ', $this->aCSSClasses);
  105. $sId = $this->GetID();
  106. if ($bEnclosingDiv)
  107. {
  108. if ($bEditMode)
  109. {
  110. $oPage->add('<div class="'.$sCSSClasses.'" id="dashlet_'.$sId.'">');
  111. }
  112. else
  113. {
  114. $oPage->add('<div class="'.$sCSSClasses.'">');
  115. }
  116. }
  117. try
  118. {
  119. $this->Render($oPage, $bEditMode, $aExtraParams);
  120. }
  121. catch(UnknownClassOqlException $e)
  122. {
  123. // Maybe the class is part of a non-installed module, fail silently
  124. // Except in Edit mode
  125. if ($bEditMode)
  126. {
  127. $oPage->add('<div class="dashlet-content">');
  128. $oPage->add('<h2>Unknown Class: '.$e->GetWrongWord().', did you mean '.OqlException::FindClosestString($e->GetWrongWord(), $e->GetSuggestions()).'?</h2>'); //TODO localize and
  129. $oPage->add('</div>');
  130. }
  131. }
  132. catch(Exception $e)
  133. {
  134. $oPage->p($e->getMessage());
  135. }
  136. if ($bEnclosingDiv)
  137. {
  138. $oPage->add('</div>');
  139. }
  140. if ($bEditMode)
  141. {
  142. $sClass = get_class($this);
  143. $oPage->add_ready_script(
  144. <<<EOF
  145. $('#dashlet_$sId').dashlet({dashlet_id: '$sId', dashlet_class: '$sClass'});
  146. EOF
  147. );
  148. }
  149. }
  150. public function SetID($sId)
  151. {
  152. $this->sId = $sId;
  153. }
  154. public function GetID()
  155. {
  156. return $this->sId;
  157. }
  158. abstract public function Render($oPage, $bEditMode = false, $aExtraParams = array());
  159. abstract public function GetPropertiesFields(DesignerForm $oForm);
  160. public function ToXml(DOMNode $oContainerNode)
  161. {
  162. }
  163. public function Update($aValues, $aUpdatedFields)
  164. {
  165. foreach($aUpdatedFields as $sProp)
  166. {
  167. if (array_key_exists($sProp, $this->aProperties))
  168. {
  169. $this->aProperties[$sProp] = $aValues[$sProp];
  170. }
  171. }
  172. return $this;
  173. }
  174. public function IsRedrawNeeded()
  175. {
  176. return $this->bRedrawNeeded;
  177. }
  178. public function IsFormRedrawNeeded()
  179. {
  180. return $this->bFormRedrawNeeded;
  181. }
  182. static public function GetInfo()
  183. {
  184. return array(
  185. 'label' => '',
  186. 'icon' => '',
  187. 'description' => '',
  188. );
  189. }
  190. public function GetForm()
  191. {
  192. $oForm = new DesignerForm();
  193. $oForm->SetPrefix("dashlet_". $this->GetID());
  194. $oForm->SetParamsContainer('params');
  195. $this->GetPropertiesFields($oForm);
  196. $oDashletClassField = new DesignerHiddenField('dashlet_class', '', get_class($this));
  197. $oForm->AddField($oDashletClassField);
  198. $oDashletIdField = new DesignerHiddenField('dashlet_id', '', $this->GetID());
  199. $oForm->AddField($oDashletIdField);
  200. return $oForm;
  201. }
  202. static public function IsVisible()
  203. {
  204. return true;
  205. }
  206. static public function CanCreateFromOQL()
  207. {
  208. return false;
  209. }
  210. public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL)
  211. {
  212. // Default: do nothing since it's not supported
  213. }
  214. }
  215. class DashletEmptyCell extends Dashlet
  216. {
  217. public function __construct($sId)
  218. {
  219. parent::__construct($sId);
  220. }
  221. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  222. {
  223. $oPage->add('&nbsp;');
  224. }
  225. public function GetPropertiesFields(DesignerForm $oForm)
  226. {
  227. }
  228. static public function GetInfo()
  229. {
  230. return array(
  231. 'label' => 'Empty Cell',
  232. 'icon' => 'images/dashlet-text.png',
  233. 'description' => 'Empty Cell Dashlet Placeholder',
  234. );
  235. }
  236. static public function IsVisible()
  237. {
  238. return false;
  239. }
  240. }
  241. class DashletRichText extends Dashlet
  242. {
  243. public function __construct($sId)
  244. {
  245. parent::__construct($sId);
  246. $this->aProperties['text'] = "<h1>Lorem ipsum</h1>\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor, orci nec nonummy molestie, enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper.</p><p>Duis arcu massa, scelerisque vitae, consequat in, pretium a, enim. Pellentesque congue. Ut in risus volutpat libero pharetra tempor. Cras vestibulum bibendum augue. Praesent egestas leo in pede. Praesent blandit odio eu enim. Pellentesque sed dui ut augue blandit sodales. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Aliquam nibh. Mauris ac mauris sed pede pellentesque fermentum. Maecenas adipiscing ante non diam sodales hendrerit. Ut velit mauris, egestas sed, gravida nec, ornare ut, mi. Aenean ut orci vel massa suscipit pulvinar. Nulla sollicitudin. Fusce varius, ligula non tempus aliquam, nunc turpis ullamcorper nibh, in tempus sapien eros vitae ligula. Pellentesque rhoncus nunc et augue.</p><p>Integer id felis. Curabitur aliquet pellentesque diam. Integer quis metus vitae elit lobortis egestas. Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi vel erat non mauris convallis vehicula. Nulla et sapien. Integer tortor tellus, aliquam faucibus, convallis id, congue eu, quam. Mauris ullamcorper felis vitae erat. Proin feugiat, augue non elementum posuere, metus purus iaculis lectus, et tristique ligula justo vitae magna. Aliquam convallis sollicitudin purus. Praesent aliquam, enim at fermentum mollis, ligula massa adipiscing nisl, ac euismod nibh nisl eu lectus. Fusce vulputate sem at sapien. Vivamus leo. Aliquam euismod libero eu enim. Nulla nec felis sed leo placerat imperdiet.</p><p>Aenean suscipit nulla in justo. Suspendisse cursus rutrum augue. Nulla tincidunt tincidunt mi. Curabitur iaculis, lorem vel rhoncus faucibus, felis magna fermentum augue, et ultricies lacus lorem varius purus. Curabitur eu amet.</p>";
  247. }
  248. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  249. {
  250. $sText = $this->aProperties['text'];
  251. $sId = 'richtext_'.($bEditMode? 'edit_' : '').$this->sId;
  252. $oPage->add('<div id='.$sId.'" class="dashlet-content">'.$sText.'</div>');
  253. }
  254. public function GetPropertiesFields(DesignerForm $oForm)
  255. {
  256. $oField = new DesignerLongTextField('text', 'Text', $this->aProperties['text']);
  257. $oForm->AddField($oField);
  258. }
  259. static public function GetInfo()
  260. {
  261. return array(
  262. 'label' => 'Rich text',
  263. 'icon' => 'images/dashlet-text.png',
  264. 'description' => 'Text formatted with HTML tags',
  265. );
  266. }
  267. }
  268. class DashletObjectList extends Dashlet
  269. {
  270. public function __construct($sId)
  271. {
  272. parent::__construct($sId);
  273. $this->aProperties['title'] = 'Hardcoded list of "my requests"';
  274. $this->aProperties['query'] = 'SELECT UserRequest AS i WHERE i.caller_id = :current_contact_id AND status NOT IN ("closed", "resolved")';
  275. $this->aProperties['menu'] = false;
  276. }
  277. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  278. {
  279. $sTitle = $this->aProperties['title'];
  280. $sQuery = $this->aProperties['query'];
  281. $sShowMenu = $this->aProperties['menu'] ? '1' : '0';
  282. $oPage->add('<div class="dashlet-content">');
  283. $sHtmlTitle = htmlentities(Dict::S($sTitle), ENT_QUOTES, 'UTF-8'); // done in the itop block
  284. if ($sHtmlTitle != '')
  285. {
  286. $oPage->add('<h1>'.$sHtmlTitle.'</h1>');
  287. }
  288. $oFilter = DBObjectSearch::FromOQL($sQuery);
  289. $oBlock = new DisplayBlock($oFilter, 'list');
  290. $aExtraParams = array(
  291. 'menu' => $sShowMenu,
  292. );
  293. $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
  294. $oBlock->Display($oPage, $sBlockId, $aExtraParams);
  295. $oPage->add('</div>');
  296. }
  297. public function GetPropertiesFields(DesignerForm $oForm)
  298. {
  299. $oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
  300. $oForm->AddField($oField);
  301. $oField = new DesignerLongTextField('query', 'Query', $this->aProperties['query']);
  302. $oForm->AddField($oField);
  303. $oField = new DesignerBooleanField('menu', 'Menu', $this->aProperties['menu']);
  304. $oForm->AddField($oField);
  305. }
  306. static public function GetInfo()
  307. {
  308. return array(
  309. 'label' => 'Object list',
  310. 'icon' => 'images/dashlet-list.png',
  311. 'description' => 'Object list dashlet',
  312. );
  313. }
  314. static public function CanCreateFromOQL()
  315. {
  316. return true;
  317. }
  318. public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL)
  319. {
  320. $oField = new DesignerTextField('title', 'Title', '');
  321. $oForm->AddField($oField);
  322. $oField = new DesignerHiddenField('query', 'Query', $sOQL);
  323. $oForm->AddField($oField);
  324. $oField = new DesignerBooleanField('menu', 'Menu', $this->aProperties['menu']);
  325. $oForm->AddField($oField);
  326. }
  327. }
  328. abstract class DashletGroupBy extends Dashlet
  329. {
  330. public function __construct($sId)
  331. {
  332. parent::__construct($sId);
  333. $this->aProperties['title'] = 'Contacts grouped by location';
  334. $this->aProperties['query'] = 'SELECT Contact';
  335. $this->aProperties['group_by'] = 'location_name';
  336. $this->aProperties['style'] = 'table';
  337. }
  338. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  339. {
  340. $sTitle = $this->aProperties['title'];
  341. $sQuery = $this->aProperties['query'];
  342. $sGroupBy = $this->aProperties['group_by'];
  343. $sStyle = $this->aProperties['style'];
  344. if ($sQuery == '')
  345. {
  346. $oPage->add('<p>Please enter a valid OQL query</p>');
  347. }
  348. elseif ($sGroupBy == '')
  349. {
  350. $oPage->add('<p>Please select the field on which the objects will be grouped together</p>');
  351. }
  352. else
  353. {
  354. $oFilter = DBObjectSearch::FromOQL($sQuery);
  355. $sClassAlias = $oFilter->GetClassAlias();
  356. if (preg_match('/^(.*):(.*)$/', $sGroupBy, $aMatches))
  357. {
  358. $sAttCode = $aMatches[1];
  359. $sFunction = $aMatches[2];
  360. switch($sFunction)
  361. {
  362. case 'hour':
  363. $sGroupByLabel = 'Hour of '.$sAttCode. ' (0-23)';
  364. $sGroupByExpr = "DATE_FORMAT($sClassAlias.$sAttCode, '%H')"; // 0 -> 31
  365. break;
  366. case 'month':
  367. $sGroupByLabel = 'Month of '.$sAttCode. ' (1 - 12)';
  368. $sGroupByExpr = "DATE_FORMAT($sClassAlias.$sAttCode, '%m')"; // 0 -> 31
  369. break;
  370. case 'day_of_week':
  371. $sGroupByLabel = 'Day of week for '.$sAttCode. ' (sunday to saturday)';
  372. $sGroupByExpr = "DATE_FORMAT($sClassAlias.$sAttCode, '%w')";
  373. break;
  374. case 'day_of_month':
  375. $sGroupByLabel = 'Day of month for'.$sAttCode;
  376. $sGroupByExpr = "DATE_FORMAT($sClassAlias.$sAttCode, '%e')"; // 0 -> 31
  377. break;
  378. default:
  379. $sGroupByLabel = 'Unknown group by function '.$sFunction;
  380. $sGroupByExpr = $sClassAlias.'.'.$sAttCode;
  381. }
  382. }
  383. else
  384. {
  385. $sAttCode = $sGroupBy;
  386. $sGroupByExpr = $sClassAlias.'.'.$sAttCode;
  387. $sGroupByLabel = MetaModel::GetLabel($oFilter->GetClass(), $sAttCode);
  388. }
  389. switch($sStyle)
  390. {
  391. case 'bars':
  392. $sType = 'open_flash_chart';
  393. $aExtraParams = array(
  394. 'chart_type' => 'bars',
  395. 'chart_title' => $sTitle,
  396. 'group_by' => $sGroupByExpr,
  397. 'group_by_label' => $sGroupByLabel,
  398. );
  399. $sHtmlTitle = ''; // done in the itop block
  400. break;
  401. case 'pie':
  402. $sType = 'open_flash_chart';
  403. $aExtraParams = array(
  404. 'chart_type' => 'pie',
  405. 'chart_title' => $sTitle,
  406. 'group_by' => $sGroupByExpr,
  407. 'group_by_label' => $sGroupByLabel,
  408. );
  409. $sHtmlTitle = ''; // done in the itop block
  410. break;
  411. case 'table':
  412. default:
  413. $sHtmlTitle = htmlentities(Dict::S($sTitle), ENT_QUOTES, 'UTF-8'); // done in the itop block
  414. $sType = 'count';
  415. $aExtraParams = array(
  416. 'group_by' => $sGroupByExpr,
  417. 'group_by_label' => $sGroupByLabel,
  418. );
  419. break;
  420. }
  421. $oPage->add('<div style="text-align:center" class="dashlet-content">');
  422. if ($sHtmlTitle != '')
  423. {
  424. $oPage->add('<h1>'.$sHtmlTitle.'</h1>');
  425. }
  426. $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
  427. $oBlock = new DisplayBlock($oFilter, $sType);
  428. $oBlock->Display($oPage, $sBlockId, $aExtraParams);
  429. $oPage->add('</div>');
  430. }
  431. }
  432. public function GetPropertiesFields(DesignerForm $oForm)
  433. {
  434. $oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
  435. $oForm->AddField($oField);
  436. $oField = new DesignerLongTextField('query', 'Query', $this->aProperties['query']);
  437. $oForm->AddField($oField);
  438. try
  439. {
  440. // Group by field: build the list of possible values (attribute codes + ...)
  441. $oSearch = DBObjectSearch::FromOQL($this->aProperties['query']);
  442. $sClass = $oSearch->GetClass();
  443. $aGroupBy = array();
  444. foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
  445. {
  446. if (!$oAttDef->IsScalar()) continue; // skip link sets
  447. $sLabel = $oAttDef->GetLabel();
  448. if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
  449. {
  450. $sLabel = $oAttDef->GetLabel().' (strict)';
  451. }
  452. $aGroupBy[$sAttCode] = $sLabel;
  453. if ($oAttDef instanceof AttributeDateTime)
  454. {
  455. $aGroupBy[$sAttCode.':hour'] = $oAttDef->GetLabel().' (hour)';
  456. $aGroupBy[$sAttCode.':month'] = $oAttDef->GetLabel().' (month)';
  457. $aGroupBy[$sAttCode.':day_of_week'] = $oAttDef->GetLabel().' (day of week)';
  458. $aGroupBy[$sAttCode.':day_of_month'] = $oAttDef->GetLabel().' (day of month)';
  459. }
  460. }
  461. $oField = new DesignerComboField('group_by', 'Group by', $this->aProperties['group_by']);
  462. $oField->SetAllowedValues($aGroupBy);
  463. }
  464. catch(Exception $e)
  465. {
  466. $oField = new DesignerTextField('group_by', 'Group by', $this->aProperties['group_by']);
  467. }
  468. $oForm->AddField($oField);
  469. $aStyles = array(
  470. 'pie' => 'Pie chart',
  471. 'bars' => 'Bar chart',
  472. 'table' => 'Table',
  473. );
  474. $oField = new DesignerComboField('style', 'Style', $this->aProperties['style']);
  475. $oField->SetAllowedValues($aStyles);
  476. $oForm->AddField($oField);
  477. }
  478. public function Update($aValues, $aUpdatedFields)
  479. {
  480. if (in_array('query', $aUpdatedFields))
  481. {
  482. try
  483. {
  484. $sCurrQuery = $aValues['query'];
  485. $oCurrSearch = DBObjectSearch::FromOQL($sCurrQuery);
  486. $sCurrClass = $oCurrSearch->GetClass();
  487. $sPrevQuery = $this->aProperties['query'];
  488. $oPrevSearch = DBObjectSearch::FromOQL($sPrevQuery);
  489. $sPrevClass = $oPrevSearch->GetClass();
  490. if ($sCurrClass != $sPrevClass)
  491. {
  492. $this->bFormRedrawNeeded = true;
  493. // wrong but not necessary - unset($aUpdatedFields['group_by']);
  494. $this->aProperties['group_by'] = '';
  495. }
  496. }
  497. catch(Exception $e)
  498. {
  499. $this->bFormRedrawNeeded = true;
  500. }
  501. }
  502. $oDashlet = parent::Update($aValues, $aUpdatedFields);
  503. if (in_array('style', $aUpdatedFields))
  504. {
  505. switch($aValues['style'])
  506. {
  507. // Style changed, mutate to the specified type of chart
  508. case 'pie':
  509. $oDashlet = new DashletGroupByPie($this->sId);
  510. break;
  511. case 'bars':
  512. $oDashlet = new DashletGroupByBars($this->sId);
  513. break;
  514. case 'table':
  515. $oDashlet = new DashletGroupByTable($this->sId);
  516. break;
  517. }
  518. $oDashlet->FromParams($aValues);
  519. $oDashlet->bRedrawNeeded = true;
  520. $oDashlet->bFormRedrawNeeded = true;
  521. }
  522. return $oDashlet;
  523. }
  524. static public function GetInfo()
  525. {
  526. return array(
  527. 'label' => 'Objects grouped by...',
  528. 'icon' => 'images/dashlet-object-grouped.png',
  529. 'description' => 'Grouped objects dashlet',
  530. );
  531. }
  532. static public function CanCreateFromOQL()
  533. {
  534. return true;
  535. }
  536. public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL)
  537. {
  538. $oField = new DesignerTextField('title', 'Title', '');
  539. $oForm->AddField($oField);
  540. $oField = new DesignerHiddenField('query', 'Query', $sOQL);
  541. $oForm->AddField($oField);
  542. try
  543. {
  544. // Group by field: build the list of possible values (attribute codes + ...)
  545. $oSearch = DBObjectSearch::FromOQL($this->aProperties['query']);
  546. $sClass = $oSearch->GetClass();
  547. $aGroupBy = array();
  548. foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
  549. {
  550. if (!$oAttDef->IsScalar()) continue; // skip link sets
  551. if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE)) continue; // skip external keys
  552. $aGroupBy[$sAttCode] = $oAttDef->GetLabel();
  553. if ($oAttDef instanceof AttributeDateTime)
  554. {
  555. //date_format(start_date, '%d')
  556. $aGroupBy['date_of_'.$sAttCode] = 'Day of '.$oAttDef->GetLabel();
  557. }
  558. }
  559. $oField = new DesignerComboField('group_by', 'Group by', $this->aProperties['group_by']);
  560. $oField->SetAllowedValues($aGroupBy);
  561. }
  562. catch(Exception $e)
  563. {
  564. $oField = new DesignerTextField('group_by', 'Group by', $this->aProperties['group_by']);
  565. }
  566. $oForm->AddField($oField);
  567. $oField = new DesignerHiddenField('style', '', $this->aProperties['style']);
  568. $oForm->AddField($oField);
  569. }
  570. }
  571. class DashletGroupByPie extends DashletGroupBy
  572. {
  573. public function __construct($sId)
  574. {
  575. parent::__construct($sId);
  576. $this->aProperties['style'] = 'pie';
  577. }
  578. static public function GetInfo()
  579. {
  580. return array(
  581. 'label' => 'Pie Chart',
  582. 'icon' => 'images/dashlet-pie-chart.png',
  583. 'description' => 'Pie Chart',
  584. );
  585. }
  586. }
  587. class DashletGroupByPie2 extends DashletGroupByPie
  588. {
  589. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  590. {
  591. $sTitle = addslashes($this->aProperties['title']);
  592. $sQuery = $this->aProperties['query'];
  593. $sGroupBy = $this->aProperties['group_by'];
  594. $oSearch = DBObjectSearch::FromOQL($sQuery);
  595. $sClassAlias = $oSearch->GetClassAlias();
  596. $aQueryParams = array();
  597. $aGroupBy = array();
  598. $oGroupByExp = Expression::FromOQL($sClassAlias.'.'.$sGroupBy);
  599. $aGroupBy['grouped_by_1'] = $oGroupByExp;
  600. $sSql = MetaModel::MakeGroupByQuery($oSearch, $aQueryParams, $aGroupBy);
  601. $aRes = CMDBSource::QueryToArray($sSql);
  602. $aGroupBy = array();
  603. $aLabels = array();
  604. $iTotalCount = 0;
  605. foreach ($aRes as $aRow)
  606. {
  607. $sValue = $aRow['grouped_by_1'];
  608. $aLabels[] = ($sValue == '') ? 'Empty (%%.%%)' : $sValue.' (%%.%%)'; //TODO: localize
  609. $aGroupBy[] = (int) $aRow['_itop_count_'];
  610. $iTotalCount += $aRow['_itop_count_'];
  611. }
  612. $aURLs = array();
  613. $sContext = ''; //TODO get the context ??
  614. foreach($aGroupBy as $sValue => $iValue)
  615. {
  616. // Build the search for this subset
  617. $oSubsetSearch = clone $oSearch;
  618. $oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
  619. $oSubsetSearch->AddConditionExpression($oCondition);
  620. $aURLs[] = 'http://www.combodo.com/itop'; //utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html{$sContext}&filter=".addslashes($oSubsetSearch->serialize());
  621. }
  622. $sJSValues = json_encode($aGroupBy);
  623. $sJSHrefs = json_encode($aURLs);
  624. $sJSLabels = json_encode($aLabels);
  625. $sId = 'chart_'.($bEditMode? 'edit_' : '').$this->sId;
  626. $oPage->add('<div id="chart_'.$sId.'" class="dashlet-content"></div>');
  627. $oPage->add_ready_script("$('#chart_{$sId}').pie_chart({chart_label: '$sTitle', values: $sJSValues, labels: $sJSLabels, hrefs: $sJSHrefs });");
  628. }
  629. static public function GetInfo()
  630. {
  631. return array(
  632. 'label' => 'Pie (Raphael)',
  633. 'icon' => 'images/dashlet-pie-chart.png',
  634. 'description' => 'Pure JS Pie Chart',
  635. );
  636. }
  637. }
  638. class DashletGroupByBars extends DashletGroupBy
  639. {
  640. public function __construct($sId)
  641. {
  642. parent::__construct($sId);
  643. $this->aProperties['style'] = 'bars';
  644. }
  645. static public function GetInfo()
  646. {
  647. return array(
  648. 'label' => 'Bar Chart',
  649. 'icon' => 'images/dashlet-bar-chart.png',
  650. 'description' => 'Bar Chart',
  651. );
  652. }
  653. }
  654. class DashletGroupByTable extends DashletGroupBy
  655. {
  656. public function __construct($sId)
  657. {
  658. parent::__construct($sId);
  659. $this->aProperties['style'] = 'table';
  660. }
  661. static public function GetInfo()
  662. {
  663. return array(
  664. 'label' => 'Group By (table)',
  665. 'icon' => 'images/dashlet-groupby-table.png',
  666. 'description' => 'List (Grouped by a field)',
  667. );
  668. }
  669. }
  670. class DashletHeaderStatic extends Dashlet
  671. {
  672. public function __construct($sId)
  673. {
  674. parent::__construct($sId);
  675. $this->aProperties['title'] = 'Contacts';
  676. $this->aProperties['icon'] = 'itop-config-mgmt-1.0.0/images/contact.png';
  677. }
  678. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  679. {
  680. $sTitle = $this->aProperties['title'];
  681. $sIcon = $this->aProperties['icon'];
  682. $sIconPath = utils::GetAbsoluteUrlModulesRoot().$sIcon;
  683. $oPage->add('<div class="dashlet-content">');
  684. $oPage->add('<div class="main_header">');
  685. $oPage->add('<img src="'.$sIconPath.'">');
  686. $oPage->add('<h1>'.Dict::S($sTitle).'</h1>');
  687. $oPage->add('</div>');
  688. $oPage->add('</div>');
  689. }
  690. public function GetPropertiesFields(DesignerForm $oForm)
  691. {
  692. $oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
  693. $oForm->AddField($oField);
  694. $oField = new DesignerIconSelectionField('icon', 'Icon', $this->aProperties['icon']);
  695. $aAllIcons = self::FindIcons(APPROOT.'env-'.utils::GetCurrentEnvironment());
  696. ksort($aAllIcons);
  697. $aValues = array();
  698. foreach($aAllIcons as $sFilePath)
  699. {
  700. $aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
  701. }
  702. $oField->SetAllowedValues($aValues);
  703. $oForm->AddField($oField);
  704. }
  705. static public function GetInfo()
  706. {
  707. return array(
  708. 'label' => 'Header',
  709. 'icon' => 'images/dashlet-header.png',
  710. 'description' => 'Header with stats (grouped by...)',
  711. );
  712. }
  713. static public function FindIcons($sBaseDir, $sDir = '')
  714. {
  715. $aResult = array();
  716. // Populate automatically the list of icon files
  717. if ($hDir = @opendir($sBaseDir.'/'.$sDir))
  718. {
  719. while (($sFile = readdir($hDir)) !== false)
  720. {
  721. $aMatches = array();
  722. if (($sFile != '.') && ($sFile != '..') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile))
  723. {
  724. $sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
  725. $aResult = array_merge($aResult, self::FindIcons($sBaseDir, $sDirSubPath));
  726. }
  727. if (preg_match("/\.(png|jpg|jpeg|gif)$/i", $sFile, $aMatches)) // png, jp(e)g and gif are considered valid
  728. {
  729. $aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile;
  730. }
  731. }
  732. closedir($hDir);
  733. }
  734. return $aResult;
  735. }
  736. }
  737. class DashletHeaderDynamic extends Dashlet
  738. {
  739. public function __construct($sId)
  740. {
  741. parent::__construct($sId);
  742. $this->aProperties['title'] = 'Contacts';
  743. $this->aProperties['icon'] = 'itop-config-mgmt-1.0.0/images/contact.png';
  744. $this->aProperties['subtitle'] = 'Contacts';
  745. $this->aProperties['query'] = 'SELECT Contact';
  746. $this->aProperties['group_by'] = 'status';
  747. $this->aProperties['values'] = 'active,inactive,terminated';
  748. }
  749. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  750. {
  751. $sTitle = $this->aProperties['title'];
  752. $sIcon = $this->aProperties['icon'];
  753. $sSubtitle = $this->aProperties['subtitle'];
  754. $sQuery = $this->aProperties['query'];
  755. $sGroupBy = $this->aProperties['group_by'];
  756. $sValues = $this->aProperties['values'];
  757. $oFilter = DBObjectSearch::FromOQL($sQuery);
  758. $sClass = $oFilter->GetClass();
  759. $sIconPath = utils::GetAbsoluteUrlModulesRoot().$sIcon;
  760. $aValues = null;
  761. if (MetaModel::IsValidAttCode($sClass, $sGroupBy))
  762. {
  763. if ($sValues == '')
  764. {
  765. $aAllowed = MetaModel::GetAllowedValues_att($sClass, $sGroupBy);
  766. if (is_array($aAllowed))
  767. {
  768. $aValues = array_keys($aAllowed);
  769. }
  770. }
  771. else
  772. {
  773. $aValues = explode(',', $sValues);
  774. }
  775. }
  776. if (is_array($aValues))
  777. {
  778. // Stats grouped by <group_by>
  779. $aCSV = implode(',', $aValues);
  780. $aExtraParams = array(
  781. 'title[block]' => $sTitle,
  782. 'label[block]' => $sSubtitle,
  783. 'status[block]' => $sGroupBy,
  784. 'status_codes[block]' => $aCSV,
  785. 'context_filter' => 1,
  786. );
  787. }
  788. else
  789. {
  790. // Simple stats
  791. $aExtraParams = array(
  792. 'title[block]' => $sTitle,
  793. 'label[block]' => $sSubtitle,
  794. 'context_filter' => 1,
  795. );
  796. }
  797. $oPage->add('<div class="dashlet-content">');
  798. $oPage->add('<div class="main_header">');
  799. $oPage->add('<img src="'.$sIconPath.'">');
  800. $oBlock = new DisplayBlock($oFilter, 'summary');
  801. $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
  802. $oBlock->Display($oPage, $sBlockId, $aExtraParams);
  803. $oPage->add('</div>');
  804. $oPage->add('</div>');
  805. }
  806. public function GetPropertiesFields(DesignerForm $oForm)
  807. {
  808. $oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
  809. $oForm->AddField($oField);
  810. $oField = new DesignerIconSelectionField('icon', 'Icon', $this->aProperties['icon']);
  811. $aAllIcons = DashletHeaderStatic::FindIcons(APPROOT.'env-'.utils::GetCurrentEnvironment());
  812. ksort($aAllIcons);
  813. $aValues = array();
  814. foreach($aAllIcons as $sFilePath)
  815. {
  816. $aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
  817. }
  818. $oField->SetAllowedValues($aValues);
  819. $oForm->AddField($oField);
  820. $oField = new DesignerTextField('subtitle', 'Subtitle', $this->aProperties['subtitle']);
  821. $oForm->AddField($oField);
  822. $oField = new DesignerTextField('query', 'Query', $this->aProperties['query']);
  823. $oForm->AddField($oField);
  824. try
  825. {
  826. // Group by field: build the list of possible values (attribute codes + ...)
  827. $oSearch = DBObjectSearch::FromOQL($this->aProperties['query']);
  828. $sClass = $oSearch->GetClass();
  829. $aGroupBy = array();
  830. foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
  831. {
  832. if (!$oAttDef->IsScalar()) continue; // skip link sets
  833. $sLabel = $oAttDef->GetLabel();
  834. if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
  835. {
  836. $sLabel = $oAttDef->GetLabel().' (strict)';
  837. }
  838. $aGroupBy[$sAttCode] = $sLabel;
  839. }
  840. $oField = new DesignerComboField('group_by', 'Group by', $this->aProperties['group_by']);
  841. $oField->SetAllowedValues($aGroupBy);
  842. }
  843. catch(Exception $e)
  844. {
  845. $oField = new DesignerTextField('group_by', 'Group by', $this->aProperties['group_by']);
  846. }
  847. $oForm->AddField($oField);
  848. $oField = new DesignerTextField('values', 'Values (CSV list)', $this->aProperties['values']);
  849. $oForm->AddField($oField);
  850. }
  851. public function Update($aValues, $aUpdatedFields)
  852. {
  853. if (in_array('query', $aUpdatedFields))
  854. {
  855. try
  856. {
  857. $sCurrQuery = $aValues['query'];
  858. $oCurrSearch = DBObjectSearch::FromOQL($sCurrQuery);
  859. $sCurrClass = $oCurrSearch->GetClass();
  860. $sPrevQuery = $this->aProperties['query'];
  861. $oPrevSearch = DBObjectSearch::FromOQL($sPrevQuery);
  862. $sPrevClass = $oPrevSearch->GetClass();
  863. if ($sCurrClass != $sPrevClass)
  864. {
  865. $this->bFormRedrawNeeded = true;
  866. // wrong but not necessary - unset($aUpdatedFields['group_by']);
  867. $this->aProperties['group_by'] = '';
  868. }
  869. }
  870. catch(Exception $e)
  871. {
  872. $this->bFormRedrawNeeded = true;
  873. }
  874. }
  875. return parent::Update($aValues, $aUpdatedFields);
  876. }
  877. static public function GetInfo()
  878. {
  879. return array(
  880. 'label' => 'Header with statistics',
  881. 'icon' => 'images/dashlet-header-stats.png',
  882. 'description' => 'Header with stats (grouped by...)',
  883. );
  884. }
  885. }
  886. class DashletBadge extends Dashlet
  887. {
  888. public function __construct($sId)
  889. {
  890. parent::__construct($sId);
  891. $this->aProperties['class'] = 'Contact';
  892. $this->aCSSClasses[] = 'dashlet-inline';
  893. $this->aCSSClasses[] = 'dashlet-badge';
  894. }
  895. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  896. {
  897. $sClass = $this->aProperties['class'];
  898. $oPage->add('<div class="dashlet-content">');
  899. $oFilter = new DBObjectSearch($sClass);
  900. $oBlock = new DisplayBlock($oFilter, 'actions');
  901. $aExtraParams = array(
  902. 'context_filter' => 1,
  903. );
  904. $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
  905. $oBlock->Display($oPage, $sBlockId, $aExtraParams);
  906. $oPage->add('</div>');
  907. if ($bEditMode)
  908. {
  909. // Since the container div is not rendered the same way in edit mode, add the 'inline' style to it
  910. $oPage->add_ready_script("$('#dashlet_".$this->sId."').addClass('dashlet-inline');");
  911. }
  912. }
  913. public function GetPropertiesFields(DesignerForm $oForm)
  914. {
  915. $oClassesSet = new ValueSetEnumClasses('bizmodel', array());
  916. $aClasses = $oClassesSet->GetValues(array());
  917. $aLinkClasses = array();
  918. foreach(MetaModel::GetClasses('bizmodel') as $sClass)
  919. {
  920. foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
  921. {
  922. if ($oAttDef instanceof AttributeLinkedSetIndirect)
  923. {
  924. $aLinkClasses[$oAttDef->GetLinkedClass()] = true;
  925. }
  926. }
  927. }
  928. $oField = new DesignerIconSelectionField('class', 'Class', $this->aProperties['class']);
  929. ksort($aClasses);
  930. $aValues = array();
  931. foreach($aClasses as $sClass => $sClass)
  932. {
  933. if (!array_key_exists($sClass, $aLinkClasses))
  934. {
  935. $sIconUrl = MetaModel::GetClassIcon($sClass, false);
  936. $sIconFilePath = str_replace(utils::GetAbsoluteUrlAppRoot(), APPROOT, $sIconUrl);
  937. if (($sIconUrl == '') || !file_exists($sIconFilePath))
  938. {
  939. // The icon does not exist, leet's use a transparent one of the same size.
  940. $sIconUrl = utils::GetAbsoluteUrlAppRoot().'images/transparent_32_32.png';
  941. }
  942. $aValues[] = array('value' => $sClass, 'label' => $sClass, 'icon' => $sIconUrl);
  943. }
  944. }
  945. $oField->SetAllowedValues($aValues);
  946. $oForm->AddField($oField);
  947. }
  948. static public function GetInfo()
  949. {
  950. return array(
  951. 'label' => 'Badge',
  952. 'icon' => 'images/dashlet-badge.png',
  953. 'description' => 'Object Icon with new/search',
  954. );
  955. }
  956. }
  957. class DashletProto extends Dashlet
  958. {
  959. public function __construct($sId)
  960. {
  961. parent::__construct($sId);
  962. $this->aProperties['class'] = 'Foo';
  963. }
  964. protected $aValues1;
  965. protected $aValues2;
  966. protected $aStats;
  967. protected $sGroupByLabel1;
  968. protected $sGroupByLabel2;
  969. protected $iTotalCount;
  970. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  971. {
  972. $this->Compute();
  973. //$sHtmlTitle = $this->aProperties['title'];
  974. $sHtmlTitle = "Group by made on two dimensions";
  975. // Build the output
  976. $aDisplayData = array();
  977. foreach ($this->aValues1 as $sValue1 => $sDisplayValue1)
  978. {
  979. foreach ($this->aValues2 as $sValue2 => $sDisplayValue2)
  980. {
  981. @$aResData = $this->aStats[$sValue1][$sValue2];
  982. if (is_array($aResData))
  983. {
  984. $aDisplayData[] = array (
  985. 'group1' => $sDisplayValue1,
  986. 'group2' => $sDisplayValue2,
  987. 'value' => $aResData['digurl']
  988. );
  989. }
  990. else
  991. {
  992. // Missing result => 0
  993. $aDisplayData[] = array (
  994. 'group1' => $sDisplayValue1,
  995. 'group2' => $sDisplayValue2,
  996. 'value' => "-"
  997. );
  998. }
  999. }
  1000. }
  1001. $aAttribs =array(
  1002. 'group1' => array('label' => $this->sGroupByLabel1, 'description' => ''),
  1003. 'group2' => array('label' => $this->sGroupByLabel2, 'description' => ''),
  1004. 'value' => array('label'=> Dict::S('UI:GroupBy:Count'), 'description' => Dict::S('UI:GroupBy:Count+'))
  1005. );
  1006. $oPage->add('<div style="text-align:center" class="dashlet-content">');
  1007. $oPage->add('<h1>'.$sHtmlTitle.'</h1>');
  1008. $oPage->p(Dict::Format('UI:Pagination:HeaderNoSelection', $this->iTotalCount));
  1009. $oPage->table($aAttribs, $aDisplayData);
  1010. $oPage->add('</div>');
  1011. }
  1012. protected function Compute()
  1013. {
  1014. // Read and interpret parameters
  1015. //
  1016. //$sFoo = $this->aProperties['foo'];
  1017. if (true)
  1018. {
  1019. $oFilter = DBObjectSearch::FromOQL('SELECT FunctionalCI');
  1020. $sGroupBy1 = 'FunctionalCI.status';
  1021. $this->sGroupByLabel1 = MetaModel::GetLabel('FunctionalCI', 'status');
  1022. $aFill1 = array(
  1023. 'implementation',
  1024. 'production',
  1025. 'obsolete',
  1026. );
  1027. //$aFill1 = null;
  1028. //$sGroupBy2 = 'org_id_friendlyname';
  1029. $sGroupBy2 = 'FunctionalCI.org_id';
  1030. $this->sGroupByLabel2 = MetaModel::GetLabel('FunctionalCI', 'org_id');
  1031. $aFill2 = array(
  1032. '2',
  1033. '1',
  1034. );
  1035. //$aFill2 = null;
  1036. }
  1037. else
  1038. {
  1039. $oFilter = DBObjectSearch::FromOQL('SELECT Incident AS i');
  1040. $sGroupBy1 = "DATE_FORMAT(i.start_date, '%H')";
  1041. $this->sGroupByLabel1 = 'Hour of '.MetaModel::GetLabel('Incident', 'start_date');
  1042. $aFill1 = array(9, 10, 11, 12, 13, 14, 15, 16, 17, 18);
  1043. //$aFill1 = null;
  1044. $sGroupBy2 = "DATE_FORMAT(i.start_date, '%w')";
  1045. $this->sGroupByLabel2 = 'Week day of '.MetaModel::GetLabel('Incident', 'start_date');
  1046. $aFill2 = null;
  1047. }
  1048. // Do compute the statistics
  1049. //
  1050. $this->aValues1 = array();
  1051. $this->aValues2 = array();
  1052. $this->aStats = array();
  1053. $this->iTotalCount = 0;
  1054. $sAlias = $oFilter->GetClassAlias();
  1055. //$oGroupByExp1 = new FieldExpression($sGroupBy1, $sAlias);
  1056. $oGroupByExp1 = Expression::FromOQL($sGroupBy1);
  1057. //$oGroupByExp2 = new FieldExpression($sGroupBy2, $sAlias);
  1058. $oGroupByExp2 = Expression::FromOQL($sGroupBy2);
  1059. $aGroupBy = array();
  1060. $aGroupBy['grouped_by_1'] = $oGroupByExp1;
  1061. $aGroupBy['grouped_by_2'] = $oGroupByExp2;
  1062. $sSql = MetaModel::MakeGroupByQuery($oFilter, array(), $aGroupBy);
  1063. $aRes = CMDBSource::QueryToArray($sSql);
  1064. // Prepare a blank and ordered grid
  1065. if (is_array($aFill1))
  1066. {
  1067. foreach ($aFill1 as $sValue1)
  1068. {
  1069. $sDisplayValue1 = $aGroupBy['grouped_by_1']->MakeValueLabel($oFilter, $sValue1, $sValue1); // default to the raw value
  1070. $this->aValues1[$sValue1] = $sDisplayValue1;
  1071. }
  1072. }
  1073. if (is_array($aFill2))
  1074. {
  1075. foreach ($aFill2 as $sValue2)
  1076. {
  1077. $sDisplayValue2 = $aGroupBy['grouped_by_2']->MakeValueLabel($oFilter, $sValue2, $sValue2); // default to the raw value
  1078. $aValues2[$sValue2] = $sDisplayValue2;
  1079. }
  1080. }
  1081. $oAppContext = new ApplicationContext();
  1082. $sParams = $oAppContext->GetForLink();
  1083. foreach ($aRes as $aRow)
  1084. {
  1085. $iCount = $aRow['_itop_count_'];
  1086. $this->iTotalCount += $iCount;
  1087. $sValue1 = $aRow['grouped_by_1'];
  1088. $sValue2 = $aRow['grouped_by_2'];
  1089. $bValidResult = true;
  1090. if (is_array($aFill1) && !in_array($sValue1, $aFill1))
  1091. {
  1092. $bValidResult = false;
  1093. }
  1094. if (is_array($aFill2) && !in_array($sValue2, $aFill2))
  1095. {
  1096. $bValidResult = false;
  1097. }
  1098. if ($bValidResult)
  1099. {
  1100. // Build the search for this subset
  1101. $oSubsetSearch = clone $oFilter;
  1102. $oCondition = new BinaryExpression($oGroupByExp1, '=', new ScalarExpression($sValue1));
  1103. $oSubsetSearch->AddConditionExpression($oCondition);
  1104. $oCondition = new BinaryExpression($oGroupByExp2, '=', new ScalarExpression($sValue2));
  1105. $oSubsetSearch->AddConditionExpression($oCondition);
  1106. $sFilter = urlencode($oSubsetSearch->serialize());
  1107. $this->aStats[$sValue1][$sValue2] = array (
  1108. 'digurl' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"
  1109. );
  1110. if (!array_key_exists($sValue1, $this->aValues1))
  1111. {
  1112. $sDisplayValue1 = $aGroupBy['grouped_by_1']->MakeValueLabel($oFilter, $sValue1, $sValue1); // default to the raw value
  1113. $this->aValues1[$sValue1] = $sDisplayValue1;
  1114. }
  1115. if (!array_key_exists($sValue2, $this->aValues2))
  1116. {
  1117. $sDisplayValue2 = $aGroupBy['grouped_by_2']->MakeValueLabel($oFilter, $sValue2, $sValue2); // default to the raw value
  1118. $this->aValues2[$sValue2] = $sDisplayValue2;
  1119. }
  1120. }
  1121. }
  1122. }
  1123. public function GetPropertiesFields(DesignerForm $oForm)
  1124. {
  1125. $oField = new DesignerTextField('class', 'Class', $this->aProperties['class']);
  1126. $oForm->AddField($oField);
  1127. }
  1128. static public function GetInfo()
  1129. {
  1130. return array(
  1131. 'label' => 'Test3D',
  1132. 'icon' => 'images/dashlet-groupby2-table.png',
  1133. 'description' => 'Group by on two dimensions',
  1134. );
  1135. }
  1136. }
  1137. class DashletHeatMap extends Dashlet
  1138. {
  1139. public function __construct($sId)
  1140. {
  1141. parent::__construct($sId);
  1142. $this->aProperties['class'] = 'Contact';
  1143. $this->aProperties['title'] = 'Test';
  1144. }
  1145. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  1146. {
  1147. $sTitle = addslashes($this->aProperties['title']);
  1148. $sId = 'chart_'.($bEditMode? 'edit_' : '').$this->sId;
  1149. $oPage->add('<div id="chart_'.$sId.'" class="dashlet-content"></div>');
  1150. $oPage->add_ready_script("$('#chart_{$sId}').heatmap_chart({chart_label: '$sTitle'});");
  1151. }
  1152. public function GetPropertiesFields(DesignerForm $oForm)
  1153. {
  1154. $oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
  1155. $oForm->AddField($oField);
  1156. $oField = new DesignerTextField('class', 'Class', $this->aProperties['class']);
  1157. $oForm->AddField($oField);
  1158. }
  1159. static public function GetInfo()
  1160. {
  1161. return array(
  1162. 'label' => 'Heatmap (Raphael)',
  1163. 'icon' => 'images/dashlet-heatmap.png',
  1164. 'description' => 'Pure JS Heat Map Chart',
  1165. );
  1166. }
  1167. }