⚠️ Urgent! Active Attacks on MODX Revolution Sites Below Revolution 2.6.5
Subscribe: RSS
  • Hi!

    I'm making an MODX extra that draws it's data from an external API and displays it in an ExtJS grid panel on a custom manager page.

    It's not too difficult to extend a basic processor, put the API data in a grid friendly format then return it to the grid.

    The problem is, I want it to take advantage of the powerful built-in grid functionality such as pagination and sorting.

    I originally started by extending modObjectGetListProcessor but of course this expects everything to be coming from a database table, and expects the data to be within a specific xPDO object format.
    After pulling my hair out for a while, I went back to just extending the basic modProcessor and attempted to mimic the modObjectGetListProcessor but without using objects and database tables.

    I've got pagination working. I just haven't been able to get sorting going yet.
    Here's my very rough "modAPIGetListProcessor". Does anyone know what needs to be done to get the sorting working? I'm sure it'll be dependent on what properties are passed from the grid when a column is clicked on.

    <?php
    class modAPIGetListProcessor extends modProcessor {
        /** @var string $defaultSortField The default field to sort by */
        public $defaultSortField = 'id';
        /** @var string $defaultSortDirection The default direction to sort */
        public $defaultSortDirection = 'ASC';
        /** @var int $currentIndex The current index of successful iteration */
        public $currentIndex = 0;
    
    
        public function initialize() {
            $this->setDefaultProperties(array(
                'start' => 0,
                'limit' => 20,
                'sort' => $this->defaultSortField,
                'dir' => $this->defaultSortDirection,
                'combo' => false,
                'query' => '',
            ));
            return parent::initialize();
        }
    
        public function getAPIData() {
            $whmcsUrl = $this->modx->getOption('modwhmcs.whmcs_url');
            $username = $this->modx->getOption('modwhmcs.username');
            $password = $this->modx->getOption('modwhmcs.password');
    
            // Set post values
            $postfields = array(
                'username' => $username,
                'password' => md5($password),
                'action' => 'gettickets',
                'responsetype' => 'json',
            );
    
            // Call the API
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, $whmcsUrl . 'includes/api.php');
            curl_setopt($ch, CURLOPT_POST, 1);
            curl_setopt($ch, CURLOPT_TIMEOUT, 30);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postfields));
            $response = curl_exec($ch);
            if (curl_error($ch)) {
                die('Unable to connect: ' . curl_errno($ch) . ' - ' . curl_error($ch));
            }
            curl_close($ch);
            // Attempt to decode response as json
            $jsonData = json_decode($response, true);
            $apiData['total'] = $jsonData['numreturned'];
            $apiData['results'] = $jsonData['tickets']['ticket'];
            return $apiData;
        }
    
        public function process() {
            $data = $this->getData();
            return $this->outputArray($data['results'],$data['total']);
        }
    
        public function getData() {
            $apiData = $this->getAPIData();
            $data = array();
            $data['results'] = array();
            $data['total'] = $apiData['total'];
    
            $limit = intval($this->getProperty('limit'));
            $start = intval($this->getProperty('start'));
    
            $count = 0;
            foreach ($apiData['results'] as $key => $value) {
                if ($key >= $start) {
                    if($count < $limit) {
                        array_push($data['results'], $value);
                        $count++;
                    }
                }
                if ($key > $limit) break;
            }
    
            //Sorting code should go here. 
     
            
    
            return $data;
        }
    }
    return 'modAPIGetListProcessor';
    





    [ed. note: muzzstick last edited this post 2 years, 5 months ago.]
      I'm lead developer at Digital Penguin Creative Studio in Hong Kong. https://www.digitalpenguin.hk
      Check out the MODX tutorial series on my blog at https://www.hkwebdeveloper.com
    • OK got it working now. Thanks to @joeke in the MODX Community Slack channel (modx.org) for helping me realise the property I was looking for was there all along wink

      This should work with any API data as long as you format it correctly. Just replace the code inside the getAPIData() function.

      Updated working code:

      <?php
      class modAPIGetListProcessor extends modProcessor {
          /** @var string $defaultSortField The default field to sort by */
          public $defaultSortField = 'id';
          /** @var string $defaultSortDirection The default direction to sort */
          public $defaultSortDirection = 'DESC';
      
          public function initialize() {
              $this->setDefaultProperties(array(
                  'start' => 0,
                  'limit' => 20,
                  'sort' => $this->defaultSortField,
                  'dir' => $this->defaultSortDirection,
                  'combo' => false,
                  'query' => '',
              ));
              return parent::initialize();
          }
      
          public function getAPIData() {
              $whmcsUrl = $this->modx->getOption('modwhmcs.whmcs_url');
              $username = $this->modx->getOption('modwhmcs.username');
              $password = $this->modx->getOption('modwhmcs.password');
      
              // Set post values
              $postfields = array(
                  'username' => $username,
                  'password' => md5($password),
                  'action' => 'gettickets',
                  'responsetype' => 'json',
              );
      
              // Call the API
              $ch = curl_init();
              curl_setopt($ch, CURLOPT_URL, $whmcsUrl . 'includes/api.php');
              curl_setopt($ch, CURLOPT_POST, 1);
              curl_setopt($ch, CURLOPT_TIMEOUT, 30);
              curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
              curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postfields));
              $response = curl_exec($ch);
              if (curl_error($ch)) {
                  die('Unable to connect: ' . curl_errno($ch) . ' - ' . curl_error($ch));
              }
              curl_close($ch);
              // Attempt to decode response as json
              $jsonData = json_decode($response, true);
              $apiData['total'] = $jsonData['numreturned'];
              $apiData['results'] = $jsonData['tickets']['ticket'];
              return $apiData;
          }
      
          public function process() {
              $data = $this->getData();
              return $this->outputArray($data['results'],$data['total']);
          }
      
          public function getData() {
              $apiData = $this->getAPIData();
              $data = array();
              $data['results'] = array();
              $data['total'] = $apiData['total'];
      
              $limit = intval($this->getProperty('limit'));
              $start = intval($this->getProperty('start'));
      
              $count = 0;
              foreach ($apiData['results'] as $key => $value) {
                  if ($key >= $start) {
                      if($count < $limit) {
                          array_push($data['results'], $value);
                          $count++;
                      }
                  }
                  if ($key > $limit) break;
              }
      
              //Sort
              if (empty($sortKey = $this->getProperty('sort'))) $sortKey = $this->defaultSortField;
              if (empty($sortDir = $this->getProperty('dir'))) $sortDir = $this->defaultSortDirection;
              if ($sortDir == 'DESC') {
                  foreach ($data['results'] as $key => $row) {
                      $dates[$key]  = $row[$sortKey];
                  }
                  array_multisort($dates, SORT_DESC, $data['results']);
              } else {
                  foreach ($data['results'] as $key => $row) {
                      $dates[$key]  = $row[$sortKey];
                  }
                  array_multisort($dates, SORT_ASC, $data['results']);
              }
      
              return $data;
          }
      }
      return 'modAPIGetListProcessor';
      
      [ed. note: muzzstick last edited this post 2 years, 5 months ago.]
        I'm lead developer at Digital Penguin Creative Studio in Hong Kong. https://www.digitalpenguin.hk
        Check out the MODX tutorial series on my blog at https://www.hkwebdeveloper.com