jsEvents()) . ')\b/', ' v-on:', $str); $dom = new DOMDocument('1.0', 'UTF-8'); libxml_use_internal_errors(true); $dom->loadHTML("
$str"); $errors = libxml_get_errors(); foreach ($errors as $error) { // template and ui-predicate tags are expected, so ignore them... /* @var $error LibXMLError */ if ( ! in_array( $error->message, [ "Tag template invalid\n", "Tag ui-predicate invalid\n", "Tag trackerrules invalid\n", "Tag durationpickeramount invalid\n", "Tag durationpickermodal invalid\n", ] ) ) { trigger_error($error->message); } } libxml_clear_errors(); $script = $dom->getElementsByTagName('script'); $template = $dom->getElementsByTagName('template'); $style = $dom->getElementsByTagName('style'); if (! $name && $app) { $name = 'App'; } if ($script->length) { // required $javascript = $script[0]->nodeValue; preg_match('/export default {(.*)}/ms', $javascript, $match); if ($match) { $originalExport = $export = $match[1]; if ($template->length) { $templateNode = $template[0]; $export .= ', template: `' . $this->getInnerHtml($templateNode) . '`'; $javascript = str_replace($originalExport, $export, $javascript); } //$headerlib->add_js_module($javascript); // embedded modules cannot export apparently, also can't be found by import fns } global $tikidomainslash; $tempDir = './temp/public/' . $tikidomainslash; $hash = $name ? $name : md5(serialize($javascript)); $file = $tempDir . "vue_" . $hash . ".js"; if ($minify) { $minifier = new MatthiasMullie\Minify\JS($javascript); $minifier->minify($file); } else { file_put_contents($file, $javascript); } chmod($file, 0644); if ($app) { $data = json_encode($data); $headerlib->add_js_module( " import $name from \"$file\"; var vm = new Vue({ render: h => h($name), data: function () { return $data; }, }).\$mount(`#$name`); observeVueApp(vm); " ); return ""; } } return ''; } // thanks dpetroff https://www.php.net/manual/en/class.domelement.php#101243 private function getInnerHtml($node) { $innerHTML = ''; $children = $node->childNodes; foreach ($children as $child) { $innerHTML .= $child->ownerDocument->saveXML($child); } return str_replace(' ', "\r", $innerHTML); } /** * generates a predicate ui vue.js based rules gui for a tracker field * * @param array $params * * @return string * @throws Exception */ public function getFieldRules($params) { if (empty($params['fieldId'])) { Feedback::error(tr('No fieldId for Field Rules')); } // remove empties (?) $targetFields = array_values(array_filter($params['targetFields'])); foreach ($targetFields as & $field) { $this->setFieldType($field); } // remove auto-inc and other non-compatible field types $params['targetFields'] = array_values(array_filter($targetFields)); if (is_string($params['rules'])) { $params['rules'] = json_decode(html_entity_decode($params['rules'])); } if (! is_object($params['rules']) || empty($params['rules'])) { $params['rules'] = [ 'conditions' => null, 'actions' => null, 'else' => null, ]; } $params['definitiion'] = \Tracker\Rule\Definition::get(); $appHtml = $this->processVue('lib/vue/rules/TrackerRulesApp.vue', 'TrackerRulesApp', true, $params); $appHtml .= $this->processVue('lib/vue/rules/TextArgument.vue', 'TextArgument'); $appHtml .= $this->processVue('lib/vue/rules/NumberArgument.vue', 'NumberArgument'); $appHtml .= $this->processVue('lib/vue/rules/DateArgument.vue', 'DateArgument'); $appHtml .= $this->processVue('lib/vue/rules/NothingArgument.vue', 'NoArgument'); $appHtml .= $this->processVue('lib/vue/rules/BoolArgument.vue', 'BoolArgument'); $appHtml .= $this->processVue('lib/vue/rules/CollectionArgument.vue', 'CollectionArgument'); $appHtml .= $this->processVue('lib/vue/rules/TrackerRules.vue', 'TrackerRules'); return $appHtml; } public function generateTrackerRulesJS($fields, $parentSelector = '.form-group:first') { $js = ''; TikiLib::lib('header')->add_jsfile('lib/jquery_tiki/tiki-tracker-rules.js'); foreach (array_filter($fields) as $field) { if (! empty($field['rules']) && $field['rules'] !== '{"conditions":null,"actions":null,"else":null}') { $rules = Tiki\Lib\core\Tracker\Rule\Rules::fromData($field['fieldId'], $field['rules']); $js .= $rules->getJavaScript($parentSelector, $field); } } return $js; } /** * @return array */ private function jsEvents(): array { return [ 'abort', 'afterprint', 'animationcancel', 'animationend', 'animationiteration', 'animationstart', 'audioprocess', 'auxclick', 'beforeprint', 'beforeunload', 'blur', 'canplay', 'canplaythrough', 'change', 'click', 'close', 'complete', 'compositionend', 'compositionstart', 'compositionupdate', 'contextmenu', 'copy', 'cut', 'dblclick', 'drag', 'dragend', 'dragenter', 'dragleave', 'dragover', 'dragstart', 'drop', 'durationchange', 'emptied', 'ended', 'error', 'focus', 'fullscreenchange', 'fullscreenerror', 'keydown', 'keypress', 'keyup', 'load', 'load', 'loadeddata', 'loadedmetadata', 'loadend', 'loadstart', 'message', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'offline', 'online', 'open', 'pagehide', 'pageshow', 'paste', 'pause', 'play', 'playing', 'pointerlockchange', 'pointerlockerror', 'popstate', 'progress', 'ratechange', 'reset', 'resize', 'scroll', 'seeked', 'seeking', 'select', 'stalled', 'submit', 'suspend', 'timeout', 'timeupdate', 'transitioncancel', 'transitionend', 'transitionrun', 'transitionstart', 'unload', 'volumechange', 'waiting', 'wheel', // seems to be a custom vue or ui-predicate event? 'initialize', ]; } /** * @param array $field (by reference) * @param string $insPrefix */ private function setFieldType(array &$field, string $insPrefix = 'ins_'): void { global $prefs; switch ($field['type']) { case 'f': // datetime case 'j': // datepicker case 'CAL': // calendar item $field['argumentType'] = 'DateTime'; break; case 'n': // number case 'b': // currency $field['argumentType'] = 'Number'; break; case 'c': // checkbox $field['argumentType'] = 'Boolean'; break; case 'e': // Category case 'M': // Multiselect $field['argumentType'] = 'Collection'; break; case 'q': // auto increment (not used client-side) $field = []; return; default: $field['argumentType'] = 'Text'; break; } $field['ins_id'] = $insPrefix . $field['fieldId']; if ( $field['type'] === 'r' && $field['options_map']['selectMultipleValues'] // ItemLink || $field['type'] === 'w' && $field['options_map']['selectMultipleValues'] // DynamicItemsList ) { $field['argumentType'] = 'Collection'; $field['ins_id'] = $field['ins_id'] . '[]'; } if ($field['type'] === 'u' && $field['options_map']['multiple']) { // UserSelector // check if it will be using an autocomplete input $groups = array_filter($field['options_map']['groupIds']); $count = TikiLib::lib('user')->count_users_by_groupIds($groups); if ($prefs['user_selector_threshold'] > $count) { $field['argumentType'] = 'Collection'; $field['ins_id'] = $field['ins_id'] . '[]'; } } } }