MODExt Tutorial Series - Part 2

By: Murray Wood Published on: 02/07/2015

Upload an image into the MODExt grid.

This series of tutorials is aimed at web developers and written to expand upon what was learned in the official Developing an Extra in MODX Revolution tutorials.

In this post we will look at adding more fields into our Doodles database table via the xPDO schema, adding an image upload function to the create doodle window and displaying that image in the grid.

Upload an image into the MODExt grid.
  1. Modifying the schema
  2. Create a location for images
  3. Adding an image upload field
  4. Modify the create processor to handle the file
  5. Display the uploaded image in the grid
  6. Complete Code

Requirements

For this tutorial to make sense, you should have already completed the following three in order:

  1. Developing an Extra in MODX Revolution - Part 1
  2. Developing an Extra in MODX Revolution - Part 2
  3. MODExt Tutorial Series - Part 1

STEP 1 - MODIFYING THE SCHEMA

We're going to start by making a very simple modification to our schema which involves adding a field for a filename. Open up your schema file which should be located at /www/doodles/core/components/doodles/model/schema/doodles.mysql.schema.xml.

Currently your schema should look like below except for on line 6 where we've added a new field with a key of "filename". Add that in now.

<?xml version="1.0" encoding="UTF-8"?>
<model package="doodles" baseClass="xPDOObject" platform="mysql" defaultEngine="MyISAM" version="1.0">
    <object class="Doodle" table="doodles" extends="xPDOSimpleObject">
        <field key="name" dbtype="varchar" precision="255" phptype="string" null="false" default=""/>
        <field key="description" dbtype="text" phptype="string" null="false" default=""/>
        <field key="filename" dbtype="varchar" precision="50" phptype="string" null="true" default=""/> 
        <field key="createdon" dbtype="datetime" phptype="datetime" null="true"/>
        <field key="createdby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0" />
        <field key="editedon" dbtype="datetime" phptype="datetime" null="true"/>
        <field key="editedby" dbtype="int" precision="10" attributes="unsigned" phptype="integer" null="false" default="0" />

        <aggregate alias="CreatedBy" class="modUser" local="createdby" foreign="id" cardinality="one" owner="foreign" />
        <aggregate alias="EditedBy" class="modUser" local="editedby" foreign="id" cardinality="one" owner="foreign" />
    </object>
</model>

Save the file.

Do you remember the build script (build.schema.php) located in our _build directory that was used to create the doodles database table in the original tutorial? Well, because we made a change to the schema we now need this to be reflected in both the database and the xPDO class files.

The good news is we don't need to make any changes to this script as we are still only using the one table. (We'll create another next time!) What we do have to do however is delete the xPDO class files that were previously generated by the script so we can create new ones.

Navigate to /www/doodles/core/components/doodles/model/doodles/

MODX Doodles CMP Directory Structure

This directory contains the xPDO class files that were generated by the build script last time as well as our custom Doodles class. We want to delete everything here except for our doodles.class.php file. Yes, that means delete the mysql folder and its contents too!

We should now only have the one file in that directory as shown in the image below.

MODX Doodles CMP Directory Structure Modified

Just before we run the script again, let's manually remove the modx_doodles table from our database. Using a tool such as PHPMyAdmin find our doodles table and delete it. Of course be very careful not to touch anything else!

MODX Doodles CMP Remove Tables in PHPMyAdmin

Now it's time to run the build script again. Open your browser and navigate to the script's location. If you're using localhost this should be: http://localhost/doodles/_build/build.schema.php

If you have followed all the steps correctly there shouldn't be any errors and you'll be presented with an output similar to the following:

[2015-06-02 08:34:46] (INFO @ /doodles/_build/build.schema.php)
Created table `modx_doodles`
SQL: CREATE TABLE `modx_doodles` (`id` INTEGER unsigned NOT NULL AUTO_INCREMENT, `name` VARCHAR(255) NOT NULL DEFAULT '', `description` TEXT NOT NULL, `filename` VARCHAR(50) NULL DEFAULT '', `createdon` DATETIME NULL, `createdby` INT(10) unsigned NOT NULL DEFAULT '0', `editedon` DATETIME NULL, `editedby` INT(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`)) ENGINE=MyISAM
[2015-06-02 08:34:46] (INFO @ /doodles/_build/build.schema.php)
Done!

To summarise, whenever you make a change to a table in the schema:

  1. Delete xPDO class files (or at least move them somewhere else).
  2. Delete the specific database table you wish to regenerate.

STEP 2 - CREATE A LOCATION FOR IMAGES

Create a new directory at /www/your-modx-install/assets/components/doodles/uploads/ . Note how it's in our MODX sub-directory and not our Doodles sub-directory. Although they are separate now, when Doodles is installed as an Extra they will become one.

Rather than hard-code the location, allow the user to easily change it by including a system setting.

Create a third Doodles setting called doodles.upload_url and have the value point to the uploads/ directory. This one is a little different as it's not based in our Doodles directory. See the image below.

MODX Doodles CMP New System Setting for image

Next, let's go into our Doodles class file located at /www/doodles/core/components/doodles/model/doodles.class.php and we'll add 2 lines. The first will be a getOption() command on line 10 which will get the upload url from our new system setting and if the system setting doesn't exist it will set a default location. On line 21 we will merge that value into our config array with all the others so we can access it from JavaScript.

$basePath = $this->modx->getOption('doodles.core_path',$config,$this->modx->getOption('core_path').'components/doodles/');
$assetsUrl = $this->modx->getOption('doodles.assets_url',$config,$this->modx->getOption('assets_url').'components/doodles/');
$uploadUrl = $this->modx->getOption('doodles.upload_url',$config,$this->modx->getOption('assets_url').'components/doodles/uploads/');
$this->config = array_merge(array(
    'basePath' => $basePath,
    'corePath' => $basePath,
    'modelPath' => $basePath.'model/',
    'processorsPath' => $basePath.'processors/',
    'templatesPath' => $basePath.'templates/',
    'chunksPath' => $basePath.'elements/chunks/',
    'jsUrl' => $assetsUrl.'js/',
    'cssUrl' => $assetsUrl.'css/',
    'assetsUrl' => $assetsUrl,
    'uploadUrl' => $uploadUrl,
    'connectorUrl' => $assetsUrl.'connector.php',
),$config);
$this->modx->addPackage('doodles',$this->config['modelPath']);

STEP 3 - ADDING AN IMAGE UPLOAD FIELD

Now we're going to have a play with uploading images into our doodles via our custom manager page.

Just like in the last tutorial let's open up the code for our grid at /www/doodles/assets/components/doodles/js/mgr/widgets/doodles.grid.js and scroll all the way to the bottom.

You can see below the extra code that has been added to our CreateDoodle window. The first one on line 188 is a property that allows a file to be uploaded. Below that, it's simple a matter of adding an extra field but rather than using an xtype of textfield, we need to use fileuploadfield. We added filename to our database table earlier so we use that for the name and we can add a fieldLabel and some emptyText (text that appears in the empty field) via our lexicon tags.

Doodles.window.CreateDoodle = function(config) {
    config = config || {};
    Ext.applyIf(config,{
        title: _('doodles.doodle_create')
        ,url: Doodles.config.connectorUrl
        ,baseParams: {
            action: 'mgr/doodle/create'
        }
        ,fileUpload: true
        ,fields: [{
            xtype: 'textfield'
            ,fieldLabel: _('doodles.name')
            ,name: 'name'
            ,anchor: '100%'
        },{
            xtype: 'textarea'
            ,fieldLabel: _('doodles.description')
            ,name: 'description'
            ,anchor: '100%'
        },{
            xtype: 'fileuploadfield'
            ,fieldLabel: _('doodles.file_upload')
            ,name: 'filename'
            ,emptyText: _('doodles.select_image')
        }]
    });
    Doodles.window.CreateDoodle.superclass.constructor.call(this,config);
};
Ext.extend(Doodles.window.CreateDoodle,MODx.Window);
Ext.reg('doodles-window-doodle-create',Doodles.window.CreateDoodle);    

Don't forget to add the following 2 lines to your lexicon file! (Whenever altering your lexicon file you won't see the results rendered until you clear your cache)

$_lang['doodles.file_upload'] = 'Select Image to Upload';
$_lang['doodles.select_image'] = 'Select an image';

We now have a file selector field as part of our window!

MODX Doodles CMP Upload Field

STEP 4 - MODIFY THE CREATE PROCESSOR TO HANDLE THE FILE

Things will start to get a bit more involved now. Open your CreateDoodleProcessor at /www/doodles/core/components/doodles/processors/mgr/doodle/create.class.php. Currently, we're just overriding the beforeSave() public method in order to check for duplicates in the name field. We're also going to check for duplicate file names but we won't make them a requirement.

    public function beforeSave() {
        $name = $this->getProperty('name');
        if (empty($name)) {
            $this->addFieldError('name',$this->modx->lexicon('doodles.doodle_err_ns_name'));
        } else if ($this->doesAlreadyExist(array('name' => $name))) {
            $this->addFieldError('name',$this->modx->lexicon('doodles.doodle_err_ae'));
        }
    $fileName = $this-&gt;getProperty('filename');
    if ($this-&gt;doesAlreadyExist(array('filename' =&gt; $fileName))) {
        $this-&gt;addFieldError('filename',$this-&gt;modx-&gt;lexicon('doodles.doodle_err_ae_image'));
    }

    return parent::beforeSave();
}


You can see we're using getProperty() to grab the filename value and then on line 16 we're using the doesAlreadyExist() function to check if it's unique.

We're going to now add a public variable to put our media source object into, add a function to the active media source as well as override a public function. Let's start with the variable and private function. Add the following just below your beforeSave() function:

    /** @var modMediaSource $source */
    public $source;
    /**
     * Get the active Source
     * @return modMediaSource|boolean
     */
    private function getSource() {
        $this->modx->loadClass('sources.modMediaSource');
        $this->source = modMediaSource::getDefaultSource($this->modx,$this->getProperty('source'));
        if (empty($this->source) || !$this->source->getWorkingContext()) {
            return false;
        }
        return $this->source;
    }

The above is taken directly from the MODX core. The $source variable holds the media source object and the function loads the media source and puts it into the variable before returning it.

It's time for the heavy lifting. We're going to upload our file, get our file name and save it to the database. Override the public process() function.

    public function process() {
        // Get filesystem source
        if (!$this->getSource()) {
            return $this->failure($this->modx->lexicon('permission_denied'));
        }
        $this->source->initialize();
        if (!$this->source->checkPolicy('create')) {
            return $this->failure($this->modx->lexicon('permission_denied'));
        }
        $uploadUrl = $this->modx->getOption('doodles.upload_url',$this->modx->config,$this->modx->getOption('assets_url').'components/doodles/uploads/');
        $success = $this->source->uploadObjectsToContainer($uploadUrl,$_FILES);
        if (empty($success)) {
            $msg = '';
            $errors = $this->source->getErrors();
            foreach ($errors as $k => $msg) {
                $this->modx->error->addField($k,$msg);
            }
            return $this->failure($msg);
        }
    // Get the filename string from the $_FILES array
    $filenames = array();
    if (is_array($_FILES)) {
        foreach ($_FILES as $file) {
            if (!empty($file['name'])) {
                $filenames[] = $file['name'];
            }
        }
    }
    // We only want the first filename is there are somehow more than one.
    $fileName = $filenames[0];
    $this->setProperty('filename', $fileName);

    return parent::process();
}

On lines 40 - 44 we get the media source using our handy getSource() function from above and then on lines 45 - 47 we confirm we have the correct permission to create something on the media source.

On line 48 we set our $uploadURL using a similar technique to what we did in our doodles.class.php file. (It gets the location from the system setting, and if it can't it falls back to a default). We then use the media source's uploadObjectsToContainer() function to save our file. The boolean result is saved to $success and if not positive we can report an error. Return to the parent and we're done!

We check that $_FILES is an array and then take all the filenames and put them into our own $filenames array. As this time we're only looking to upload a single file we'll just take the first filename in the array. After that, use setProperty() to add the filename to our processor. We then call our getSource() function from above checking the user has the necessary permissions. Return to the parent's process() function to continue. This will allow the processor to go ahead and save our filename into the database along with the other fields.

To recap, in our overridden process() function we:

  1. Retrieved the active media source object
  2. Initialized the media source
  3. Checked user has the create permission
  4. Grabbed the target URL from our doodles.upload_url setting
  5. Saved the file to our target location with uploadObjectsToContainer()
  6. Checked the save was successful
  7. Retrieved the filename
  8. Set the filename as a property on our processor

Phew! Now go back to the MODX manager and try adding a new Doodle using the create button. Go to the upload directory you specified and you should see the image sitting there happily. But wait a minute, we want to see the image in the manager!

STEP 5 - DISPLAY THE UPLOADED IMAGE IN THE GRID

At the moment on our grid we only have three columns ID, Name and Description. Let's create a fourth one called Image. Open up your grid file at /www/doodles/assets/components/doodles/js/mgr/widgets/doodles.grid.js

    ,fields: ['filename','id','name','description','menu']

On line 8 add filename to your fields!

On around line 31 your columns should be defined. Let's add our image column to the top just above our ID column:

    ,columns: [{
            header: _('doodles.image')
            ,dataIndex: 'filename'
            ,sortable: false
            ,width:150
            ,renderer: function(value, metaData, record){
                return '<img src="' + MODx.config.site_url + MODx.config['doodles.upload_url'] + value + '">';
            }
        },{
            header: _('id')
            ,dataIndex: 'id'
            ,sortable: true
            ,width: 60
        },{

We define the column header with a tag to our lexicon entry (if you haven't added that one, do it now). Our database field is filename so set the dataIndex to the same. We don't want to sort our rows by the image filename so that can be false. Width is whatever you like.

The renderer is something we haven't seen before. We specify a function for the renderer passing in parameters from the grid itself. All our function will do is return an img HTML tag. We create the img src path using 2 tags and the filename value passed in from the grid. The MODx.config.site_url is owned by the MODx object. The MODx.config['doodles.upload_url'] is the tag we specified in our system settings and our doodles.class.php file. Finally, value is the filename from the grid.

Reload your Doodles grid and you should see your images there in all their glory!

MODX Doodles CMP Image upload complete

This second tutorial is already longer than I wanted it to be so in the next installment we'll look at how to resize those images on the fly so they fit your grid properly, and also how to remove the images from your uploads directory when you delete a Doodle from your database.

STEP 6 - COMPLETE CODE

/www/doodles/assets/components/doodles/js/mgr/widgets/doodles.grid.js

Doodles.grid.Doodles = function(config) {
    config = config || {};
    Ext.applyIf(config,{
        id: 'doodles-grid-doodles'
        ,url: Doodles.config.connectorUrl
        ,baseParams: { action: 'mgr/doodle/getList' }
        ,fields: ['filename','id','name','description','menu']
        ,paging: true
        ,remoteSort: true
        ,anchor: '97%'
        ,autoExpandColumn: 'name'
        ,save_action: 'mgr/doodle/updateFromGrid'
        ,autosave: true
        ,listeners: {
            render: function() {
                if (this.store.getCount() == 0) {
                    console.log('store not loaded yet');
                    this.store.on('load', function() {
                        console.log('load after render');
                        Ext.getCmp('recNum').update('Records: '+this.store.getCount());
                    }, this, {
                        single: true
                    });
                } else {
                    console.log('store already loaded');
    
                }
            }
        }
        ,columns: [{
            header: _('doodles.image')
            ,dataIndex: 'filename'
            ,sortable: false
            ,width:200
            ,renderer: function(value, metaData, record){
                return '&lt;img src=&quot;' + MODx.config.site_url + MODx.config['doodles.upload_url'] + value + '&quot;&gt;';
            }
        },{
            header: _('id')
            ,dataIndex: 'id'
            ,sortable: true
            ,width: 60
        },{
            header: _('doodles.name')
            ,dataIndex: 'name'
            ,sortable: true
            ,width: 100
            ,editor: { xtype: 'textfield' }
        },{
            header: _('doodles.description')
            ,dataIndex: 'description'
            ,sortable: false
            ,width: 200
            ,editor: { xtype: 'textfield' }
        }],tbar:[{
            text: _('doodles.doodle_create')
            ,handler: this.createDoodle
        },{
            xtype: 'tbtext'
            ,id: 'recNum'
            ,text: 'Loading...'
            ,style: 'color:red; font-size:20px;'
        },'-&gt;',{
            xtype: 'textfield'
            ,id: 'doodles-search-filter'
            ,emptyText: _('doodles.search...')
            ,listeners: {
                'change': {fn:this.search,scope:this}
                ,'render': {fn: function(cmp) {
                    new Ext.KeyMap(cmp.getEl(), {
                        key: Ext.EventObject.ENTER
                        ,fn: function() {
                            this.fireEvent('change',this);
                            this.blur();
                            return true;
                        }
                        ,scope: cmp
                    });
                },scope:this}
            }
        },{
            xtype: 'button'
            ,id: 'doodles-filter-clear'
            ,text: _('filter_clear')
            ,listeners: {
                'click': {fn: this.clearFilter, scope: this}
            }
        }]
        ,getMenu: function() {
            return [{
                text: _('doodles.doodle_update')
                ,handler: this.updateDoodle
            },'-',{
                text: _('doodles.doodle_remove')
                ,handler: this.removeDoodle
            }];
    
        }
    
    });
    Doodles.grid.Doodles.superclass.constructor.call(this,config);
};
Ext.extend(Doodles.grid.Doodles,MODx.grid.Grid, {
search: function (tf, nv, ov) {
var s = this.getStore();
s.baseParams.query = tf.getValue();
this.getBottomToolbar().changePage(1);
this.refresh();
}, clearFilter: function () {
this.getStore().baseParams = {
action: 'mgr/doodle/getList'
};
Ext.getCmp('doodles-search-filter').reset();
this.getBottomToolbar().changePage(1);
this.refresh();
},createDoodle: function(btn,e) {
if (!this.createDoodleWindow) {
this.createDoodleWindow = MODx.load({
xtype: 'doodles-window-doodle-create'
,blankValues: true
,listeners: {
'success': {fn:function() {
this.refresh();
this.store.on('load', function() {
Ext.getCmp('recNum').update('Records: '+Ext.getCmp('doodles-grid-doodles').store.getCount());
});
},scope:this}
}
});
}
this.createDoodleWindow.show(e.target);
},updateDoodle: function(btn,e) {
if (!this.updateDoodleWindow) {
this.updateDoodleWindow = MODx.load({
xtype: 'doodles-window-doodle-update'
,record: this.menu.record
,listeners: {
'success': {fn:this.refresh,scope:this}
}
});
}
this.updateDoodleWindow.setValues(this.menu.record);
this.updateDoodleWindow.show(e.target);
},removeDoodle: function() {
MODx.msg.confirm({
title: _('doodles.doodle_remove')
,text: _('doodles.doodle_remove_confirm')
,url: this.config.url
,params: {
action: 'mgr/doodle/remove'
,id: this.menu.record.id
}
,listeners: {
'success': {fn:function() {
this.refresh();
this.store.on('load', function() {
Ext.getCmp('recNum').update('Records: '+Ext.getCmp('doodles-grid-doodles').store.getCount());
});
},scope:this}
}
});
}</p>
<p>});
Ext.reg('doodles-grid-doodles',Doodles.grid.Doodles);</p>
<p>Doodles.window.UpdateDoodle = function(config) {
config = config || {};
Ext.applyIf(config,{
title: _('doodles.doodle_update')
,url: Doodles.config.connectorUrl
,baseParams: {
action: 'mgr/doodle/update'
}
,fields: [{
xtype: 'hidden'
,name: 'id'
},{
xtype: 'textfield'
,fieldLabel: _('doodles.name')
,name: 'name'
,anchor: '100%'
},{
xtype: 'textarea'
,fieldLabel: _('doodles.description')
,name: 'description'
,anchor: '100%'
}]
});
Doodles.window.UpdateDoodle.superclass.constructor.call(this,config);
};
Ext.extend(Doodles.window.UpdateDoodle,MODx.Window);
Ext.reg('doodles-window-doodle-update',Doodles.window.UpdateDoodle);</p>
<p>Doodles.window.CreateDoodle = function(config) {
config = config || {};
Ext.applyIf(config,{
title: _('doodles.doodle_create')
,url: Doodles.config.connectorUrl
,baseParams: {
action: 'mgr/doodle/create'
}
,fileUpload: true
,fields: [{
xtype: 'textfield'
,fieldLabel: _('doodles.name')
,name: 'name'
,anchor: '100%'
},{
xtype: 'textarea'
,fieldLabel: _('doodles.description')
,name: 'description'
,anchor: '100%'
},{
xtype: 'fileuploadfield'
,fieldLabel: _('doodles.file_upload')
,name: 'filename'
,emptyText: _('doodles.select_image')
}]
});
Doodles.window.CreateDoodle.superclass.constructor.call(this,config);
};
Ext.extend(Doodles.window.CreateDoodle,MODx.Window);
Ext.reg('doodles-window-doodle-create',Doodles.window.CreateDoodle);

/www/doodles/core/components/doodles/model/doodles.class.php

<?php
class Doodles {
    public $modx;
    public $config = array();
    function __construct(modX &$modx,array $config = array()) {
        $this->modx =& $modx;
        $basePath = $this-&gt;modx-&gt;getOption('doodles.core_path',$config,$this-&gt;modx-&gt;getOption('core_path').'components/doodles/');
        $assetsUrl = $this-&gt;modx-&gt;getOption('doodles.assets_url',$config,$this-&gt;modx-&gt;getOption('assets_url').'components/doodles/');
        $uploadUrl = $this-&gt;modx-&gt;getOption('doodles.upload_url',$config,$this-&gt;modx-&gt;getOption('assets_url').'components/doodles/uploads/');
        $this-&gt;config = array_merge(array(
            'basePath' =&gt; $basePath,
            'corePath' =&gt; $basePath,
            'modelPath' =&gt; $basePath.'model/',
            'processorsPath' =&gt; $basePath.'processors/',
            'templatesPath' =&gt; $basePath.'templates/',
            'chunksPath' =&gt; $basePath.'elements/chunks/',
            'jsUrl' =&gt; $assetsUrl.'js/',
            'cssUrl' =&gt; $assetsUrl.'css/',
            'assetsUrl' =&gt; $assetsUrl,
            'uploadUrl' =&gt; $uploadUrl,
            'connectorUrl' =&gt; $assetsUrl.'connector.php',
        ),$config);
        $this-&gt;modx-&gt;addPackage('doodles',$this-&gt;config['modelPath']);
    }
    
    public function getChunk($name,$properties = array()) {
        $chunk = null;
        if (!isset($this-&gt;chunks[$name])) {
            $chunk = $this-&gt;modx-&gt;getObject('modChunk',array('name' =&gt; $name));
            if (empty($chunk) || !is_object($chunk)) {
                $chunk = $this-&gt;_getTplChunk($name);
                if ($chunk == false) return false;
            }
            $this-&gt;chunks[$name] = $chunk-&gt;getContent();
        } else {
            $o = $this-&gt;chunks[$name];
            $chunk = $this-&gt;modx-&gt;newObject('modChunk');
            $chunk-&gt;setContent($o);
        }
        $chunk-&gt;setCacheable(false);
        return $chunk-&gt;process($properties);
    }
    
    private function _getTplChunk($name,$postfix = '.chunk.tpl') {
        $chunk = false;
        $f = $this-&gt;config['chunksPath'].strtolower($name).$postfix;
        if (file_exists($f)) {
            $o = file_get_contents($f);
            $chunk = $this-&gt;modx-&gt;newObject('modChunk');
            $chunk-&gt;set('name',$name);
            $chunk-&gt;setContent($o);
        }
        return $chunk;
    }

}

/www/doodles/core/components/doodles/processors/mgr/doodle/create.class.php

<?php
class DoodleCreateProcessor extends modObjectCreateProcessor {
    public $classKey = 'Doodle';
    public $languageTopics = array('doodles:default');
    public $objectType = 'doodles.doodle';
    public function beforeSave() {
        $name = $this-&gt;getProperty('name');
        if (empty($name)) {
            $this-&gt;addFieldError('name',$this-&gt;modx-&gt;lexicon('doodles.doodle_err_ns_name'));
        } else if ($this-&gt;doesAlreadyExist(array('name' =&gt; $name))) {
            $this-&gt;addFieldError('name',$this-&gt;modx-&gt;lexicon('doodles.doodle_err_ae'));
        }
    
        $fileName = $this-&gt;getProperty('filename');
        if ($this-&gt;doesAlreadyExist(array('filename' =&gt; $fileName))) {
            $this-&gt;addFieldError('filename',$this-&gt;modx-&gt;lexicon('doodles.doodle_err_ae_image'));
        }
    
        return parent::beforeSave();
    }
    
    /** @var modMediaSource $source */
    public $source;
    /**
     * Get the active Source
     * @return modMediaSource|boolean
     */
    private function getSource() {
        $this-&gt;modx-&gt;loadClass('sources.modMediaSource');
        $this-&gt;source = modMediaSource::getDefaultSource($this-&gt;modx,$this-&gt;getProperty('source'));
        if (empty($this-&gt;source) || !$this-&gt;source-&gt;getWorkingContext()) {
            return false;
        }
        return $this-&gt;source;
    }
    
    public function process() {
        // Get filesystem source
        if (!$this-&gt;getSource()) {
            return $this-&gt;failure($this-&gt;modx-&gt;lexicon('permission_denied'));
        }
        $this-&gt;source-&gt;initialize();
        if (!$this-&gt;source-&gt;checkPolicy('create')) {
            return $this-&gt;failure($this-&gt;modx-&gt;lexicon('permission_denied'));
        }
        $uploadUrl = $this-&gt;modx-&gt;getOption('doodles.upload_url',$this-&gt;modx-&gt;config,$this-&gt;modx-&gt;getOption('assets_url').'components/doodles/uploads/');
        $success = $this-&gt;source-&gt;uploadObjectsToContainer($uploadUrl,$_FILES);
        if (empty($success)) {
            $msg = '';
            $errors = $this-&gt;source-&gt;getErrors();
            foreach ($errors as $k =&gt; $msg) {
                $this-&gt;modx-&gt;error-&gt;addField($k,$msg);
            }
            return $this-&gt;failure($msg);
        }
    
        // Get the filename string from the $_FILES array
        $filenames = array();
        if (is_array($_FILES)) {
            foreach ($_FILES as $file) {
                if (!empty($file['name'])) {
                    $filenames[] = $file['name'];
                }
            }
        }
        // We only want the first filename is there are somehow more than one.
        $fileName = $filenames[0];
        $this-&gt;setProperty('filename', $fileName);
    
        return parent::process();
    }

}
return 'DoodleCreateProcessor';

Post Details

Category MODExt Tutorial Series
Date 02/07/2015
Author Murray Wood
About This series expands upon the "Doodles Extra" tutorial in the official MODX documentation.

Latest Posts

LocationResources
Date 20/01/2017

CycleResources
Date 18/07/2016

MODExt Tutorial Series - Part 2
Date 02/07/2015

MODExt Tutorial Series - Part 1
Date 26/06/2015

Comments