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);