diff --git a/module/ai/config.php b/module/ai/config.php index 26aa2262f1..3e856b46ba 100644 --- a/module/ai/config.php +++ b/module/ai/config.php @@ -193,9 +193,8 @@ $config->ai->menuPrint = new stdclass(); $config->ai->menuPrint->locations = array(); $config->ai->menuPrint->locations['story']['view'] = (object)array( 'module' => 'story', - 'targetContainer' => '#mainContent .main-col .cell:first-of-type .detail:first-of-type .detail-title', - 'class' => 'pull-right', - 'stylesheet' => '#mainContent .cell:first-of-type .detail:first-of-type .detail-title>button {margin-left: 10px;} #mainContent .cell:first-of-type .detail:first-of-type .detail-content {margin-top: 12px;}' + 'targetContainer' => '#mainContent .detail-body .section:first-of-type > div:first-of-type', + 'stylesheet' => '#mainContent .detail-body .section:first-of-type > div:first-of-type {width: 100%; justify-content: space-between;}' ); $config->ai->menuPrint->locations['task']['view'] = clone $config->ai->menuPrint->locations['story']['view']; $config->ai->menuPrint->locations['task']['view']->module = 'task'; @@ -206,7 +205,7 @@ $config->ai->menuPrint->locations['bug']['view']->module = 'bug'; $config->ai->menuPrint->locations['projectstory']['view'] = clone $config->ai->menuPrint->locations['story']['view']; $config->ai->menuPrint->locations['execution']['storyView'] = $config->ai->menuPrint->locations['story']['view']; -$config->ai->menuPrint->locations['execution']['view'] = (object)array( +$config->ai->menuPrint->locations['execution']['view'] = (object)array( // TODO: fix this. 'module' => 'execution', 'injectMethod' => 'prepend', 'targetContainer' => '#mainContent.main-row > .col-4.side-col .detail:first-child > .detail-title', @@ -218,7 +217,7 @@ $config->ai->menuPrint->locations['project']['view']->module = 'project'; $config->ai->menuPrint->locations['project']['view'] = clone $config->ai->menuPrint->locations['execution']['view']; $config->ai->menuPrint->locations['project']['view']->module = 'project'; -$config->ai->menuPrint->locations['product']['view'] = (object)array( +$config->ai->menuPrint->locations['product']['view'] = (object)array( // TODO: fix this. 'module' => 'product', 'injectMethod' => 'append', 'targetContainer' => '#mainContent.main-row > .col-8.main-col .detail:first-child > .detail-title', @@ -227,7 +226,8 @@ $config->ai->menuPrint->locations['product']['view'] = (object)array( $config->ai->menuPrint->locations['productplan']['view'] = (object)array( 'module' => 'productplan', - 'targetContainer' => '#mainMenu>.btn-toolbar.pull-right', + 'injectMethod' => 'prepend', + 'targetContainer' => '#mainContent .toolbar', 'objectVarName' => 'plan' ); $config->ai->menuPrint->locations['projectplan']['view'] = $config->ai->menuPrint->locations['productplan']['view']; @@ -239,11 +239,10 @@ $config->ai->menuPrint->locations['projectrelease']['view']->objectVarName = nul $config->ai->menuPrint->locations['doc']['view'] = (object)array( 'module' => 'doc', - 'injectMethod' => 'prepend', - 'targetContainer' => '#mainMenu>.btn-toolbar.pull-right', + 'targetContainer' => '#docPanel > .panel-heading > .panel-actions > .toolbar', ); -$config->ai->injectAuditButton = new stdclass(); +$config->ai->injectAuditButton = new stdclass(); // TODO: fix this. $config->ai->injectAuditButton->locations = array(); $config->ai->injectAuditButton->locations['task']['edit'] = array( 'toolbar' => (object)array( diff --git a/module/ai/control.php b/module/ai/control.php index 343466785e..8d0c301f6c 100644 --- a/module/ai/control.php +++ b/module/ai/control.php @@ -789,8 +789,9 @@ class ai extends control if($prompt->status == 'draft') $_SESSION['auditPrompt']['time'] = time(); - $promptViewLink = $this->inlink('promptview', "promptID=$promptId"); - echo js::start() . "const currentApp = parent.$.apps.getLastApp().code; parent.$.apps.open('$location'); if(currentApp == 'admin') {location.href = '$promptViewLink';} else if(currentApp != parent.$.apps.getLastApp().code) {parent.$.apps.close(currentApp);}" . js::end(); + $this->view->formLocation = $location; + $this->view->promptViewLink = $this->inlink('promptview', "promptID=$promptId"); + $this->display(); } /** diff --git a/module/ai/model.php b/module/ai/model.php index a7b2c64528..e6fcb79449 100644 --- a/module/ai/model.php +++ b/module/ai/model.php @@ -2019,7 +2019,8 @@ class aiModel extends model $schema = $this->getFunctionCallSchema($prompt->targetForm); if(empty($schema)) return -5; - $response = $this->{$this->config->ai->models[$this->modelConfig->type] == 'ernie' ? 'converseTwiceForJSON' : 'converseForJSON'}($prompt->model , array((object)array('role' => 'user', 'content' => $wholePrompt)), $schema); + $this->useLanguageModel($prompt->model); + $response = $this->{$this->config->ai->models[$this->modelConfig->type] == 'ernie' ? 'converseTwiceForJSON' : 'converseForJSON'}($prompt->model, array((object)array('role' => 'user', 'content' => $wholePrompt)), $schema); if(empty($response)) return -6; return current($response); diff --git a/module/ai/ui/inputinject.html.php b/module/ai/ui/inputinject.html.php new file mode 100644 index 0000000000..03d344d945 --- /dev/null +++ b/module/ai/ui/inputinject.html.php @@ -0,0 +1,148 @@ + + * @package ai + * @link https://www.zentao.net + */ + +namespace zin; + +$inputInject = function() +{ + $this->app->loadConfig('ai'); + $this->app->loadLang('ai'); + $module = $this->app->getModuleName(); + $method = $this->app->getMethodName(); + if(!isset($this->config->ai->availableForms[$module]) || !in_array($method, $this->config->ai->availableForms[$module])) return; + + if(isset($_SESSION['aiInjectData']) && isset($_SESSION['aiInjectData'][$module]) && isset($_SESSION['aiInjectData'][$module][$method])) $injectData = $_SESSION['aiInjectData'][$module][$method]; + if(empty($injectData)) return; + + jsVar('window.injectData', $injectData); + unset($_SESSION['aiInjectData'][$module]); + + $this->app->loadLang('ai'); + + js(<<< JAVASCRIPT + window.injectToInputElement = (inputName, data, index) => + { + if(typeof index !== 'undefined') inputName = inputName + '[' + index + ']'; + + const inputEl = $('[name="' + inputName + '"]'); + if(!inputEl.length) return; + + const inputType = inputEl.prop('nodeName'); + switch(inputType) // Contains case fallthroughs, on purpose. + { + case 'TEXTAREA': + /* Textareas might be controlled by KindEditors. */ + if(typeof KindEditor !== 'undefined' && KindEditor.instances.length) + { + const editorInstance = KindEditor.instances.find(e => e.srcElement.attr('name') == inputName); + if(editorInstance) + { + if(data === null) data = ''; + + /* Set editor content and sync with textarea. */ + editorInstance.html(data); + editorInstance.sync(); + break; + } + } + default: + /* For normal inputs, just set the value is enough. */ + inputEl.val(data); + break; + } + } + window.inject = (obj) => + { + for(const key in obj) + { + window.dealWithSpecialInputType(key, obj[key]); + if(Array.isArray(obj[key])) + { + const isStartWith0 = $('[name="' + key + '[0]' + '"]').length; + const arr = obj[key]; + for(let i = 0; i < arr.length; i++) + { + for(const key in arr[i]) + { + window.injectToInputElement(key, arr[i][key], isStartWith0 ? i : i + 1); + } + } + } + else if(typeof obj[key] === 'Object') + { + inject(obj[key]); + } + else + { + window.injectToInputElement(key, obj[key]) + } + } + } + + window.dealWithSpecialInputType = (key, value) => + { + if(key === 'steps') + { + const steps = document.getElementById('steps'); + if(!steps) return; + if(value.length > steps.children.length) + { + const gap = value.length - steps.children.length; + for(let i = 0; i < gap; i++) + { + const addButton = document.querySelector('#steps > tr:last-child > .step-actions button:first-child'); + if(!addButton) return; + addButton.click(); + } + } + } + } + + requestAnimationFrame(() => + { + try + { + const data = JSON.parse(window.injectData); + if(!data) return; + + window.inject(data); + + zui.Messager.show({content: '{$this->lang->ai->dataInject->success}', type: 'success'}); + } + catch(e) + { + zui.Messager.show({content:'{$this->lang->ai->dataInject->fail}', type: 'danger'}); + console.error(e); + } + finally + { + /* Set injected in oreder to cancel loading class on object view (see ./promptmenu.html.php). */ + sessionStorage.setItem('ai-prompt-data-injected', true); + + const container = window.frameElement?.closest('.load-indicator'); + if(container && container.dataset.loading) + { + sessionStorage.removeItem('ai-prompt-data-injected'); + delete container.dataset.loading; + + container.classList.remove('loading'); + container.classList.remove('no-delay'); + } + } + }); + JAVASCRIPT); + + /* TODO: handle prompt audit, see original ../view/inputinject.html.php. */ +}; +$inputInject(); diff --git a/module/ai/ui/promptexecute.html.php b/module/ai/ui/promptexecute.html.php new file mode 100644 index 0000000000..829ca2929a --- /dev/null +++ b/module/ai/ui/promptexecute.html.php @@ -0,0 +1,6 @@ + setTimeout(() => openUrl(`{$formLocation}`), 1000));"); diff --git a/module/ai/ui/promptmenu.html.php b/module/ai/ui/promptmenu.html.php new file mode 100644 index 0000000000..ac8ee0c2f7 --- /dev/null +++ b/module/ai/ui/promptmenu.html.php @@ -0,0 +1,95 @@ + + * @package ai + * @link https://www.zentao.net + */ + +namespace zin; + +$promptMenuInject = function() +{ + $this->loadModel('ai'); + if(!$this->ai->hasModelsAvailable() || !commonModel::hasPriv('ai', 'promptExecute')) return; + + $this->app->loadConfig('ai'); + $module = $this->app->getModuleName(); + $method = $this->app->getMethodName(); + if(!isset($this->config->ai->menuPrint->locations[$module][$method])) return; + + $menuOptions = $this->config->ai->menuPrint->locations[$module][$method]; + $prompts = $this->ai->getPromptsForUser($menuOptions->module); + $prompts = $this->ai->filterPromptsForExecution($prompts, true); + if(empty($prompts)) return; + + $html = ''; + $objectVarName = empty($menuOptions->objectVarName) ? $menuOptions->module : $menuOptions->objectVarName; + $currentObjectId = !empty($this->view->$objectVarName) ? $this->view->$objectVarName->id : 0; + if(count($prompts) > 1) + { + $html .= ''; + } + else + { + $prompt = current($prompts); + $html .= html::linkButton($prompt->name . ($prompt->status != 'active' ? '' . $this->lang->ai->prompts->statuses[$prompt->status] . '' : ''), helper::createLink('ai', 'promptExecute', "promptId=$prompt->id&objectId=$currentObjectId"), 'self', (empty($prompt->unauthorized) ? '' : 'disabled') . (empty($prompt->desc) ? '' : " data-toggle='popover' data-container='body' data-trigger='hover' data-content='$prompt->desc' data-title='$prompt->name' data-placement='bottom'"), 'prompt btn ghost size-sm font-medium' . ((isset($menuOptions->class) ? ' ' . $menuOptions->class : '') . (isset($menuOptions->buttonClass) ? ' ' . $menuOptions->buttonClass : ''))); + } + + /* Assemble injector script. */ + $script = <<< JAVASCRIPT + if(window.location.search.includes('onlybody')) return; + const container = window.frameElement?.closest('.load-indicator'); + if(container && container.dataset.loading) + { + delete container.dataset.loading; + container.classList.remove('loading'); + container.classList.remove('no-delay'); + } + JAVASCRIPT; + $script .= "$(`$menuOptions->targetContainer`)." . (!empty($menuOptions->injectMethod) ? $menuOptions->injectMethod : 'append') . "(`$html`);\n"; + $script .= <<< JAVASCRIPT + $('[data-toggle="popover"]').popover({template: '

'}); + JAVASCRIPT; + $script .= count($prompts) > 1 ? "$('.prompts.dropdown .dropdown-menu').on('click', 'a', e =>" : "$('.prompt').on('click', e =>"; + $script .= <<< JAVASCRIPT + { + if(!container) return; + container.dataset.loading = e.target.querySelector('.label') ? '{$this->lang->ai->execute->auditing}' : '{$this->lang->ai->execute->loading}'; + container.classList.add('loading'); + container.classList.add('no-delay'); + + /* Checks for session storage to cancel loading status (see inputinject.html.php). */ + sessionStorage.removeItem('ai-prompt-data-injected'); + const loadCheckInterval = setInterval(() => + { + if(sessionStorage.getItem('ai-prompt-data-injected')) + { + if(container && container.dataset.loading) + { + delete container.dataset.loading; + container.classList.remove('loading'); + container.classList.remove('no-delay'); + } + + sessionStorage.removeItem('ai-prompt-data-injected'); + clearInterval(loadCheckInterval); + } + }, 200); + }); + JAVASCRIPT; + + /* Perform injection. */ + if(isset($menuOptions->stylesheet)) h('style', $menuOptions->stylesheet); + js($script); +}; +$promptMenuInject(); diff --git a/module/bug/ui/create.html.php b/module/bug/ui/create.html.php index 34c3e09aac..10bad539f3 100644 --- a/module/bug/ui/create.html.php +++ b/module/bug/ui/create.html.php @@ -11,6 +11,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + $fields = useFields('bug.create'); if(!empty($executionType) && $executionType == 'kanban') $fields->merge('bug.kanban'); diff --git a/module/bug/ui/edit.html.php b/module/bug/ui/edit.html.php index ae5d8478bb..713c0afb4a 100644 --- a/module/bug/ui/edit.html.php +++ b/module/bug/ui/edit.html.php @@ -11,6 +11,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + jsVar('bug', $bug); jsVar('confirmChangeProduct', $lang->bug->notice->confirmChangeProduct); jsVar('moduleID', $bug->module); diff --git a/module/bug/ui/view.html.php b/module/bug/ui/view.html.php index 50102f6807..8b5b6a4a7e 100644 --- a/module/bug/ui/view.html.php +++ b/module/bug/ui/view.html.php @@ -10,6 +10,9 @@ declare(strict_types=1); * @link https://www.zentao.net */ namespace zin; + +include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); + jsVar('bugID', $bug->id); jsVar('productID', $bug->product); jsVar('branchID', $bug->branch); diff --git a/module/doc/ui/view.html.php b/module/doc/ui/view.html.php index d87d9fd786..a31610a37c 100644 --- a/module/doc/ui/view.html.php +++ b/module/doc/ui/view.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); + featureBar ( li(backBtn(setClass('ghost'), set::icon('back'), $lang->goback)), diff --git a/module/execution/ui/view.html.php b/module/execution/ui/view.html.php index 13259c4a6f..d490c97039 100644 --- a/module/execution/ui/view.html.php +++ b/module/execution/ui/view.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); + $progress = ($execution->totalConsumed + $execution->totalLeft) ? floor($execution->totalConsumed / ($execution->totalConsumed + $execution->totalLeft) * 1000) / 1000 * 100 : 0; $isKanban = isset($execution->type) && $execution->type == 'kanban'; $chartURL = createLink('execution', $isKanban ? 'ajaxGetCFD' : 'ajaxGetBurn', "executionID={$execution->id}"); diff --git a/module/product/ui/view.html.php b/module/product/ui/view.html.php index b41035d013..a016413685 100644 --- a/module/product/ui/view.html.php +++ b/module/product/ui/view.html.php @@ -12,6 +12,8 @@ declare(strict_types=1); namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); + /* Flag variable for hiding product code. */ $hiddenCode = (!isset($config->setCode) || $config->setCode == 0 || empty($product->code)); $allStoryCount = array_sum($product->stories); diff --git a/module/productplan/ui/create.html.php b/module/productplan/ui/create.html.php index ba0692fb79..b3a7111d39 100644 --- a/module/productplan/ui/create.html.php +++ b/module/productplan/ui/create.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + jsVar('weekend', $config->execution->weekend); jsVar('productID', $product->id); jsVar('lastLang', $lang->productplan->last); diff --git a/module/productplan/ui/edit.html.php b/module/productplan/ui/edit.html.php index 3a20b3fbbf..1230df4393 100644 --- a/module/productplan/ui/edit.html.php +++ b/module/productplan/ui/edit.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + jsVar('today', helper::today()); jsVar('weekend', $config->execution->weekend); jsVar('productID', $productID); diff --git a/module/productplan/ui/view.html.php b/module/productplan/ui/view.html.php index f3cab39869..1800abf0dc 100644 --- a/module/productplan/ui/view.html.php +++ b/module/productplan/ui/view.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); + $unlinkURL['story'] = helper::createLink('productplan', 'unlinkStory', "storyID=%s&planID={$plan->id}"); $unlinkURL['bug'] = helper::createLink('productplan', 'unlinkBug', "bugID=%s&planID={$plan->id}"); diff --git a/module/programplan/ui/create.html.php b/module/programplan/ui/create.html.php index 5ce2adaf9c..f76822f074 100644 --- a/module/programplan/ui/create.html.php +++ b/module/programplan/ui/create.html.php @@ -12,6 +12,8 @@ declare(strict_types=1); namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + $fields = $this->config->programplan->form->create; /* Generate title that is tailored to specific situation. */ diff --git a/module/project/ui/view.html.php b/module/project/ui/view.html.php index e5b8069301..a150eb9687 100644 --- a/module/project/ui/view.html.php +++ b/module/project/ui/view.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); + jsVar('confirmDeleteTip', $lang->project->confirmDelete); $programDom = null; diff --git a/module/release/ui/view.html.php b/module/release/ui/view.html.php index 0d06daa163..82b0c980a3 100644 --- a/module/release/ui/view.html.php +++ b/module/release/ui/view.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); + $releaseModule = $app->rawModule == 'projectrelease' ? 'projectrelease' : 'release'; jsVar('initLink', $link); diff --git a/module/story/ui/batchcreate.html.php b/module/story/ui/batchcreate.html.php index 054a481106..081c8e0b45 100644 --- a/module/story/ui/batchcreate.html.php +++ b/module/story/ui/batchcreate.html.php @@ -12,6 +12,8 @@ declare(strict_types=1); namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + data('activeMenuID', $type); data('storyType', $type); jsVar('storyType', $type); diff --git a/module/story/ui/change.html.php b/module/story/ui/change.html.php index 1b26e425d2..6f5c564d5d 100644 --- a/module/story/ui/change.html.php +++ b/module/story/ui/change.html.php @@ -9,6 +9,8 @@ declare(strict_types=1); * @link https://www.zentao.net */ namespace zin; + +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); include './affected.html.php'; data('activeMenuID', $story->type); diff --git a/module/story/ui/create.html.php b/module/story/ui/create.html.php index 6c0e11c4aa..f4bb636dca 100644 --- a/module/story/ui/create.html.php +++ b/module/story/ui/create.html.php @@ -11,6 +11,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + data('storyType', $type); data('activeMenuID', $type); diff --git a/module/story/ui/view.html.php b/module/story/ui/view.html.php index 7baca229d2..8808ec0046 100644 --- a/module/story/ui/view.html.php +++ b/module/story/ui/view.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); + $confirmDelete = $this->lang->story->confirmDelete; if($story->type == 'requirement') { diff --git a/module/task/ui/create.html.php b/module/task/ui/create.html.php index 5db3491182..1dd6d7e8aa 100644 --- a/module/task/ui/create.html.php +++ b/module/task/ui/create.html.php @@ -12,6 +12,8 @@ declare(strict_types=1); namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + jsVar('window.executionID', $execution->id); jsVar('vision', $config->vision); jsVar('window.lifetime', $execution->lifetime); diff --git a/module/task/ui/edit.html.php b/module/task/ui/edit.html.php index 0f3ea752fd..0fc98acee5 100644 --- a/module/task/ui/edit.html.php +++ b/module/task/ui/edit.html.php @@ -12,6 +12,7 @@ declare(strict_types=1); namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); include './taskteam.html.php'; /* ====== Preparing and processing page data ====== */ diff --git a/module/task/ui/view.html.php b/module/task/ui/view.html.php index 4082394bf7..4c4a9cde8d 100644 --- a/module/task/ui/view.html.php +++ b/module/task/ui/view.html.php @@ -9,6 +9,9 @@ declare(strict_types=1); * @link https://www.zentao.net */ namespace zin; + +include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); + global $lang; /* Join mailto. */ diff --git a/module/testcase/ui/create.html.php b/module/testcase/ui/create.html.php index 77478cb382..b74999424c 100644 --- a/module/testcase/ui/create.html.php +++ b/module/testcase/ui/create.html.php @@ -10,6 +10,9 @@ declare(strict_types=1); * @link https://www.zentao.net */ namespace zin; + +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + jsVar('tab', $this->app->tab); if($app->tab == 'execution') jsVar('objectID', $executionID); if($app->tab == 'project') jsVar('objectID', $projectID); diff --git a/module/testcase/ui/edit.html.php b/module/testcase/ui/edit.html.php index 5a2a7e0714..75ada73f1f 100644 --- a/module/testcase/ui/edit.html.php +++ b/module/testcase/ui/edit.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + jsVar('tab', $this->app->tab); jsVar('isLibCase', $isLibCase); jsVar('caseBranch', $case->branch); diff --git a/module/testcase/ui/view.html.php b/module/testcase/ui/view.html.php index 598b80ab77..71b3a5444a 100644 --- a/module/testcase/ui/view.html.php +++ b/module/testcase/ui/view.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/promptmenu.html.php'); + $canCreateCase = hasPriv('testcase', 'create'); jsVar('viewParams', "caseID={$case->id}&version={$version}&from={$from}&taskID={$taskID}&stepsType="); diff --git a/module/testreport/ui/browse.html.php b/module/testreport/ui/browse.html.php index 3d849a444c..b38c638e0b 100644 --- a/module/testreport/ui/browse.html.php +++ b/module/testreport/ui/browse.html.php @@ -10,6 +10,8 @@ declare(strict_types=1); */ namespace zin; +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + foreach($reports as $report) { $taskName = ''; @@ -63,4 +65,3 @@ dtable ); render(); - diff --git a/module/testreport/ui/create.html.php b/module/testreport/ui/create.html.php index 6f5633572c..763c25a645 100644 --- a/module/testreport/ui/create.html.php +++ b/module/testreport/ui/create.html.php @@ -9,6 +9,9 @@ declare(strict_types=1); * @link https://www.zentao.net */ namespace zin; + +include($this->app->getModuleRoot() . 'ai/ui/inputinject.html.php'); + jsVar('goalTip', $lang->testreport->goalTip); jsVar('foundBugTip', $lang->testreport->foundBugTip); jsVar('legacyBugTip', $lang->testreport->legacyBugTip);