glpi-project / glpi

GLPI is a Free Asset and IT Management Software package, Data center management, ITIL Service Desk, licenses tracking and software auditing.
https://glpi-project.org
GNU General Public License v3.0
4.24k stars 1.29k forks source link

Replace ticket actor #1262

Closed hartois closed 5 years ago

hartois commented 7 years ago

I need to implement in massive action for ticket feature for replace actor. My users really want this. Now replacing actor need two steps: add new actor, remove old actor. My task: add feature for merge this steps to one action - replace selected actor with another. But I cannot understand architecture of massive actions. Can you help me? Where I can find info about this?

hartois commented 7 years ago

I found everything that I needed for the implementation of this feature: screen7

Please close this issue.

My patch in haste:

Index: ajax/dropdownMassiveActionReplaceActor.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- ajax/dropdownMassiveActionReplaceActor.php  (date 1478764812000)
+++ ajax/dropdownMassiveActionReplaceActor.php  (date 1478764812000)
@@ -0,0 +1,16 @@
+<?php
+
+$AJAX_INCLUDE = 1;
+include ('../inc/includes.php');
+
+header("Content-Type: text/html; charset=UTF-8");
+Html::header_nocache();
+
+Session::checkRight('ticket', UPDATE);
+
+if ($_POST["actortype"] > 0) {
+    $ticket = new Ticket();
+    $rand   = mt_rand();
+    $ticket->showActorReplaceForm($_POST["actortype"], $rand, $_POST["tickets"], false);
+    echo "&nbsp;<input type='submit' name='replace_actor' class='submit' value=\""._sx('button','Replace')."\">";
+}
Index: ajax/dropdownTicketCurrentActors.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- ajax/dropdownTicketCurrentActors.php    (date 1479123873000)
+++ ajax/dropdownTicketCurrentActors.php    (date 1479123873000)
@@ -0,0 +1,97 @@
+<?php
+
+include ('../inc/includes.php');
+
+header("Content-Type: text/html; charset=UTF-8");
+Html::header_nocache();
+
+Session::checkCentralAccess();
+global $CFG_GLPI;
+// Make a select box
+if (isset($_POST["type"])
+    && isset($_POST["actortype"])
+    && isset($_POST["itemtype"])) {
+   $rand = mt_rand();
+   if ($item = getItemForItemtype($_POST["itemtype"])) {
+      switch ($_POST["type"]) {
+         case "user" :
+            $right = 'all';
+            // Only steal or own ticket whit empty assign
+            if ($_POST["to_actor"]["_type"] == 'assign') {
+               $right = "own_ticket";
+               if (!$item->canAssign()) {
+                  $right = 'id';
+               }
+            }
+
+            $options_from = array('name'        => 'from_actor',
+                'right'       => $right,
+                'rand'        => $rand,
+                'width'       => '150',
+                'ldap_import' => true);
+            $options_to = array('name'        => 'to_actor',
+                'right'       => $right,
+                'rand'        => $rand,
+                'width'       => '150',
+                'ldap_import' => true);
+            
+            $actors = [];
+            foreach($_POST['tickets'] as $ticket_id) {
+               $ticket = new Ticket();
+               $ticket->getFromDB($ticket_id);
+
+               foreach($ticket->getTicketActorsByType('user') as $actor_id => $actor)
+                  foreach($actor as $actor_type) {
+                     if ($actor_type == $_POST['actortype']) {
+                        $actorObj = new User();
+                        $actorObj->getFromDB($actor_id);
+                        $actors[$actor_id] = $actorObj->getRawName();
+                     }
+                  }
+            }
+            $rand_from = Dropdown::showFromArray('from_actor',$actors,$options);
+            $rand_to = User::dropdown($options_to);
+
+            break;
+
+         case "group" :
+            $right = 'all';
+            // Only steal or own ticket whit empty assign
+            if ($_POST["to_actor"]["_type"] == 'assign') {
+               $right = "own_ticket";
+               if (!$item->canAssign()) {
+                  $right = 'id';
+               }
+            }
+
+            $options_from = array('name'        => 'from_actor',
+                'right'       => $right,
+                'rand'        => $rand,
+                'width'       => '150',
+                'ldap_import' => true);
+            $options_to = array('name'        => 'to_actor',
+                'right'       => $right,
+                'rand'        => $rand,
+                'width'       => '150',
+                'ldap_import' => true);
+
+            $actors = [];
+            foreach($_POST['tickets'] as $ticket_id) {
+               $ticket = new Ticket();
+               $ticket->getFromDB($ticket_id);
+
+               foreach($ticket->getTicketActorsByType('group') as $actor_id => $actor)
+                  foreach($actor as $actor_type) {
+                     if ($actor_type == $_POST['actortype']) {
+                        $actorObj = new Group();
+                        $actorObj->getFromDB($actor_id);
+                        $actors[$actor_id] = $actorObj->getRawName();
+                     }
+                  }
+            }
+            $rand = Dropdown::showFromArray('from_actor',$actors,$options);
+            $rand = Group::dropdown($options_to);
+            break;
+      }
+   }
+}
Index: inc/ticket.class.php
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
--- inc/ticket.class.php    (date 1478764793000)
+++ inc/ticket.class.php    (date 1479123873000)
@@ -2211,6 +2211,8 @@
          }

          if (Session::haveRight(self::$rightname, UPDATE)) {
+            $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'replace_actor']
+                = __('Replace actor');
             $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'add_actor']
                = __('Add an actor');
             $actions[__CLASS__.MassiveAction::CLASS_ACTION_SEPARATOR.'enable_notif']
@@ -2227,10 +2229,134 @@
       return $actions;
    }

+   static function showFormMassiveActionReplaceActor($tickets) {
+      global $CFG_GLPI;

+      $types            = array(CommonITILActor::ASSIGN  => __('Assigned to'),
+          CommonITILActor::REQUESTER => __('Requester'),
+          CommonITILActor::OBSERVER => __('Watcher')
+      );
+      $rand = Dropdown::showFromArray('actortype', $types, array('display_emptychoice' => true));
-
+      
+      $paramsmassaction = array('"actortype"' => '__VALUE__',
+          'tickets'        => $tickets,
+          'right'         => array(UPDATE));

+      Ajax::updateItemOnSelectEvent("dropdown_actortype".$rand, "show_massiveaction_field",
+          $CFG_GLPI["root_doc"].
+          "/ajax/dropdownMassiveActionReplaceActor.php",
+          $paramsmassaction);
+      echo Html::scriptBlock(Html::jsSetDropdownValue("dropdown_actortype".$rand,CommonITILActor::ASSIGN));
+      echo "<br><span id='show_massiveaction_field'>&nbsp;</span>\n";

+   }
+
+   static function showMassiveActionsSubForm(MassiveAction $ma) {
+
+      switch ($ma->getAction()) {
+         case 'replace_actor' :
+            $items = $ma->getInput()['initial_items'];
+            $tickets = [];
+            if(!empty($items['Ticket']))
+               $tickets = array_keys($items['Ticket']);
+            static::showFormMassiveActionReplaceActor($tickets);
+            return true;
+      }
+
+      return parent::showMassiveActionsSubForm($ma);
+   }
+
+   function showActorReplaceForm($type, $rand_type, $tickets, $inobject=true) {
+      global $CFG_GLPI;
+
+      $types = array('user'  => __('User'), 'group' => __('Group'));
+
+      echo "<div ".($inobject?"style='display:none'":'')." id='actor$rand_type' class='actor-dropdown'>";
+      $rand   = Dropdown::showFromArray("actorobjecttype", $types,
+          array('display_emptychoice' => true, 'width'=>'200px'));
+      echo "<br />";
+      $params = array('type'            => '__VALUE__',
+          'actortype'       => $type,
+          'itemtype'        => $this->getType(),
+          'tickets'         => $tickets
+      );
+
+      Ajax::updateItemOnSelectEvent("dropdown_actorobjecttype$rand",
+          "from_actor$rand",
+          $CFG_GLPI["root_doc"]."/ajax/dropdownTicketCurrentActors.php",
+          $params);
+      echo "<span id='from_actor$rand' class='actor-dropdown'>&nbsp;</span>";
+      if ($inobject) {
+         echo "<hr>";
+      }
+      echo "</div>";
+   }
+
+   static function processMassiveActionsForOneItemtype(MassiveAction $ma, CommonDBTM $item,
+                                                       array $ids) {
+      
+      switch ($ma->getAction()) {
+         case 'replace_actor' :
+            $input = $ma->getInput();
+            $actorObj = $input['actorobjecttype'];
+            switch($actorObj){
+               case 'user':
+                  $actorLinkObj = new Ticket_User();
+                  break;
+               case 'group':
+                  $actorLinkObj = new Group_Ticket();
+                  break;
+            }
+            $actorIdField = $actorObj.'s_id';
+            foreach ($ids as $id) {
+               $actorLinkObj->getFromDB($id);
+               $addData = ['id' => $id];
+
+               switch($input['actortype']) {
+                  case CommonITILActor::REQUESTER:
+                     $addData['_itil_requester'] = [$actorIdField => $input['to_actor'], '_type' => $actorObj];
+                     break;
+                  case CommonITILActor::ASSIGN:
+                     $addData['_itil_assign'] = [$actorIdField => $input['to_actor'], '_type' => $actorObj];
+                     break;
+                  case CommonITILActor::OBSERVER:
+                     $addData['_itil_observer'] = [$actorIdField => $input['to_actor'], '_type' => $actorObj];
+                     break;
+                  default:
+                     $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
+                     $ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
+                     continue;
+                     break;
+               }
+               if(!$item->can($id, UPDATE)){
+                  $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_NORIGHT);
+                  $ma->addMessage($item->getErrorMessage(ERROR_RIGHT));
+                  continue;
+               }
+               if(!$item->update($addData)) {
+                  $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
+                  $ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
+                  continue;
+               }
+
+               $find_from = $actorLinkObj->find("`tickets_id` = $id AND `type` = '".$input['actortype']
+                   ."' AND `".$actorIdField."` = ".$input['from_actor']);
+
+               if(!empty($find_from)){
+                  $recId = array_keys($find_from)[0];
+                  if(!$actorLinkObj->delete(['id' => $recId])){
+                     $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_KO);
+                     $ma->addMessage($item->getErrorMessage(ERROR_ON_ACTION));
+                     continue;
+                  }
+               }
+               $ma->itemDone($item->getType(), $id, MassiveAction::ACTION_OK);
+            }
+            return;
+      }
+      parent::processMassiveActionsForOneItemtype($ma, $item, $ids);
+   }
+
    function getSearchOptions() {

       $tab                          = array();
@@ -6597,6 +6723,31 @@
       return $ticket_users_keys;
    }

+   function getTicketActorsByType($objtype = 'user') {
+      global $DB;
+
+      switch($objtype) {
+         case 'user':
+            $sql = "SELECT tu.`users_id` AS actor_id, tu.`type` AS type
+                            FROM `glpi_tickets_users` tu
+                            WHERE tu.`tickets_id` = ".$this->getId()." GROUP BY tu.`users_id`, tu.`type`";
+            break;
+
+         case 'group':
+            $sql = "SELECT gt.`groups_id` AS actor_id, gt.`type` AS type
+                            FROM `glpi_groups_tickets` gt
+                            WHERE gt.`tickets_id` = ".$this->getId()." GROUP BY gt.`groups_id`, gt.`type`";
+            break;
+      }
+      $res = $DB->query($sql);
+      $actors = [];
+      while($actor = $DB->fetch_assoc($res)) {
+         if(!isset($actors[$actor['actor_id']]))
+            $actors[$actor['actor_id']] = [];
+         $actors[$actor['actor_id']][] = $actor['type'];
+      }
+      return $actors;
+   }

    /**
     * @since version 0.90
orthagh commented 6 years ago

implementation in #3229 is completly broken (some warnings, replace non selected users by another)

Ux pov, the feature is poor understandable for a new comer (two GLPI dev didn't understand without explanation). Maybe a little radical, but adding an option to delete user could be better understandable.

cedric-anne commented 5 years ago

Please close this issue.