Exports and Imports

This is the implementation of imports and exports in Thelia.

This PR introduces 3 new features:
- [Imports / Exports](#imports)
- [Formatting](#formatting)
- [Archive building](#archive_building)

Imports / Exports <a name="imports"></a>
===

Exports
---
Exports are similar to Thelia loops. In fact, they are so close that you can use a loop for an export ;)

### How to create an export ?
To create an export you have to create a class that extends Thelia\ImportExport\Export\ExportHandler . You have to implement the methods:
```php
 public function getHandledTypes();
 public function buildDataSet(Lang $lang);
```

```getHandledTypes``` must return an array or a string, this is used to match with Formatters ( see [Formatting](#formatting) ), an export may be handled by only one formatter, are by all.
To match them, we use strings defined in ```Thelia\Core\FileFormat\FormatType```

```buildDataSet``` may return 3 types of object:
- An array
- A Propel ModelCriteria ( Query objects )
- A BaseLoop

In the case of an array, the data is directly placed for formatting.
In the case of a ModelCriteria, the query is executed and the result is placed for formatting
In the case of a BaseLoop, the loop is executed and the results are explored, placed into an array, and placed for formatting.

Two other methods may be useful:
```php
protected function getAliases();
protected function getDefaultOrder();
```

```getAliases``` has to return an associative array, with the "real row name" as a key, and the aliased name as the value. "real row name" meens: array keys, MySQL Select name or LoopResultRow variable name.

```getDefaultOrder``` has to return a array. Place your aliased names in the order you want them to appear in the file ( for formatters which support this option ( CSV ) ).

Moreover, you have an helper for the loops:
```php
public function buildDataSet(Lang $lang) {
    return $this->renderLoop("myLoopName", ["my"=>1, "super"=>["arguments"]]);
}
```
And you're done with your export ;)

### Register an export <a name="register_export"></a>
To register an export in a module, you have to edit your Config/config.xml

Your have to add in "exports" a tag with that skeleton:
```xml
<exports>
    <export id="your.export.id" class="Your\ExportHandler" category_id="the.category_id">
        <descriptive locale="en_US">
            <title>Your export title </title>
             <!-- you may add an optionnal description -->
             <description> ... </description>
        </descriptive>
        <descriptive locale="fr_FR">
            <!-- Here's for another locale -->
        </descriptive>
    </export>
    <export>
        <!-- here's another export -->
    </export>
</exports>
```

Thelia export categories ids are:
- thelia.export.customer : Exports conserning Customers
- thelia.export.products : Exports conserning Products
- thelia.export.content : Exports conserning Contents
- thelia.export.orders : Exports conserning Orders

If you want to create a new category, you have to put in your Config/config.xml:
```xml
<export_categories>
        <export_category id="your.category.id">
            <title locale="en_US">A title</title>
            <title locale="fr_FR">Un titre</title>
        </export_category>
        <export_category id="your.other.category.id">
            <!-- here's another export category -->
        </export_category
</export_categories>
```

Imports
---

### Create an Import
For an import, you have to create a class that extends Thelia\ImportExport\Import\ImportHandler  and implement the methods:
```php
protected function getMandatoryColumns();
public function retrieveFromFormatterData(FormatterData $data);
```
```getMandatoryColumns``` must return an array with the name of mandatory columns. If you're doing a couple export/import, it should have the same values as your mandatory export aliases.

```retrieveFromFormatterData(FormatterData $data)``` is the method were you must put your import logic. ```Thelia\Core\FileFormat\Formatting\FormatterData``` is an array wrapper but is not Iterable. A good way to treat your data is to do:
```php
public function retrieveFromFormatterData(FormatterData $data) {
    while(null !== $row = $data->popRow()) {
         $this->checkMandatoryColumns($row);

         // Your treatement here
    }
}
```

### Register an Import
This is the same as registering exports, see [Register an export](#register_export)
Juste replace "export" by import :)

Formatting <a name="formatting"></a>
---
3 formatters are provided here: XML, Json, CSV

### Get a formatter
In order to get a formatter, you can use the trait ```Thelia\Core\FileFormat\Formatting\FormatterManagerTrait``` :
```php
class Foo 
{
    use FormatterManagerTrait;
    public function myMethod() {
        $archiveBuilderManager = $this->getFormatterManager($myContainer);
    }
}
```
or inject it in a service (e.g: an event listener):
```xml
<service id="my.super.service" class="My\Super\Class">
            <argument type="service" id="thelia.manager.formatter_manager" />
</service>
```

### Use a formatter
A formatter must implement 6 methods:
```php
/**
 * This method goes from a FormatterData object and must return the encoded value.
 * It may return null if the formatter is decode only
 */
public function encode(FormatterData $data);

/**
 * This method goes from a rawData ( string, object, ... ) and must return a FormatterData
 * It may return null if the formatter is encode only
 */
public function decode($rawData);

/**
 * This method returns a string containing the handled Type that is used to match with a export or an  
 * import
 */
public function getHandledType();

/**
 * As a formatter should belong to a file format, here are the basic information about a format
 * that the formatter must return.
 */

// The format name
public function getName();

// The format extension
public function getExtension();

// The format mime type
public function getMimeType();
```

### Create a formatter
If you want to create a formatter, you must create a class that extends ```Thelia\Core\FileFormat\Formatting\AbstractFormatter``` and implements the 6 previous methods. 

### Register a formatter
In Thelia, a formatter is a service. If you to register your formatter into the manager, create a tagged service like this:
```xml
<service id="my.super.formatter" class="My\Super\Formatter">
            <tag name="thelia.manager.formatter" />
</service>
```

Archive building <a name="archive_building"></a>
---
4 archive formats are handled: zip, tar, tar.gz and tar.bz2

### Get an archive builder

In order to get an archive builder, you can use the trait ```Thelia\Core\FileFormat\Archive\ArchiveBuilderManagerTrait```  :
```php
class Foo 
{
    use ArchiveBuilderManagerTrait;
    public function myMethod() {
        $archiveBuilderManager = $this->getArchiveBuilderManager($myContainer);
    }
}
```
Or you can inject it in a service (e.g: an event listener):
```xml
<service id="my.super.service" class="My\Super\Class">
            <argument type="service" id="thelia.manager.archive_builder_manager" />
</service>
```

### Use an archive builder

Archive builders are defined by the abstract class ```Thelia\Core\FileFormat\Archive\AbstractArchiveBuilder```.
Here are the methods that you have:
```php
// Get the zip archive builder in the manager
$zip = $archiveBuilderManager->get("ZIP");

// Let's create an zip
// Add a file the is on the server
$zip->addFile(
    "path/to/my_file", 
    "directory/where/my/file/will/be/in/the/archive",
    "name_of_my_file_in_the_archive"
);

// Add an online file
$zip->addFile(
    "http://example.com/path/to/my_file.ext",
    "directory/where/my/file/will/be/in/the/archive",
    "name_of_my_file_in_the_archive",
    true
);

// Create a file from a string
$zip->addFileFromString("the content of my file", "name_of_my_file", "directory/in/the/archive");

// Get the content of a file
$zip->getFileContent("path/to/my_file_in_the_archive");

// Delete a file in the archive
$zip->deleteFile("path/to/my_file_in_the_archive");

// Add an empty directory
$zip->addDirectory("path/of/the/new/directory");

// Check if the archive contains a file
$zip->hasFile("path/to/my_file_in_the_archive");

// Check if the archive contains a directory
$zip->hasDirectory("directory/in/the/archive");

// Build a Thelia Response with the archive file as content
$response = $zip->buildArchiveResponse("filename_of_the_export");

// Get the archive format name
$zip ->getName();

// Get the Mime Type of the archive format
$zip->getMimeType();

// Get the extension of the archive format
$zip->getExtension();
```

Create an archive builder
---

If you want to create an archive builder, create a class that extends ```Thelia\Core\FileFormat\Archive\AbstractArchiveBuilder``` and implement the functions above, then create a service with the tag ```thelia.manager.archive_builder```, like this:
```xml
<service id="thelia.manager.zip_archive_builder" class="Thelia\Core\FileFormat\Archive\ArchiveBuilder\ZipArchiveBuilder">
            <tag name="thelia.manager.archive_builder" />
</service>
```
inject the kernel cache dir repo and not the whole container : 

```
<argument>%kernel.cache_dir%</argument>
```