We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 38878
    • 255 Posts
    Maybe someone can assist. I have read everything I can find on ModExt, and followed the Doodles tuts and docs to the letter (I think). I am developing a CMP in Revo 2.3.5 and the issue I am having is some (apparently not all) the config array values from the class file are undefined in the index.php page. The index.php loads fine from the menu but the calls to the js and css are giving 404 errors. The paths are defined in the main class file (just like in the tuts). No errors in the logs, just 404 errors in the browser console.

    The _ construct function from the class file (with the config array) is pretty straightforward. I just removed some paths I didn't need:
    function __construct(modX &$modx, array $config = array()) {
          $this->modx =& $modx;
    
          $corePath   = $this->modx->getOption('myAddon.core_path',null,$modx->getOption('core_path').'components/myAddon/');
          $assetsUrl  = $this->modx->getOption('myAddon.assets_url', null, $this->modx->getOption('assets_url').'components/myAddon/');
          $this->modx->lexicon->load('myAddon:default');
          $this->config = array_merge(array(
            'corePath' => $corePath,
            'modelPath' => $corePath.'model/',
            'managerPath' => $corePath.'manager/',
            'controllersPath' => $corePath.'controllers/',
            'templatesPath' => $corePath.'templates/',
            'baseUrl' => $assetsUrl,
            'cssUrl' => $assetsUrl.'css/',
            'jsUrl' => $assetsUrl.'js/',
              ),$config);
        }


    And the references from the index.php are also pretty simple (taken also from the tuts):
    require_once dirname(dirname(dirname(__FILE__))) . '/model/myAddon/myAddon.class.php';
    class MyAddonIndexManagerController extends modExtraManagerController {
        /** @var MyAddon $myAddon */
        public $myAddon; 
        public function initialize() {
            $this->myAddon = new MyAddon($this->modx);
            $this->addCss($this->myAddon->config['cssUrl'].'mgr.css');
            $this->addJavascript($this->myAddon->config['jsUrl'].'mgr/myAddon.js');
            $this->addHtml('<script type="text/javascript">
            Ext.onReady(function() {
                MyAddon.config = '.$this->modx->toJSON($this->myAddon->config).';
            });
            </script>');
            return parent::initialize();
        }
        public function getLanguageTopics() {
                return array('myAddon:default');
        }
        public function checkPermissions() { return true;}
        public function process(array $scriptProperties = array()) {}
        public function getPageTitle() { return $this->modx->lexicon('myAddon'); }
        public function loadCustomCssJs() {
            $this->addJavascript($this->myAddon->config['jsUrl'].'mgr/widgets/myAddon.grid.js');
            $this->addJavascript($this->myAddon->config['jsUrl'].'mgr/widgets/home.panel.js');
            $this->addLastJavascript($this->myAddon->config['jsUrl'].'mgr/sections/index.js');
        }
        public function getTemplateFile() {
            return $this->myAddon->config['templatesPath'].'home.tpl';
        }
    }


    The template at the bottom loads fine (and that references the config array) but the references to the js and css files do not resolve the path. Instead they return as
    http:/mysite.com/manager/mgr/filename.js 
    and not the config path. Also, the call
     MyAddon.config = '.$this->modx->toJSON($this->myAddon->config).';
    returns a blank array even though the template is loading. I hard-coded the paths to test and everything looks fine, but I don't want to do that obviously. I'm hoping it's something obvious I'm overlooking, but I've been banging on it and see no reason why these wouldn't resolve.

    This question has been answered by multiple community members. See the first response.

    • discuss.answer
      • 38878
      • 255 Posts
      Just to followup in case anyone else runs into this, and to answer my own question...

      I figured out the issue with the undefined config. According to https://rtfm.modx.com/revolution/2.x/case-studies-and-tutorials/developing-an-extra-in-modx-revolution/developing-an-extra-in-modx-revolution,-part-ii, the "require_once" that includes the main class and (and it's config) is outside the initialize function.

      I found another thread related 2.3 CMP where Bob posts his example CMP calling the main class at http://forums.modx.com/thread/?thread=92833&page=2 inside the initialize function, which works.


        • 44195
        • 293 Posts
        Hmm, works for me.
        Is your index.class.php file in the controllers directory?
          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
          • 38878
          • 255 Posts
          Quote from: muzzstick at Aug 06, 2015, 01:16 AM
          Hmm, works for me.
          Is your index.class.php file in the controllers directory?

          Thanks. The index.class.php is in the controllers/default directory. In 2.3.X that's where it looks by "default" from what I've read though the tuts seem to be confusing on this point. But it wouldn't resolve unless I put the controller there. I just move the class require into the initialize function and it seems to work.

          I'm a bit of a noob on ModExt and ExtJs but I'm fumbling through. Now if I could only find some documentation on retrieving external rest data into a ModExt grid...
            • 44195
            • 293 Posts
            Glad you got it working but it shouldn't need to be in a default directory.
            The index.class.php controller shown in the Doodles tutorial will work fine if it's just located in the /core/components/doodles/controllers/ directory. If you set your menu action to index (or whatever you've named your file) it'll find it.
            Make sure you don't have an index.php file one level up in your doodles directory though (an older way of doing it).

            Can you retrieve your external rest data by overriding the getlist processor? Some of the comments touch on that at http://www.hkwebdeveloper.com/modext-tutorial-series-part-2.html
              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
              • 38878
              • 255 Posts
              Quote from: muzzstick at Aug 06, 2015, 01:57 AM
              Glad you got it working but it shouldn't need to be in a default directory.
              The index.class.php controller shown in the Doodles tutorial will work fine if it's just located in the /core/components/doodles/controllers/ directory. If you set your menu action to index (or whatever you've named your file) it'll find it.
              Make sure you don't have an index.php file one level up in your doodles directory though (an older way of doing it).

              Can you retrieve your external rest data by overriding the getlist processor? Some of the comments touch on that at http://www.hkwebdeveloper.com/modext-tutorial-series-part-2.html

              Thanks. Apreciate the help on this. I'm trying to glue together the two threads (the one above and http://forums.modx.com/thread/97744/modx-ext-js-retrieve-value-from-php-file#dis-post-528711) to do what I need. I'm trying to retrieve a list of items from a 3rd party rest api and am at the point of modifying the getList processor so it will render to the grid. In my getList.class.php so far I've got:

              <?php
              class EventGetListProcessor extends modObjectGetListProcessor {
                  public function outputEvents(array $output, $count = false) {
              
                      /* not sure this is required here */
                      $path = $modx->getOption('ebents.core_path',NULL, $modx->getOption('core_path') .'components/ebents/') . 'model/ebents/';
                      require_once $path . 'ebents.class.php';
                      
                      // calls to main class functions to retrieve events
                      $mgr_client = new Ebents($modx);
                      
                      $params = array ();
                      $params['expand'] = 'venue,organizer';
                      $args = array('id' => 'me', 'data' => 'owned_events', 'params' => $params);
                      $output = $mgr_client->getData('users', $args);
                      
                      $count = count($output->events);
                      //error_log("count :" . $count); 
                      $msg = ($count>0)?"OK":NULL;
              
                      print '{"total":"'.$count.'","results":'.$modx->toJSON($output->events).',"success":true,"msg":"'.$msg.'"}';
                       
                      //error_log(print_r($output,true)); 
                  }
              }
              return 'EventGetListProcessor';


              I've followed through the threads ( i think) and modified my grid.js etc. as discussed in the two threads.

              My questions are:

              1. should I really be extending modObjectGetListProcessor as that class is really for modx objects in the db and it seems kinda "hacky" to me?
              2. do I need to recall my main class or is it already defined via the connector?
              3. how do I call outputEvents function to get this loaded into the store?

              Maybe this isn't a common scenario, but it sure would be useful to have a tut documented for 3rd party rest CMP integration based on these two threads. Maybe more folks would integrate 3rd party services and that could only help adoption as MOdExt and ExtJS can be daunting for ExtJS noobs like yours truly. I'll post a request the github for such.
                • 38878
                • 255 Posts
                Following up. I got the answer to my first question about extending modObjectGetListProcessor and changed to your modProcessor example. I modified my home.panel.js according to your getValue example:
                ...
                Ext.extend(Ebents.panel.Home,MODx.Panel, {
                    checkValue: function() {
                        MODx.Ajax.request({
                            url: Ebents.config.connectorUrl
                            , params: {
                                action: 'mgr/events/getEvents'
                            }
                            , listeners: {
                                success: {
                                    fn: function (response) {
                                        console.log('The value returned from the processor is: '+response.msg);
                                        if(response.msg == "OK") {
                                            this.loadGrid();
                                        }
                                    }, scope: this
                                }
                            }
                        });
                    },loadGrid: function() {
                        var ebentsGrid = MODx.load({
                            xtype: 'ebents-grid-ebents'
                            ,cls: 'main-wrapper'
                            ,preventRender: true
                            ,flex: 1
                        });
                        var tab = Ext.getCmp('main-tab');
                        tab.add(ebentsGrid);
                        this.doLayout();
                    }
                });
                ...


                And my getevents.class.php is:
                <?php
                class GetEventsProcessor extends modProcessor {
                    public $output = array();
                    public $count;
                    public $msg;
                    public function process() {
                
                        // calls to main class functions to retrieve events
                        $this->mgr_client = new Ebents($modx);
                        
                        $this->params = array ();
                        $this->params['expand'] = 'venue,organizer';
                        $this->args = array('id' => 'me', 'data' => 'owned_events', 'params' => $this->params);
                        $this->output = $this->mgr_client->getData('users', $this->args);
                        
                        $this->count = count($this->output->events);
                        //error_log("count :" . $this->count); 
                        $this->msg = ($this->count > 0)?"OK":NULL;
                
                        return '{"total":"'.$this->count.'","results":'.$modx->toJSON($this->output->events).',"success":true,"msg":"'.$this->msg.'"}';
                         
                        //error_log(print_r($this->output,true)); 
                    }
                }
                return 'GetEventsProcessor';


                Im not sure this will render right but I can't fire it anyway due to a 500 error:
                PHP Fatal error:  Call to a member function getOption() on null in /paas/c0059/www/core/components/ebents/model/ebents/ebents.class.php on line 14


                Online 14 of the main class is a getOption call to retrieve system setting which works fine everywhere else so it seems to be that $modx is not defined here which seems a bit strange to me. I need to evoke my main class to retrieve the data from the rest endpoint. Is this not the right place to do it? [ed. note: harveyev last edited this post 8 years, 9 months ago.]
                  • 44195
                  • 293 Posts
                  Only looking at this from my phone but can you try passing $this->modx instead of just $modx to your class?

                  EDIT:

                  At a computer now. smiley
                  From inside a processor, you need to access it via $this->modx instead of just $modx like you might in a snippet. [ed. note: muzzstick last edited this post 8 years, 9 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
                    • 38878
                    • 255 Posts
                    Quote from: muzzstick at Aug 07, 2015, 02:06 AM
                    Only looking at this from my phone but can you try passing $this->modx instead of just $modx to your class?

                    EDIT:

                    At a computer now. smiley
                    From inside a processor, you need to access it via $this->modx instead of just $modx like you might in a snippet.

                    Yeah that did it wink. Thanks! I really appreciate your help on this. Probably should have caught that... I am now retrieving the data fine but am at the point of rendering in the grid. My data is nested. Two issues I see at this point:


                    1. I think I'm having a timing problem with the render. In the browser console I'm getting the error when I add the grid to the index.js:
                    Uncaught TypeError: b[(d.xtype || e)] is not a function
                    . Some research point to the render happening before the target element is present in the dom, but IDK if that's the root of the issue since I am setting preventRender: true on the grid as per your code. In my case it probably has to do with the next issue.

                    My home.js looks like this: ( I reverted back to the original from the tut for simplicity and I don't think I need the getValue check):
                    Ebents.panel.Home = function(config) {
                        config = config || {};
                        Ext.apply(config,{
                            border: false
                            ,baseCls: 'modx-formpanel'
                            ,cls: 'container'
                            ,items: [{
                                html: '<h2>'+_('debents.management')+'</h2>'
                                ,border: false
                                ,cls: 'modx-page-header'
                            },{
                                xtype: 'modx-tabs'
                                ,defaults: { border: false ,autoHeight: true }
                                ,border: true
                                ,items: [{
                                    title: _('ebents.panel_1_header')
                                    ,defaults: { autoHeight: true }
                                    ,items: [{
                                        html: '<p>'+_('ebents.management_desc')+'</p>'
                                        ,border: false
                                        ,bodyCssClass: 'panel-desc'
                                    },{
                                        xtype: 'ebents-grid-ebents'
                                        ,cls: 'main-wrapper'
                                        ,preventRender: true
                                    }]
                                }]
                                // only to redo the grid layout after the content is rendered
                                // to fix overflow components' panels, especially when scroll bar is shown up
                                ,listeners: {
                                    'afterrender': function(tabPanel) {
                                        tabPanel.doLayout();
                                    }
                                }
                            }]
                        });
                        Ebents.panel.Home.superclass.constructor.call(this,config);
                    };
                    Ext.extend(Ebents.panel.Home,MODx.Panel);
                    Ext.reg('ebents-panel-home',Ebents.panel.Home);


                    And my grid.js looks like this:
                    Ebents.grid.Ebents = function(config) {
                        config = config || {};
                        Ext.applyIf(config,{
                            id: 'ebents-grid-events'
                            ,url: Ebents.config.connectorUrl
                            ,baseParams: { action: 'mgr/event/getList' }
                            ,fields: ['id','name->text','description->text','organizer->name','venue->name','start->local','end->local','status']
                            //<- should field refs be in JSON dot notation or array? And if so, where is it converted to array (or where should it be)?
                            ,trackMouseOver:true 
                            ,disableSelection:true 
                            ,loadMask: true  
                            ,paging: true
                            ,remoteSort: true
                            ,anchor: '97%'
                            ,columns: [{
                                    header: _('ebents.event_id')
                                    ,dataIndex: 'id'
                                    ,sortable: true
                                    ,width: 70
                                },{
                                    header: _('ebents.event_name')
                                    ,dataIndex: 'name->text'//<- nested data in array form but where is this converted from JSON to array if so? 
                                    ,sortable: true
                                    ,width: 150
                                    ,editor: { xtype: 'textfield' }
                                },{
                                    header: _('ebents.event_description')
                                    ,dataIndex: 'description->text'
                                    ,sortable: false
                                    ,width: 250
                                    ,editor: { xtype: 'textfield' }
                                },{
                                    header: _('ebents.event_organizer')
                                    ,dataIndex: 'organizer->name'
                                    ,sortable: true
                                    ,width: 80
                                    ,editor: { xtype: 'textfield' }
                                },{
                                    header: _('ebents.event_venue')
                                    ,dataIndex: 'venue->name'
                                    ,sortable: true
                                    ,width: 80
                                    ,editor: { xtype: 'textfield' }
                                },{
                                    header: _('ebents.event_start')
                                    ,dataIndex: 'start->local'
                                    ,sortable: true
                                    ,width: 80
                                    ,editor: { xtype: 'textfield' }
                                },{
                                    header: _('ebents.event_end')
                                    ,dataIndex: 'end->local'
                                    ,sortable: true
                                    ,width: 80
                                    ,editor: { xtype: 'textfield' }
                                },{
                                    header: _('ebents.event_status')
                                    ,dataIndex: 'status'
                                    ,sortable: true
                                    ,width: 80
                                    ,editor: { xtype: 'textfield' }
                                }
                            ]
                        });
                        Ebents.grid.Ebents.superclass.constructor.call(this,config)
                    };
                    Ext.extend(Ebents.grid.Ebents,MODx.grid.Grid);
                    Ext.reg('ebents-grid-events',Ebents.grid.Ebents);


                    And my getevents.class.php :
                    <?php
                    class GetEventsProcessor extends modProcessor {
                        public $output = array();
                        public $count;
                        public $msg;
                        public $modx;
                        public function process() {
                            $this->mgr_client = new Ebents($this->modx);
                            $this->params = array ();
                            $this->params['expand'] = 'venue,organizer';
                            $this->args = array('id' => 'me', 'data' => 'owned_events', 'params' => $this->params);
                            $this->output = $this->mgr_client->getData('users', $this->args);
                            $this->count = count($this->output->events);
                            //error_log("count :" . $this->count); 
                            return '{"total":"'.$this->count.'","results":'.$this->modx->toJSON($this->output->events).',"success":true}';
                            //error_log(print_r($this->output->events,true)); 
                        }
                    }
                    return 'GetEventsProcessor';


                    2. I know the data coming from the ajax response is supposed to be passed as JSON to the store and the data is converted, but I see no store defined as per the Tut. Is the store "implied" if not defined? And other examples I see are converting data to an array for rendering. The ExtJS docs are weak here as far as manipulating the store and grid output and if ModExt is abstracting store manipulation stuff for convenience I don't see it documented.

                      • 44195
                      • 293 Posts
                      Can you show some of the sample json that is returned to the grid?

                      Use Firebug to have a look at how the standard getlist processor structures the json that is returned to the grid.

                      The fields should be the the json field names which off the top of my head would be the same as the array keys after they've been converted.
                      ExtJS has a JsonReader which is implemented in the MODX.grid superclass. You can find it in /manager/assets/modext/widgets/core/modx.grid.js

                      I haven't attempted feeding data into a grid from anything besides an overridden getlist processor before but I'm definitely keen to give it a go as soon as I get some time.

                      There's an example of an overridden modprocessor in the core for lexicons which are read from a file and then populate a grid.
                      Have a look at:
                      /core/model/modx/processors/workspace/lexicon/getlist.class.php




                        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