career.lib.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671
  1. <?php
  2. /* For licensing terms, see /license.txt */
  3. use Fhaculty\Graph\Graph;
  4. use Fhaculty\Graph\Vertex;
  5. /**
  6. * Class Career
  7. */
  8. class Career extends Model
  9. {
  10. public $table;
  11. public $columns = array(
  12. 'id',
  13. 'name',
  14. 'description',
  15. 'status',
  16. 'created_at',
  17. 'updated_at'
  18. );
  19. /**
  20. * Constructor
  21. */
  22. public function __construct()
  23. {
  24. $this->table = Database::get_main_table(TABLE_CAREER);
  25. }
  26. /**
  27. * Get the count of elements
  28. * @return int
  29. */
  30. public function get_count()
  31. {
  32. $row = Database::select(
  33. 'count(*) as count',
  34. $this->table,
  35. array(),
  36. 'first'
  37. );
  38. return $row['count'];
  39. }
  40. /**
  41. * @param array $where_conditions
  42. * @return array
  43. */
  44. public function get_all($where_conditions = array())
  45. {
  46. return Database::select(
  47. '*',
  48. $this->table,
  49. array('where' => $where_conditions, 'order' => 'name ASC')
  50. );
  51. }
  52. /**
  53. * Update all promotion status by career
  54. * @param int $career_id
  55. * @param int $status (1 or 0)
  56. */
  57. public function update_all_promotion_status_by_career_id($career_id, $status)
  58. {
  59. $promotion = new Promotion();
  60. $promotion_list = $promotion->get_all_promotions_by_career_id($career_id);
  61. if (!empty($promotion_list)) {
  62. foreach ($promotion_list as $item) {
  63. $params['id'] = $item['id'];
  64. $params['status'] = $status;
  65. $promotion->update($params);
  66. $promotion->update_all_sessions_status_by_promotion_id($params['id'], $status);
  67. }
  68. }
  69. }
  70. /**
  71. * Displays the title + grid
  72. */
  73. public function display()
  74. {
  75. echo '<div class="actions" style="margin-bottom:20px">';
  76. echo '<a href="career_dashboard.php">'.
  77. Display::return_icon('back.png', get_lang('Back'), '', ICON_SIZE_MEDIUM).'</a>';
  78. echo '<a href="'.api_get_self().'?action=add">'.
  79. Display::return_icon('new_career.png', get_lang('Add'), '', ICON_SIZE_MEDIUM).'</a>';
  80. echo '</div>';
  81. echo Display::grid_html('careers');
  82. }
  83. /**
  84. * @return array
  85. */
  86. public function get_status_list()
  87. {
  88. return array(
  89. CAREER_STATUS_ACTIVE => get_lang('Unarchived'),
  90. CAREER_STATUS_INACTIVE => get_lang('Archived')
  91. );
  92. }
  93. /**
  94. * Returns a Form validator Obj
  95. * @todo the form should be auto generated
  96. * @param string $url
  97. * @param string $action add, edit
  98. * @return FormValidator
  99. */
  100. public function return_form($url, $action)
  101. {
  102. $form = new FormValidator('career', 'post', $url);
  103. // Setting the form elements
  104. $header = get_lang('Add');
  105. if ($action == 'edit') {
  106. $header = get_lang('Modify');
  107. }
  108. $form->addElement('header', $header);
  109. $id = isset($_GET['id']) ? intval($_GET['id']) : '';
  110. $form->addElement('hidden', 'id', $id);
  111. $form->addElement('text', 'name', get_lang('Name'), array('size' => '70'));
  112. $form->addHtmlEditor(
  113. 'description',
  114. get_lang('Description'),
  115. false,
  116. false,
  117. array(
  118. 'ToolbarSet' => 'Careers',
  119. 'Width' => '100%',
  120. 'Height' => '250'
  121. )
  122. );
  123. $status_list = $this->get_status_list();
  124. $form->addElement('select', 'status', get_lang('Status'), $status_list);
  125. if ($action == 'edit') {
  126. $form->addElement('text', 'created_at', get_lang('CreatedAt'));
  127. $form->freeze('created_at');
  128. }
  129. if ($action == 'edit') {
  130. $form->addButtonSave(get_lang('Modify'), 'submit');
  131. } else {
  132. $form->addButtonCreate(get_lang('Add'), 'submit');
  133. }
  134. // Setting the defaults
  135. $defaults = $this->get($id);
  136. if (!empty($defaults['created_at'])) {
  137. $defaults['created_at'] = api_convert_and_format_date($defaults['created_at']);
  138. }
  139. if (!empty($defaults['updated_at'])) {
  140. $defaults['updated_at'] = api_convert_and_format_date($defaults['updated_at']);
  141. }
  142. $form->setDefaults($defaults);
  143. // Setting the rules
  144. $form->addRule('name', get_lang('ThisFieldIsRequired'), 'required');
  145. return $form;
  146. }
  147. /**
  148. * Copies the career to a new one
  149. * @param integer Career ID
  150. * @param boolean Whether or not to copy the promotions inside
  151. * @return integer New career ID on success, false on failure
  152. */
  153. public function copy($id, $copy_promotions = false)
  154. {
  155. $career = $this->get($id);
  156. $new = array();
  157. foreach ($career as $key => $val) {
  158. switch ($key) {
  159. case 'id':
  160. case 'updated_at':
  161. break;
  162. case 'name':
  163. $val .= ' '.get_lang('CopyLabelSuffix');
  164. $new[$key] = $val;
  165. break;
  166. case 'created_at':
  167. $val = api_get_utc_datetime();
  168. $new[$key] = $val;
  169. break;
  170. default:
  171. $new[$key] = $val;
  172. break;
  173. }
  174. }
  175. $cid = $this->save($new);
  176. if ($copy_promotions) {
  177. //Now also copy each session of the promotion as a new session and register it inside the promotion
  178. $promotion = new Promotion();
  179. $promo_list = $promotion->get_all_promotions_by_career_id($id);
  180. if (!empty($promo_list)) {
  181. foreach ($promo_list as $item) {
  182. $promotion->copy($item['id'], $cid, true);
  183. }
  184. }
  185. }
  186. return $cid;
  187. }
  188. /**
  189. * @param int $career_id
  190. * @return bool
  191. */
  192. public function get_status($career_id)
  193. {
  194. $TBL_CAREER = Database::get_main_table(TABLE_CAREER);
  195. $career_id = intval($career_id);
  196. $sql = "SELECT status FROM $TBL_CAREER WHERE id = '$career_id'";
  197. $result = Database::query($sql);
  198. if (Database::num_rows($result) > 0) {
  199. $data = Database::fetch_array($result);
  200. return $data['status'];
  201. } else {
  202. return false;
  203. }
  204. }
  205. /**
  206. * @param array $params
  207. * @param bool $show_query
  208. * @return bool
  209. */
  210. public function save($params, $show_query = false)
  211. {
  212. if (isset($params['description'])) {
  213. $params['description'] = Security::remove_XSS($params['description']);
  214. }
  215. $id = parent::save($params);
  216. if (!empty($id)) {
  217. Event::addEvent(
  218. LOG_CAREER_CREATE,
  219. LOG_CAREER_ID,
  220. $id,
  221. api_get_utc_datetime(),
  222. api_get_user_id()
  223. );
  224. }
  225. return $id;
  226. }
  227. /**
  228. * Delete a record from the career table and report in the default events log table
  229. * @param int $id The ID of the career to delete
  230. * @return bool True if the career could be deleted, false otherwise
  231. */
  232. public function delete($id)
  233. {
  234. $res = parent::delete($id);
  235. if ($res) {
  236. $extraFieldValues = new ExtraFieldValue('career');
  237. $extraFieldValues->deleteValuesByItem($id);
  238. Event::addEvent(
  239. LOG_CAREER_DELETE,
  240. LOG_CAREER_ID,
  241. $id,
  242. api_get_utc_datetime(),
  243. api_get_user_id()
  244. );
  245. }
  246. return $res;
  247. }
  248. /**
  249. * Update the career table with the given params
  250. * @param array $params The field values to be set
  251. * @return bool Returns true if the record could be updated, false otherwise
  252. */
  253. public function update($params)
  254. {
  255. if (isset($params['description'])) {
  256. $params['description'] = Security::remove_XSS($params['description']);
  257. }
  258. return parent::update($params);
  259. }
  260. /**
  261. * @param array
  262. * @param Graph $graph
  263. *
  264. * @return string
  265. */
  266. public static function renderDiagram($careerInfo, $graph)
  267. {
  268. if (!($graph instanceof Graph)) {
  269. return '';
  270. }
  271. $debug = false;
  272. // Getting max column
  273. $maxColumn = 0;
  274. foreach ($graph->getVertices() as $vertex) {
  275. $groupId = (int) $vertex->getGroup();
  276. if ($groupId > $maxColumn) {
  277. $maxColumn = $groupId;
  278. }
  279. }
  280. $list = [];
  281. /** @var Vertex $vertex */
  282. foreach ($graph->getVertices() as $vertex) {
  283. $group = $vertex->getAttribute('Group');
  284. $subGroup = $vertex->getAttribute('SubGroup');
  285. $subGroupData = explode(':', $subGroup);
  286. $column = $vertex->getGroup();
  287. $row = $vertex->getAttribute('Row');
  288. $subGroupId = $subGroupData[0];
  289. $label = isset($subGroupData[1]) ? $subGroupData[1] : '';
  290. $list[$group][$subGroupId]['columns'][$column][$row] = $vertex;
  291. $list[$group][$subGroupId]['label'] = $label;
  292. }
  293. $maxGroups = count($list);
  294. $widthGroup = 30;
  295. if (!empty($maxGroups)) {
  296. $widthGroup = 85 / $maxGroups;
  297. }
  298. $connections = '';
  299. $groupDrawLine = [];
  300. $groupCourseList = [];
  301. // Read Connections column
  302. foreach ($list as $group => $subGroupList) {
  303. foreach ($subGroupList as $subGroupData) {
  304. $columns = $subGroupData['columns'];
  305. $showGroupLine = true;
  306. if (count($columns) == 1) {
  307. $showGroupLine = false;
  308. }
  309. $groupDrawLine[$group] = $showGroupLine;
  310. //if ($showGroupLine == false) {
  311. /** @var Vertex $vertex */
  312. foreach ($columns as $row => $items) {
  313. foreach ($items as $vertex) {
  314. if ($vertex instanceof Vertex) {
  315. $groupCourseList[$group][] = $vertex->getId();
  316. $connectionList = $vertex->getAttribute(
  317. 'Connections'
  318. );
  319. $firstConnection = '';
  320. $secondConnection = '';
  321. if (!empty($connectionList)) {
  322. $explode = explode('-', $connectionList);
  323. $pos = strpos($explode[0], 'SG');
  324. if ($pos === false) {
  325. $pos = strpos($explode[0], 'G');
  326. if (is_numeric($pos)) {
  327. // group_123 id
  328. $groupValueId = (int)str_replace(
  329. 'G',
  330. '',
  331. $explode[0]
  332. );
  333. $firstConnection = 'group_'.$groupValueId;
  334. $groupDrawLine[$groupValueId] = true;
  335. } else {
  336. // Course block (row_123 id)
  337. if (!empty($explode[0])) {
  338. $firstConnection = 'row_'.(int) $explode[0];
  339. }
  340. }
  341. } else {
  342. // subgroup__123 id
  343. $firstConnection = 'subgroup_'.(int)str_replace('SG', '', $explode[0]);
  344. }
  345. $pos = strpos($explode[1], 'SG');
  346. if ($pos === false) {
  347. $pos = strpos($explode[1], 'G');
  348. if (is_numeric($pos)) {
  349. $groupValueId = (int)str_replace(
  350. 'G',
  351. '',
  352. $explode[1]
  353. );
  354. $secondConnection = 'group_'.$groupValueId;
  355. $groupDrawLine[$groupValueId] = true;
  356. } else {
  357. // Course block (row_123 id)
  358. if (!empty($explode[0])) {
  359. $secondConnection = 'row_'.(int) $explode[1];
  360. }
  361. }
  362. } else {
  363. $secondConnection = 'subgroup_'.(int)str_replace('SG', '', $explode[1]);
  364. }
  365. if (!empty($firstConnection) && !empty($firstConnection)) {
  366. $connections .= self::createConnection(
  367. $firstConnection,
  368. $secondConnection,
  369. ['Left', 'Right']
  370. );
  371. }
  372. }
  373. }
  374. }
  375. }
  376. //}
  377. }
  378. }
  379. $graphHtml = '<div class="container">';
  380. foreach ($list as $group => $subGroupList) {
  381. $showGroupLine = false;
  382. if (isset($groupDrawLine[$group]) && $groupDrawLine[$group]) {
  383. $showGroupLine = true;
  384. }
  385. $graphHtml .= self::parseSubGroups(
  386. $groupCourseList,
  387. $group,
  388. $showGroupLine,
  389. $subGroupList,
  390. $widthGroup
  391. );
  392. }
  393. $graphHtml .= '</div>';
  394. $graphHtml .= $connections;
  395. return $graphHtml;
  396. }
  397. /**
  398. * @param array $groupCourseList list of groups and their courses
  399. * @param int $group
  400. * @param bool $showGroupLine
  401. * @param array $subGroupList
  402. * @param $widthGroup
  403. * @return string
  404. */
  405. public static function parseSubGroups(
  406. $groupCourseList,
  407. $group,
  408. $showGroupLine,
  409. $subGroupList,
  410. $widthGroup
  411. ) {
  412. $topValue = 90;
  413. $defaultSpace = 40;
  414. $leftGroup = $defaultSpace.'px';
  415. if ($group == 1) {
  416. $leftGroup = 0;
  417. }
  418. $groupIdTag = "group_$group";
  419. $borderLine = $showGroupLine === true ? 'border-style:solid;' : '';
  420. // padding:15px;
  421. $graphHtml = '<div id="'.$groupIdTag.'" class="career_group" style=" '.$borderLine.' padding:15px; float:left; margin-left:'.$leftGroup.'; width:'.$widthGroup.'%">';
  422. foreach ($subGroupList as $subGroup => $subGroupData) {
  423. $subGroupLabel = $subGroupData['label'];
  424. $columnList = $subGroupData['columns'];
  425. $line = '';
  426. if (!empty($subGroup)) {
  427. $line = 'border-style:solid;';
  428. }
  429. // padding:15px;
  430. $graphHtml .= '<div id="subgroup_'.$subGroup.'" class="career_subgroup" style="'.$line.' margin-bottom:20px; padding:15px; float:left; margin-left:0px; width:100%">';
  431. if (!empty($subGroupLabel)) {
  432. $graphHtml .= '<h3>'.$subGroupLabel.'</h3>';
  433. }
  434. foreach ($columnList as $column => $rows) {
  435. $leftColumn = $defaultSpace.'px';
  436. if ($column == 1) {
  437. $leftColumn = 0;
  438. }
  439. if (count($columnList) == 1) {
  440. $leftColumn = 0;
  441. }
  442. $widthColumn = 85 / count($columnList);
  443. $graphHtml .= '<div id="col_'.$column.'" class="career_column" style="padding:15px;float:left; margin-left:'.$leftColumn.'; width:'.$widthColumn.'%">';
  444. $maxRow = 0;
  445. foreach ($rows as $row => $vertex) {
  446. if ($row > $maxRow) {
  447. $maxRow = $row;
  448. }
  449. }
  450. $newRowList = [];
  451. $defaultSubGroup = -1;
  452. $subGroupCountList = [];
  453. for ($i = 0; $i < $maxRow; $i++) {
  454. /** @var Vertex $vertex */
  455. $vertex = isset($rows[$i + 1]) ? $rows[$i + 1] : null;
  456. if (!is_null($vertex)) {
  457. $subGroup = $vertex->getAttribute('SubGroup');
  458. if ($subGroup == '' || empty($subGroup)) {
  459. $defaultSubGroup = 0;
  460. } else {
  461. $defaultSubGroup = (int)$subGroup;
  462. }
  463. }
  464. $newRowList[$i + 1][$defaultSubGroup][] = $vertex;
  465. if (!isset($subGroupCountList[$defaultSubGroup])) {
  466. $subGroupCountList[$defaultSubGroup] = 1;
  467. } else {
  468. $subGroupCountList[$defaultSubGroup]++;
  469. }
  470. }
  471. $subGroup = null;
  472. $subGroupAdded = [];
  473. /** @var Vertex $vertex */
  474. foreach ($newRowList as $row => $subGroupList) {
  475. foreach ($subGroupList as $subGroup => $vertexList) {
  476. if (!empty($subGroup) && $subGroup != -1) {
  477. if (!isset($subGroupAdded[$subGroup])) {
  478. $subGroupAdded[$subGroup] = 1;
  479. } else {
  480. $subGroupAdded[$subGroup]++;
  481. }
  482. }
  483. foreach ($vertexList as $vertex) {
  484. if (is_null($vertex)) {
  485. $graphHtml .= '<div class="career_empty" style="height: 130px">';
  486. $graphHtml .= '</div>';
  487. continue;
  488. }
  489. $id = $vertex->getId();
  490. $rowId = "row_$row";
  491. $graphHtml .= '<div id = "row_'.$id.'" class="'.$rowId.' career_row" >';
  492. $color = '';
  493. if (!empty($vertex->getAttribute('DefinedColor'))) {
  494. $color = $vertex->getAttribute('DefinedColor');
  495. }
  496. $content = $vertex->getAttribute('Notes');
  497. $content .= '<div class="pull-right">['.$id.']</div>';
  498. $graphHtml .= Display::panel(
  499. $content,
  500. $vertex->getAttribute('graphviz.label'),
  501. null,
  502. null,
  503. null,
  504. null,
  505. $color
  506. );
  507. $graphHtml .= '</div>';
  508. $arrow = $vertex->getAttribute('DrawArrowFrom');
  509. $found = false;
  510. if (!empty($arrow)) {
  511. $pos = strpos($arrow, 'SG');
  512. if ($pos === false) {
  513. $pos = strpos($arrow, 'G');
  514. if (is_numeric($pos)) {
  515. $parts = explode('G', $arrow);
  516. if (empty($parts[0]) && count($parts) == 2) {
  517. $groupArrow = $parts[1];
  518. $graphHtml .= self::createConnection(
  519. "group_$groupArrow",
  520. "row_$id",
  521. ['Left', 'Right']
  522. );
  523. $found = true;
  524. }
  525. }
  526. } else {
  527. $parts = explode('SG', $arrow);
  528. if (empty($parts[0]) && count($parts) == 2) {
  529. $subGroupArrow = $parts[1];
  530. /*var_dump($subGroupArrow);
  531. var_dump(array_keys($subGroupList));*/
  532. $graphHtml .= self::createConnection(
  533. "subgroup_$subGroupArrow",
  534. "row_$id",
  535. ['Left', 'Right']
  536. );
  537. $found = true;
  538. }
  539. }
  540. }
  541. if ($found == false) {
  542. $defaultArrow = ['Left', 'Right'];
  543. if (isset($groupCourseList[$group]) &&
  544. in_array($arrow, $groupCourseList[$group])
  545. ) {
  546. $defaultArrow = ['Top', 'Bottom'];
  547. }
  548. $graphHtml .= self::createConnection(
  549. "row_$arrow",
  550. "row_$id",
  551. $defaultArrow
  552. );
  553. }
  554. }
  555. }
  556. }
  557. $graphHtml .= '</div>';
  558. }
  559. $graphHtml .= '</div>';
  560. }
  561. $graphHtml .= '</div>';
  562. return $graphHtml;
  563. }
  564. /**
  565. * @param string $source
  566. * @param string $target
  567. * @param array $anchor
  568. * @return string
  569. */
  570. public static function createConnection($source, $target, $anchor = [])
  571. {
  572. if (empty($anchor)) {
  573. // Default
  574. $anchor = ['Bottom', 'Right'];
  575. }
  576. $anchor = implode('","', $anchor);
  577. $html = '<script>
  578. var connectorPaintStyle = {
  579. strokeWidth: 2,
  580. stroke: "#a31ed3",
  581. joinstyle: "round",
  582. outlineStroke: "white",
  583. outlineWidth: 2
  584. },
  585. // .. and this is the hover style.
  586. connectorHoverStyle = {
  587. strokeWidth: 3,
  588. stroke: "#216477",
  589. outlineWidth: 5,
  590. outlineStroke: "white"
  591. },
  592. endpointHoverStyle = {
  593. fill: "#E80CAF",
  594. stroke: "#E80CAF"
  595. };
  596. jsPlumb.ready(function() { ';
  597. $html .= 'jsPlumb.connect({
  598. source:"'.$source.'",
  599. target:"'.$target.'",
  600. endpoint:[ "Rectangle", { width:1, height:1 }],
  601. connector: ["Flowchart"],
  602. paintStyle: connectorPaintStyle,
  603. hoverPaintStyle: endpointHoverStyle,
  604. anchor: ["'.$anchor.'"],
  605. overlays: [
  606. [
  607. "Arrow",
  608. {
  609. location:1,
  610. width:11,
  611. length:11
  612. }
  613. ],
  614. ],
  615. });';
  616. $html .= '});</script>'.PHP_EOL;
  617. return $html;
  618. }
  619. }