improved prior 8.2 fix
[openemr.git] / portal / import_template.php
blob24637da77f4427c0c626ed2d19d02a547b75aa2b
1 <?php
3 /**
4 * import_template.php
6 * @package OpenEMR
7 * @link https://www.open-emr.org
8 * @author Jerry Padgett <sjpadgett@gmail.com>
9 * @copyright Copyright (c) 2016-2021 Jerry Padgett <sjpadgett@gmail.com>
10 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
13 require_once("../interface/globals.php");
15 use OpenEMR\Core\Header;
16 use OpenEMR\Services\DocumentTemplates\DocumentTemplateService;
18 $templateService = new DocumentTemplateService();
20 $patient = json_decode($_POST['upload_pid'] ?? '');
22 $template_content = null;
24 if ($_POST['mode'] === 'save_profiles') {
25 $profiles = json_decode($_POST['profiles']);
26 $rtn = $templateService->saveAllProfileTemplates($profiles);
27 if ($rtn) {
28 echo xlt("Profiles successfully saved.");
29 } else {
30 echo xlt('Error! Profiles save failed. Check your Profile lists.');
32 exit;
35 if ($_REQUEST['mode'] === 'renderProfile') {
36 echo renderProfileHtml();
37 exit;
40 if ($_REQUEST['mode'] === 'getPdf') {
41 if ($_REQUEST['docid']) {
42 $template = $templateService->fetchTemplate($_REQUEST['docid']);
43 echo "data:application/pdf;base64," . base64_encode($template['template_content']);
44 exit();
46 die(xlt('Invalid File'));
49 if ($_POST['mode'] === 'get') {
50 if ($_REQUEST['docid']) {
51 $template = $templateService->fetchTemplate($_POST['docid']);
52 echo $template['template_content'];
53 exit();
55 die(xlt('Invalid File'));
58 if ($_POST['mode'] === 'send') {
59 if (!empty($_POST['docid'])) {
60 $pids_array = json_decode($_POST['docid']) ?: ['0'];
61 // profiles are in an array with flag to indicate a group of template id's
62 $ids = json_decode($_POST['checked']) ?: [];
63 $master_ids = [];
64 foreach ($ids as $id) {
65 if (is_array($id)) {
66 if ($id[1] !== true) {
67 continue;
69 $profile = $id[0];
70 // get all template ids for this profile
71 $rtn_ids = sqlStatement('SELECT `template_id` as id FROM `document_template_profiles` WHERE `profile` = ?', array($profile));
72 while ($rtn_id = sqlFetchArray($rtn_ids)) {
73 $master_ids[] = $rtn_id['id'];
75 continue;
77 $master_ids[] = $id;
79 $master_ids = array_unique($master_ids);
81 $last_id = $templateService->sendTemplate($pids_array, $master_ids, $_POST['category']);
82 if ($last_id) {
83 echo xlt('Templates Successfully sent to Locations.');
84 } else {
85 echo xlt('Error. Problem sending one or more templates. Some templates may not have been sent.');
87 exit;
89 die(xlt('Invalid Request'));
92 if ($_POST['mode'] === 'save') {
93 if ($_POST['docid']) {
94 if (stripos($_POST['content'], "<?php") === false) {
95 $template = $templateService->updateTemplateContent($_POST['docid'], $_POST['content']);
96 if ($_POST['service'] === 'window') {
97 echo "<script>if (typeof parent.dlgopen === 'undefined') window.close(); else parent.dlgclose();</script>";
99 } else {
100 die(xlt('Invalid Content'));
102 } else {
103 die(xlt('Invalid File'));
105 } elseif ($_POST['mode'] === 'delete') {
106 if ($_POST['docid']) {
107 $template = $templateService->deleteTemplate($_POST['docid']);
108 exit($template);
110 die(xlt('Invalid File'));
111 } elseif ($_POST['mode'] === 'update_category') {
112 if ($_POST['docid']) {
113 $template = $templateService->updateTemplateCategory($_POST['docid'], $_POST['category']);
114 echo xlt('Template Category successfully changed to new Category') . ' ' . text($_POST['category']);
115 exit;
117 die(xlt('Invalid Request Parameters'));
118 } elseif (!empty($_FILES["template_files"])) {
119 // so it is a template file import. create record(s).
120 $import_files = $_FILES["template_files"];
121 $total = count($_FILES['template_files']['name']);
122 for ($i = 0; $i < $total; $i++) {
123 if ($_FILES['template_files']['error'][$i] !== UPLOAD_ERR_OK) {
124 header('refresh:3;url= import_template_ui.php');
125 echo '<title>' . xlt('Error') . " ...</title><h4 style='color:red;'>" .
126 xlt('An error occurred: Missing file to upload. Returning to form.') . '</h4>';
127 exit;
129 // parse out what we need
130 $name = preg_replace("/[^A-Z0-9.]/i", " ", $_FILES['template_files']['name'][$i]);
131 if (preg_match("/(.*)\.(php|php7|php8|doc|docx)$/i", $name) !== 0) {
132 die(xlt('Invalid file type.'));
134 $parts = pathinfo($name);
135 $name = ucwords(strtolower($parts["filename"]));
136 if (empty($patient)) {
137 $patient = ['-1'];
139 // get em and dispose
140 $success = $templateService->uploadTemplate($name, $_POST['template_category'], $_FILES['template_files']['tmp_name'][$i], $patient);
141 if (!$success) {
142 echo "<p>" . xlt("Unable to save files. Use back button!") . "</p>";
143 exit;
146 header("location: " . $_SERVER['HTTP_REFERER']);
147 die();
150 if ($_REQUEST['mode'] === 'editor_render_html') {
151 if ($_REQUEST['docid']) {
152 $content = $templateService->fetchTemplate($_REQUEST['docid']);
153 $template_content = $content['template_content'];
154 if ($content['mime'] === 'application/pdf') {
155 $content = "<iframe width='100%' height='100%' src='data:application/pdf;base64, " .
156 attr(base64_encode($template_content)) . "'></iframe>";
157 echo $content;
158 exit;
160 renderEditorHtml($_REQUEST['docid'], $template_content);
161 } else {
162 die(xlt('Invalid File'));
164 } elseif (!empty($_GET['templateHtml'] ?? null)) {
165 renderEditorHtml($_REQUEST['docid'], $_GET['templateHtml']);
169 * @param $template_id
170 * @param $content
172 function renderEditorHtml($template_id, $content)
174 $lists = [
175 '{ParseAsHTML}', '{TextInput}', '{sizedTextInput:120px}', '{smTextInput}', '{TextBox:03x080}', '{CheckMark}', '{ynRadioGroup}', '{TrueFalseRadioGroup}', '{DatePicker}', '{DateTimePicker}', '{StandardDatePicker}', '{CurrentDate:"global"}', '{CurrentTime}', '{DOS}', '{ReferringDOC}', '{PatientID}', '{PatientName}', '{PatientSex}', '{PatientDOB}', '{PatientPhone}', '{Address}', '{City}', '{State}', '{Zip}', '{PatientSignature}', '{AdminSignature}', '{AcknowledgePdf: : }', '{EncounterForm:LBF}', '{Medications}', '{ProblemList}', '{Allergies}', '{ChiefComplaint}', '{DEM: }', '{HIS: }', '{LBF: }', '{GRP}{/GRP}'
178 <!DOCTYPE html>
179 <html>
180 <head>
181 <?php Header::setupHeader(['ckeditor']); ?>
182 </head>
183 <style>
184 input:focus,
185 input:active {
186 outline: 0 !important;
187 -webkit-appearance: none;
188 box-shadow: none !important;
191 .list-group-item {
192 font-size: .9rem;
194 </style>
195 <body>
196 <div class="container-fluid">
197 <div class="row">
198 <div class="col-10 px-1 sticky-top">
199 <form class="sticky-top" action='./import_template.php' method='post'>
200 <input type="hidden" name="docid" value="<?php echo attr($template_id) ?>">
201 <input type='hidden' name='mode' value="save_profiles">
202 <input type='hidden' name='service' value='window'>
203 <textarea cols='80' rows='10' id='templateContent' name='content'><?php echo text($content) ?></textarea>
204 <div class="row btn-group mt-1 float-right">
205 <div class='col btn-group mt-1 float-right'>
206 <button type="submit" class="btn btn-sm btn-primary"><?php echo xlt("Save"); ?></button>
207 <button type='button' class='btn btn-sm btn-secondary' onclick='parent.window.close() || parent.dlgclose()'><?php echo xlt('Cancel'); ?></button>
208 </div>
209 </div>
210 </form>
211 </div>
212 <div class="col-sm-2 px-0">
213 <div class='h4'><?php echo xlt("Directives") ?></div>
214 <ul class='list-group list-group-flush pl-1 mb-5'>
215 <?php
216 foreach ($lists as $list) {
217 echo '<input class="list-group-item p-1" value="' . attr($list) . '">';
220 </ul>
221 </div>
222 </div>
223 </div>
224 </body>
225 <script>
226 let isDialog = false;
227 let height = 550;
228 let max = 680;
229 <?php if (!empty($_REQUEST['dialog'] ?? '')) { ?>
230 isDialog = true;
231 height = 425;
232 max = 600;
233 <?php } ?>
234 let editor = '';
235 document.addEventListener('DOMContentLoaded', function () {
236 document.querySelectorAll('.list-group-item').forEach(item => {
237 item.addEventListener('mouseup', event => {
238 let input = event.currentTarget;
239 input.focus();
240 input.select();
243 editor = CKEDITOR.instances['templateContent'];
244 if (editor) {
245 editor.destroy(true);
247 CKEDITOR.disableAutoInline = true;
248 CKEDITOR.config.extraPlugins = "preview,save,docprops,justify";
249 CKEDITOR.config.allowedContent = true;
250 //CKEDITOR.config.fullPage = true;
251 CKEDITOR.config.height = height;
252 CKEDITOR.config.width = '100%';
253 CKEDITOR.config.resize_dir = 'both';
254 CKEDITOR.config.resize_minHeight = max / 2;
255 CKEDITOR.config.resize_maxHeight = max;
256 CKEDITOR.config.resize_minWidth = '50%';
257 CKEDITOR.config.resize_maxWidth = '100%';
259 editor = CKEDITOR.replace('templateContent', {
260 removeButtons: 'PasteFromWord'
263 </script>
264 </html>
265 <?php }
270 function renderProfileHtml()
272 global $templateService;
274 $category_list = $templateService->getDefaultCategories();
275 $profile_list = $templateService->getDefaultProfiles();
277 <!DOCTYPE html>
278 <html>
279 <head>
280 <?php Header::setupHeader(['opener', 'sortablejs']); ?>
281 </head>
282 <style>
283 .list-group-item .move-handle {
284 cursor: move;
286 #trashDrop {
287 border: 1px dashed #f60;
288 min-height: 100px;
289 position: relative;
291 #trashDrop::before {
292 color: #ccc;
293 font-size: 20px;
294 content: 'Remove Drop Zone';
295 display: block;
296 text-align: center;
297 padding-top: 10px;
299 </style>
300 <script>
301 const profiles = <?php echo js_escape($profile_list); ?>;
302 document.addEventListener('DOMContentLoaded', function () {
303 // init drag and drop
304 let repository = document.getElementById('dragRepository');
305 Sortable.create(repository, {
306 group: {
307 name: 'repo',
308 handle: '.move-handle',
309 pull: 'clone'
311 animation: 150,
312 onAdd: function (evt) {
313 let el = evt.item;
314 el.parentNode.removeChild(el);
317 let trashDrop = document.getElementById('trashDrop');
318 Sortable.create(trashDrop, {
319 group: {
320 name: 'trash',
321 put: 'repo'
323 animation: 150,
324 onAdd: function (evt) {
325 let el = evt.item;
326 el.parentNode.removeChild(el);
329 Object.keys(profiles).forEach(key => {
330 let profileEl = profiles[key]['option_id']
331 let id = document.getElementById(profileEl);
332 Sortable.create(id, {
333 group: {
334 name: 'repo',
335 delay: 1000,
336 handle: '.move-handle',
337 put: (to, from, dragEl, event) => {
338 for (let i = 0; i < to.el.children.length; i++) {
339 if (to.el.children[i].getAttribute('data-id') === dragEl.getAttribute('data-id')) {
340 return false
343 return true
346 animation: 150
350 function submitProfiles() {
351 top.restoreSession();
352 let target = document.getElementById('edit-profiles');
353 let profileTarget = target.querySelectorAll('ul');
354 let profileArray = [];
355 let listData = {};
356 profileTarget.forEach((ulItem, index) => {
357 let lists = ulItem.querySelectorAll('li');
358 lists.forEach((item, index) => {
359 //console.log({index, item})
360 listData = {
361 'profile': ulItem.dataset.profile,
362 'id': item.dataset.id,
363 'category': item.dataset.category,
364 'name': item.dataset.name
366 profileArray.push(listData);
370 const data = new FormData();
371 data.append('profiles', JSON.stringify(profileArray));
372 data.append('mode', 'save_profiles');
373 fetch('./import_template.php', {
374 method: 'POST',
375 body: data,
376 }).then(rtn => rtn.text()).then((rtn) => {
377 (async (time) => {
378 await asyncAlertMsg(rtn, time, 'success', 'lg');
379 })(1000).then(rtn => {
380 opener.document.edit_form.submit();
381 dlgclose();
383 }).catch((error) => {
384 console.error('Error:', error);
387 </script>
388 <body>
389 <div class='container-fluid'>
390 <?php
391 $templates = $templateService->getTemplateListAllCategories(-1);
393 <div class='row'>
394 <div class='col-6'>
395 <div class="sticky-top border-left border-right">
396 <h5 class='bg-dark text-light py-1 text-center'><?php echo xlt('Available Templates'); ?></h5>
397 <ul id='dragRepository' class='list-group mx-2 mb-2'>
398 <?php
399 foreach ($templates as $cat => $files) {
400 if (empty($cat)) {
401 $cat = xlt('General');
403 foreach ($files as $file) {
404 $template_id = attr($file['id']);
405 $title = $category_list[$cat]['title'] ?: $cat;
406 $title_esc = attr($title);
407 $this_name = attr($file['template_name']);
408 if ($file['mime'] === 'application/pdf') {
409 continue;
411 echo "<li class='list-group-item px-1 py-1 mb-2' data-id='$template_id' data-name='$this_name' data-category='$title_esc'>" .
412 '<i class="fa fa-arrows-alt move-handle mx-1"></i>' . "<strong>" . text($file['template_name']) .
413 '</strong>' . ' ' . xlt('in category') . ' ' .
414 '<strong>' . text($title) . '</strong>' . '</li>' . "\n";
418 </ul>
419 <div id='trashDrop' class='list-group'></div>
420 <div class="btn-group">
421 <button class='btn btn-primary btn-save my-2' onclick='return submitProfiles();'><?php echo xlt('Save Profiles'); ?></button>
422 <button class='btn btn-secondary btn-cancel my-2' onclick='dlgclose();'><?php echo xlt('Quit'); ?></button>
423 </div>
424 </div>
425 </div>
426 <div class='col-6'>
427 <div id="edit-profiles" class='control-group mx-1 border-left border-right'>
428 <?php
429 foreach ($profile_list as $profile => $profiles) {
430 $profile_items_list = $templateService->getProfileListByProfile($profile);
431 $profile_esc = attr($profile);
432 echo "<h5 class='bg-dark text-light py-1 text-center'>" . text($profiles['title']) . "</h5>\n";
433 echo "<ul id='$profile_esc' class='list-group mx-2 mb-2 droppable' data-profile='$profile_esc'>\n";
434 foreach ($profile_items_list as $cat => $files) {
435 if (empty($cat)) {
436 $cat = xlt('General');
438 foreach ($files as $file) {
439 $template_id = attr($file['id']);
440 $this_cat = attr($file['category']);
441 $title = $category_list[$file['category']]['title'] ?: $cat;
442 $this_name = attr($file['template_name']);
443 if ($file['mime'] === 'application/pdf') {
444 continue;
446 echo "<li class='list-group-item px-1 py-1 mb-2' data-id='$template_id' data-name='$this_name' data-category='$this_cat'>" .
447 '<i class="fa fa-arrows-alt move-handle mx-1"></i>' . text($file['template_name']) . ' ' . xlt('in category') . ' ' . text($title) . "</li>\n";
450 echo "</ul>\n";
453 </div>
454 </div>
455 </div>
456 </div>
457 <hr />
458 </body>
459 </html>
460 <?php }