dashlet.class.inc.php 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269
  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, $aExtraParams = array())
  103. {
  104. $sCSSClasses = implode(' ', $this->aCSSClasses);
  105. if ($bEditMode)
  106. {
  107. $sId = $this->GetID();
  108. $oPage->add('<div class="'.$sCSSClasses.'" id="dashlet_'.$sId.'">');
  109. }
  110. else
  111. {
  112. $oPage->add('<div class="'.$sCSSClasses.'">');
  113. }
  114. $this->Render($oPage, $bEditMode, $aExtraParams);
  115. $oPage->add('</div>');
  116. if ($bEditMode)
  117. {
  118. $sClass = get_class($this);
  119. $oPage->add_ready_script(
  120. <<<EOF
  121. $('#dashlet_$sId').dashlet({dashlet_id: '$sId', dashlet_class: '$sClass'});
  122. EOF
  123. );
  124. }
  125. }
  126. public function SetID($sId)
  127. {
  128. $this->sId = $sId;
  129. }
  130. public function GetID()
  131. {
  132. return $this->sId;
  133. }
  134. abstract public function Render($oPage, $bEditMode = false, $aExtraParams = array());
  135. abstract public function GetPropertiesFields(DesignerForm $oForm);
  136. public function ToXml(DOMNode $oContainerNode)
  137. {
  138. }
  139. public function Update($aValues, $aUpdatedFields)
  140. {
  141. foreach($aUpdatedFields as $sProp)
  142. {
  143. if (array_key_exists($sProp, $this->aProperties))
  144. {
  145. $this->aProperties[$sProp] = $aValues[$sProp];
  146. }
  147. }
  148. return $this;
  149. }
  150. public function IsRedrawNeeded()
  151. {
  152. return $this->bRedrawNeeded;
  153. }
  154. public function IsFormRedrawNeeded()
  155. {
  156. return $this->bFormRedrawNeeded;
  157. }
  158. static public function GetInfo()
  159. {
  160. return array(
  161. 'label' => '',
  162. 'icon' => '',
  163. 'description' => '',
  164. );
  165. }
  166. public function GetForm()
  167. {
  168. $oForm = new DesignerForm();
  169. $oForm->SetPrefix("dashlet_". $this->GetID());
  170. $oForm->SetParamsContainer('params');
  171. $this->GetPropertiesFields($oForm);
  172. $oDashletClassField = new DesignerHiddenField('dashlet_class', '', get_class($this));
  173. $oForm->AddField($oDashletClassField);
  174. $oDashletIdField = new DesignerHiddenField('dashlet_id', '', $this->GetID());
  175. $oForm->AddField($oDashletIdField);
  176. return $oForm;
  177. }
  178. static public function IsVisible()
  179. {
  180. return true;
  181. }
  182. static public function CanCreateFromOQL()
  183. {
  184. return false;
  185. }
  186. public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL)
  187. {
  188. // Default: do nothing since it's not supported
  189. }
  190. }
  191. class DashletEmptyCell extends Dashlet
  192. {
  193. public function __construct($sId)
  194. {
  195. parent::__construct($sId);
  196. }
  197. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  198. {
  199. $oPage->add('&nbsp;');
  200. }
  201. public function GetPropertiesFields(DesignerForm $oForm)
  202. {
  203. }
  204. static public function GetInfo()
  205. {
  206. return array(
  207. 'label' => 'Empty Cell',
  208. 'icon' => 'images/dashlet-text.png',
  209. 'description' => 'Empty Cell Dashlet Placeholder',
  210. );
  211. }
  212. static public function IsVisible()
  213. {
  214. return false;
  215. }
  216. }
  217. class DashletRichText extends Dashlet
  218. {
  219. public function __construct($sId)
  220. {
  221. parent::__construct($sId);
  222. $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>";
  223. }
  224. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  225. {
  226. $sText = $this->aProperties['text'];
  227. $sId = 'richtext_'.($bEditMode? 'edit_' : '').$this->sId;
  228. $oPage->add('<div id='.$sId.'" class="dashlet-content">'.$sText.'</div>');
  229. }
  230. public function GetPropertiesFields(DesignerForm $oForm)
  231. {
  232. $oField = new DesignerLongTextField('text', 'Text', $this->aProperties['text']);
  233. $oForm->AddField($oField);
  234. }
  235. static public function GetInfo()
  236. {
  237. return array(
  238. 'label' => 'Rich text',
  239. 'icon' => 'images/dashlet-text.png',
  240. 'description' => 'Text formatted with HTML tags',
  241. );
  242. }
  243. }
  244. class DashletObjectList extends Dashlet
  245. {
  246. public function __construct($sId)
  247. {
  248. parent::__construct($sId);
  249. $this->aProperties['title'] = 'Hardcoded list of "my requests"';
  250. $this->aProperties['query'] = 'SELECT UserRequest AS i WHERE i.caller_id = :current_contact_id AND status NOT IN ("closed", "resolved")';
  251. $this->aProperties['menu'] = false;
  252. }
  253. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  254. {
  255. $sTitle = $this->aProperties['title'];
  256. $sQuery = $this->aProperties['query'];
  257. $sShowMenu = $this->aProperties['menu'] ? '1' : '0';
  258. $oPage->add('<div class="dashlet-content">');
  259. $sHtmlTitle = htmlentities(Dict::S($sTitle), ENT_QUOTES, 'UTF-8'); // done in the itop block
  260. if ($sHtmlTitle != '')
  261. {
  262. $oPage->add('<h1>'.$sHtmlTitle.'</h1>');
  263. }
  264. $oFilter = DBObjectSearch::FromOQL($sQuery);
  265. $oBlock = new DisplayBlock($oFilter, 'list');
  266. $aExtraParams = array(
  267. 'menu' => $sShowMenu,
  268. );
  269. $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
  270. $oBlock->Display($oPage, $sBlockId, $aExtraParams);
  271. $oPage->add('</div>');
  272. }
  273. public function GetPropertiesFields(DesignerForm $oForm)
  274. {
  275. $oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
  276. $oForm->AddField($oField);
  277. $oField = new DesignerLongTextField('query', 'Query', $this->aProperties['query']);
  278. $oForm->AddField($oField);
  279. $oField = new DesignerBooleanField('menu', 'Menu', $this->aProperties['menu']);
  280. $oForm->AddField($oField);
  281. }
  282. static public function GetInfo()
  283. {
  284. return array(
  285. 'label' => 'Object list',
  286. 'icon' => 'images/dashlet-list.png',
  287. 'description' => 'Object list dashlet',
  288. );
  289. }
  290. static public function CanCreateFromOQL()
  291. {
  292. return true;
  293. }
  294. public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL)
  295. {
  296. $oField = new DesignerTextField('title', 'Title', '');
  297. $oForm->AddField($oField);
  298. $oField = new DesignerHiddenField('query', 'Query', $sOQL);
  299. $oForm->AddField($oField);
  300. $oField = new DesignerBooleanField('menu', 'Menu', $this->aProperties['menu']);
  301. $oForm->AddField($oField);
  302. }
  303. }
  304. abstract class DashletGroupBy extends Dashlet
  305. {
  306. public function __construct($sId)
  307. {
  308. parent::__construct($sId);
  309. $this->aProperties['title'] = 'Contacts grouped by location';
  310. $this->aProperties['query'] = 'SELECT Contact';
  311. $this->aProperties['group_by'] = 'location_name';
  312. $this->aProperties['style'] = 'table';
  313. }
  314. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  315. {
  316. $sTitle = $this->aProperties['title'];
  317. $sQuery = $this->aProperties['query'];
  318. $sGroupBy = $this->aProperties['group_by'];
  319. $sStyle = $this->aProperties['style'];
  320. if ($sQuery == '')
  321. {
  322. $oPage->add('<p>Please enter a valid OQL query</p>');
  323. }
  324. elseif ($sGroupBy == '')
  325. {
  326. $oPage->add('<p>Please select the field on which the objects will be grouped together</p>');
  327. }
  328. else
  329. {
  330. $oFilter = DBObjectSearch::FromOQL($sQuery);
  331. $sClassAlias = $oFilter->GetClassAlias();
  332. if (preg_match('/^(.*):(.*)$/', $sGroupBy, $aMatches))
  333. {
  334. $sAttCode = $aMatches[1];
  335. $sFunction = $aMatches[2];
  336. switch($sFunction)
  337. {
  338. case 'hour':
  339. $sGroupByLabel = 'Hour of '.$sAttCode. ' (0-23)';
  340. $sGroupByExpr = "DATE_FORMAT($sClassAlias.$sAttCode, '%H')"; // 0 -> 31
  341. break;
  342. case 'month':
  343. $sGroupByLabel = 'Month of '.$sAttCode. ' (1 - 12)';
  344. $sGroupByExpr = "DATE_FORMAT($sClassAlias.$sAttCode, '%m')"; // 0 -> 31
  345. break;
  346. case 'day_of_week':
  347. $sGroupByLabel = 'Day of week for '.$sAttCode. ' (sunday to saturday)';
  348. $sGroupByExpr = "DATE_FORMAT($sClassAlias.$sAttCode, '%w')";
  349. break;
  350. case 'day_of_month':
  351. $sGroupByLabel = 'Day of month for'.$sAttCode;
  352. $sGroupByExpr = "DATE_FORMAT($sClassAlias.$sAttCode, '%e')"; // 0 -> 31
  353. break;
  354. default:
  355. $sGroupByLabel = 'Unknown group by function '.$sFunction;
  356. $sGroupByExpr = $sClassAlias.'.'.$sAttCode;
  357. }
  358. }
  359. else
  360. {
  361. $sAttCode = $sGroupBy;
  362. $sGroupByExpr = $sClassAlias.'.'.$sAttCode;
  363. $sGroupByLabel = MetaModel::GetLabel($oFilter->GetClass(), $sAttCode);
  364. }
  365. switch($sStyle)
  366. {
  367. case 'bars':
  368. $sType = 'open_flash_chart';
  369. $aExtraParams = array(
  370. 'chart_type' => 'bars',
  371. 'chart_title' => $sTitle,
  372. 'group_by' => $sGroupByExpr,
  373. 'group_by_label' => $sGroupByLabel,
  374. );
  375. $sHtmlTitle = ''; // done in the itop block
  376. break;
  377. case 'pie':
  378. $sType = 'open_flash_chart';
  379. $aExtraParams = array(
  380. 'chart_type' => 'pie',
  381. 'chart_title' => $sTitle,
  382. 'group_by' => $sGroupByExpr,
  383. 'group_by_label' => $sGroupByLabel,
  384. );
  385. $sHtmlTitle = ''; // done in the itop block
  386. break;
  387. case 'table':
  388. default:
  389. $sHtmlTitle = htmlentities(Dict::S($sTitle), ENT_QUOTES, 'UTF-8'); // done in the itop block
  390. $sType = 'count';
  391. $aExtraParams = array(
  392. 'group_by' => $sGroupByExpr,
  393. 'group_by_label' => $sGroupByLabel,
  394. );
  395. break;
  396. }
  397. $oPage->add('<div style="text-align:center" class="dashlet-content">');
  398. if ($sHtmlTitle != '')
  399. {
  400. $oPage->add('<h1>'.$sHtmlTitle.'</h1>');
  401. }
  402. $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
  403. $oBlock = new DisplayBlock($oFilter, $sType);
  404. $oBlock->Display($oPage, $sBlockId, $aExtraParams);
  405. $oPage->add('</div>');
  406. }
  407. }
  408. public function GetPropertiesFields(DesignerForm $oForm)
  409. {
  410. $oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
  411. $oForm->AddField($oField);
  412. $oField = new DesignerLongTextField('query', 'Query', $this->aProperties['query']);
  413. $oForm->AddField($oField);
  414. // Group by field: build the list of possible values (attribute codes + ...)
  415. $oSearch = DBObjectSearch::FromOQL($this->aProperties['query']);
  416. $sClass = $oSearch->GetClass();
  417. $aGroupBy = array();
  418. foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
  419. {
  420. if (!$oAttDef->IsScalar()) continue; // skip link sets
  421. $sLabel = $oAttDef->GetLabel();
  422. if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
  423. {
  424. $sLabel = $oAttDef->GetLabel().' (strict)';
  425. }
  426. $aGroupBy[$sAttCode] = $sLabel;
  427. if ($oAttDef instanceof AttributeDateTime)
  428. {
  429. $aGroupBy[$sAttCode.':hour'] = $oAttDef->GetLabel().' (hour)';
  430. $aGroupBy[$sAttCode.':month'] = $oAttDef->GetLabel().' (month)';
  431. $aGroupBy[$sAttCode.':day_of_week'] = $oAttDef->GetLabel().' (day of week)';
  432. $aGroupBy[$sAttCode.':day_of_month'] = $oAttDef->GetLabel().' (day of month)';
  433. }
  434. }
  435. $oField = new DesignerComboField('group_by', 'Group by', $this->aProperties['group_by']);
  436. $oField->SetAllowedValues($aGroupBy);
  437. $oForm->AddField($oField);
  438. $aStyles = array(
  439. 'pie' => 'Pie chart',
  440. 'bars' => 'Bar chart',
  441. 'table' => 'Table',
  442. );
  443. $oField = new DesignerComboField('style', 'Style', $this->aProperties['style']);
  444. $oField->SetAllowedValues($aStyles);
  445. $oForm->AddField($oField);
  446. }
  447. public function Update($aValues, $aUpdatedFields)
  448. {
  449. if (in_array('query', $aUpdatedFields))
  450. {
  451. $sCurrQuery = $aValues['query'];
  452. $oCurrSearch = DBObjectSearch::FromOQL($sCurrQuery);
  453. $sCurrClass = $oCurrSearch->GetClass();
  454. $sPrevQuery = $this->aProperties['query'];
  455. $oPrevSearch = DBObjectSearch::FromOQL($sPrevQuery);
  456. $sPrevClass = $oPrevSearch->GetClass();
  457. if ($sCurrClass != $sPrevClass)
  458. {
  459. $this->bFormRedrawNeeded = true;
  460. // wrong but not necessary - unset($aUpdatedFields['group_by']);
  461. $this->aProperties['group_by'] = '';
  462. }
  463. }
  464. $oDashlet = parent::Update($aValues, $aUpdatedFields);
  465. if (in_array('style', $aUpdatedFields))
  466. {
  467. switch($aValues['style'])
  468. {
  469. // Style changed, mutate to the specified type of chart
  470. case 'pie':
  471. $oDashlet = new DashletGroupByPie($this->sId);
  472. break;
  473. case 'bars':
  474. $oDashlet = new DashletGroupByBars($this->sId);
  475. break;
  476. case 'table':
  477. $oDashlet = new DashletGroupByTable($this->sId);
  478. break;
  479. }
  480. $oDashlet->FromParams($aValues);
  481. $oDashlet->bRedrawNeeded = true;
  482. $oDashlet->bFormRedrawNeeded = true;
  483. }
  484. return $oDashlet;
  485. }
  486. static public function GetInfo()
  487. {
  488. return array(
  489. 'label' => 'Objects grouped by...',
  490. 'icon' => 'images/dashlet-object-grouped.png',
  491. 'description' => 'Grouped objects dashlet',
  492. );
  493. }
  494. static public function CanCreateFromOQL()
  495. {
  496. return true;
  497. }
  498. public function GetPropertiesFieldsFromOQL(DesignerForm $oForm, $sOQL)
  499. {
  500. $oField = new DesignerTextField('title', 'Title', '');
  501. $oForm->AddField($oField);
  502. $oField = new DesignerHiddenField('query', 'Query', $sOQL);
  503. $oForm->AddField($oField);
  504. // Group by field: build the list of possible values (attribute codes + ...)
  505. $oSearch = DBObjectSearch::FromOQL($this->aProperties['query']);
  506. $sClass = $oSearch->GetClass();
  507. $aGroupBy = array();
  508. foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
  509. {
  510. if (!$oAttDef->IsScalar()) continue; // skip link sets
  511. if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE)) continue; // skip external keys
  512. $aGroupBy[$sAttCode] = $oAttDef->GetLabel();
  513. if ($oAttDef instanceof AttributeDateTime)
  514. {
  515. //date_format(start_date, '%d')
  516. $aGroupBy['date_of_'.$sAttCode] = 'Day of '.$oAttDef->GetLabel();
  517. }
  518. }
  519. $oField = new DesignerComboField('group_by', 'Group by', $this->aProperties['group_by']);
  520. $oField->SetAllowedValues($aGroupBy);
  521. $oForm->AddField($oField);
  522. $oField = new DesignerHiddenField('style', '', $this->aProperties['style']);
  523. $oForm->AddField($oField);
  524. }
  525. }
  526. class DashletGroupByPie extends DashletGroupBy
  527. {
  528. public function __construct($sId)
  529. {
  530. parent::__construct($sId);
  531. $this->aProperties['style'] = 'pie';
  532. }
  533. static public function GetInfo()
  534. {
  535. return array(
  536. 'label' => 'Pie Chart',
  537. 'icon' => 'images/dashlet-pie-chart.png',
  538. 'description' => 'Pie Chart',
  539. );
  540. }
  541. }
  542. class DashletGroupByPie2 extends DashletGroupByPie
  543. {
  544. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  545. {
  546. $sTitle = addslashes($this->aProperties['title']);
  547. $sQuery = $this->aProperties['query'];
  548. $sGroupBy = $this->aProperties['group_by'];
  549. $oSearch = DBObjectSearch::FromOQL($sQuery);
  550. $sClassAlias = $oSearch->GetClassAlias();
  551. $aQueryParams = array();
  552. $aGroupBy = array();
  553. $oGroupByExp = Expression::FromOQL($sClassAlias.'.'.$sGroupBy);
  554. $aGroupBy['grouped_by_1'] = $oGroupByExp;
  555. $sSql = MetaModel::MakeGroupByQuery($oSearch, $aQueryParams, $aGroupBy);
  556. $aRes = CMDBSource::QueryToArray($sSql);
  557. $aGroupBy = array();
  558. $aLabels = array();
  559. $iTotalCount = 0;
  560. foreach ($aRes as $aRow)
  561. {
  562. $sValue = $aRow['grouped_by_1'];
  563. $aLabels[] = ($sValue == '') ? 'Empty (%%.%%)' : $sValue.' (%%.%%)'; //TODO: localize
  564. $aGroupBy[] = (int) $aRow['_itop_count_'];
  565. $iTotalCount += $aRow['_itop_count_'];
  566. }
  567. $aURLs = array();
  568. $sContext = ''; //TODO get the context ??
  569. foreach($aGroupBy as $sValue => $iValue)
  570. {
  571. // Build the search for this subset
  572. $oSubsetSearch = clone $oSearch;
  573. $oCondition = new BinaryExpression($oGroupByExp, '=', new ScalarExpression($sValue));
  574. $oSubsetSearch->AddConditionExpression($oCondition);
  575. $aURLs[] = 'http://www.combodo.com/itop'; //utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&format=html{$sContext}&filter=".addslashes($oSubsetSearch->serialize());
  576. }
  577. $sJSValues = json_encode($aGroupBy);
  578. $sJSHrefs = json_encode($aURLs);
  579. $sJSLabels = json_encode($aLabels);
  580. $sId = 'chart_'.($bEditMode? 'edit_' : '').$this->sId;
  581. $oPage->add('<div id="chart_'.$sId.'" class="dashlet-content"></div>');
  582. $oPage->add_ready_script("$('#chart_{$sId}').pie_chart({chart_label: '$sTitle', values: $sJSValues, labels: $sJSLabels, hrefs: $sJSHrefs });");
  583. }
  584. static public function GetInfo()
  585. {
  586. return array(
  587. 'label' => 'Pie (Raphael)',
  588. 'icon' => 'images/dashlet-pie-chart.png',
  589. 'description' => 'Pure JS Pie Chart',
  590. );
  591. }
  592. }
  593. class DashletGroupByBars extends DashletGroupBy
  594. {
  595. public function __construct($sId)
  596. {
  597. parent::__construct($sId);
  598. $this->aProperties['style'] = 'bars';
  599. }
  600. static public function GetInfo()
  601. {
  602. return array(
  603. 'label' => 'Bar Chart',
  604. 'icon' => 'images/dashlet-bar-chart.png',
  605. 'description' => 'Bar Chart',
  606. );
  607. }
  608. }
  609. class DashletGroupByTable extends DashletGroupBy
  610. {
  611. public function __construct($sId)
  612. {
  613. parent::__construct($sId);
  614. $this->aProperties['style'] = 'table';
  615. }
  616. static public function GetInfo()
  617. {
  618. return array(
  619. 'label' => 'Group By (table)',
  620. 'icon' => 'images/dashlet-groupby-table.png',
  621. 'description' => 'List (Grouped by a field)',
  622. );
  623. }
  624. }
  625. class DashletHeaderStatic extends Dashlet
  626. {
  627. public function __construct($sId)
  628. {
  629. parent::__construct($sId);
  630. $this->aProperties['title'] = 'Contacts';
  631. $this->aProperties['icon'] = 'itop-config-mgmt-1.0.0/images/contact.png';
  632. }
  633. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  634. {
  635. $sTitle = $this->aProperties['title'];
  636. $sIcon = $this->aProperties['icon'];
  637. $sIconPath = utils::GetAbsoluteUrlModulesRoot().$sIcon;
  638. $oPage->add('<div class="dashlet-content">');
  639. $oPage->add('<div class="main_header">');
  640. $oPage->add('<img src="'.$sIconPath.'">');
  641. $oPage->add('<h1>'.Dict::S($sTitle).'</h1>');
  642. $oPage->add('</div>');
  643. $oPage->add('</div>');
  644. }
  645. public function GetPropertiesFields(DesignerForm $oForm)
  646. {
  647. $oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
  648. $oForm->AddField($oField);
  649. $oField = new DesignerIconSelectionField('icon', 'Icon', $this->aProperties['icon']);
  650. $aAllIcons = self::FindIcons(APPROOT.'env-'.utils::GetCurrentEnvironment());
  651. ksort($aAllIcons);
  652. $aValues = array();
  653. foreach($aAllIcons as $sFilePath)
  654. {
  655. $aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
  656. }
  657. $oField->SetAllowedValues($aValues);
  658. $oForm->AddField($oField);
  659. }
  660. static public function GetInfo()
  661. {
  662. return array(
  663. 'label' => 'Header',
  664. 'icon' => 'images/dashlet-header.png',
  665. 'description' => 'Header with stats (grouped by...)',
  666. );
  667. }
  668. static public function FindIcons($sBaseDir, $sDir = '')
  669. {
  670. $aResult = array();
  671. // Populate automatically the list of icon files
  672. if ($hDir = @opendir($sBaseDir.'/'.$sDir))
  673. {
  674. while (($sFile = readdir($hDir)) !== false)
  675. {
  676. $aMatches = array();
  677. if (($sFile != '.') && ($sFile != '..') && is_dir($sBaseDir.'/'.$sDir.'/'.$sFile))
  678. {
  679. $sDirSubPath = ($sDir == '') ? $sFile : $sDir.'/'.$sFile;
  680. $aResult = array_merge($aResult, self::FindIcons($sBaseDir, $sDirSubPath));
  681. }
  682. if (preg_match("/\.(png|jpg|jpeg|gif)$/i", $sFile, $aMatches)) // png, jp(e)g and gif are considered valid
  683. {
  684. $aResult[$sFile.'_'.$sDir] = $sDir.'/'.$sFile;
  685. }
  686. }
  687. closedir($hDir);
  688. }
  689. return $aResult;
  690. }
  691. }
  692. class DashletHeaderDynamic extends Dashlet
  693. {
  694. public function __construct($sId)
  695. {
  696. parent::__construct($sId);
  697. $this->aProperties['title'] = 'Contacts';
  698. $this->aProperties['icon'] = 'itop-config-mgmt-1.0.0/images/contact.png';
  699. $this->aProperties['subtitle'] = 'Contacts';
  700. $this->aProperties['query'] = 'SELECT Contact';
  701. $this->aProperties['group_by'] = 'status';
  702. $this->aProperties['values'] = 'active,inactive,terminated';
  703. }
  704. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  705. {
  706. $sTitle = $this->aProperties['title'];
  707. $sIcon = $this->aProperties['icon'];
  708. $sSubtitle = $this->aProperties['subtitle'];
  709. $sQuery = $this->aProperties['query'];
  710. $sGroupBy = $this->aProperties['group_by'];
  711. $sValues = $this->aProperties['values'];
  712. $oFilter = DBObjectSearch::FromOQL($sQuery);
  713. $sClass = $oFilter->GetClass();
  714. $sIconPath = utils::GetAbsoluteUrlModulesRoot().$sIcon;
  715. $aValues = null;
  716. if (MetaModel::IsValidAttCode($sClass, $sGroupBy))
  717. {
  718. if ($sValues == '')
  719. {
  720. $aAllowed = MetaModel::GetAllowedValues_att($sClass, $sGroupBy);
  721. if (is_array($aAllowed))
  722. {
  723. $aValues = array_keys($aAllowed);
  724. }
  725. }
  726. else
  727. {
  728. $aValues = explode(',', $sValues);
  729. }
  730. }
  731. if (is_array($aValues))
  732. {
  733. // Stats grouped by <group_by>
  734. $aCSV = implode(',', $aValues);
  735. $aExtraParams = array(
  736. 'title[block]' => $sTitle,
  737. 'label[block]' => $sSubtitle,
  738. 'status[block]' => $sGroupBy,
  739. 'status_codes[block]' => $aCSV,
  740. 'context_filter' => 1,
  741. );
  742. }
  743. else
  744. {
  745. // Simple stats
  746. $aExtraParams = array(
  747. 'title[block]' => $sTitle,
  748. 'label[block]' => $sSubtitle,
  749. 'context_filter' => 1,
  750. );
  751. }
  752. $oPage->add('<div class="dashlet-content">');
  753. $oPage->add('<div class="main_header">');
  754. $oPage->add('<img src="'.$sIconPath.'">');
  755. $oBlock = new DisplayBlock($oFilter, 'summary');
  756. $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
  757. $oBlock->Display($oPage, $sBlockId, $aExtraParams);
  758. $oPage->add('</div>');
  759. $oPage->add('</div>');
  760. }
  761. public function GetPropertiesFields(DesignerForm $oForm)
  762. {
  763. $oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
  764. $oForm->AddField($oField);
  765. $oField = new DesignerIconSelectionField('icon', 'Icon', $this->aProperties['icon']);
  766. $aAllIcons = DashletHeaderStatic::FindIcons(APPROOT.'env-'.utils::GetCurrentEnvironment());
  767. ksort($aAllIcons);
  768. $aValues = array();
  769. foreach($aAllIcons as $sFilePath)
  770. {
  771. $aValues[] = array('value' => $sFilePath, 'label' => basename($sFilePath), 'icon' => utils::GetAbsoluteUrlModulesRoot().$sFilePath);
  772. }
  773. $oField->SetAllowedValues($aValues);
  774. $oForm->AddField($oField);
  775. $oField = new DesignerTextField('subtitle', 'Subtitle', $this->aProperties['subtitle']);
  776. $oForm->AddField($oField);
  777. $oField = new DesignerTextField('query', 'Query', $this->aProperties['query']);
  778. $oForm->AddField($oField);
  779. // Group by field: build the list of possible values (attribute codes + ...)
  780. $oSearch = DBObjectSearch::FromOQL($this->aProperties['query']);
  781. $sClass = $oSearch->GetClass();
  782. $aGroupBy = array();
  783. foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
  784. {
  785. if (!$oAttDef->IsScalar()) continue; // skip link sets
  786. $sLabel = $oAttDef->GetLabel();
  787. if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE))
  788. {
  789. $sLabel = $oAttDef->GetLabel().' (strict)';
  790. }
  791. $aGroupBy[$sAttCode] = $sLabel;
  792. }
  793. $oField = new DesignerComboField('group_by', 'Group by', $this->aProperties['group_by']);
  794. $oField->SetAllowedValues($aGroupBy);
  795. $oForm->AddField($oField);
  796. $oField = new DesignerTextField('values', 'Values (CSV list)', $this->aProperties['values']);
  797. $oForm->AddField($oField);
  798. }
  799. static public function GetInfo()
  800. {
  801. return array(
  802. 'label' => 'Header with statistics',
  803. 'icon' => 'images/dashlet-header-stats.png',
  804. 'description' => 'Header with stats (grouped by...)',
  805. );
  806. }
  807. }
  808. class DashletBadge extends Dashlet
  809. {
  810. public function __construct($sId)
  811. {
  812. parent::__construct($sId);
  813. $this->aProperties['class'] = 'Contact';
  814. $this->aCSSClasses[] = 'dashlet-inline';
  815. $this->aCSSClasses[] = 'dashlet-badge';
  816. }
  817. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  818. {
  819. $sClass = $this->aProperties['class'];
  820. $oPage->add('<div class="dashlet-content">');
  821. $oFilter = new DBObjectSearch($sClass);
  822. $oBlock = new DisplayBlock($oFilter, 'actions');
  823. $aExtraParams = array(
  824. 'context_filter' => 1,
  825. );
  826. $sBlockId = 'block_'.$this->sId.($bEditMode ? '_edit' : ''); // make a unique id (edition occuring in the same DOM)
  827. $oBlock->Display($oPage, $sBlockId, $aExtraParams);
  828. $oPage->add('</div>');
  829. if ($bEditMode)
  830. {
  831. // Since the container div is not rendered the same way in edit mode, add the 'inline' style to it
  832. $oPage->add_ready_script("$('#dashlet_".$this->sId."').addClass('dashlet-inline');");
  833. }
  834. }
  835. public function GetPropertiesFields(DesignerForm $oForm)
  836. {
  837. $oClassesSet = new ValueSetEnumClasses('bizmodel', array());
  838. $aClasses = $oClassesSet->GetValues(array());
  839. $aLinkClasses = array();
  840. foreach(MetaModel::GetClasses('bizmodel') as $sClass)
  841. {
  842. foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
  843. {
  844. if ($oAttDef instanceof AttributeLinkedSetIndirect)
  845. {
  846. $aLinkClasses[$oAttDef->GetLinkedClass()] = true;
  847. }
  848. }
  849. }
  850. $oField = new DesignerIconSelectionField('class', 'Class', $this->aProperties['class']);
  851. ksort($aClasses);
  852. $aValues = array();
  853. foreach($aClasses as $sClass => $sClass)
  854. {
  855. if (!array_key_exists($sClass, $aLinkClasses))
  856. {
  857. $sIconUrl = MetaModel::GetClassIcon($sClass, false);
  858. $sIconFilePath = str_replace(utils::GetAbsoluteUrlAppRoot(), APPROOT, $sIconUrl);
  859. if (($sIconUrl == '') || !file_exists($sIconFilePath))
  860. {
  861. // The icon does not exist, leet's use a transparent one of the same size.
  862. $sIconUrl = utils::GetAbsoluteUrlAppRoot().'images/transparent_32_32.png';
  863. }
  864. $aValues[] = array('value' => $sClass, 'label' => $sClass, 'icon' => $sIconUrl);
  865. }
  866. }
  867. $oField->SetAllowedValues($aValues);
  868. $oForm->AddField($oField);
  869. }
  870. static public function GetInfo()
  871. {
  872. return array(
  873. 'label' => 'Badge',
  874. 'icon' => 'images/dashlet-badge.png',
  875. 'description' => 'Object Icon with new/search',
  876. );
  877. }
  878. }
  879. class DashletProto extends Dashlet
  880. {
  881. public function __construct($sId)
  882. {
  883. parent::__construct($sId);
  884. $this->aProperties['class'] = 'Foo';
  885. }
  886. protected $aValues1;
  887. protected $aValues2;
  888. protected $aStats;
  889. protected $sGroupByLabel1;
  890. protected $sGroupByLabel2;
  891. protected $iTotalCount;
  892. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  893. {
  894. $this->Compute();
  895. //$sHtmlTitle = $this->aProperties['title'];
  896. $sHtmlTitle = "Group by made on two dimensions";
  897. // Build the output
  898. $aDisplayData = array();
  899. foreach ($this->aValues1 as $sValue1 => $sDisplayValue1)
  900. {
  901. foreach ($this->aValues2 as $sValue2 => $sDisplayValue2)
  902. {
  903. @$aResData = $this->aStats[$sValue1][$sValue2];
  904. if (is_array($aResData))
  905. {
  906. $aDisplayData[] = array (
  907. 'group1' => $sDisplayValue1,
  908. 'group2' => $sDisplayValue2,
  909. 'value' => $aResData['digurl']
  910. );
  911. }
  912. else
  913. {
  914. // Missing result => 0
  915. $aDisplayData[] = array (
  916. 'group1' => $sDisplayValue1,
  917. 'group2' => $sDisplayValue2,
  918. 'value' => "-"
  919. );
  920. }
  921. }
  922. }
  923. $aAttribs =array(
  924. 'group1' => array('label' => $this->sGroupByLabel1, 'description' => ''),
  925. 'group2' => array('label' => $this->sGroupByLabel2, 'description' => ''),
  926. 'value' => array('label'=> Dict::S('UI:GroupBy:Count'), 'description' => Dict::S('UI:GroupBy:Count+'))
  927. );
  928. $oPage->add('<div style="text-align:center" class="dashlet-content">');
  929. $oPage->add('<h1>'.$sHtmlTitle.'</h1>');
  930. $oPage->p(Dict::Format('UI:Pagination:HeaderNoSelection', $this->iTotalCount));
  931. $oPage->table($aAttribs, $aDisplayData);
  932. $oPage->add('</div>');
  933. }
  934. protected function Compute()
  935. {
  936. // Read and interpret parameters
  937. //
  938. //$sFoo = $this->aProperties['foo'];
  939. if (true)
  940. {
  941. $oFilter = DBObjectSearch::FromOQL('SELECT FunctionalCI');
  942. $sGroupBy1 = 'FunctionalCI.status';
  943. $this->sGroupByLabel1 = MetaModel::GetLabel('FunctionalCI', 'status');
  944. $aFill1 = array(
  945. 'implementation',
  946. 'production',
  947. 'obsolete',
  948. );
  949. //$aFill1 = null;
  950. //$sGroupBy2 = 'org_id_friendlyname';
  951. $sGroupBy2 = 'FunctionalCI.org_id';
  952. $this->sGroupByLabel2 = MetaModel::GetLabel('FunctionalCI', 'org_id');
  953. $aFill2 = array(
  954. '2',
  955. '1',
  956. );
  957. //$aFill2 = null;
  958. }
  959. else
  960. {
  961. $oFilter = DBObjectSearch::FromOQL('SELECT Incident AS i');
  962. $sGroupBy1 = "DATE_FORMAT(i.start_date, '%H')";
  963. $this->sGroupByLabel1 = 'Hour of '.MetaModel::GetLabel('Incident', 'start_date');
  964. $aFill1 = array(9, 10, 11, 12, 13, 14, 15, 16, 17, 18);
  965. //$aFill1 = null;
  966. $sGroupBy2 = "DATE_FORMAT(i.start_date, '%w')";
  967. $this->sGroupByLabel2 = 'Week day of '.MetaModel::GetLabel('Incident', 'start_date');
  968. $aFill2 = null;
  969. }
  970. // Do compute the statistics
  971. //
  972. $this->aValues1 = array();
  973. $this->aValues2 = array();
  974. $this->aStats = array();
  975. $this->iTotalCount = 0;
  976. $sAlias = $oFilter->GetClassAlias();
  977. //$oGroupByExp1 = new FieldExpression($sGroupBy1, $sAlias);
  978. $oGroupByExp1 = Expression::FromOQL($sGroupBy1);
  979. //$oGroupByExp2 = new FieldExpression($sGroupBy2, $sAlias);
  980. $oGroupByExp2 = Expression::FromOQL($sGroupBy2);
  981. $aGroupBy = array();
  982. $aGroupBy['grouped_by_1'] = $oGroupByExp1;
  983. $aGroupBy['grouped_by_2'] = $oGroupByExp2;
  984. $sSql = MetaModel::MakeGroupByQuery($oFilter, array(), $aGroupBy);
  985. $aRes = CMDBSource::QueryToArray($sSql);
  986. // Prepare a blank and ordered grid
  987. if (is_array($aFill1))
  988. {
  989. foreach ($aFill1 as $sValue1)
  990. {
  991. $sDisplayValue1 = $aGroupBy['grouped_by_1']->MakeValueLabel($oFilter, $sValue1, $sValue1); // default to the raw value
  992. $this->aValues1[$sValue1] = $sDisplayValue1;
  993. }
  994. }
  995. if (is_array($aFill2))
  996. {
  997. foreach ($aFill2 as $sValue2)
  998. {
  999. $sDisplayValue2 = $aGroupBy['grouped_by_2']->MakeValueLabel($oFilter, $sValue2, $sValue2); // default to the raw value
  1000. $aValues2[$sValue2] = $sDisplayValue2;
  1001. }
  1002. }
  1003. $oAppContext = new ApplicationContext();
  1004. $sParams = $oAppContext->GetForLink();
  1005. foreach ($aRes as $aRow)
  1006. {
  1007. $iCount = $aRow['_itop_count_'];
  1008. $this->iTotalCount += $iCount;
  1009. $sValue1 = $aRow['grouped_by_1'];
  1010. $sValue2 = $aRow['grouped_by_2'];
  1011. $bValidResult = true;
  1012. if (is_array($aFill1) && !in_array($sValue1, $aFill1))
  1013. {
  1014. $bValidResult = false;
  1015. }
  1016. if (is_array($aFill2) && !in_array($sValue2, $aFill2))
  1017. {
  1018. $bValidResult = false;
  1019. }
  1020. if ($bValidResult)
  1021. {
  1022. // Build the search for this subset
  1023. $oSubsetSearch = clone $oFilter;
  1024. $oCondition = new BinaryExpression($oGroupByExp1, '=', new ScalarExpression($sValue1));
  1025. $oSubsetSearch->AddConditionExpression($oCondition);
  1026. $oCondition = new BinaryExpression($oGroupByExp2, '=', new ScalarExpression($sValue2));
  1027. $oSubsetSearch->AddConditionExpression($oCondition);
  1028. $sFilter = urlencode($oSubsetSearch->serialize());
  1029. $this->aStats[$sValue1][$sValue2] = array (
  1030. 'digurl' => "<a href=\"".utils::GetAbsoluteUrlAppRoot()."pages/UI.php?operation=search&dosearch=1&$sParams&filter=$sFilter\">$iCount</a>"
  1031. );
  1032. if (!array_key_exists($sValue1, $this->aValues1))
  1033. {
  1034. $sDisplayValue1 = $aGroupBy['grouped_by_1']->MakeValueLabel($oFilter, $sValue1, $sValue1); // default to the raw value
  1035. $this->aValues1[$sValue1] = $sDisplayValue1;
  1036. }
  1037. if (!array_key_exists($sValue2, $this->aValues2))
  1038. {
  1039. $sDisplayValue2 = $aGroupBy['grouped_by_2']->MakeValueLabel($oFilter, $sValue2, $sValue2); // default to the raw value
  1040. $this->aValues2[$sValue2] = $sDisplayValue2;
  1041. }
  1042. }
  1043. }
  1044. }
  1045. public function GetPropertiesFields(DesignerForm $oForm)
  1046. {
  1047. $oField = new DesignerTextField('class', 'Class', $this->aProperties['class']);
  1048. $oForm->AddField($oField);
  1049. }
  1050. static public function GetInfo()
  1051. {
  1052. return array(
  1053. 'label' => 'Test3D',
  1054. 'icon' => 'images/dashlet-groupby2-table.png',
  1055. 'description' => 'Group by on two dimensions',
  1056. );
  1057. }
  1058. }
  1059. class DashletHeatMap extends Dashlet
  1060. {
  1061. public function __construct($sId)
  1062. {
  1063. parent::__construct($sId);
  1064. $this->aProperties['class'] = 'Contact';
  1065. $this->aProperties['title'] = 'Test';
  1066. }
  1067. public function Render($oPage, $bEditMode = false, $aExtraParams = array())
  1068. {
  1069. $sTitle = addslashes($this->aProperties['title']);
  1070. $sId = 'chart_'.($bEditMode? 'edit_' : '').$this->sId;
  1071. $oPage->add('<div id="chart_'.$sId.'" class="dashlet-content"></div>');
  1072. $oPage->add_ready_script("$('#chart_{$sId}').heatmap_chart({chart_label: '$sTitle'});");
  1073. }
  1074. public function GetPropertiesFields(DesignerForm $oForm)
  1075. {
  1076. $oField = new DesignerTextField('title', 'Title', $this->aProperties['title']);
  1077. $oForm->AddField($oField);
  1078. $oField = new DesignerTextField('class', 'Class', $this->aProperties['class']);
  1079. $oForm->AddField($oField);
  1080. }
  1081. static public function GetInfo()
  1082. {
  1083. return array(
  1084. 'label' => 'Heatmap (Raphael)',
  1085. 'icon' => 'images/dashlet-heatmap.png',
  1086. 'description' => 'Pure JS Heat Map Chart',
  1087. );
  1088. }
  1089. }