PageRenderTime 70ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/Services/DocumentTemplates/DocumentTemplateService.php

https://bitbucket.org/openemr/openemr
PHP | 909 lines | 662 code | 57 blank | 190 comment | 102 complexity | 9094bb0180a277d913aa7085e84962cc MD5 | raw file
Possible License(s): Apache-2.0, AGPL-1.0, GPL-2.0, LGPL-3.0, BSD-3-Clause, Unlicense, MPL-2.0, GPL-3.0, LGPL-2.1
  1. <?php
  2. /**
  3. * Service for handling Document templates
  4. *
  5. * @package OpenEMR
  6. * @link https://www.open-emr.org
  7. * @author Jerry Padgett <sjpadgett@gmail.com>
  8. * @copyright Copyright (c) 2021 Jerry Padgett <sjpadgett@gmail.com>
  9. * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
  10. */
  11. namespace OpenEMR\Services\DocumentTemplates;
  12. use Exception;
  13. use RuntimeException;
  14. /**
  15. *
  16. */
  17. class DocumentTemplateService
  18. {
  19. public function __construct()
  20. {
  21. }
  22. public function uniqueByKey($source, $key): array
  23. {
  24. $i = 0;
  25. $rtn_array = array();
  26. $key_array = array();
  27. foreach ($source as $val) {
  28. if (!in_array($val[$key], $key_array)) {
  29. $key_array[$i] = $val[$key];
  30. $rtn_array[$i] = $val;
  31. }
  32. $i++;
  33. }
  34. return $rtn_array;
  35. }
  36. /**
  37. * Resolve all templates for portal.
  38. * Also called from getTemplateCategoriesByPids() transaction.
  39. *
  40. * @param int $pid
  41. * @param string $category
  42. * @param false $is_portal
  43. * @return array
  44. */
  45. public function getPortalAssignedTemplates($pid = 0, $category = '', $is_portal = false): array
  46. {
  47. // change at your peril! Seriously be careful, lots going on here.
  48. $bind = array();
  49. $pid_where = '';
  50. $cat_where = '';
  51. $cat_where_add = '';
  52. if (empty($pid)) {
  53. $pid_where = 'pid > ?';
  54. $bind = array(0);
  55. } else {
  56. $pid_where = 'pid = ?';
  57. $bind = array($pid);
  58. }
  59. if (!empty($category)) {
  60. $cat_where = 'Where `category` = ?';
  61. $cat_where_add = 'And `category` = ?';
  62. $bind[] = $category;
  63. }
  64. $results = [];
  65. try {
  66. $sql_patient = "SELECT `pid`, Concat_Ws(', ', `lname`, `fname`) as name FROM `patient_data` WHERE `pid` = ?";
  67. // get all assigned templates for profiles with patient groups
  68. $sql = "Select pd.pid, Concat_Ws(', ', `lname`, `fname`) as name, pd.patient_groups, tplId.profile, tplId.member_of, tplId.recurring, tplId.event_trigger, tplId.period, tplId.modified_date as profile_date, tpl.* From `patient_data` pd " .
  69. "Join `document_template_profiles` as ptd On pd.patient_groups LIKE CONCAT('%',ptd.member_of, '%') And pd.$pid_where " .
  70. "Join (Select * From `document_template_profiles`) tplId On tplId.profile = ptd.profile And ptd.active = '1' " .
  71. "Join (Select `id`, `category`, `template_name`, `location`, `template_content`, `mime`, `modified_date` From `document_templates` $cat_where) as tpl On tpl.id = tplId.template_id Group By `pid`, `category`, `template_name` Order By `lname`";
  72. $query_result = sqlStatement($sql, $bind);
  73. while ($row = sqlFetchArray($query_result)) {
  74. if (is_array($row)) {
  75. $cat = $row['category'] ?: '';
  76. if ($is_portal) {
  77. $results[$cat][] = $row;
  78. } else {
  79. $name = $row['name'];
  80. $row['template_content'] = '';
  81. $results[$name][$cat][] = $row;
  82. }
  83. }
  84. }
  85. // if no templates assigned to any groups
  86. // then proceed to get any templates sent by either profiles or other.
  87. if (!$is_portal && !empty($results)) {
  88. // we have group assigned templates.
  89. // so we get any templates directly sent to patients
  90. // then add to group resolved templates.
  91. foreach ($results as $name => $templates) {
  92. $t = array_shift($templates);
  93. $bind = array($t[0]['pid']);
  94. if (!empty($category)) {
  95. $bind[] = $category;
  96. }
  97. $sql = "SELECT * FROM `document_templates` WHERE `pid` = ? $cat_where_add ORDER BY pid, category";
  98. $query_result = sqlStatement($sql, $bind);
  99. while ($row = sqlFetchArray($query_result)) {
  100. if (is_array($row)) {
  101. $cat = $row['category'] ?: '';
  102. $row['template_content'] = '';
  103. $results[$name][$cat][] = $row;
  104. }
  105. }
  106. }
  107. } else {
  108. // Because we don't have groups then get any templates directly
  109. // sent to patients and/or same for portal
  110. if (!$is_portal) {
  111. $sql = "SELECT * FROM `document_templates` WHERE ($pid_where) $cat_where_add ORDER BY pid, category";
  112. } else {
  113. $sql = "SELECT * FROM `document_templates` WHERE (`pid` = '0' Or $pid_where) $cat_where_add ORDER BY pid, category";
  114. }
  115. $query_result = sqlStatement($sql, $bind);
  116. while ($row = sqlFetchArray($query_result)) {
  117. if (is_array($row)) {
  118. $cat = $row['category'] ?: '';
  119. $name = '';
  120. if (!empty($row['pid'] && !$is_portal)) {
  121. $name = sqlQuery($sql_patient, array($row['pid']))['name'] ?? '';
  122. }
  123. if ($is_portal) {
  124. $results[$cat][] = $row;
  125. } else {
  126. $cat = $row['category'] ?: '';
  127. $row['template_content'] = '';
  128. $results[$name][$cat][] = $row;
  129. }
  130. }
  131. }
  132. }
  133. } catch (Exception $e) {
  134. throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
  135. }
  136. return $results;
  137. }
  138. /**
  139. * @param $pids
  140. * @param null $category
  141. * @return array
  142. */
  143. public function getTemplateCategoriesByPids($pids, $category = null): array
  144. {
  145. $results = array();
  146. if (empty($pids)) {
  147. return $results;
  148. }
  149. foreach ($pids as $pid) {
  150. if ($pid <= 0) {
  151. continue;
  152. }
  153. $result = $this->getPortalAssignedTemplates($pid, $category);
  154. if (empty($result)) {
  155. continue;
  156. }
  157. $results = array_merge_recursive($results, $result);
  158. }
  159. return $results;
  160. }
  161. /**
  162. * @return array
  163. */
  164. public function fetchAllProfileEvents(): array
  165. {
  166. $result = [];
  167. $events = sqlStatement("SELECT `profile`, `recurring`, `event_trigger`, `period` FROM `document_template_profiles` WHERE `template_id` > '0' GROUP BY `profile`");
  168. foreach ($events as $event) {
  169. $result[$event['profile']] = $event;
  170. }
  171. return $result;
  172. }
  173. /**
  174. * @return array
  175. */
  176. public function fetchProfileEvents($profile): array
  177. {
  178. return sqlQuery("SELECT `profile`, `recurring`, `event_trigger`, `period` FROM `document_template_profiles` WHERE `template_id` > '0' AND `profile` = ? GROUP BY `profile` LIMIT 1", array($profile));
  179. }
  180. /**
  181. * @param $template
  182. * @return mixed int|date
  183. */
  184. public function showTemplateFromEvent($template, $show_due_date = false)
  185. {
  186. $in_review = false;
  187. $result = sqlQuery("SELECT * FROM `onsite_documents` WHERE `pid` = ? AND `file_path` = ? ORDER BY `create_date` DESC LIMIT 1", array($template['pid'], $template['id']));
  188. if (!$result) {
  189. return true;
  190. }
  191. $in_review = $result['denial_reason'] === 'Locked' || $result['denial_reason'] === 'In Review';
  192. // must be a saved doc pending review so don't show.
  193. // patient selects the edited doc from their history.
  194. if (!$in_review) {
  195. return false;
  196. }
  197. if (!isset($template['trigger_event'])) {
  198. // these are sent templates. Not in group.
  199. if (!empty($template['profile'])) {
  200. $event = $this->fetchProfileEvents($template['profile']);
  201. $template['event_trigger'] = '';
  202. $template['recurring'] = 1;
  203. $template['period'] = 0;
  204. if (is_array($event)) {
  205. $template['event_trigger'] = $event['event_trigger'];
  206. $template['recurring'] = $event['recurring'];
  207. $template['period'] = $event['period'];
  208. }
  209. } else {
  210. return false; // in review or locked so don't show. @todo possibly delete sent template.
  211. }
  212. }
  213. if ($template['event_trigger'] === 'once') {
  214. return false;
  215. }
  216. $period = $template['period'] ?: 0;
  217. $formatted_future = date('Y-m-d', strtotime($result['create_date'] . "+$period days"));
  218. $future_date = strtotime($formatted_future);
  219. $currentDate = strtotime(date('Y-m-d'));
  220. if ($show_due_date) {
  221. if (!empty($template['recurring'])) {
  222. if ($future_date > $currentDate) {
  223. return $future_date;
  224. }
  225. return 1;
  226. } else {
  227. return 0;
  228. }
  229. }
  230. if (!empty($template['recurring'])) {
  231. if ($future_date > $currentDate) {
  232. return false;
  233. }
  234. }
  235. return true;
  236. }
  237. /**
  238. * @return array
  239. */
  240. public function getPatientsByAllGroups(): array
  241. {
  242. $results = [];
  243. // @TODO limit to portal patients?
  244. $query_result = sqlStatement(
  245. 'SELECT pid, pubpid, fname, mname, lname, DOB, patient_groups FROM patient_data WHERE patient_groups <> "" ORDER BY `lname`'
  246. );
  247. while ($row = sqlFetchArray($query_result)) {
  248. $groups = explode('|', $row['patient_groups']);
  249. foreach ($groups as $group) {
  250. $results[$group][] = $row;
  251. }
  252. }
  253. return $results;
  254. }
  255. /**
  256. * @param $profile
  257. * @return array
  258. */
  259. public function getPatientGroupsByProfile($profile): array
  260. {
  261. $rtn = sqlStatement('SELECT `profile`, `member_of`, `active` FROM `document_template_profiles` WHERE `profile` = ? AND `member_of` > ""', array($profile));
  262. $profile_list = array();
  263. while ($row = sqlFetchArray($rtn)) {
  264. $profile_list[$row['profile']][] = $row;
  265. }
  266. return $profile_list;
  267. }
  268. /**
  269. * @param $profile
  270. * @return array
  271. */
  272. public function getTemplateListByProfile($profile): array
  273. {
  274. $rtn = sqlStatement('SELECT `template_id`, `category` FROM `document_template_profiles` WHERE `profile` = ? AND `template_id` > 0', array($profile));
  275. $profile_list = array();
  276. while ($row = sqlFetchArray($rtn)) {
  277. $profile_list[$row['category']][] = $this->fetchTemplate($row['template_id']);
  278. }
  279. return $profile_list;
  280. }
  281. /**
  282. * @param $id
  283. * @param null $template_name
  284. * @return array|false|null
  285. */
  286. public function fetchTemplate($id, $template_name = null)
  287. {
  288. $return = null;
  289. if (!empty($id)) {
  290. $return = sqlQuery('SELECT * FROM `document_templates` WHERE `id` = ?', array($id));
  291. } elseif (!empty($template_name)) {
  292. $return = sqlQuery('SELECT * FROM `document_templates` WHERE `template_name` = ?', array($template_name));
  293. }
  294. return $return;
  295. }
  296. /**
  297. * @param $profile_groups
  298. * @return bool
  299. */
  300. public function savePatientGroupsByProfile($profile_groups): bool
  301. {
  302. sqlStatementNoLog('SET autocommit=0');
  303. sqlStatementNoLog('START TRANSACTION');
  304. try {
  305. sqlQuery('DELETE From `document_template_profiles` WHERE `template_id` = 0');
  306. $sql = 'INSERT INTO `document_template_profiles` (`id`, `template_id`, `profile`, `template_name`, `category`, `provider`, `modified_date`, `member_of`, `active`) VALUES (NULL, 0, ?, "", "Group", ?, current_timestamp(), ?, ?)';
  307. foreach ($profile_groups as $profile => $groups) {
  308. foreach ($groups as $group) {
  309. $rtn = sqlInsert($sql, array($profile, $_SESSION['authUserID'] ?? null, $group['group'] ?? '', $group['active']));
  310. }
  311. }
  312. } catch (Exception $e) {
  313. throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
  314. }
  315. sqlStatementNoLog('COMMIT');
  316. sqlStatementNoLog('SET autocommit=1');
  317. return $rtn ?? 0;
  318. }
  319. /**
  320. * @param $patients
  321. * @return bool
  322. */
  323. public function updateGroupsInPatients($patients): bool
  324. {
  325. sqlStatementNoLog('SET autocommit=0');
  326. sqlStatementNoLog('START TRANSACTION');
  327. try {
  328. $rtn = sqlQuery('UPDATE `patient_data` SET `patient_groups` = ? WHERE `pid` > ?', array(null, 0));
  329. foreach ($patients as $id => $groups) {
  330. $rtn = sqlQuery('UPDATE `patient_data` SET `patient_groups` = ? WHERE `pid` = ?', array($groups, $id));
  331. }
  332. } catch (Exception $e) {
  333. throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
  334. }
  335. sqlStatementNoLog('COMMIT');
  336. sqlStatementNoLog('SET autocommit=1');
  337. return !$rtn;
  338. }
  339. /**
  340. * @param false $patients_only
  341. * @return array|\string[][]
  342. */
  343. public function fetchPortalAuthUsers($patients_only = false): array
  344. {
  345. $response = sqlStatement("SELECT `pid`, `pubpid`, DOB as dob, Concat_Ws(', ', `lname`, `fname`) as ptname FROM `patient_data` WHERE `allow_patient_portal` = 'YES' ORDER BY `lname`");
  346. $result_data = [];
  347. if (!$patients_only) {
  348. $result_data = array(
  349. ['pid' => '0', 'ptname' => 'All Patients'],
  350. ['pid' => '-1', 'ptname' => 'Repository'],
  351. );
  352. }
  353. while ($row = sqlFetchArray($response)) {
  354. $result_data[] = $row;
  355. }
  356. return $result_data;
  357. }
  358. /**
  359. * @param int $pid
  360. * @return array
  361. */
  362. public function getTemplateListAllCategories($pid = 0, $exclude_sent = false): array
  363. {
  364. $results = array();
  365. $query_result = sqlStatement('SELECT * FROM `document_templates` WHERE pid = ? ORDER BY `category`', array($pid));
  366. $exclude = null;
  367. if ($exclude_sent) {
  368. $query = sqlStatement('SELECT `template_name` FROM `document_templates` WHERE pid = "0" ORDER BY `category`');
  369. while ($row = sqlFetchArray($query)) {
  370. if (is_array($row)) {
  371. $exclude[] = $row['template_name'];
  372. }
  373. }
  374. }
  375. while ($row = sqlFetchArray($query_result)) {
  376. if (is_array($row)) {
  377. if ($exclude_sent && in_array($row['template_name'], $exclude)) {
  378. continue;
  379. }
  380. $results[$row['category'] ?? ''][] = $row;
  381. }
  382. }
  383. return $results;
  384. }
  385. /**
  386. * Reserved to prevent duplicate templates across profiles. TBD.
  387. *
  388. * @return array
  389. */
  390. public function getTemplateListUnique(): array
  391. {
  392. $results = array();
  393. $sql = "SELECT * FROM `document_templates` " .
  394. "Where `id` Not In (Select `template_id` From `document_template_profiles` Where `template_id` != '0') And `pid` = '-1'";
  395. $query_result = sqlStatement($sql);
  396. while ($row = sqlFetchArray($query_result)) {
  397. if (is_array($row)) {
  398. // eliminate templates already in all patients
  399. $duh = sqlQuery("Select `id` From `document_templates` Where `pid` = '0' And `template_name` = ?", array($row['template_name']))['id'];
  400. if ($duh) {
  401. continue;
  402. }
  403. $results[$row['category'] ?? ''][] = $row;
  404. }
  405. }
  406. return $results;
  407. }
  408. /**
  409. * @param null $category
  410. * @param int $pid
  411. * @return array|null[]
  412. */
  413. public function getTemplateListByCategory($category = null, $pid = 0): array
  414. {
  415. $results = array($category => null);
  416. $query_result = sqlStatement('SELECT * FROM `document_templates` WHERE category = ? AND pid = ?', array($category, $pid));
  417. while ($row = sqlFetchArray($query_result)) {
  418. if (is_array($row)) {
  419. $results[$category][] = $row;
  420. }
  421. }
  422. return $results;
  423. }
  424. /**
  425. * @param null $pid
  426. * @param null $category
  427. * @param bool $include_content
  428. * @return array
  429. */
  430. public function getTemplatesByPatient($pid = null, $category = null, $include_content = true): array
  431. {
  432. $results = array();
  433. $bind = array();
  434. if (empty($pid)) {
  435. $where = 'WHERE pid > ?';
  436. $bind = array(0);
  437. } else {
  438. $where = 'WHERE pid = ?';
  439. $bind = array($pid);
  440. }
  441. if (!empty($category)) {
  442. $where .= ' AND category = ?';
  443. $bind[] = $category;
  444. }
  445. $sql = "SELECT * FROM `document_templates` $where ORDER BY location, category";
  446. $query_result = sqlStatement($sql, $bind);
  447. while ($row = sqlFetchArray($query_result)) {
  448. if (is_array($row)) {
  449. if (!$include_content) {
  450. $row['content'] = ''; // not needed in views.
  451. }
  452. $results[$row['location']][] = $row;
  453. }
  454. }
  455. return $results;
  456. }
  457. // can delete
  458. /**
  459. * @param null $pid
  460. * @param null $category
  461. * @return array
  462. */
  463. public function getTemplateCategoriesByPatient($pid = null, $category = null): array
  464. {
  465. $results = array();
  466. $bind = array();
  467. if (empty($pid)) {
  468. $where = 'WHERE pid > ?';
  469. $bind = array($pid ?? 0);
  470. } else {
  471. $where = 'WHERE pid = ?';
  472. $bind = array($pid);
  473. }
  474. if (!empty($category)) {
  475. $where .= ' AND category = ?';
  476. $bind[] = $category;
  477. }
  478. $sql = "SELECT * FROM `document_templates` $where ORDER BY pid, category";
  479. $query_result = sqlStatement($sql, $bind);
  480. while ($row = sqlFetchArray($query_result)) {
  481. if (is_array($row)) {
  482. $results[$row['category']][] = $row;
  483. }
  484. }
  485. return $results;
  486. }
  487. /**
  488. * @param $template_name
  489. * @param $category
  490. * @param $file
  491. * @param array $pids
  492. * @return int
  493. */
  494. public function uploadTemplate($template_name, $category, $file, $pids = []): int
  495. {
  496. $mimetype = null;
  497. if (function_exists('finfo_open')) {
  498. $finfo = finfo_open(FILEINFO_MIME_TYPE);
  499. $mimetype = finfo_file($finfo, $file);
  500. finfo_close($finfo);
  501. } elseif (function_exists('mime_content_type')) {
  502. $mimetype = mime_content_type($file);
  503. } else {
  504. if (stripos($file, '.pdf') !== false) {
  505. $mimetype = 'application/pdf';
  506. }
  507. }
  508. $content = file_get_contents($file);
  509. $id = 0;
  510. foreach ($pids as $pid) {
  511. $id = $this->insertTemplate($pid, $category, $template_name, $content, $mimetype);
  512. }
  513. return $id;
  514. }
  515. /**
  516. * @param $pid
  517. * @param $category
  518. * @param $template
  519. * @param $content
  520. * @param null $mimetype
  521. * @param null $profile
  522. * @return int
  523. */
  524. public function insertTemplate($pid, $category, $template, $content, $mimetype = null, $profile = null): int
  525. {
  526. // prevent template save if unsafe. Check for escaped and unescaped content.
  527. if (stripos($content, text('<script')) !== false || stripos($content, '<script') !== false) {
  528. throw new \RuntimeException(xlt("Template rejected. JavaScript not allowed"));
  529. }
  530. $name = null;
  531. if (!empty($pid)) {
  532. $name = sqlQuery("SELECT `pid`, Concat_Ws(', ', `lname`, `fname`) as name FROM `patient_data` WHERE `pid` = ?", array($pid))['name'] ?? '';
  533. } elseif ($pid == -1) {
  534. $name = 'Repository';
  535. }
  536. $sql = "INSERT INTO `document_templates`
  537. (`pid`, `provider`,`profile`, `category`, `template_name`, `location`, `status`, `template_content`, `size`, `mime`)
  538. VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
  539. ON DUPLICATE KEY UPDATE `pid` = ?, `provider`= ?, `template_content`= ?, `size`= ?, `modified_date` = NOW(), `mime` = ?";
  540. return sqlInsert($sql, array($pid, ($_SESSION['authUserID'] ?? null), ($profile ?: ''), $category ?: '', $template, $name, 'New', $content, strlen($content), $mimetype, $pid, ($_SESSION['authUserID'] ?? null), $content, strlen($content), $mimetype));
  541. }
  542. /**
  543. * @param $profiles
  544. * @return int
  545. */
  546. public function setProfileActiveStatus($profiles): int
  547. {
  548. sqlQuery("UPDATE `document_template_profiles` SET `active` = '0' WHERE `template_id` = 0");
  549. foreach ($profiles as $profile) {
  550. $rtn = sqlQuery("UPDATE `document_template_profiles` SET `active` = '1' WHERE `profile` = ? AND `template_id` = 0", array($profile));
  551. }
  552. return !$rtn;
  553. }
  554. /**
  555. * @param $profile
  556. * @return int
  557. */
  558. public function getProfileActiveStatus($profile)
  559. {
  560. $rtn = sqlQuery("Select `active` From `document_template_profiles` WHERE `profile` = ? And `template_id` = 0", array($profile));
  561. if ($rtn === false) {
  562. return '0';
  563. }
  564. return $rtn['active'] ?: '0';
  565. }
  566. /**
  567. * @param $profiles
  568. * @return int
  569. */
  570. public function sendProfileWithGroups($profiles): int
  571. {
  572. $result = 0;
  573. sqlStatementNoLog('SET autocommit=0');
  574. sqlStatementNoLog('START TRANSACTION');
  575. $results = [];
  576. try {
  577. foreach ($profiles as $profile) {
  578. $sql = 'Select pd.pid, ptd.profile, ptd.member_of, tpl.* From `patient_data` pd ' .
  579. "Join `document_template_profiles` as ptd On pd.patient_groups LIKE CONCAT('%',ptd.member_of, '%') And ptd.profile = ? " .
  580. 'Join (Select * From `document_template_profiles`) tplId On tplId.profile = ptd.profile ' .
  581. 'Join (Select `id`, `category`, `template_name`, `location`, `template_content`, `mime` From `document_templates`) as tpl On tpl.id = tplId.template_id';
  582. $query_result = sqlStatement($sql, array($profile));
  583. while ($row = sqlFetchArray($query_result)) {
  584. if (is_array($row)) {
  585. $tid = $row['template_name'];
  586. $result = $this->insertTemplate(
  587. $row['pid'],
  588. $row['category'],
  589. $row['template_name'],
  590. $row['template_content'],
  591. $row['mime'],
  592. $profile
  593. );
  594. //$results[$row['pid']][$row['profile']][$tid] = $row;
  595. }
  596. }
  597. }
  598. } catch (Exception $e) {
  599. throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
  600. }
  601. sqlStatementNoLog('COMMIT');
  602. sqlStatementNoLog('SET autocommit=1');
  603. return $result;
  604. }
  605. /**
  606. * @param $pids
  607. * @param $templates
  608. * @param null $category
  609. * @return int
  610. */
  611. public function sendTemplate($pids, $templates, $category = null): int
  612. {
  613. $result = 0;
  614. sqlStatementNoLog('SET autocommit=0');
  615. sqlStatementNoLog('START TRANSACTION');
  616. try {
  617. foreach ($templates as $id => $profile) {
  618. $template = $this->fetchTemplate($id);
  619. $destination_category = $template['category'];
  620. if ($destination_category === 'repository') {
  621. $destination_category = $category;
  622. }
  623. $content = $template['template_content'];
  624. $name = $template['template_name'];
  625. foreach ($pids as $pid) {
  626. $result = $this->insertTemplate($pid, $destination_category, $name, $content, $template['mime'], $profile);
  627. }
  628. }
  629. } catch (Exception $e) {
  630. throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
  631. }
  632. sqlStatementNoLog('COMMIT');
  633. sqlStatementNoLog('SET autocommit=1');
  634. return $result;
  635. }
  636. /**
  637. * @param $id
  638. * @param $content
  639. * @return array|false|null
  640. */
  641. public function updateTemplateContent($id, $content)
  642. {
  643. // prevent template save if unsafe. Check for escaped and unescaped content.
  644. if (stripos($content, text('<script')) !== false || stripos($content, '<script') !== false) {
  645. throw new \RuntimeException(xlt("Template rejected. JavaScript not allowed"));
  646. }
  647. return sqlQuery('UPDATE `document_templates` SET `template_content` = ?, modified_date = NOW() WHERE `id` = ?', array($content, $id));
  648. }
  649. /**
  650. * @param $id
  651. * @param $category
  652. * @return array|false|null
  653. */
  654. public function updateTemplateCategory($id, $category)
  655. {
  656. return sqlQuery('UPDATE `document_templates` SET `category` = ? WHERE `id` = ?', array($category, $id));
  657. }
  658. /**
  659. * @param $id
  660. * @param null $template
  661. * @return bool
  662. */
  663. public function deleteTemplate($id, $template = null): bool
  664. {
  665. $profile_delete = false;
  666. if (!empty($template)) {
  667. $profile_delete = sqlQuery('DELETE FROM `document_template_profiles` WHERE `template_id` = ?', array($id));
  668. $delete = sqlQuery('DELETE FROM `document_templates` WHERE `template_name` = ?', array($template));
  669. } else {
  670. $delete = sqlQuery('DELETE FROM `document_templates` WHERE `id` = ?', array($id));
  671. }
  672. return ($delete && $profile_delete);
  673. }
  674. /**
  675. * @param $profiles_array
  676. * @return false|int
  677. */
  678. public function saveAllProfileTemplates($profiles_array)
  679. {
  680. sqlStatementNoLog('SET autocommit=0');
  681. sqlStatementNoLog('START TRANSACTION');
  682. try {
  683. sqlQuery("DELETE FROM `document_template_profiles` WHERE `template_id` > 0");
  684. $rtn = false;
  685. foreach ($profiles_array as $profile_array) {
  686. $form_data = [];
  687. foreach ($profile_array['form'] as $form) {
  688. $form_data[$form['name']] = trim($form['value'] ?? '');
  689. }
  690. $rtn = sqlInsert(
  691. "INSERT INTO `document_template_profiles`
  692. (`template_id`, `profile`, `template_name`, `category`, `provider`, `recurring`, `event_trigger`, `period`) VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
  693. array($profile_array['id'], $profile_array['profile'],
  694. $profile_array['name'], $profile_array['category'], ($_SESSION['authUserID'] ?? null),
  695. $form_data['recurring'] ? 1 : 0, $form_data['when'], $form_data['days'])
  696. );
  697. }
  698. } catch (Exception $e) {
  699. throw new RuntimeException($e->getMessage(), $e->getCode(), $e);
  700. }
  701. sqlStatementNoLog('COMMIT');
  702. sqlStatementNoLog('SET autocommit=1');
  703. return $rtn;
  704. }
  705. /**
  706. * @return array
  707. */
  708. public function fetchDefaultGroups(): array
  709. {
  710. $rtn = sqlStatement('SELECT `option_id`, `title`, `seq` FROM `list_options` WHERE `list_id` = ? ORDER BY `seq`', array('Patient_Groupings'));
  711. $category_list = array();
  712. while ($row = sqlFetchArray($rtn)) {
  713. $group_list[$row['option_id']] = $row;
  714. }
  715. return $group_list;
  716. }
  717. /**
  718. * @return array
  719. */
  720. public function fetchDefaultCategories(): array
  721. {
  722. $rtn = sqlStatement('SELECT `option_id`, `title`, `seq` FROM `list_options` WHERE `list_id` = ? ORDER BY `seq`', array('Document_Template_Categories'));
  723. $category_list = array();
  724. $category_list[''] = array(
  725. 'option_id' => '',
  726. 'title' => '',
  727. 'seq' => '',
  728. );
  729. $category_list['General'] = array(
  730. 'option_id' => '',
  731. 'title' => '',
  732. 'seq' => '',
  733. );
  734. while ($row = sqlFetchArray($rtn)) {
  735. $category_list[$row['option_id']] = $row;
  736. }
  737. return $category_list;
  738. }
  739. /**
  740. * @return array
  741. */
  742. public function fetchDefaultProfiles(): array
  743. {
  744. $rtn = sqlStatement('SELECT `option_id`, `title`, `seq` FROM `list_options` WHERE `list_id` = ? ORDER BY `seq`', array('Document_Template_Profiles'));
  745. $profile_list = array();
  746. while ($row = sqlFetchArray($rtn)) {
  747. $profile_list[$row['option_id']] = $row;
  748. }
  749. return $profile_list;
  750. }
  751. /**
  752. * @return array
  753. */
  754. public function getFormattedCategories(): array
  755. {
  756. $rtn = sqlStatement('SELECT `option_id`, `title`, `seq` FROM `list_options` WHERE `list_id` = ? ORDER BY `seq`', array('Document_Template_Categories'));
  757. $category_list = array();
  758. while ($row = sqlFetchArray($rtn)) {
  759. $category_list[$row['option_id']] = $row;
  760. }
  761. return $category_list;
  762. }
  763. /**
  764. * @param $pid
  765. * @param $id
  766. * @return array|false|null
  767. */
  768. public function fetchTemplateStatus($pid, $name)
  769. {
  770. $sql = "SELECT `pid`, `create_date`, `doc_type`, `patient_signed_time`, `authorize_signed_time`, `patient_signed_status`, `review_date`, `denial_reason`, `file_name`, `file_path` FROM `onsite_documents` WHERE `pid` = ? AND `file_path` = ? ORDER BY `create_date` DESC LIMIT 1";
  771. return sqlQuery($sql, array($pid, $name));
  772. }
  773. /**
  774. * @param $profile
  775. * @return mixed
  776. */
  777. public function fetchProfileStatus($profile)
  778. {
  779. return sqlQuery('SELECT active FROM `document_template_profiles` WHERE `template_id` = "0" AND `profile` = ?', array($profile))['active'];
  780. }
  781. /**
  782. * @param $token
  783. * @return array
  784. */
  785. public function fetchPatientListByIssuesSearch($token): array
  786. {
  787. $result = [];
  788. $search = '%' . $token . '%';
  789. $sql = "SELECT pd.pid, pd.`pubpid`, pd.`lname`, pd.`fname`, pd.`mname`, pd.`DOB`, pd.`providerID`, l.title, l.diagnosis FROM `patient_data` pd " .
  790. "JOIN(SELECT * FROM `lists` WHERE `type` = 'medical_problem' AND (`title` LIKE ? OR `diagnosis` LIKE ?)) AS l ON l.`pid` = pd.`pid` GROUP BY pd.`pid`";
  791. $rtn = sqlStatement($sql, array($search, $search));
  792. while ($row = sqlFetchArray($rtn)) {
  793. $result[] = $row;
  794. }
  795. return $result;
  796. }
  797. /**
  798. * @param $current_patient
  799. * @param $current_user
  800. * @return string
  801. */
  802. public function renderPortalTemplateMenu($current_patient, $current_user, $dropdown = false): string
  803. {
  804. $menu = "";
  805. $category_list = $this->getFormattedCategories();
  806. $all_templates = $this->getPortalAssignedTemplates($current_patient, '', true);
  807. ksort($all_templates);
  808. foreach ($all_templates as $category => $templates) {
  809. if (is_array($templates)) {
  810. $is_category = $category_list[$category]['title'] ?? $category;
  811. if ($is_category === 'default') {
  812. $is_category = '';
  813. }
  814. $cat_name = text($is_category);
  815. $flag = false;
  816. foreach ($templates as $template) {
  817. if ((int)$template['pid'] === 0) {
  818. $template['pid'] = $current_patient;
  819. }
  820. $in_edit = sqlQuery("Select `id`, `doc_type`, `denial_reason` From `onsite_documents` Where (`denial_reason` = 'Editing' Or `denial_reason` = 'In Review') And `pid` = ? And `file_path` = ? Limit 1", array($template['pid'], $template['id'])) ?? 0;
  821. if (empty($in_edit)) {
  822. $test = $this->showTemplateFromEvent($template);
  823. if (!$test) {
  824. continue;
  825. }
  826. }
  827. if ($template['template_name'] === 'Help') {
  828. continue;
  829. }
  830. if ((int)$template['pid'] !== 0 && (int)$template['pid'] !== (int)$current_patient) {
  831. continue;
  832. }
  833. if (!$flag) {
  834. $flag = true;
  835. $menu .= "<div class='h6 text-center'>$cat_name</div>\n";
  836. }
  837. $id = $template['id'];
  838. $btnname = $template['template_name'];
  839. if (!empty($in_edit)) {
  840. $menu .= '<a class="dropdown-item template-item text-danger btn btn-link" id="' . attr($id) . '"' . ' href="#" onclick="page.editHistoryDocument(' . attr_js($in_edit['id']) . ')">' . text($btnname) . "</a>\n";
  841. } else {
  842. $menu .= '<a class="dropdown-item template-item text-success btn btn-link" id="' . attr($id) . '"' . ' href="#" onclick="page.newDocument(' . attr_js($current_patient) . ', ' . attr_js($current_user) . ', ' . attr_js($btnname) . ', ' . attr_js($id) . ')">' . text($btnname) . "</a>\n";
  843. }
  844. }
  845. if (!$flag) {
  846. $menu .= "<div class='dropdown-divider'></div>\n";
  847. }
  848. }
  849. }
  850. return $menu;
  851. }
  852. }