[gzg@operation itop]$ cd /var/www/html/itop [gzg@operation itop]$ mkdir yh [gzg@operation itop]$ cd yh [gzg@operation itop]$ touch upticketdates.php
開發簡要說明:
字典文件位置: itop/env-production/dictionaries/zh-cn.dict.php 'Class:UserRequest/Attribute:resolutiondate' => '解決時間', 'Class:UserRequest/Attribute:resolutiondate+' => '',
iTop的底層數據支撐框架為CMDB
一個數據模型類的繼承層次為(如:UserRequest):
UserRequest < Ticket < _Ticket < cmdbAbstractObject < CMDBObject < DBObject
這些類的文件位置是:
- UserRequest => O:\itop\env-production\itop-request-mgmt-itil\model.itop-request-mgmt-itil.php
- Ticket => O:\itop\env-production\itop-tickets\model.itop-tickets.php
- _Ticket => O:\itop\env-toolkit\itop-tickets\main.itop-tickets.php
- cmdbAbstractObject => O:\itop\application\cmdbabstract.class.inc.php
- CMDBObject => O:\itop\core\cmdbobject.class.inc.php
- DBObject => O:\itop\core\dbobject.class.php
DBObject安裝注釋中的說明: "Class dbObject: the root of persistent classes" 它是“可持久化”的“數據模型類”的“根類”,而DBObject中操作數據庫依靠的的更底層的CMDBSource(itop/core/cmdbsource.class.inc.php) CMDBSource中的數據操作則依靠“mysqli”
self::$m_oMysqli = new mysqli(self::$m_sDBHost, self::$m_sDBUser, self::$m_sDBPwd);
例如其 DBUpdate() 方法中的兩行代碼: $sSQL = $oFilter->MakeUpdateQuery($aChanges); CMDBSource::Query($sSQL);
引入三個文件后,可以使用 MetaModel, 裝載更新數據記錄
require_once('../approot.inc.php');
require_once(APPROOT.'/application/application.inc.php');
require_once(APPROOT.'/application/startup.inc.php');
類MetaModel定義位於: itop/core/metamodel.class.php
靜態方法MetaModel::GetObject可以裝載指定數據類 GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null) 參數說明: $sClass:數據類名,例如表“itopticket”的類名為“Ticket”,可以使用基類裝載子類,也可以用子類直接load $iKey:數據行的id $bMustBeFound:該id的數據行是否必須存在, 默認值為false,未找到返回null,如果設置為true,未找到拋出異常;MySQLException $bAllowAllData:是否load全部字段?? $aModifierProperties:可修改字段數組?? 使用舉例: $data = MetaModel::GetObject('Ticket',1296); getclass($data); // UserRequest, 這是一條request $data->get('resolutiondate'); // 獲取解決時間 $data->Set('resolutiondate','2019-12-19 12:33:34'); // 設置解決時間 $data->DBUpdate(); // 更新數據庫
最后更新時間的修改
Ticket的三個子類 UserRequest,Problem,Incident 都覆蓋了基類的OnUpdate方法,其中主要是做了更新追蹤記錄,設置最后更新時間 因此想要覆蓋更新這個字段,只能繞開 數據模型類,幸運的是這個字段是在 基類表 Ticket中,因此可以使用CMDBSource直接修改數據庫中的字段值, 從而實現最后更新時間的修改。
CMDBSource::query("update itop_ticket set last_update='$last_update' where id = $ticket_id");
整個過程實現源碼:
<?php require_once('../approot.inc.php'); require_once(APPROOT.'/application/application.inc.php'); require_once(APPROOT.'/application/startup.inc.php'); // "http://192.168.0.105/itop/pages/UI.php?c[menu]=UserRequest:OpenRequests" $tbls = array('UserRequest'=>'itop_ticket_request','Incident'=>'itop_ticket_incident','Problem'=>'itop_ticket_problem'); $baseFlds = array('start_date','end_date','close_date'); $extFlds = array('assignment_date','resolution_date'); $ticket_id=$start_date=$end_date=$assignment_date=$close_date=$resolution_date=$last_update=""; $error_msg = $msg= ''; $btn=''; /** this function , just for example */ function __update(){ $t=MetaModel::GetObject('Ticket',1296); $rd=$t->Get('resolution_date'); $t->Set('resolution_date','2019-12-19 12:33:34'); $t->DBUpdate(); // direct access $ds = CMDBSource::query("update itop_ticket set start_date='2012-12-12 12:12:12' where id =1295"); // print $ds; // this will display: 1; } function pm($name){ return $_POST[$name]; } function has_pm($name){ return array_key_exists($name,$_POST); } /** startup script */ if ($_SERVER["REQUEST_METHOD"] == "POST"){ try{ $ticket_id=pm('ticket_id'); //echo("<pre>ticket_id=[$ticket_id]</pre>"); if(strlen($ticket_id)==0){ $error_msg='請輸入id!'; }else{ $data = MetaModel::GetObject('Ticket',$ticket_id, false); $dataClass = get_class($data); // ex: UserRequest //echo("data loaded=$data"); if(!$data){ echo("<script>var oDateLoaded = false;</script>"); $error_msg='數據未找到,該ID並不存在!'; goto END; } if(has_pm('btn_load')){ $btn = 'btn_load'; if($data){ $start_date = $data->get('start_date'); $end_date=$data->get('end_date'); $assignment_date=$data->get('assignment_date'); $close_date=$data->get('close_date'); $resolution_date=$data->get('resolution_date'); $last_update=$data->get('last_update'); echo("<script>var oDateLoaded = true;</script>"); $msg="已成功查詢到數據!"; }else{ $error_msg = "Ticket id=[" + $ticket_id +"] 未找到!"; } }else{ /** btn_save */ $btn = 'btn_save'; $start_date = pm('start_date'); $end_date=pm('end_date'); $assignment_date=pm('assignment_date'); $close_date=pm('close_date'); $resolution_date=pm('resolution_date'); $last_update=pm('last_update'); if($start_date)$data->set('start_date',$start_date); if($end_date)$data->set('end_date',$end_date); if($assignment_date)$data->set('assignment_date',$assignment_date); if($close_date)$data->set('close_date',$close_date); if($resolution_date)$data->set('resolution_date',$resolution_date); if($last_update)$data->set('last_update',$last_update); //不起作用!!$data->onUpdate=function(){}; $data->DBUpdate(); // if($last_update){ CMDBSource::query("update itop_ticket set last_update='$last_update' where id = $ticket_id"); } // echo("<script>var oDateLoaded = true;</script>"); $msg = "ok, 保存成功了! 您可以繼續修改當前記錄。"; } } }catch(Exception $ex){ $error_msg = $ex->getMessage(); } }else{ /** GET */ echo("<script>var oDateLoaded = false;</script>"); } /** end of HTTP method */ END: echo '' ?> <div class="form" id="pane_up_ticket_dates"> <style scoped="scoped"> .grid-x, .button-group{ display: flex; margin:8px; } .button-group.align-center{ justify-content: center; } .msg-info{ color: lightblue; font-size: 1.2em; } </style> <form method="post" action="../yh/up_ticket_dates.php" name="form_up_ticket_dates" id='form_up_ticket_dates' "> <h2>[請首先輸入id並查詢]</h2> <div class="grid-x"> <label>事件ID[形如:1298]:<input type="number" name="ticket_id" value="<?php echo $ticket_id; ?>"></label> <button name="btn_load">查詢</button> </div> <hr/><p class="msg-info"> <?php if($msg){ ?> <?php echo $msg ?> <?php } ?> </p><hr/> <br/><br/> <div class="grid-x"> <label>起始日期:<input type="datetime" name="start_date" value="<?php echo $start_date; ?>"></label> </div> <div class="grid-x"> <label>結束日期:<input type="datetime" name="end_date" value="<?php echo $end_date; ?>"></label> </div> <div class="grid-x"> <label>指派日期:<input type="datetime" name="assignment_date" value="<?php echo $assignment_date; ?>"></label> </div class="grid-x"> <div class="grid-x"> <label>關閉日期:<input type="datetime" name="close_date" value="<?php echo $close_date; ?>"></label> </div> <div class="grid-x"> <label>解決日期:<input type="datetime" name="resolution_date" value="<?php echo $resolution_date; ?>"></label> </div> <div class="grid-x"> <label>最后更新:<input type="datetime" name="last_update" value="<?php echo $last_update; ?>"></label> </div> <div class="grid-x button-group align-center"> <button name="btn_reset">清除</button><div style="width:5em;"></div> <button name="btn_save" >更新</button> </div> </form> <br/> <div id="error_pad" style="color:red;"> <?php if($error_msg){ ?> <?php echo($error_msg); ?> <?php } ?> </div> <script> $(function(){ var pane= $('#pane_up_ticket_dates'), frm= pane.find('form'),url=frm.attr('action'), ppad = $('.ui-layout-content'); var errBox= pane.find('#error_pad'), msgBox = pane.find('.msg-info'); if(!oDateLoaded){ pane.find('button[name="btn_save"]').attr("disabled",true); } frm.find('button').on('click',function(){ var btn=$(this), btnName=btn.attr('name'); if(btnName=='btn_reset'){ oDateLoaded = false; frm.find('input').val(''); errBox.html(''); return false; } if(btnName === 'btn_save' && !oDateLoaded){ var msg="請首先查詢,然后修改保存!"; errBox.html(msg); alert(msg); return false; } msgBox.html('請稍等......'); var ps = frm.serialize() + "&" + btnName + "="; $.ajax({url: url,data: ps,type: 'post', dataType:'html'}).done(function(rep,textStatus,jqXHR){ ppad.html(rep); }).fail(function (jqXHR, textStatus, errorThrown) { ppad.html(jqXHR.responseText); }); //.always(hbusy); return false; }); }); </script> </div>
二、插入到管理菜單組,實現功能界面動態裝載到iTop界面的右側工作區域。
1,實現MyWebPageMenuNode
想要添加自己定義的菜單,就要用到iTop的應用菜單類ApplicationMenu,這個類的實現位於itop/application/menunode.class.inc.php
首先找到WebPageMenuNode這個類,由於需要實現ajax動態裝載,我們不能直接使用這個類,復制一份本類的源碼,改名為MyWebPageMenuNode,對其GetHyperlink方法稍作修改
class MyWebPageMenuNode extends MenuNode { protected $sHyperlink; /** * Create a menu item that points to any web page (not only UI.php) * @param string $sMenuId Unique identifier of the menu (used to identify the menu for bookmarking, and for getting the labels from the dictionary) * @param string $sHyperlink URL to the page to load. Use relative URL if you want to keep the application portable ! * @param integer $iParentIndex ID of the parent menu * @param float $fRank Number used to order the list, any number will do, but for a given level (i.e same parent) all menus are sorted based on this value * @param string $sEnableClass Name of class of object * @param integer $iActionCode Either UR_ACTION_READ, UR_ACTION_MODIFY, UR_ACTION_DELETE, UR_ACTION_BULKREAD, UR_ACTION_BULKMODIFY or UR_ACTION_BULKDELETE * @param integer $iAllowedResults Expected "rights" for the action: either UR_ALLOWED_YES, UR_ALLOWED_NO, UR_ALLOWED_DEPENDS or a mix of them... * @return MenuNode */ public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null) { parent::__construct($sMenuId, $iParentIndex, $fRank, $sEnableClass, $iActionCode, $iAllowedResults, $sEnableStimulus); $this->sHyperlink = $sHyperlink; $this->aReflectionProperties['url'] = $sHyperlink; } public function GetHyperlink($aExtraParams) { $aExtraParams['c[menu]'] = $this->GetMenuId(); //return $this->AddParams( $this->sHyperlink, $aExtraParams); $link=$this->AddParams( $this->sHyperlink, $aExtraParams); return "javascript:openMyPages('$link')"; } public function RenderContent(WebPage $oPage, $aExtraParams = array()) { assert(false); // Shall never be called, the external web page will handle the display by itself } }
2,修改ApplicationMenu類,添加自定義方法
/** * ---------------------------------------------------------------------------------------------------------------------------- * ---- 定制菜單 * ---------------------------------------------------------------------------------------------------------------------------- */ static public function AddMyMenu($oPage){ //public function __construct($sMenuId, $sHyperlink, $iParentIndex, $fRank = 0, $sEnableClass = null, $iActionCode = null, $iAllowedResults = UR_ALLOWED_YES, $sEnableStimulus = null) //AdminTools $link="../yh/up_ticket_dates.php"; //$link= "../pages/UI.php?c[menu]=UpTicketDates:Modify"; $mi = new MyWebPageMenuNode('UpTicketDates',$link,0); self::InsertMenu($mi,1,20); $oPage->add_ready_script('window.openMyPages=function(link){$(".ui-layout-content").load(link);};'); }
在這個方法中,實現了兩個功能,創建一個新的菜單項, 然后在頁面中注冊了點擊菜單時需要用到的js函數(注意:js函數名稱和上面自定義類中的名稱呼應)。
3,修改DisplayMenu方法,調用上面的自定義方法:
static public function DisplayMenu($oPage, $aExtraParams) { self::LoadAdditionalMenus(); // Sort the root menu based on the rank usort(self::$aRootMenus, array('ApplicationMenu', 'CompareOnRank')); // 添加自定義菜單 self::AddMyMenu($oPage); // $iAccordion = 0; $iActiveMenu = self::GetMenuIndexById(self::GetActiveNodeId()); foreach(self::$aRootMenus as $aMenu) { $oMenuNode = self::GetMenuNode($aMenu['index']); if (!$oMenuNode->IsEnabled()) continue; // Don't display a non-enabled menu $oPage->AddToMenu('<h3>'.$oMenuNode->GetTitle().'</h3>'); $oPage->AddToMenu('<div>'); $aChildren = self::GetChildren($aMenu['index']); if (count($aChildren) > 0) { $oPage->AddToMenu('<ul>'); $bActive = self::DisplaySubMenu($oPage, $aChildren, $aExtraParams, $iActiveMenu); $oPage->AddToMenu('</ul>'); if ($bActive) { //$oPage->add_ready_script("$('#accordion').accordion('activate', $iAccordion);"); // $oPage->add_ready_script("$('#accordion').accordion('option', {collapsible: true});"); // Make it auto-collapsible once it has been opened properly $oPage->add_ready_script("$('#accordion').accordion('option', {collapsible: true, active: $iAccordion});"); // Make it auto-collapsible once it has been opened properly } } $oPage->AddToMenu('</div>'); $iAccordion++; } }