diff --git a/.gitignore b/.gitignore
index 380e437..a737644 100755
--- a/.gitignore
+++ b/.gitignore
@@ -7,3 +7,4 @@ composer.lock
.*.sw[a-z]
*.un~
Session.vim
+/.idea
diff --git a/Datatables/Datatable.php b/Datatables/Datatable.php
index a65fdf1..86576e6 100755
--- a/Datatables/Datatable.php
+++ b/Datatables/Datatable.php
@@ -19,10 +19,10 @@
* { "mData": "customer.location.address" }
*
* Felix-Antoine Paradis is the author of the original implementation this is
- * built off of, see: https://gist.github.com/1638094
+ * built off of, see: https://gist.github.com/1638094
*/
-namespace LanKit\DatatablesBundle\Datatables;
+namespace Tejadong\DatatablesBundle\Datatables;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadata;
@@ -30,10 +30,11 @@
use Doctrine\ORM\QueryBuilder;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Tools\Pagination\Paginator;
-
use Symfony\Component\DependencyInjection\Container;
use Symfony\Component\Config\Definition\Exception\Exception;
use Symfony\Component\HttpFoundation\Response;
+use HTMLPurifier;
+use HTMLPurifier_Config;
class Datatable
{
@@ -194,6 +195,8 @@ class Datatable
*/
protected $datatable;
+ protected $htmlPurifier;
+
public function __construct(array $request, EntityRepository $repository, ClassMetadata $metadata, EntityManager $em, $serializer)
{
$this->em = $em;
@@ -201,8 +204,8 @@ public function __construct(array $request, EntityRepository $repository, ClassM
$this->repository = $repository;
$this->metadata = $metadata;
$this->serializer = $serializer;
- $this->tableName = Container::camelize($metadata->getTableName());
- $this->defaultJoinType = self::JOIN_INNER;
+ $this->tableName = Container::camelize(explode('\\', $metadata->getName())[count(explode('\\', $metadata->getName()))-1]);
+ $this->defaultJoinType = self::JOIN_LEFT;
$this->defaultResultType = self::RESULT_RESPONSE;
$this->setParameters();
$this->qb = $em->createQueryBuilder();
@@ -213,6 +216,8 @@ public function __construct(array $request, EntityRepository $repository, ClassM
$identifiers = $this->metadata->getIdentifierFieldNames();
$this->rootEntityIdentifier = array_shift($identifiers);
+
+ $this->htmlPurifierInit();
}
/**
@@ -302,23 +307,28 @@ protected function setRelatedEntityColumnInfo(array &$association, array $fields
// loop through the related entities, checking the associations as we go
$metadata = $this->metadata;
+
while ($field = array_shift($fields)) {
$columnName .= empty($columnName) ? $field : ".$field";
$entityName = lcfirst(Container::camelize($field));
+
if ($metadata->hasAssociation($entityName)) {
$joinOn = "$joinName.$entityName";
- if ($metadata->isCollectionValuedAssociation($entityName)) {
+
+ if ($metadata->isCollectionValuedAssociation($entityName))
$association['containsCollections'] = true;
- }
- $metadata = $this->em->getClassMetadata(
- $metadata->getAssociationTargetClass($entityName)
- );
+
+ $metadata = $this->em->getClassMetadata($metadata->getAssociationTargetClass($entityName));
+
$joinName .= '_' . $this->getJoinName(
$metadata,
- Container::camelize($metadata->getTableName()),
+ Container::camelize(explode('\\', $metadata->getName())[count(explode('\\', $metadata->getName()))-1]),
$entityName
);
- // The join required to get to the entity in question
+
+ $joinName .= '_' . $entityName;
+
+ // The join required to get to the entity in question
if (!isset($this->assignedJoins[$joinName])) {
$this->assignedJoins[$joinName]['joinOn'] = $joinOn;
$this->assignedJoins[$joinName]['mdataColumn'] = $columnName;
@@ -327,7 +337,7 @@ protected function setRelatedEntityColumnInfo(array &$association, array $fields
}
else {
throw new Exception(
- "Association '$entityName' not found ($mdataName)",
+ "Relación '$entityName' no encontrada ($mdataName)",
'404'
);
}
@@ -336,7 +346,7 @@ protected function setRelatedEntityColumnInfo(array &$association, array $fields
// Check the last field on the last related entity of the dotted notation
if (!$metadata->hasField(lcfirst($lastField))) {
throw new Exception(
- "Field '$lastField' on association '$entityName' not found ($mdataName)",
+ "Propiedad '$lastField' en la relación '$entityName' no encontrada ($mdataName)",
'404'
);
}
@@ -356,15 +366,14 @@ protected function setSingleFieldColumnInfo(array &$association, $fieldName) {
$fieldName = Container::camelize($fieldName);
if (!$this->metadata->hasField(lcfirst($fieldName))) {
- throw new Exception(
- "Field '$fieldName' not found.)",
- '404'
- );
- }
-
- $association['fieldName'] = $fieldName;
- $association['entityName'] = $this->tableName;
- $association['fullName'] = $this->tableName . '.' . lcfirst($fieldName);
+ $association['fieldName'] = $fieldName;
+ $association['entityName'] = null;
+ $association['fullName'] = lcfirst($fieldName);
+ }else{
+ $association['fieldName'] = $fieldName;
+ $association['entityName'] = $this->tableName;
+ $association['fullName'] = $this->tableName . '.' . lcfirst($fieldName);
+ }
}
/**
@@ -380,7 +389,7 @@ protected function getJoinName(ClassMetadata $metadata, $tableName, $entityName)
// If it is self-referencing then we must avoid collisions
if ($metadata->getName() == $this->metadata->getName()) {
- $joinName .= "_$entityName";
+ $joinName = "$entityName";
}
return $joinName;
@@ -480,8 +489,25 @@ public function setWhere(QueryBuilder $qb)
for ($i=0 ; $i < count($this->parameters); $i++) {
if (isset($this->request['bSearchable_'.$i]) && $this->request['bSearchable_'.$i] == "true") {
$qbParam = "sSearch_global_{$this->associations[$i]['entityName']}_{$this->associations[$i]['fieldName']}";
+ $fieldType = $this->metadata->getTypeOfField(lcfirst($this->associations[$i]['fieldName']));
+
+ switch ($fieldType) {
+ case "datetime":
+ $fieldName = "DATE_FORMAT(".$this->associations[$i]['fullName'].", '%d/%m/%Y %H:%i:%s')";
+ break;
+ case "time":
+ $fieldName = "DATE_FORMAT(".$this->associations[$i]['fullName'].", '%H:%i:%s')";
+ break;
+ case "date":
+ $fieldName = "DATE_FORMAT(".$this->associations[$i]['fullName'].", '%d/%m/%Y')";
+ break;
+ default:
+ $fieldName = $this->associations[$i]['fullName'];
+ break;
+ }
+
$orExpr->add($qb->expr()->like(
- $this->associations[$i]['fullName'],
+ $fieldName,
":$qbParam"
));
$qb->setParameter($qbParam, "%" . $this->request['sSearch'] . "%");
@@ -495,13 +521,31 @@ public function setWhere(QueryBuilder $qb)
for ($i=0 ; $i < count($this->parameters); $i++) {
if (isset($this->request['bSearchable_'.$i]) && $this->request['bSearchable_'.$i] == "true" && $this->request['sSearch_'.$i] != '') {
$qbParam = "sSearch_single_{$this->associations[$i]['entityName']}_{$this->associations[$i]['fieldName']}";
+ $fieldType = $this->metadata->getTypeOfField(lcfirst($this->associations[$i]['fieldName']));
+
+ switch ($fieldType) {
+ case "datetime":
+ $fieldName = "DATE_FORMAT(".$this->associations[$i]['fullName'].", '%d/%m/%Y %H:%i:%s')";
+ break;
+ case "time":
+ $fieldName = "DATE_FORMAT(".$this->associations[$i]['fullName'].", '%H:%i:%s')";
+ break;
+ case "date":
+ $fieldName = "DATE_FORMAT(".$this->associations[$i]['fullName'].", '%d/%m/%Y')";
+ break;
+ default:
+ $fieldName = $this->associations[$i]['fullName'];
+ break;
+ }
+
$andExpr->add($qb->expr()->like(
- $this->associations[$i]['fullName'],
- ":$qbParam"
- ));
+ $fieldName,
+ ":$qbParam")
+ );
$qb->setParameter($qbParam, "%" . $this->request['sSearch_'.$i] . "%");
}
}
+
if ($andExpr->count() > 0) {
$qb->andWhere($andExpr);
}
@@ -549,7 +593,12 @@ public function setSelect(QueryBuilder $qb)
// Combine all columns to pull
foreach ($this->associations as $column) {
$parts = explode('.', $column['fullName']);
- $columns[$parts[0]][] = $parts[1];
+
+ if(count($parts) > 1){
+ $columns[$parts[0]][] = $parts[1];
+ }else{
+ $columns['Custom'][] = $parts[0];
+ }
}
// Partial column results on entities require that we include the identifier as part of the selection
@@ -565,7 +614,8 @@ public function setSelect(QueryBuilder $qb)
}
foreach ($columns as $columnName => $fields) {
- $partials[] = 'partial ' . $columnName . '.{' . implode(',', $fields) . '}';
+ if($columnName != 'Custom')
+ $partials[] = 'partial ' . $columnName . '.{' . implode(',', $fields) . '}';
}
$qb->select(implode(',', $partials));
@@ -576,7 +626,7 @@ public function setSelect(QueryBuilder $qb)
* Method to execute after constructing this object. Configures the object before
* executing getSearchResults()
*/
- public function makeSearch()
+ public function makeSearch()
{
$this->setSelect($this->qb);
$this->setAssociations($this->qb);
@@ -630,7 +680,7 @@ public function executeSearch()
while ($field = array_shift($fields)) {
$rowRef = &$rowRef[$field];
// We ran into a collection based entity. Combine, merge, and continue on...
- if (!empty($fields) && !$this->isAssocArray($rowRef)) {
+ if (!empty($fields) && $rowRef !== null && !$this->isAssocArray($rowRef)) {
$children = array();
while ($childItem = array_shift($rowRef)) {
$children = array_merge_recursive($children, $childItem);
@@ -639,6 +689,10 @@ public function executeSearch()
}
}
}
+
+ // Iterate and sanitize
+ $item = $this->purifyRecursive($item);
+
$output['aaData'][] = $item;
}
@@ -746,7 +800,7 @@ public function getCountAllResults()
return (int) $qb->getQuery()->getSingleScalarResult();
}
-
+
/**
* @return int Total query results after searches/filtering
*/
@@ -795,4 +849,66 @@ public function getQueryBuilder()
{
return $this->qb;
}
+
+ public function setCustom($response, $nombre, $clase, $funcion, $parameters = []){
+
+ $data = json_decode($response->getContent(), true);
+
+ foreach($data['aaData'] as $key => $registro){
+
+ $parametros = [];
+
+ if(count($parameters) == 0)
+ $parametros = array($registro['id']);
+ else{
+ $parametros = array_merge(array($registro['id']), $parameters);
+ }
+
+ $data['aaData'][$key][$nombre] = call_user_func_array( array( $clase, $funcion), $parametros );
+ }
+
+ return $response->setContent(json_encode($data));
+
+ }
+
+ private function htmlPurifierInit()
+ {
+ $config = HTMLPurifier_Config::createDefault();
+
+ // Permitir todos los elementos y atributos válidos HTML5, para no limitar nada
+ $config->set('HTML.Allowed', null); // No restringir etiquetas ni atributos
+
+ // Permitir completamente el CSS, para evitar que modifique estilos
+ $config->set('CSS.AllowedProperties', null);
+
+ // No transformar ni corregir el HTML
+ $config->set('HTML.Doctype', 'HTML 4.01 Transitional'); // un doctype flexible
+ $config->set('HTML.Trusted', false); // seguridad contra XSS
+ $config->set('Core.EscapeNonASCIICharacters', false);
+ $config->set('Core.ConvertDocumentToFragment', true);
+
+ // Evitar auto-corrección de etiquetas
+ $config->set('HTML.DefinitionID', 'custom-def');
+ $config->set('HTML.DefinitionRev', 1);
+ $config->set('AutoFormat.AutoParagraph', false);
+ $config->set('AutoFormat.RemoveEmpty', false);
+
+ // Deshabilitar filtro CSS agresivo
+ $config->set('CSS.Trusted', false);
+ $config->set('CSS.AllowImportant', true);
+
+ $this->htmlPurifier = new HTMLPurifier($config);
+ }
+
+ protected function purifyRecursive($data)
+ {
+ foreach ($data as $key => $value) {
+ if (is_string($value)) {
+ $data[$key] = $this->htmlPurifier->purify($value);
+ } elseif (is_array($value)) {
+ $data[$key] = $this->purifyRecursive($value);
+ }
+ }
+ return $data;
+ }
}
diff --git a/Datatables/DatatableManager.php b/Datatables/DatatableManager.php
index b7edf4f..d2e22cc 100755
--- a/Datatables/DatatableManager.php
+++ b/Datatables/DatatableManager.php
@@ -1,6 +1,6 @@
= 3
+ ? $this->container->get('request_stack')->getCurrentRequest()->query->all()
+ : $this->container->get('request')->query->all();
+
$class = $this->getClassName($class);
$metadata = $this->doctrine->getManager()->getClassMetadata($class);
$repository = $this->doctrine->getRepository($class);
$datatable = new Datatable(
- $this->container->get('request')->query->all(),
+ $request,
$this->doctrine->getRepository($class),
$this->doctrine->getManager()->getClassMetadata($class),
$this->doctrine->getManager(),
- $this->container->get('lankit_datatables.serializer')
+ $this->container->get('tejadong_datatables.serializer')
);
- return $datatable->useDoctrinePaginator($this->useDoctrinePaginator);
+ return $datatable->useDoctrinePaginator($this->useDoctrinePaginator);
}
}
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 443b3f2..313f4fa 100755
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -1,6 +1,6 @@
root('lankit_datatables');
+ $rootNode = $treeBuilder->root('tejadong_datatables');
$rootNode
->children()
diff --git a/DependencyInjection/LanKitDatatablesExtension.php b/DependencyInjection/TejadongDatatablesExtension.php
old mode 100755
new mode 100644
similarity index 84%
rename from DependencyInjection/LanKitDatatablesExtension.php
rename to DependencyInjection/TejadongDatatablesExtension.php
index 9c08ec3..636c8b7
--- a/DependencyInjection/LanKitDatatablesExtension.php
+++ b/DependencyInjection/TejadongDatatablesExtension.php
@@ -1,6 +1,6 @@
setAlias($this->getAlias() . '.' . $key, $service);
}
- $container->getDefinition('lankit_datatables')
+ $container->getDefinition('tejadong_datatables')
->replaceArgument(2, $config['datatable']['use_doctrine_paginator']);
}
@@ -38,6 +38,6 @@ public function load(array $configs, ContainerBuilder $container)
*/
public function getAlias()
{
- return 'lankit_datatables';
+ return 'tejadong_datatables';
}
}
diff --git a/LanKitDatatablesBundle.php b/LanKitDatatablesBundle.php
deleted file mode 100755
index 48579ef..0000000
--- a/LanKitDatatablesBundle.php
+++ /dev/null
@@ -1,14 +0,0 @@
-
- LanKit\DatatablesBundle\Datatables\DatatableManager
+ Tejadong\DatatablesBundle\Datatables\DatatableManager
-
+
diff --git a/Resources/doc/index.md b/Resources/doc/index.md
index 1077e2a..e3d634e 100755
--- a/Resources/doc/index.md
+++ b/Resources/doc/index.md
@@ -1,5 +1,6 @@
-Getting Started With LanKitDatatablesBundle
+Getting Started With TejadongDatatablesBundle
===========================================
+*¡¡Attention!!, This bundle is a copy adapted to add compatibility with Symfony 3, the real repository belongs to [LanKit/DatatablesBundle](https://github.com/LanKit/DatatablesBundle).*
* [Prerequisites](#prerequisites)
* [Installation](#installation)
@@ -46,29 +47,20 @@ serializer service in your config file...
```yml
// app/config.yml
-lankit_datatables:
+tejadong_datatables:
services:
serializer: some_other_serializer # Defaults to jms_serializer.serializer
```
## Installation
-### Step 1: Download LanKitDatatablesBundle using composer
+### Step 1: Download TejadongDatatablesBundle using composer
-Add LanKitDatatablesBundle to your composer.json:
-
-```js
-{
- "require": {
- "lankit/datatables-bundle": "*"
- }
-}
-```
Use composer to download the bundle using the following command:
``` bash
-$ php composer.phar update lankit/datatables-bundle
+$ php composer require tejadong/datatables-bundle
```
### Step 2: Enable the bundle
@@ -83,11 +75,24 @@ public function registerBundles()
{
$bundles = array(
// ...
- new LanKit\DatatablesBundle\LanKitDatatablesBundle(),
+ new Tejadong\DatatablesBundle\TejadongDatatablesBundle(),
);
}
```
+### Step 3: Add the config format
+
+
+Add settings to support date filtering d/m/Y H:i:s :
+
+```yml
+doctrine:
+ orm:
+ dql:
+ datetime_functions:
+ date_format: DoctrineExtensions\Query\Mysql\DateFormat
+```
+
## Usage
To respond to a DataTables.js request from a controller, you can do the following:
@@ -96,7 +101,7 @@ To respond to a DataTables.js request from a controller, you can do the followin
public function getDatatableAction()
{
- $datatable = $this->get('lankit_datatables')->getDatatable('AcmeDemoBundle:Customer');
+ $datatable = $this->get('tejadong_datatables')->getDatatable('AcmeDemoBundle:Customer');
return $datatable->getSearchResults();
}
@@ -108,12 +113,12 @@ By default an entity association is inner joined. This can be changed as a defau
can be set on a per columm basis:
``` php
-use LanKit\DatatablesBundle\Datatables\DataTable;
+use Tejadong\DatatablesBundle\Datatables\DataTable;
...
public function getDatatableAction()
{
- $datatable = $this->get('lankit_datatables')->getDatatable('AcmeDemoBundle:Customer');
+ $datatable = $this->get('tejadong_datatables')->getDatatable('AcmeDemoBundle:Customer');
// The default type for all joins is inner. Change it to left if desired.
$datatable->setDefaultJoinType(Datatable::JOIN_LEFT);
@@ -132,12 +137,12 @@ If you need a different format for the response, you can specify the result type
constants `Datatable::RESULT_ARRAY` and `Datatable::RESULT_JSON`:
``` php
-use LanKit\DatatablesBundle\Datatables\DataTable;
+use Tejadong\DatatablesBundle\Datatables\DataTable;
...
public function getDatatableAction()
{
- $datatable = $this->get('lankit_datatables')->getDatatable('AcmeDemoBundle:Customer');
+ $datatable = $this->get('tejadong_datatables')->getDatatable('AcmeDemoBundle:Customer');
// Get the results as an array
$datatableArray = $datatable->getSearchResults(Datatable::RESULT_ARRAY);
@@ -156,7 +161,7 @@ is passed the QueryBuilder instance as an argument.
public function getDatatableAction()
{
- $datatable = $this->get('lankit_datatables')->getDatatable('AcmeDemoBundle:Customer');
+ $datatable = $this->get('tejadong_datatables')->getDatatable('AcmeDemoBundle:Customer');
// Add the $datatable variable, or other needed variables, to the callback scope
$datatable->addWhereBuilderCallback(function($qb) use ($datatable) {
@@ -187,7 +192,7 @@ then you can toggle it with the `hideFilteredCount` method.
public function getDatatableAction()
{
- $datatable = $this->get('lankit_datatables')
+ $datatable = $this->get('tejadong_datatables')
->getDatatable('AcmeDemoBundle:Customer')
->addWhereBuilderCallback(function($qb) use ($datatable) {
// ...
@@ -245,7 +250,7 @@ You can toggle and modify these properties with the methods `setDtRowClass`, `us
public function getDatatableAction()
{
- $datatable = $this->get('lankit_datatables')
+ $datatable = $this->get('tejadong_datatables')
->getDatatable('AcmeDemoBundle:Customer')
->setDtRowClass('special-class') // Add whatever class(es) you want. Separate classes with a space.
->useDtRowId(true);
@@ -266,7 +271,7 @@ MS SQL. You may receive an error like...
To get around this you can disable the use of the Paginator by doing the following...
```yml
-lankit_datatables:
+tejadong_datatables:
datatable:
use_doctrine_paginator: false
```
diff --git a/TejadongDatatablesBundle.php b/TejadongDatatablesBundle.php
new file mode 100644
index 0000000..5f6c1b3
--- /dev/null
+++ b/TejadongDatatablesBundle.php
@@ -0,0 +1,14 @@
+