[原]iTop自定义修改相关时间字段的实现要点记录

[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++;
        }
    }

 

posted @ 2020-07-31 15:48  柒零壹  阅读(1137)  评论(0编辑  收藏  举报