Make controller resolving working for old base classes, added example routes to Users and Acme modules.
Merge pull request #1519 from cmfcmf/controller-resolving

This PR was merged into zikula:1.3 branch.

Discussion
----------

Make controller resolving working, added example routes to ZikulaUsersModule and AcmeExampleModule

| Q             | A
| ------------- | ---
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | ---
| Fixed tickets | ---
| Refs tickets  | ---
| License       | MIT
| Doc PR        | ---

# Routing
I'll give some more explanations on routing, updated and relying on this PR. First and most important: Read the Symfony docs on it, you must understand the basic idea behind routing:
- http://symfony.com/doc/current/book/routing.html

## The url styles
Now to the Zikula routing system. First of all, let's define three kinds of url matching / routing in Zikula. First, the old ones:
- `index.php?module=bla&type=taa&func=laa&arg1=foo&arg2?bar`: The oldest and ugliest way of urls, I'll call them **argument urls**.
- The current **short urls** you know from ZK 1.3.6 and below.

These url styles will be entirely *replaced* by **Symfony Routes**. However a BC layer will keep them working until ???, at least until Symfony routes are fully implemented. If you ask "why?", here are the advantages I know of:
- URLs look beautiful (like *yourdomain.com/login* for Login, etc.)
- The current **short urls** are problematic, as it is not possible to determine if the url really exists and it's hard to extract the controller from it, etc. (@drak can explain better I think)
- It's Symfony. This means less of our code, more well maintained code.

## This PR
What does this PR do? It enables Symfony routes for
- The AcmeExampleModule
- The ZikulaUsersModule (UserController and "main" + "view" action of AdminController)

### To be able to test that PR, you have to:
- Run `composer update`

## Tasks for you to make your Module capable of Symfony Routing

Now the tasks for each module which want's to use Symfony Routing (all optional for now, remember the old routing works as well).

### Add the routes
You can do so just as stated in the Symfony docs in YAML, PHP or XML and addtionally, due to our use of the [*SensioFrameworkExtraBundle*](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/index.html), in [annotations](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html#frameworkextra-annotations-routing-activation). Depending on how you want to define the module's routes, you have to override the [`getRoutingConfig`](https://github.com/zikula/core/blob/1.3/src/lib/Zikula/Core/AbstractBundle.php#L58) method in your module's bundle configuration file (i.e. `SettingsModule/ZikulaSettingsModule.php`). By the default it looks like so:
```php
public function getRoutingConfig()
{
    return "@{$this->name}/Resources/config/routing.yml";
}
```

In this PR, I'm using annotations. To enable those, I just followed the instructions [here](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html#frameworkextra-annotations-routing-activation).
` src/system/Zikula/Module/UsersModule/Resources/config/routing.yml`:
```yaml
zikulausersmodule:
    resource: "@ZikulaUsersModule/Controller/UserController.php"
    prefix:   /
    type:     annotation
```
As you see, here we tell Symfony that the *UserController* of the *ZikulaUsersModule* adds routes using annotations. See the UserController file of this PR for some basic routes I added. [Documentation on those annotations.](http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/routing.html) 

These are the steps modules must do if they want to use the new Symfony routes.
Keep in mind:
> Every module MUST make sure it isn't overriding routes of other modules. For now this
means that every module has to use a unique prefix prepended to all it's routes. In the
example above, the prefix is "acmeexample".

(I broke the rule with this PR, as I added a non-prefixed `/login` route for the users module, as this rule will only apply as long as conflicting routes are not handled by the core).
 
## Core internals

### URL generation
As of this PR, `ModUtil::url()` will try to generate new-style Symfony urls but *ModUtil::url()* is deprecated for URL generation now, for the new URL generator see [the Symfony docs](http://symfony.com/doc/current/book/routing.html#generating-urls) (of course you cannot generate **argument urls** and **short urls** with it).

### How are the routes of the modules now loaded?
Routes are loaded from the `app/cache/dev|prod/appDevZikulaRoutes|appProdZikulaRoutes.php` file. Routes are added to this file on module installation. If the file doesn't exist, it is generated and all routes of all modules are added. The [CmfRoutingBundle](http://symfony.com/doc/current/cmf/book/routing.html), is used to register a custom Zikula Router loading these routes. See the `RouteProvider.php` of this PR.

### Expansion of `System::queryStringDecode`
As you propably know, this function tries calculate the module, type, func parameters from an url. At the top of it, a new section is added trying to match the current url with an route.
```php
// Try to match a route first.
/** @var \Symfony\Cmf\Component\Routing\ChainRouter $router */
$router = ServiceUtil::get('router');
try {
    $parameters = $router->matchRequest($request);

    if (!isset($parameters['_module']) || !isset($parameters['_type']) || !isset($parameters['_func'])) {
        // This might be the web profiler or another native bundle.
    } else {
        $request->attributes->set('_module', strtolower($parameters['_module']));
        $request->attributes->set('_type', strtolower($parameters['_type']));
        $request->attributes->set('_func', strtolower($parameters['_func']));
        $request->query->set('module', strtolower($parameters['_module']));
        $request->query->set('type', strtolower($parameters['_type']));
        $request->query->set('func', strtolower($parameters['_func']));
        $request->overrideGlobals();

        return;
    }

} catch (ResourceNotFoundException $e) {
    // This is an old style url.
} catch (RouteNotFoundException $e) {
    // This is an old style url.
}
```
As you see, it fails silently if no route is found, and will calculate the parameters the old way. 

## Conclusion
I hope this made the routing stuff a bit clearer to all of you. If you have questions, just ask!

Commits
-------

e9b5ac316536e34db11f0fbe547bb484db4b400d Make controller resolving working for old base classes, added example routes to Users and Acme modules. cmfcmf
Added legacy layer to ModuleStateEvent.