Skip to content

Commit

Permalink
Merge pull request #128 from nicolasmure/event-dispatcher
Browse files Browse the repository at this point in the history
Command execution events + subrequests support + volume_id setting
  • Loading branch information
helios-ag committed Jun 4, 2015
2 parents 6a04bbd + b2bfad3 commit ff339d2
Show file tree
Hide file tree
Showing 10 changed files with 290 additions and 7 deletions.
5 changes: 5 additions & 0 deletions Configuration/ElFinderConfigurationReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ public function getConfiguration($instance)
'archiveMimes' => $parameter['archive_mimes'],
'archivers' => $parameter['archivers']
);

if ($parameter['volume_id'] > 0) {
$driverOptions['id'] = $parameter['volume_id'];
}

if(!$parameter['show_hidden']) {
$driverOptions['accessControl'] = array($this, 'access');
};
Expand Down
22 changes: 19 additions & 3 deletions Controller/ElFinderController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use FM\ElfinderBundle\Event\ElFinderEvents;
use FM\ElfinderBundle\Event\ElFinderPreExecutionEvent;
use FM\ElfinderBundle\Event\ElFinderPostExecutionEvent;

/**
* Loader service for Elfinder backend
Expand Down Expand Up @@ -144,13 +147,26 @@ private function selectEditor($parameters, $instance, $homeFolder, $assetsPath,

/**
* Loader service init
* @param Request $request
* @param string $instance
* @param string $homeFolder
* @return JsonResponse/void
*/
public function loadAction($instance)
public function loadAction(Request $request, $instance, $homeFolder)
{
$loader = $this->container->get('fm_elfinder.loader');
return new JsonResponse($loader->load($instance));
$httpKernel = $this->get('http_kernel');

$preExecutionEvent = new ElFinderPreExecutionEvent($request, $httpKernel, $instance, $homeFolder);
$this->get('event_dispatcher')->dispatch(ElFinderEvents::PRE_EXECUTION, $preExecutionEvent);

$loader = $this->get('fm_elfinder.loader');
$result = $loader->load($request, $instance);

$postExecutionEvent = new ElFinderPostExecutionEvent($request, $httpKernel, $instance, $homeFolder, $result);
$this->get('event_dispatcher')->dispatch(ElFinderEvents::POST_EXECUTION, $postExecutionEvent);

// returning result (who may have been modified by a post execution event listener)
return new JsonResponse($postExecutionEvent->getResult());
}

/**
Expand Down
1 change: 1 addition & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public function getConfigTreeBuilder()
->isRequired()
->defaultValue('LocalFileSystem')
->end() // driver
->integerNode('volume_id')->defaultValue(0)->min(0)->end()
->scalarNode('path')->defaultValue('')->end()
->scalarNode('start_path')->defaultValue('')->end()
->scalarNode('url')->defaultValue('')->end()
Expand Down
20 changes: 20 additions & 0 deletions Event/ElFinderEvents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace FM\ElfinderBundle\Event;

use Symfony\Component\EventDispatcher\Event;

abstract class ElFinderEvents extends Event
{
/**
* Event name to identify pre execution event.
* @var string
*/
const PRE_EXECUTION = 'fm_elfinder.event.pre_execution';

/**
* Event name to identify post execution event.
* @var string
*/
const POST_EXECUTION = 'fm_elfinder.event.post_execution';
}
56 changes: 56 additions & 0 deletions Event/ElFinderPostExecutionEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace FM\ElfinderBundle\Event;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class ElFinderPostExecutionEvent extends ElFinderPreExecutionEvent
{
/**
* Command execution result.
* @var array
*/
protected $result;


/**
* Constructor.
* @param Request $request
* @param HttpKernelInterface $httpKernel
* @param string $instance
* @param string $homeFolder
* @param array $result
*/
public function __construct(Request $request, HttpKernelInterface $httpKernel, $instance, $homeFolder, array $result)
{
parent::__construct($request, $httpKernel, $instance, $homeFolder);

$this->result = $result;
}

/**
* Tells if execution has encountered errors.
* @return boolean
*/
public function hasErrors()
{
return isset($this->result['error']);
}

/**
* @return array
*/
public function getResult()
{
return $this->result;
}

/**
* @param array $result
*/
public function setResult(array $result)
{
$this->result = $result;
}
}
101 changes: 101 additions & 0 deletions Event/ElFinderPreExecutionEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php

namespace FM\ElfinderBundle\Event;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\HttpKernelInterface;

class ElFinderPreExecutionEvent extends ElFinderEvents
{
/**
* Request object containing ElFinder command and parameters.
* @var Request
*/
protected $request;

/**
* Used to make sub requests.
* @var HttpKernelInterface
*/
private $httpKernel;

/**
* ElFinder instance.
* @var string
*/
protected $instance;

/**
* Home folder.
* @var string
*/
protected $homeFolder;


/**
* Constructor.
* @param Request $request
* @param HttpKernelInterface $httpKernel
* @param string $instance
* @param string $homeFolder
*/
public function __construct(Request $request, HttpKernelInterface $httpKernel, $instance, $homeFolder)
{
$this->request = $request;
$this->httpKernel = $httpKernel;
$this->instance = $instance;
$this->homeFolder = $homeFolder;
}

/**
* Makes a sub request to elFinder.
* Function based on 'forward' function from Symfony controllers.
*
* @see https://github.com/symfony/symfony/blob/2.5/src/Symfony/Bundle/FrameworkBundle/Controller/Controller.php
*
* @param array $path An array of path parameters
* @param array $query An array of query parameters
*
* @return Symfony\Component\HttpFoundation\Response A Response instance
*/
public function subRequest(array $path, array $query)
{
$path['_controller'] = 'FMElfinderBundle:ElFinder:load';
$subRequest = $this->request->duplicate($query, null, $path);

return $this->httpKernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
}

/**
* Returns executed command.
* @return string
*/
public function getCommand()
{
return $this->request->get('cmd');
}

/**
* @return Request
*/
public function getRequest()
{
return $this->request;
}

/**
* @return string
*/
public function getInstance()
{
return $this->instance;
}

/**
* @return string
*/
public function getHomeFolder()
{
return $this->homeFolder;
}
}
2 changes: 2 additions & 0 deletions FMElfinderBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace FM\ElfinderBundle;

use FM\ElfinderBundle\DependencyInjection\Compiler\TwigFormPass;
use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

Expand All @@ -17,5 +18,6 @@ public function build(ContainerBuilder $container)
parent::build($container);

$container->addCompilerPass(new TwigFormPass());
$container->addCompilerPass(new RegisterListenersPass('event_dispatcher', 'fm_elfinder.event_listener', 'fm_elfinder.event_subscriber'));
}
}
10 changes: 6 additions & 4 deletions Loader/ElFinderLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use FM\ElFinderPHP\Connector\ElFinderConnector;
use FM\ElfinderBundle\Bridge\ElFinderBridge;
use FM\ElfinderBundle\Model\ElFinderConfigurationProviderInterface;
use Symfony\Component\HttpFoundation\Request;

/**
* Class ElFinderLoader
Expand Down Expand Up @@ -49,18 +50,19 @@ public function configure()

/**
* Starts ElFinder
* @var $instance string
* @var Request $request
* @var string $instance
* @return void/array
*/
public function load($instance)
public function load(Request $request, $instance)
{
$this->setInstance($instance);
$config = $this->configure();
$connector = new ElFinderConnector(new ElFinderBridge($config));
if ($config['corsSupport']) {
return $connector->execute();
return $connector->execute($request->query->all());
} else {
$connector->run();
$connector->run($request->query->all());
}
}

Expand Down
79 changes: 79 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ Recommended bundles to use with:
- [Add configuration options to your config.yml](#add-configuration-options-to-your-configyml)
- [Use multiple upload folder by instance](#use-multiple-upload-folder-by-instance)
- [CORS support](#cors-support)
- [Events listeners / subscribers](#events-listeners--subscribers)
- [Events](#events)
- [Sub requests](#sub-requests)
- [Elfinder Form Type](#elfinder-form-type)
- [CKEditor integration](#ckeditor-integration)
- [Installation](#step-1-installation)
Expand Down Expand Up @@ -201,11 +204,13 @@ fm_elfinder:
* path_prefix - path prefix with relative_path enabled, default is slash ('/')
* show_hidden - show files and folders that starts from . (dot)
* driver - driver type, LocalFileSystem, Dropbox, FTP
* volume_id - (optional) can be used to force a volume id when mounting volume (default auto-increments). If provided, it must be an integer bigger than 0.
* alias - directory alias
* path - directory that contains files
* upload_allow: ['image/png', 'image/jpg', 'image/jpeg']
* upload_deny: ['all']
* upload_max_size: 2M

You can see the full list of roots options [here](https://github.com/Studio-42/elFinder/wiki/Connector-configuration-options#root-options "connector options list"). To use them,
convert camelCased option name to under_scored option name.

Expand Down Expand Up @@ -269,6 +274,80 @@ nelmio_cors:
allow_credentials: true
```
## Events listeners / subscribers
### Events
The bundle is throwing some events during an elFinder command execution :
- `FM\ElfinderBundle\Event\ElFinderEvents::PRE_EXECUTION` : `fm_elfinder.event.pre_execution`
- `FM\ElfinderBundle\Event\ElFinderEvents::POST_EXECUTION` : `fm_elfinder.event.post_execution`

The pre execution event has its own class (`FM\ElfinderBundle\Event\ElFinderPreExecutionEvent`) which contains
the http request object, the elFinder instance name and the home folder.

The post execution event (`FM\ElfinderBundle\Event\ElFinderPostExecutionEvent`) has the same attributes
than the pre execution events, plus the command result and a `hasErrors()` function indicating if
errors has been encountered during command execution.

You can register event listeners with the `fm_elfinder.event_listener` tag
and event subscribers with the `fm_elfinder.event_subscriber` tag.

**Note:** you must set the `cors_support` option to `true` to use events.
If you don't, the symfony life cycle won't end properly and so the event won't be dispatched.

You can access to all commands names [here](https://github.com/helios-ag/ElFinderPHP/blob/master/src/ElFinder.php#L61 "elFinder commands").

### Sub requests

Events allows you to perform sub requests (only for commands used with HTTP GET method, i.e. not to upload a file).
These subrequests are working the same way than `forward` function on symfony controllers,
and are also hookable.

**Note:** You will have to set a `volume_id` to your instance's root to be sure the volume
is mounted with the same ID between each requests.

Here is an exemple of event listener on the post execution event, making a sub request :

```xml
<!-- src/AppBundle/Resources/config/services.xml -->
<service id="app_bundle.listener.elfinder_post_execution" class="AppBundle\EventListener\ElFinder\PostExecutionListener">
<tag name="fm_elfinder.event_listener" event="fm_elfinder.event.post_execution" method="onPostExecute" />
</service>
```

```php
// src/AppBundle/EventListener/ElFinder/PostExecutionListener.php
namespace AppBundle\EventListener\ElFinder;
use FM\ElfinderBundle\Event\ElFinderPostExecutionEvent;
class PostExecutionListener
{
/**
* @param ElFinderPostExecutionEvent $event
*/
public function onPostExecute(ElFinderPostExecutionEvent $event)
{
if (!$event->hasErrors() && $event->getCommand() == 'tmb') { // 'tmb', 'mkdir', 'open', etc...
// do your stuff here
// ...
// you can perform a sub request
$queryParameters = $event->getRequest()->query->all(); // getting original request parameters
$queryParameters['cmd'] = 'info'; // changing the command to execute in sub request
$jsonResponse = $event->subRequest(array(
'instance' => $event->getInstance(), // you can also make a subrequest on an other instance
'homeFolder' => $event->getHomeFolder() // and an other homeFolder
), $queryParameters);
$data = json_decode($jsonResponse->getContent());
// work with sub request data
// ...
}
}
}
```

## Elfinder Form Type

Bundle come with custom form type, ```html <input type="text"/>```, that provide elfinder callback (opens Elfinder window).
Expand Down
1 change: 1 addition & 0 deletions Tests/DependencyInjection/ConfigurationLoadTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public function testSupportsAllConfigFormats($path)
'roots' => array(
'uploads' => array(
'driver' => 'LocalFileSystem',
'volume_id' => 0,
'disabled_commands' => array(),
'plugins' => array(),
'path' => 'uploads',
Expand Down

0 comments on commit ff339d2

Please sign in to comment.