This is Part 3 of a 3-part series. If you haven't read Part 1, I'd suggest starting there for background info that's relevant to this post.
Despite the fact that John Peca was away on a remote island while I was doing all this, it really cannot be overstated how much I relied on his "input". Again, I scrutinized the work he did on a component with similar features, and thus learned the lion's share of what I needed.
Upon his return, I got on a call with him and picked his brain. Here's the culmination of what I've learnt so far about ExtJS and CMPs. For brevity, in the following description when I refer to "core path" and "assets path", I mean the "namespaced path for the Extra", usually: {$core_path}
or {$assets_path}
and then components/{$namespace}/
. Also, I'll refer to the filenames from the OAuth2Server Extra, as examples, so it might be helpful to have the files from the repo open while you follow along.
GPM adds the menu action to the package it generates, so when you install your Extra, there'll be a menu item under "Extras". Revo > 2.3.x automatically loads an action class from the
controllers
directory of the core path, with the filename{$action}.class.php
. In this case:{$core_path}components/oauth2server/controllers/manage.class.php
.The "manage" class includes the "index" class from the parent directory. Still not sure why both these classes couldn't be in one file, even though I asked John about this—I didn't understand the answer or maybe wasn't listening closely enough LOL. It might be preference, or for code organization, because the class in the "manage" file extends that in the "index" file. Anyways these two classes have methods that register the CSS and JS required for your CMP. The "index" file also has a method that loads lexicon topics into the ExtJS config. More on lexicon usage later.
The assets folder has a file
connector.php
. This instantiates MODX itself, along with the OAuth2Server class, and acts as a "gateway" to the processors, usually found under the core folder. In other words:{$assets_url}components/oauth2server/connector.php
-> hands requests to ->{$core_path}components/oauth2server/processors/{$action}
The processors do things like: get lists of objects, validate the inputs, create objects from that input, etc. But where do all these inputs come from? Here's where the ExtJS "magic" (or "devilry", depending on your viewpoint) happens.
In the assets folder, there are subfolders for CSS and JS. CSS makes stuff look nice. Under the
js/mgr/
folder there's a JS file, in my exampleoauth2server.js
. In that file, we "extend" theExt.Component
"class" and "register" it, so ExtJS "knows" about it. (For convenience I'll use the keywords introduced in ECMA 6, but when ExtJS 3.4 and Revo were first developed, "class" wasn't really a "thing" in JS.)In a subfolder
sections/
there's a filemanage.js
, which extendsMODx.Component
and registers it.In the subfolder
widgets
is where the fun begins. Themanage.panel.js
file extends theMODx.Panel
class and registers it. But it also configures the panel with tabs, one for each custom-registered "xtype". That brings us to...The actual widgets; for example,
clients.grid.js
extendsMODx.grid.Grid
and registers it. Note the first argument toExt.reg()
is thextype
value used inmanage.panel.js
—see how that connects? A registered component can be configured as part of another component by referencing its "xtype". I'm not pretending to understand fully how this works but a lightbulb went off in my head when I saw this so I thought I'd point it out :P
There's a LOT going on inside clients.grid.js
so let's start a new list. That'll make this insanely large post more readable, right?
Near the top, there's a function
Ext.applyIf()
, which basically sets config options. Wherever you see anaction
property in the options, it's setting a target class file, within the Extra'sprocessors/
folder that we talked about earlier, to which to send requests, via the connector. (It can't send requests directly to class files in thecore
folder because thecore
folder is not supposed to be web-accessible.)There's a bunch of options to configure the grid columns. I won't go into an exhaustive review of those right now, but here are some docs on ExtJS grids.
There's a
tbar
property that adds buttons to the top of your grid. Note alot of the properties that define UI text strings or labels refer to MODX lexicon entries, like this:_('oauth2server.clients.add')
. Those are defined in{$core_path}components/{$namespace}/lexicon/{$cultureKey}/{$topic}.inc.php
and loaded by the "index" class file we talked about earlier.The
Ext.extend()
function defines our custom methods:getMenu()
adds a context (right-click) menu.addClient()
is the method called by the button defined in the config. It loads another component, which extendsMODx.Window
, defined in the fileclients.window.js
. It's the modal window with the form elements to create and update individual row items.updateClient()
is similar toaddClient()
but it passes in a custom variable—we'll go over that when we review theclients.window.js
file.duplicateClient()
calls the create action but prefills the field values.removeClient()
calls the processor that deletes a row item, with a call toMODx.msg.confirm()
, to show a confirmation window.
Now we have a grid, with a button and context-menu items that do things. The last piece of the puzzle is the clients.window.js
.
The bulk of this file is a call to Ext.applyIf()
so basically we're configuring this modal window. By now you may recognize some of the properties, like action
for example.
The fields
property defines an array of form fields, each having some options to play with. xtype
is the only one that isn't self-explanatory, because there are lots of MODX-specific xtypes as well as those added by Extras. I don't even think they're all documented in any one place, except Bob Ray has a list.
The only other interesting bit is the custom variable passed through to set the client_id
field to readonly:true
. John clued me in to the fact that custom variables can be used, and that readonly
was was the property to use. Previously I'd tried disabled: true
but in that case the client_id
field value got stripped from the request to the processor.
Take-away: don't use disabled
.
Wow. Ok, so that pretty much sums up my adventures in CMP-land. Did I wake up afterwards, only to realize it was all a crazy dream? Haha. No.
But I did have this tiny glass bottle in my pocket with a label that said "MODX Me" :P
Other posts in this series: