Ganteng Doang Upload Shell Gak Bisa


Linux server.jmdstrack.com 3.10.0-1160.119.1.el7.tuxcare.els10.x86_64 #1 SMP Fri Oct 11 21:40:41 UTC 2024 x86_64
/ home/ jmdstrac/ public_html/ devices/ src/

/home/jmdstrac/public_html/devices/src/Change.php

<?php

/**
 * ---------------------------------------------------------------------
 *
 * GLPI - Gestionnaire Libre de Parc Informatique
 *
 * http://glpi-project.org
 *
 * @copyright 2015-2023 Teclib' and contributors.
 * @copyright 2003-2014 by the INDEPNET Development Team.
 * @licence   https://www.gnu.org/licenses/gpl-3.0.html
 *
 * ---------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GLPI.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * ---------------------------------------------------------------------
 */

use Glpi\Application\View\TemplateRenderer;
use Glpi\ContentTemplates\Parameters\ChangeParameters;
use Glpi\RichText\RichText;

/**
 * Change Class
 **/
class Change extends CommonITILObject
{
   // From CommonDBTM
    public $dohistory                   = true;
    protected static $forward_entity_to = ['ChangeValidation', 'ChangeCost'];

   // From CommonITIL
    public $userlinkclass               = 'Change_User';
    public $grouplinkclass              = 'Change_Group';
    public $supplierlinkclass           = 'Change_Supplier';

    public static $rightname                   = 'change';
    protected $usenotepad               = true;

    const MATRIX_FIELD                  = 'priority_matrix';
    const URGENCY_MASK_FIELD            = 'urgency_mask';
    const IMPACT_MASK_FIELD             = 'impact_mask';
    const STATUS_MATRIX_FIELD           = 'change_status';


    const READMY                        = 1;
    const READALL                       = 1024;

   // Specific status for changes
    const REFUSED                       = 13;
    const CANCELED                      = 14;

    public static function getTypeName($nb = 0)
    {
        return _n('Change', 'Changes', $nb);
    }


    public function canSolve()
    {

        return (self::isAllowedStatus($this->fields['status'], self::SOLVED)
              // No edition on closed status
              && !in_array($this->fields['status'], $this->getClosedStatusArray())
              && (Session::haveRight(self::$rightname, UPDATE)
                  || (Session::haveRight(self::$rightname, self::READMY)
                      && ($this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
                          || (isset($_SESSION["glpigroups"])
                              && $this->haveAGroup(
                                  CommonITILActor::ASSIGN,
                                  $_SESSION["glpigroups"]
                              ))))));
    }


    public static function canView()
    {
        return Session::haveRightsOr(self::$rightname, [self::READALL, self::READMY]);
    }


    /**
     * Is the current user have right to show the current change ?
     *
     * @return boolean
     **/
    public function canViewItem()
    {

        if (!$this->checkEntity(true)) {
            return false;
        }
        return (Session::haveRight(self::$rightname, self::READALL)
              || (Session::haveRight(self::$rightname, self::READMY)
                  && ($this->isUser(CommonITILActor::REQUESTER, Session::getLoginUserID())
                      || $this->isUser(CommonITILActor::OBSERVER, Session::getLoginUserID())
                      || (isset($_SESSION["glpigroups"])
                          && ($this->haveAGroup(CommonITILActor::REQUESTER, $_SESSION["glpigroups"])
                              || $this->haveAGroup(
                                  CommonITILActor::OBSERVER,
                                  $_SESSION["glpigroups"]
                              )))
                      || ($this->isUser(CommonITILActor::ASSIGN, Session::getLoginUserID())
                          || (isset($_SESSION["glpigroups"])
                              && $this->haveAGroup(
                                  CommonITILActor::ASSIGN,
                                  $_SESSION["glpigroups"]
                              ))))));
    }


    /**
     * Is the current user have right to create the current change ?
     *
     * @return boolean
     **/
    public function canCreateItem()
    {

        if (!Session::haveAccessToEntity($this->getEntityID())) {
            return false;
        }
        return Session::haveRight(self::$rightname, CREATE);
    }


    /**
     * is the current user could reopen the current change
     *
     * @since 9.4.0
     *
     * @return boolean
     */
    public function canReopen()
    {
        return Session::haveRight('followup', CREATE)
             && in_array($this->fields["status"], $this->getClosedStatusArray())
             && ($this->isAllowedStatus($this->fields['status'], self::INCOMING)
                 || $this->isAllowedStatus($this->fields['status'], self::EVALUATION));
    }


    public function prepareInputForAdd($input)
    {
        $input =  parent::prepareInputForAdd($input);
        if ($input === false) {
            return false;
        }

        if (!isset($input['_skip_auto_assign']) || $input['_skip_auto_assign'] === false) {
           // Manage auto assign
            $auto_assign_mode = Entity::getUsedConfig('auto_assign_mode', $input['entities_id']);

            switch ($auto_assign_mode) {
                case Entity::CONFIG_NEVER:
                    break;

                case Entity::AUTO_ASSIGN_HARDWARE_CATEGORY:
                case Entity::AUTO_ASSIGN_CATEGORY_HARDWARE:
                   // Auto assign tech/group from Category
                   // Changes are not associated to a hardware then both settings behave the same way
                    $input = $this->setTechAndGroupFromItilCategory($input);
                    break;
            }
        }

        return $input;
    }


    public function prepareInputForUpdate($input)
    {
        $input = $this->transformActorsInput($input);

        $input = parent::prepareInputForUpdate($input);

        return $input;
    }


    public function pre_deleteItem()
    {
        global $CFG_GLPI;

        if (!isset($this->input['_disablenotif']) && $CFG_GLPI['use_notifications']) {
            NotificationEvent::raiseEvent('delete', $this);
        }
        return true;
    }


    public function getSpecificMassiveActions($checkitem = null)
    {

        $actions = parent::getSpecificMassiveActions($checkitem);

        if ($this->canAdminActors()) {
            $actions[__CLASS__ . MassiveAction::CLASS_ACTION_SEPARATOR . 'add_actor'] = __('Add an actor');
            $actions[__CLASS__ . MassiveAction::CLASS_ACTION_SEPARATOR . 'update_notif']
               = __('Set notifications for all actors');
        }

        return $actions;
    }

    public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0)
    {

        if (static::canView()) {
            switch ($item->getType()) {
                case __CLASS__:
                    $ong = [];
                    if ($item->canUpdate()) {
                         $ong[1] = __('Statistics');
                    }

                    return $ong;
            }
        }
        return '';
    }


    public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0)
    {

        switch ($item->getType()) {
            case __CLASS__:
                switch ($tabnum) {
                    case 1:
                        $item->showStats();
                        break;
                }
                break;
        }
        return true;
    }


    public function defineTabs($options = [])
    {
        $ong = [];
        $this->addDefaultFormTab($ong);
        $this->addStandardTab(__CLASS__, $ong, $options);
        $this->addStandardTab('ChangeValidation', $ong, $options);
        $this->addStandardTab('ChangeCost', $ong, $options);
        $this->addStandardTab('Itil_Project', $ong, $options);
        $this->addStandardTab('Change_Problem', $ong, $options);
        $this->addStandardTab('Change_Ticket', $ong, $options);
        $this->addStandardTab('Change_Item', $ong, $options);
        if ($this->hasImpactTab()) {
            $this->addStandardTab('Impact', $ong, $options);
        }
        $this->addStandardTab('KnowbaseItem_Item', $ong, $options);
        $this->addStandardTab('Notepad', $ong, $options);
        $this->addStandardTab('Log', $ong, $options);

        return $ong;
    }


    public function cleanDBonPurge()
    {

       // CommonITILTask does not extends CommonDBConnexity
        $ct = new ChangeTask();
        $ct->deleteByCriteria(['changes_id' => $this->fields['id']]);

        $this->deleteChildrenAndRelationsFromDb(
            [
            // Done by parent: Change_Group::class,
                Change_Item::class,
                Change_Problem::class,
            // Done by parent: Change_Supplier::class,
                Change_Ticket::class,
            // Done by parent: Change_User::class,
                ChangeCost::class,
                ChangeValidation::class,
            // Done by parent: ITILSolution::class,
            ]
        );

        parent::cleanDBonPurge();
    }


    public function post_updateItem($history = 1)
    {
        global $CFG_GLPI;

        parent::post_updateItem($history);

        $donotif = count($this->updates);

        if (isset($this->input['_forcenotif'])) {
            $donotif = true;
        }

        if (isset($this->input['_disablenotif'])) {
            $donotif = false;
        }

        if ($donotif && $CFG_GLPI["use_notifications"]) {
            $mailtype = "update";
            if (
                isset($this->input["status"]) && $this->input["status"]
                && in_array("status", $this->updates)
                && in_array($this->input["status"], $this->getSolvedStatusArray())
            ) {
                $mailtype = "solved";
            }

            if (
                isset($this->input["status"])
                && $this->input["status"]
                && in_array("status", $this->updates)
                && in_array($this->input["status"], $this->getClosedStatusArray())
            ) {
                $mailtype = "closed";
            }

           // Read again change to be sure that all data are up to date
            $this->getFromDB($this->fields['id']);
            NotificationEvent::raiseEvent($mailtype, $this);
        }
    }


    public function post_addItem()
    {
        global $DB;

        parent::post_addItem();

        if (isset($this->input['_tickets_id'])) {
            $ticket = new Ticket();
            if ($ticket->getFromDB($this->input['_tickets_id'])) {
                $pt = new Change_Ticket();
                $pt->add(['tickets_id' => $this->input['_tickets_id'],
                    'changes_id' => $this->fields['id']
                ]);

                if (!empty($ticket->fields['itemtype']) && $ticket->fields['items_id'] > 0) {
                     $it = new Change_Item();
                     $it->add(['changes_id' => $this->fields['id'],
                         'itemtype'   => $ticket->fields['itemtype'],
                         'items_id'   => $ticket->fields['items_id']
                     ]);
                }

                //Copy associated elements
                $iterator = $DB->request([
                    'FROM'   => Item_Ticket::getTable(),
                    'WHERE'  => [
                        'tickets_id'   => $this->input['_tickets_id']
                    ]
                ]);
                $assoc = new Change_Item();
                foreach ($iterator as $row) {
                     unset($row['tickets_id']);
                     unset($row['id']);
                     $row['changes_id'] = $this->fields['id'];
                     $assoc->add(Toolbox::addslashes_deep($row));
                }
            }
        }

        if (isset($this->input['_problems_id'])) {
            $problem = new Problem();
            if ($problem->getFromDB($this->input['_problems_id'])) {
                $cp = new Change_Problem();
                $cp->add(['problems_id' => $this->input['_problems_id'],
                    'changes_id'  => $this->fields['id']
                ]);

               //Copy associated elements
                $iterator = $DB->request([
                    'FROM'   => Item_Problem::getTable(),
                    'WHERE'  => [
                        'problems_id'   => $this->input['_problems_id']
                    ]
                ]);
                $assoc = new Change_Item();
                foreach ($iterator as $row) {
                     unset($row['problems_id']);
                     unset($row['id']);
                     $row['changes_id'] = $this->fields['id'];
                     $assoc->add(Toolbox::addslashes_deep($row));
                }
            }
        }

        $this->handleNewItemNotifications();

        if (
            isset($this->input['_from_items_id'])
            && isset($this->input['_from_itemtype'])
        ) {
            $change_item = new Change_Item();
            $change_item->add([
                'items_id'      => (int)$this->input['_from_items_id'],
                'itemtype'      => $this->input['_from_itemtype'],
                'changes_id'    => $this->fields['id'],
                '_disablenotif' => true
            ]);
        }

        $this->handleItemsIdInput();
    }


    /**
     * Get default values to search engine to override
     **/
    public static function getDefaultSearchRequest()
    {

        $search = ['criteria' => [ 0 => ['field'      => 12,
            'searchtype' => 'equals',
            'value'      => 'notold'
        ]
        ],
            'sort'     => 19,
            'order'    => 'DESC'
        ];

        return $search;
    }


    public function rawSearchOptions()
    {
        $tab = [];

        $tab = array_merge($tab, $this->getSearchOptionsMain());

        $tab[] = [
            'id'                 => '68',
            'table'              => 'glpi_changes_items',
            'field'              => 'id',
            'name'               => _x('quantity', 'Number of items'),
            'forcegroupby'       => true,
            'usehaving'          => true,
            'datatype'           => 'count',
            'massiveaction'      => false,
            'joinparams'         => [
                'jointype'           => 'child'
            ]
        ];

        $tab[] = [
            'id'                 => '13',
            'table'              => 'glpi_changes_items',
            'field'              => 'items_id',
            'name'               => _n('Associated element', 'Associated elements', Session::getPluralNumber()),
            'datatype'           => 'specific',
            'comments'           => true,
            'nosearch'           => true,
            'additionalfields'   => ['itemtype'],
            'joinparams'         => [
                'jointype'           => 'child'
            ],
            'forcegroupby'       => true,
            'massiveaction'      => false
        ];

        $tab[] = [
            'id'                 => '131',
            'table'              => 'glpi_changes_items',
            'field'              => 'itemtype',
            'name'               => _n('Associated item type', 'Associated item types', Session::getPluralNumber()),
            'datatype'           => 'itemtypename',
            'itemtype_list'      => 'ticket_types',
            'nosort'             => true,
            'additionalfields'   => ['itemtype'],
            'joinparams'         => [
                'jointype'           => 'child'
            ],
            'forcegroupby'       => true,
            'massiveaction'      => false
        ];

        $tab = array_merge($tab, $this->getSearchOptionsActors());

        $tab[] = [
            'id'                 => 'analysis',
            'name'               => __('Control list')
        ];

        $tab[] = [
            'id'                 => '60',
            'table'              => $this->getTable(),
            'field'              => 'impactcontent',
            'name'               => __('Analysis impact'),
            'massiveaction'      => false,
            'datatype'           => 'text'
        ];

        $tab[] = [
            'id'                 => '61',
            'table'              => $this->getTable(),
            'field'              => 'controlistcontent',
            'name'               => __('Control list'),
            'massiveaction'      => false,
            'datatype'           => 'text'
        ];

        $tab[] = [
            'id'                 => '62',
            'table'              => $this->getTable(),
            'field'              => 'rolloutplancontent',
            'name'               => __('Deployment plan'),
            'massiveaction'      => false,
            'datatype'           => 'text'
        ];

        $tab[] = [
            'id'                 => '63',
            'table'              => $this->getTable(),
            'field'              => 'backoutplancontent',
            'name'               => __('Backup plan'),
            'massiveaction'      => false,
            'datatype'           => 'text'
        ];

        $tab[] = [
            'id'                 => '67',
            'table'              => $this->getTable(),
            'field'              => 'checklistcontent',
            'name'               => __('Checklist'),
            'massiveaction'      => false,
            'datatype'           => 'text'
        ];

        $tab = array_merge($tab, Notepad::rawSearchOptionsToAdd());

        $tab = array_merge($tab, ChangeValidation::rawSearchOptionsToAdd());

        $tab = array_merge($tab, ITILFollowup::rawSearchOptionsToAdd());

        $tab = array_merge($tab, ChangeTask::rawSearchOptionsToAdd());

        $tab = array_merge($tab, $this->getSearchOptionsSolution());

        $tab = array_merge($tab, ChangeCost::rawSearchOptionsToAdd());

        return $tab;
    }


    /**
     * get the change status list
     * To be overridden by class
     *
     * @param $withmetaforsearch boolean (default false)
     *
     * @return array
     **/
    public static function getAllStatusArray($withmetaforsearch = false)
    {

        $tab = [self::INCOMING      => _x('status', 'New'),
            self::EVALUATION    => __('Evaluation'),
            self::APPROVAL      => _n('Approval', 'Approvals', 1),
            self::ACCEPTED      => _x('status', 'Accepted'),
            self::WAITING       => __('Pending'),
            self::TEST          => _x('change', 'Testing'),
            self::QUALIFICATION => __('Qualification'),
            self::SOLVED        => __('Applied'),
            self::OBSERVED      => __('Review'),
            self::CLOSED        => _x('status', 'Closed'),
            self::CANCELED      => _x('status', 'Cancelled'),
            self::REFUSED       => _x('status', 'Refused'),
        ];

        if ($withmetaforsearch) {
            $tab['notold']    = _x('status', 'Not solved');
            $tab['notclosed'] = _x('status', 'Not closed');
            $tab['process']   = __('Processing');
            $tab['old']       = _x('status', 'Solved + Closed');
            $tab['all']       = __('All');
        }
        return $tab;
    }


    /**
     * Get the ITIL object closed status list
     *
     * @since 0.83
     *
     * @return array
     **/
    public static function getClosedStatusArray()
    {

       // To be overridden by class
        $tab = [
            self::CLOSED,
            self::CANCELED,
            self::REFUSED,
        ];
        return $tab;
    }


    /**
     * Get the ITIL object solved or observe status list
     *
     * @since 0.83
     *
     * @return array
     **/
    public static function getSolvedStatusArray()
    {
       // To be overridden by class
        $tab = [self::OBSERVED, self::SOLVED];
        return $tab;
    }

    /**
     * Get the ITIL object new status list
     *
     * @since 0.83.8
     *
     * @return array
     **/
    public static function getNewStatusArray()
    {
        return [self::INCOMING, self::ACCEPTED, self::EVALUATION, self::APPROVAL];
    }

    /**
     * Get the ITIL object test, qualification or accepted status list
     * To be overridden by class
     *
     * @since 0.83
     *
     * @return array
     **/
    public static function getProcessStatusArray()
    {

       // To be overridden by class
        $tab = [self::ACCEPTED, self::QUALIFICATION, self::TEST];
        return $tab;
    }

    public static function getReopenableStatusArray()
    {
        return self::getClosedStatusArray();
    }

    public function getRights($interface = 'central')
    {

        $values = parent::getRights();
        unset($values[READ]);

        $values[self::READALL] = __('See all');
        $values[self::READMY]  = __('See (author)');

        return $values;
    }

    /**
     * Display changes for an item
     *
     * Will also display changes of linked items
     *
     * @param CommonDBTM      $item
     * @param boolean|integer $withtemplate
     *
     * @return boolean|void
     **/
    public static function showListForItem(CommonDBTM $item, $withtemplate = 0)
    {
        global $DB;

        if (!Session::haveRight(self::$rightname, self::READALL)) {
            return false;
        }

        if ($item->isNewID($item->getID())) {
            return false;
        }

        $restrict = [];
        $options  = [
            'criteria' => [],
            'reset'    => 'reset',
        ];

        switch ($item->getType()) {
            case 'User':
                $restrict['glpi_changes_users.users_id'] = $item->getID();

                $options['criteria'][0]['field']      = 4; // status
                $options['criteria'][0]['searchtype'] = 'equals';
                $options['criteria'][0]['value']      = $item->getID();
                $options['criteria'][0]['link']       = 'OR';

                $options['criteria'][1]['field']      = 66; // status
                $options['criteria'][1]['searchtype'] = 'equals';
                $options['criteria'][1]['value']      = $item->getID();
                $options['criteria'][1]['link']       = 'OR';

                $options['criteria'][5]['field']      = 5; // status
                $options['criteria'][5]['searchtype'] = 'equals';
                $options['criteria'][5]['value']      = $item->getID();
                $options['criteria'][5]['link']       = 'OR';

                break;

            case 'Supplier':
                $restrict['glpi_changes_suppliers.suppliers_id'] = $item->getID();

                $options['criteria'][0]['field']      = 6;
                $options['criteria'][0]['searchtype'] = 'equals';
                $options['criteria'][0]['value']      = $item->getID();
                $options['criteria'][0]['link']       = 'AND';
                break;

            case 'Group':
               // Mini search engine
                if ($item->haveChildren()) {
                    $tree = Session::getSavedOption(__CLASS__, 'tree', 0);
                    echo "<table class='tab_cadre_fixe'>";
                    echo "<tr class='tab_bg_1'><th>" . __('Last changes') . "</th></tr>";
                    echo "<tr class='tab_bg_1'><td class='center'>";
                    echo __('Child groups');
                    Dropdown::showYesNo(
                        'tree',
                        $tree,
                        -1,
                        ['on_change' => 'reloadTab("start=0&tree="+this.value)']
                    );
                } else {
                    $tree = 0;
                }
                echo "</td></tr></table>";

                $restrict['glpi_changes_groups.groups_id'] = ($tree ? getSonsOf('glpi_groups', $item->getID()) : $item->getID());

                $options['criteria'][0]['field']      = 71;
                $options['criteria'][0]['searchtype'] = ($tree ? 'under' : 'equals');
                $options['criteria'][0]['value']      = $item->getID();
                $options['criteria'][0]['link']       = 'AND';
                break;

            default:
                $restrict['items_id'] = $item->getID();
                $restrict['itemtype'] = $item->getType();
                break;
        }

       // Link to open a new change
        if (
            $item->getID()
            && Change::isPossibleToAssignType($item->getType())
            && self::canCreate()
            && !(!empty($withtemplate) && $withtemplate == 2)
            && (!isset($item->fields['is_template']) || $item->fields['is_template'] == 0)
        ) {
            echo "<div class='firstbloc'>";
            Html::showSimpleForm(
                Change::getFormURL(),
                '_add_fromitem',
                __('New change for this item...'),
                [
                    '_from_itemtype' => $item->getType(),
                    '_from_items_id' => $item->getID(),
                    'entities_id'    => $item->fields['entities_id']
                ]
            );
            echo "</div>";
        }

        $criteria = self::getCommonCriteria();
        $criteria['WHERE'] = $restrict + getEntitiesRestrictCriteria(self::getTable());
        $criteria['LIMIT'] = (int)$_SESSION['glpilist_limit'];
        $iterator = $DB->request($criteria);
        $number = count($iterator);

       // Ticket for the item
        echo "<div><table class='tab_cadre_fixe'>";

        $colspan = 11;
        if (count($_SESSION["glpiactiveentities"]) > 1) {
            $colspan++;
        }
        if ($number > 0) {
            Session::initNavigateListItems(
                'Change',
                //TRANS : %1$s is the itemtype name,
                //        %2$s is the name of the item (used for headings of a list)
                                        sprintf(
                                            __('%1$s = %2$s'),
                                            $item->getTypeName(1),
                                            $item->getName()
                                        )
            );

            echo "<tr><th colspan='$colspan'>";

           //TRANS : %d is the number of problems
            echo sprintf(_n('Last %d change', 'Last %d changes', $number), $number);

            echo "</th></tr>";
        } else {
            echo "<tr><th>" . __('No change found.') . "</th></tr>";
        }
       // Ticket list
        if ($number > 0) {
            self::commonListHeader(Search::HTML_OUTPUT);

            foreach ($iterator as $data) {
                Session::addToNavigateListItems('Problem', $data["id"]);
                self::showShort($data["id"]);
            }
            self::commonListHeader(Search::HTML_OUTPUT);
        }

        echo "</table></div>";

       // Tickets for linked items
        $linkeditems = $item->getLinkedItems();
        $restrict = [];
        if (count($linkeditems)) {
            foreach ($linkeditems as $ltype => $tab) {
                foreach ($tab as $lID) {
                    $restrict[] = ['AND' => ['itemtype' => $ltype, 'items_id' => $lID]];
                }
            }
        }

        if (count($restrict)) {
            $criteria         = self::getCommonCriteria();
            $criteria['WHERE'] = ['OR' => $restrict]
            + getEntitiesRestrictCriteria(self::getTable());
            $iterator = $DB->request($criteria);
            $number = count($iterator);

            echo "<div class='spaced'><table class='tab_cadre_fixe'>";
            echo "<tr><th colspan='$colspan'>";
            echo __('Changes on linked items');

            echo "</th></tr>";
            if ($number > 0) {
                self::commonListHeader(Search::HTML_OUTPUT);

                foreach ($iterator as $data) {
                    // Session::addToNavigateListItems(TRACKING_TYPE,$data["id"]);
                    self::showShort($data["id"]);
                }
                self::commonListHeader(Search::HTML_OUTPUT);
            } else {
                echo "<tr><th>" . __('No change found.') . "</th></tr>";
            }
            echo "</table></div>";
        }
    }


    /**
     * Display debug information for current object
     *
     * @since 0.90.2
     **/
    public function showDebug()
    {
        NotificationEvent::debugEvent($this);
    }

    public static function getDefaultValues($entity = 0)
    {
        $default_use_notif = Entity::getUsedConfig('is_notif_enable_default', $_SESSION['glpiactive_entity'], '', 1);
        return [
            '_users_id_requester'        => Session::getLoginUserID(),
            '_users_id_requester_notif'  => [
                'use_notification'  => $default_use_notif,
                'alternative_email' => ''
            ],
            '_groups_id_requester'       => 0,
            '_users_id_assign'           => 0,
            '_users_id_assign_notif'     => [
                'use_notification'  => $default_use_notif,
                'alternative_email' => ''
            ],
            '_groups_id_assign'          => 0,
            '_users_id_observer'         => 0,
            '_users_id_observer_notif'   => [
                'use_notification'  => $default_use_notif,
                'alternative_email' => ''
            ],
            '_suppliers_id_assign_notif' => [
                'use_notification'  => $default_use_notif,
                'alternative_email' => ''
            ],
            '_groups_id_observer'        => 0,
            '_suppliers_id_assign'       => 0,
            'priority'                   => 3,
            'urgency'                    => 3,
            'impact'                     => 3,
            'content'                    => '',
            'entities_id'                => $_SESSION['glpiactive_entity'],
            'name'                       => '',
            'itilcategories_id'          => 0,
            'actiontime'                 => 0,
            '_add_validation'            => 0,
            'users_id_validate'          => [],
            '_tasktemplates_id'          => [],
            'controlistcontent'          => '',
            'impactcontent'              => '',
            'rolloutplancontent'         => '',
            'backoutplancontent'         => '',
            'checklistcontent'           => '',
            'items_id'                   => 0,
            '_actors'                     => [],
        ];
    }

    /**
     * Get active changes for an item
     *
     * @since 9.5
     *
     * @param string $itemtype     Item type
     * @param integer $items_id    ID of the Item
     *
     * @return DBmysqlIterator
     */
    public function getActiveChangesForItem($itemtype, $items_id)
    {
        global $DB;

        return $DB->request([
            'SELECT'    => [
                $this->getTable() . '.id',
                $this->getTable() . '.name',
                $this->getTable() . '.priority',
            ],
            'FROM'      => $this->getTable(),
            'LEFT JOIN' => [
                'glpi_changes_items' => [
                    'ON' => [
                        'glpi_changes_items' => 'changes_id',
                        $this->getTable()    => 'id'
                    ]
                ]
            ],
            'WHERE'     => [
                'glpi_changes_items.itemtype' => $itemtype,
                'glpi_changes_items.items_id'    => $items_id,
                $this->getTable() . '.is_deleted' => 0,
                'NOT'                         => [
                    $this->getTable() . '.status' => array_merge(
                        $this->getSolvedStatusArray(),
                        $this->getClosedStatusArray()
                    )
                ]
            ]
        ]);
    }


    public static function getIcon()
    {
        return "ti ti-clipboard-check";
    }

    public static function getItemLinkClass(): string
    {
        return Change_Item::class;
    }

    public static function getStatusClass($status)
    {
        $class = null;
        $solid = true;

        switch ($status) {
            case self::REFUSED:
            case self::CANCELED:
                $class = 'circle';
                break;
            default:
                return parent::getStatusClass($status);
        }

        return $class == null
         ? ''
         : 'itilstatus ' . ($solid ? 'fas fa-' : 'far fa-') . $class .
         " " . static::getStatusKey($status);
    }

    public static function getStatusKey($status)
    {
        switch ($status) {
            case self::REFUSED:
                return 'refused';
            case self::CANCELED:
                return 'canceled';
            default:
                return parent::getStatusKey($status);
        }
    }

    public static function getTaskClass()
    {
        return ChangeTask::class;
    }

    public static function getContentTemplatesParametersClass(): string
    {
        return ChangeParameters::class;
    }

    /**
     * @param $start
     * @param $status             (default 'process')
     * @param $showgroupchanges  (true by default)
     * @since 10.0.0
     *
     */
    public static function showCentralList($start, $status = "process", $showgroupchanges = true)
    {
        global $DB, $CFG_GLPI;

        if (!static::canView()) {
            return false;
        }

        $WHERE = [
            'is_deleted' => 0
        ];
        $search_users_id = [
            'glpi_changes_users.users_id'   => Session::getLoginUserID(),
            'glpi_changes_users.type'       => CommonITILActor::REQUESTER
        ];
        $search_assign = [
            'glpi_changes_users.users_id'   => Session::getLoginUserID(),
            'glpi_changes_users.type'       => CommonITILActor::ASSIGN
        ];

        if ($showgroupchanges) {
            $search_users_id  = [0];
            $search_assign = [0];

            if (count($_SESSION['glpigroups'])) {
                $search_users_id = [
                    'glpi_changes_groups.groups_id' => $_SESSION['glpigroups'],
                    'glpi_changes_groups.type'      => CommonITILActor::REQUESTER
                ];
                $search_assign = [
                    'glpi_changes_groups.groups_id' => $_SESSION['glpigroups'],
                    'glpi_changes_groups.type'      => CommonITILActor::ASSIGN
                ];
            }
        }

        switch ($status) {
            case "waiting":
                $WHERE = array_merge(
                    $WHERE,
                    $search_assign,
                    ['status' => self::WAITING]
                );
                break;

            case "process":
                $WHERE = array_merge(
                    $WHERE,
                    $search_assign,
                    ['status' => [self::ACCEPTED, self::TEST, self::QUALIFICATION]]
                );
                break;

            default:
                $WHERE = array_merge(
                    $WHERE,
                    $search_users_id,
                    [
                        'status' => array_diff(self::getAllStatusArray(), self::getClosedStatusArray())
                    ]
                );
                $WHERE['NOT'] = $search_assign;
        }

        $criteria = [
            'SELECT'          => ['glpi_changes.id'],
            'DISTINCT'        => true,
            'FROM'            => 'glpi_changes',
            'LEFT JOIN'       => [
                'glpi_changes_users'   => [
                    'ON' => [
                        'glpi_changes_users'   => 'changes_id',
                        'glpi_changes'         => 'id'
                    ]
                ],
                'glpi_changes_groups'  => [
                    'ON' => [
                        'glpi_changes_groups'  => 'changes_id',
                        'glpi_changes'         => 'id'
                    ]
                ]
            ],
            'WHERE'           => $WHERE + getEntitiesRestrictCriteria('glpi_changes'),
            'ORDERBY'         => 'date_mod DESC'
        ];
        $iterator = $DB->request($criteria);

        $total_row_count = count($iterator);
        $displayed_row_count = min((int)$_SESSION['glpidisplay_count_on_home'], $total_row_count);

        if ($total_row_count > 0) {
            $options  = [
                'criteria' => [],
                'reset'    => 'reset',
            ];
            $forcetab         = '';
            if ($showgroupchanges) {
                switch ($status) {
                    case "waiting":
                        $options['criteria'][0]['field']      = 12; // status
                        $options['criteria'][0]['searchtype'] = 'equals';
                        $options['criteria'][0]['value']      = self::WAITING;
                        $options['criteria'][0]['link']       = 'AND';

                        $options['criteria'][1]['field']      = 8; // groups_id_assign
                        $options['criteria'][1]['searchtype'] = 'equals';
                        $options['criteria'][1]['value']      = 'mygroups';
                        $options['criteria'][1]['link']       = 'AND';

                        $main_header = "<a href=\"" . $CFG_GLPI["root_doc"] . "/front/change.php?" .
                         Toolbox::append_params($options, '&amp;') . "\">" .
                         Html::makeTitle(__('Changes on pending status'), $displayed_row_count, $total_row_count) . "</a>";
                        break;

                    case "process":
                        $options['criteria'][0]['field']      = 12; // status
                        $options['criteria'][0]['searchtype'] = 'equals';
                        $options['criteria'][0]['value']      = self::EVALUATION;
                        $options['criteria'][0]['link']       = 'AND';

                        $options['criteria'][1]['field']      = 8; // groups_id_assign
                        $options['criteria'][1]['searchtype'] = 'equals';
                        $options['criteria'][1]['value']      = 'mygroups';
                        $options['criteria'][1]['link']       = 'AND';

                        $main_header = "<a href=\"" . $CFG_GLPI["root_doc"] . "/front/change.php?" .
                        Toolbox::append_params($options, '&amp;') . "\">" .
                        Html::makeTitle(__('Changes to be processed'), $displayed_row_count, $total_row_count) . "</a>";
                        break;

                    default:
                        $options['criteria'][0]['field']      = 12; // status
                        $options['criteria'][0]['searchtype'] = 'equals';
                        $options['criteria'][0]['value']      = 'notold';
                        $options['criteria'][0]['link']       = 'AND';

                        $options['criteria'][1]['field']      = 71; // groups_id
                        $options['criteria'][1]['searchtype'] = 'equals';
                        $options['criteria'][1]['value']      = 'mygroups';
                        $options['criteria'][1]['link']       = 'AND';

                        $main_header = "<a href=\"" . $CFG_GLPI["root_doc"] . "/front/change.php?" .
                        Toolbox::append_params($options, '&amp;') . "\">" .
                        Html::makeTitle(__('Your changes in progress'), $displayed_row_count, $total_row_count) . "</a>";
                }
            } else {
                switch ($status) {
                    case "waiting":
                        $options['criteria'][0]['field']      = 12; // status
                        $options['criteria'][0]['searchtype'] = 'equals';
                        $options['criteria'][0]['value']      = self::WAITING;
                        $options['criteria'][0]['link']       = 'AND';

                        $options['criteria'][1]['field']      = 5; // users_id_assign
                        $options['criteria'][1]['searchtype'] = 'equals';
                        $options['criteria'][1]['value']      = Session::getLoginUserID();
                        $options['criteria'][1]['link']       = 'AND';

                        $main_header = "<a href=\"" . $CFG_GLPI["root_doc"] . "/front/change.php?" .
                         Toolbox::append_params($options, '&amp;') . "\">" .
                         Html::makeTitle(__('Changes on pending status'), $displayed_row_count, $total_row_count) . "</a>";
                        break;

                    case "process":
                        $options['criteria'][0]['field']      = 5; // users_id_assign
                        $options['criteria'][0]['searchtype'] = 'equals';
                        $options['criteria'][0]['value']      = Session::getLoginUserID();
                        $options['criteria'][0]['link']       = 'AND';

                        $options['criteria'][1]['field']      = 12; // status
                        $options['criteria'][1]['searchtype'] = 'equals';
                        $options['criteria'][1]['value']      = 'process';
                        $options['criteria'][1]['link']       = 'AND';

                        $main_header = "<a href=\"" . $CFG_GLPI["root_doc"] . "/front/change.php?" .
                        Toolbox::append_params($options, '&amp;') . "\">" .
                        Html::makeTitle(__('Changes to be processed'), $displayed_row_count, $total_row_count) . "</a>";
                        break;

                    default:
                        $options['criteria'][0]['field']      = 4; // users_id
                        $options['criteria'][0]['searchtype'] = 'equals';
                        $options['criteria'][0]['value']      = Session::getLoginUserID();
                        $options['criteria'][0]['link']       = 'AND';

                        $options['criteria'][1]['field']      = 12; // status
                        $options['criteria'][1]['searchtype'] = 'equals';
                        $options['criteria'][1]['value']      = 'notold';
                        $options['criteria'][1]['link']       = 'AND';

                        $main_header = "<a href=\"" . $CFG_GLPI["root_doc"] . "/front/change.php?" .
                        Toolbox::append_params($options, '&amp;') . "\">" .
                        Html::makeTitle(__('Your changes in progress'), $displayed_row_count, $total_row_count) . "</a>";
                }
            }

            $twig_params = [
                'class'        => 'table table-borderless table-striped table-hover card-table',
                'header_rows'  => [
                    [
                        [
                            'colspan'   => 3,
                            'content'   => $main_header
                        ]
                    ],
                ],
                'rows'         => []
            ];
            $i = 0;
            if ($displayed_row_count > 0) {
                $twig_params['header_rows'][] = [
                    [
                        'content'   => __('ID'),
                        'style'     => 'width: 75px'
                    ],
                    [
                        'content'   => _n('Requester', 'Requesters', 1),
                        'style'     => 'width: 20%'
                    ],
                    __('Description')
                ];
                foreach ($iterator as $data) {
                    $change = new self();
                    $rand = mt_rand();
                    $row = [
                        'values' => []
                    ];

                    if ($change->getFromDBwithData($data['id'], 0)) {
                        $bgcolor = $_SESSION["glpipriority_" . $change->fields["priority"]];
                        $name = sprintf(__('%1$s: %2$s'), __('ID'), $change->fields["id"]);
                        $row['values'][] = [
                            'class' => 'priority_block',
                            'content' => "<span style='background: $bgcolor'></span>&nbsp;$name"
                        ];

                        $requesters = [];
                        if (
                            isset($change->users[CommonITILActor::REQUESTER])
                            && count($change->users[CommonITILActor::REQUESTER])
                        ) {
                            foreach ($change->users[CommonITILActor::REQUESTER] as $d) {
                                if ($d["users_id"] > 0) {
                                    $userdata = getUserName($d["users_id"], 2);
                                    $name = '<i class="fas fa-sm fa-fw fa-user text-muted me-1"></i>' .
                                        $userdata['name'];
                                    $requesters[] = $name;
                                } else {
                                    $requesters[] = '<i class="fas fa-sm fa-fw fa-envelope text-muted me-1"></i>' .
                                        $d['alternative_email'];
                                }
                            }
                        }

                        if (
                            isset($change->groups[CommonITILActor::REQUESTER])
                            && count($change->groups[CommonITILActor::REQUESTER])
                        ) {
                            foreach ($change->groups[CommonITILActor::REQUESTER] as $d) {
                                $requesters[] = '<i class="fas fa-sm fa-fw fa-users text-muted me-1"></i>' .
                                    Dropdown::getDropdownName("glpi_groups", $d["groups_id"]);
                            }
                        }
                        $row['values'][] = implode('<br>', $requesters);

                        $link = "<a id='change" . $change->fields["id"] . $rand . "' href='" .
                            Change::getFormURLWithID($change->fields["id"]);
                        if ($forcetab != '') {
                            $link .= "&amp;forcetab=" . $forcetab;
                        }
                        $link .= "'>";
                        $link .= "<span class='b'>" . $change->fields["name"] . "</span></a>";
                        $link = sprintf(
                            __('%1$s %2$s'),
                            $link,
                            Html::showToolTip(
                                RichText::getEnhancedHtml($change->fields['content']),
                                ['applyto' => 'change' . $change->fields["id"] . $rand,
                                    'display' => false
                                ]
                            )
                        );

                        $row['values'][] = $link;
                    } else {
                        $row['class'] = 'tab_bg_2';
                        $row['values'] = [
                            [
                                'colspan' => 6,
                                'content' => "<i>" . __('No ticket in progress.') . "</i>"
                            ]
                        ];
                    }
                    $twig_params['rows'][] = $row;

                    $i++;
                    if ($i == $displayed_row_count) {
                        break;
                    }
                }
            }
            TemplateRenderer::getInstance()->display('components/table.html.twig', $twig_params);
        }
    }


    /**
     * Get changes count
     *
     * @since 10.0.0
     *
     * @param bool $foruser only for current login user as requester
     * @param bool $display if false, return html
     **/
    public static function showCentralCount(bool $foruser = false, bool $display = true)
    {
        global $DB, $CFG_GLPI;

       // show a tab with count of jobs in the central and give link
        if (!static::canView()) {
            return false;
        }
        if (!Session::haveRight(self::$rightname, self::READALL)) {
            $foruser = true;
        }

        $table = self::getTable();
        $criteria = [
            'SELECT' => [
                'status',
                'COUNT'  => '* AS COUNT',
            ],
            'FROM'   => $table,
            'WHERE'  => getEntitiesRestrictCriteria($table),
            'GROUP'  => 'status'
        ];

        if ($foruser) {
            $criteria['LEFT JOIN'] = [
                'glpi_changes_users' => [
                    'ON' => [
                        'glpi_changes_users'   => 'changes_id',
                        $table                  => 'id', [
                            'AND' => [
                                'glpi_changes_users.type' => CommonITILActor::REQUESTER
                            ]
                        ]
                    ]
                ]
            ];
            $WHERE = ['glpi_changes_users.users_id' => Session::getLoginUserID()];

            if (
                isset($_SESSION["glpigroups"])
                && count($_SESSION["glpigroups"])
            ) {
                $criteria['LEFT JOIN']['glpi_changes_groups'] = [
                    'ON' => [
                        'glpi_changes_groups'  => 'changes_id',
                        $table                  => 'id', [
                            'AND' => [
                                'glpi_changes_groups.type' => CommonITILActor::REQUESTER
                            ]
                        ]
                    ]
                ];
                $WHERE['glpi_changes_groups.groups_id'] = $_SESSION['glpigroups'];
            }
            $criteria['WHERE'][] = ['OR' => $WHERE];
        }

        $deleted_criteria = $criteria;
        $criteria['WHERE']['glpi_changes.is_deleted'] = 0;
        $deleted_criteria['WHERE']['glpi_changes.is_deleted'] = 1;
        $iterator = $DB->request($criteria);
        $deleted_iterator = $DB->request($deleted_criteria);

        $status = [];
        foreach (self::getAllStatusArray() as $key => $val) {
            $status[$key] = 0;
        }

        foreach ($iterator as $data) {
            $status[$data["status"]] = $data["COUNT"];
        }

        $number_deleted = 0;
        foreach ($deleted_iterator as $data) {
            $number_deleted += $data["COUNT"];
        }

        $options = [];
        $options['criteria'][0]['field']      = 12;
        $options['criteria'][0]['searchtype'] = 'equals';
        $options['criteria'][0]['value']      = 'new';
        $options['criteria'][0]['link']       = 'AND';
        $options['reset']                     = 'reset';

        $twig_params = [
            'title'     => [
                'link'   => $CFG_GLPI["root_doc"] . "/front/change.php?" . Toolbox::append_params($options),
                'text'   => self::getTypeName(Session::getPluralNumber()),
                'icon'   => self::getIcon(),
            ],
            'items'     => []
        ];

        foreach ($status as $key => $val) {
            $options['criteria'][0]['value'] = $key;
            $twig_params['items'][] = [
                'link'   => $CFG_GLPI["root_doc"] . "/front/change.php?" . Toolbox::append_params($options),
                'text'   => self::getStatus($key),
                'icon'   => self::getStatusClass($key),
                'count'  => $val
            ];
        }

        $options['criteria'][0]['value'] = 'all';
        $options['is_deleted']  = 1;
        $twig_params['items'][] = [
            'link'   => $CFG_GLPI["root_doc"] . "/front/change.php?" . Toolbox::append_params($options),
            'text'   => __('Deleted'),
            'icon'   => 'fas fa-trash bg-red-lt',
            'count'  => $number_deleted
        ];

        $output = TemplateRenderer::getInstance()->render('central/lists/itemtype_count.html.twig', $twig_params);
        if ($display) {
            echo $output;
        } else {
            return $output;
        }
    }

    /**
     * @since 10.0.0
     *
     * @param $ID
     * @param $forcetab  string   name of the tab to force at the display (default '')
     **/
    public static function showVeryShort($ID, $forcetab = '')
    {
       // Prints a job in short form
       // Should be called in a <table>-segment
       // Print links or not in case of user view
       // Make new job object and fill it from database, if success, print it
        $viewusers = User::canView();

        $change   = new self();
        $rand      = mt_rand();
        if ($change->getFromDBwithData($ID, 0)) {
            $bgcolor = $_SESSION["glpipriority_" . $change->fields["priority"]];
            $name    = sprintf(__('%1$s: %2$s'), __('ID'), $change->fields["id"]);
            echo "<tr class='tab_bg_2'>";
            echo "<td>
            <div class='priority_block' style='border-color: $bgcolor'>
               <span style='background: $bgcolor'></span>&nbsp;$name
            </div>
         </td>";
            echo "<td class='center'>";

            if (
                isset($change->users[CommonITILActor::REQUESTER])
                && count($change->users[CommonITILActor::REQUESTER])
            ) {
                foreach ($change->users[CommonITILActor::REQUESTER] as $d) {
                    if ($d["users_id"] > 0) {
                         $userdata = getUserName($d["users_id"], 2);
                         $name     = "<span class='b'>" . $userdata['name'] . "</span>";
                        if ($viewusers) {
                            $name = sprintf(
                                __('%1$s %2$s'),
                                $name,
                                Html::showToolTip(
                                    $userdata["comment"],
                                    ['link'    => $userdata["link"],
                                        'display' => false
                                    ]
                                )
                            );
                        }
                         echo $name;
                    } else {
                        echo $d['alternative_email'] . "&nbsp;";
                    }
                    echo "<br>";
                }
            }

            if (
                isset($change->groups[CommonITILActor::REQUESTER])
                && count($change->groups[CommonITILActor::REQUESTER])
            ) {
                foreach ($change->groups[CommonITILActor::REQUESTER] as $d) {
                    echo Dropdown::getDropdownName("glpi_groups", $d["groups_id"]);
                    echo "<br>";
                }
            }

            echo "</td>";

            echo "<td>";
            $link = "<a id='change" . $change->fields["id"] . $rand . "' href='" .
            Change::getFormURLWithID($change->fields["id"]);
            if ($forcetab != '') {
                $link .= "&amp;forcetab=" . $forcetab;
            }
            $link .= "'>";
            $link .= "<span class='b'>" . $change->fields["name"] . "</span></a>";
            $link = printf(
                __('%1$s %2$s'),
                $link,
                Html::showToolTip(
                    $change->fields['content'],
                    ['applyto' => 'change' . $change->fields["id"] . $rand,
                        'display' => false
                    ]
                )
            );

            echo "</td>";

           // Finish Line
            echo "</tr>";
        } else {
            echo "<tr class='tab_bg_2'>";
            echo "<td colspan='6' ><i>" . __('No change found.') . "</i></td></tr>";
        }
    }
}
			
			


Thanks For 0xGh05T - DSRF14 - Mr.Dan07 - Leri01 - FxshX7 - AlkaExploiter - xLoveSyndrome'z - Acep Gans'z

JMDS TRACK – Just Another Diagnostics Lab Site

Home

JMDS TRACK Cameroon

Boost the productivity of your mobile ressources


Make An Appointment


Fleet management

  1. Reduce the operting cost and the unavailability of your vehicles
  2. reduce the fuel consumption of your fleet
  3. Improve the driving dehavior and safety of your drivers
  4. optimize the utilization rate of your equipment 
  5. protect your vehicle against theft
  6. Improve the quality of your customer service


Find out more

Assets management

  1. Track the roaming of your equipment
  2. Optimise the management of your assets on site and during transport
  3. Secure the transport of your goods
  4. Make your team responsible for preventing the loss of tools, equipment
  5. Take a real-time inventory of your equipment on site
  6. Easily find your mobile objects or equipment



Find out more



Find out more

Antitheft solutions

  1. Secure your vehicles and machinery and increase your chances of recovering them in the event of theft
  2. Protect your assets and reduce the costs associated with their loss
  3. Combine immobiliser and driver identification and limit the risk of theft
  4. Identify fuel theft and reduce costs
  5. Protect your goods and take no more risks
  6. Be alerted to abnormal events

Our Location

 Douala BP cité 

     and

Yaoundé Total Essos


Make An Appointment


Get Directions

682230363/ 677481892

What makes us different from others

  • young and dynamic team
  • call center 24/24 7/7
  • roaming throughout Africa
  • team of developers who can develop customer-specific solutions
  • diversity of services
  • reactive and prompt after-sales service when soliciting a customer or a malfunction
  • Free Maintenance and installation in the cities of Douala and Yaounde

https://youtu.be/xI1cz_Jh2x8

15+
years of experience in GPS system development, production and deployment.

15 Collaborators

More than 15 employees dedicated to the research and development of new applications and to customer care

5 000 Vehicles and mobile assets

5 000 vehicles and mobile assets under management, in Africa

Our Partners










Latest Case Studies

Our current projects 

5/5
Bon SAV , SATISFAIT DU TRAITEMENT DES REQUETES

M DIPITA CHRISTIAN
Logistic Safety Manager Road Safety Manager
5/5
La réactivité de JMDS est excellente
Nous restons satisfait dans l’ensemble des prestations relatives a la couverture de notre parc automobile

Hervé Frédéric NDENGUE
Chef Service Adjoint de la Sécurité Générale (CNPS)
5/5
L’APPLICATION EMIXIS est convivial A L’utilisation
BEIG-3 SARL
DIRECTOR GENERAL
5/5
Nevertheless I am delighted with the service
MR. BISSE BENJAMIN
CUSTOMER

Subsribe To Our Newsletter

Stay in touch with us to get latest news and special offers.



Address JMDS TRACK

Douala bp cité



and

YAOUNDE Total Essos

Call Us

+237682230363



Email Us


info@jmdstrack.cm