From f7ae6c501f938f720313aefa9fedce4a6d6d1aa7 Mon Sep 17 00:00:00 2001 From: Jean Pasdeloup Date: Wed, 18 May 2016 17:46:59 +0200 Subject: [PATCH] first commit --- .gitignore | 19 + LICENCE | 19 + README.md | 122 + composer.json | 38 + composer.lock | 3206 +++++++++++++++++ src/.htaccess | 7 + .../Command/GenerateDoctrineCrudCommand.php | 344 ++ .../Datatable/Data/DatatableData.php | 98 + .../Datatable/Data/DatatableDataManager.php | 114 + .../Generator/DoctrineCrudGenerator.php | 357 ++ .../Generator/DoctrineDatatableGenerator.php | 95 + .../Generator/DoctrineFormGenerator.php | 85 + .../DoctrineTranslationGenerator.php | 105 + .../Generator/Generator.php | 103 + .../skeleton/crud/BaseCrudController.php.twig | 14 + .../skeleton/crud/DefaultController.php.twig | 19 + .../crud/actions/collection_add.php.twig | 25 + .../actions/collection_datatable.php.twig | 28 + .../crud/actions/collection_index.php.twig | 24 + .../crud/actions/collection_remove.php.twig | 25 + .../crud/actions/collection_search.php.twig | 35 + .../skeleton/crud/actions/datatable.php.twig | 23 + .../skeleton/crud/actions/edit.php.twig | 22 + .../crud/actions/entity_search.php.twig | 31 + .../skeleton/crud/actions/index.php.twig | 23 + .../skeleton/crud/actions/new.php.twig | 22 + .../skeleton/crud/actions/show.php.twig | 23 + .../skeleton/crud/config/routing.php.twig | 60 + .../skeleton/crud/config/routing.xml.twig | 46 + .../skeleton/crud/config/routing.yml.twig | 40 + .../skeleton/crud/controller.php.twig | 93 + .../crud/tests/others/full_scenario.php.twig | 44 + .../crud/tests/others/short_scenario.php.twig | 14 + .../skeleton/crud/tests/test.php.twig | 24 + .../crud/views/default.html.twig.twig | 29 + .../skeleton/crud/views/edit.html.twig.twig | 37 + .../skeleton/crud/views/form.html.twig.twig | 14 + .../skeleton/crud/views/index.html.twig.twig | 32 + .../skeleton/crud/views/new.html.twig.twig | 33 + .../crud/views/object/index.html.twig.twig | 18 + .../object/renderResultSelect2.html.twig.twig | 1 + .../crud/views/others/actions.html.twig.twig | 12 + .../others/record_actions.html.twig.twig | 17 + .../skeleton/crud/views/show.html.twig.twig | 122 + .../AbstractCrudDatatableView.php.twig | 68 + .../skeleton/datatable/Datatable.php.twig | 104 + .../datatable/collection_Datatable.php.twig | 100 + .../Resources/skeleton/form/FormType.php.twig | 87 + .../skeleton/translations/admin.en.yml.twig | 26 + .../skeleton/translations/admin.fr.yml.twig | 26 + .../SedonaSBOGeneratorBundle.php | 18 + src/Sedona/SBORuntimeBundle/ClassUtils.php | 47 + .../Controller/BaseCrudController.php | 628 ++++ .../Datatable/Data/DatatableDataManager.php | 141 + .../Datatable/Data/DatatableQuery.php | 1051 ++++++ .../DependencyInjection/Configuration.php | 38 + .../SedonaSBORuntimeExtension.php | 37 + .../Event/AdminAssociationActionEvent.php | 107 + .../SBORuntimeBundle/Event/AdminCrudEvent.php | 85 + .../DataTransformer/EntityToIdTransformer.php | 142 + .../Form/Type/CollectionSelect2Type.php | 118 + .../Form/Type/ColorPickerType.php | 81 + .../Form/Type/EntitySelect2Type.php | 115 + .../Form/Type/EntityTextType.php | 69 + .../Form/Type/Wysihtml5Type.php | 49 + .../Resources/config/services.xml | 53 + .../views/Breadcrumb/breadcrumb.html.twig | 14 + .../Resources/views/layout/macros.html.twig | 44 + .../Resources/views/layout_admin.html.twig | 52 + .../SedonaSBORuntimeBundle.php | 26 + .../SBORuntimeBundle/Twig/WidgetExtension.php | 89 + 71 files changed, 9077 insertions(+) create mode 100644 .gitignore create mode 100644 LICENCE create mode 100644 README.md create mode 100644 composer.json create mode 100644 composer.lock create mode 100644 src/.htaccess create mode 100644 src/Sedona/SBOGeneratorBundle/Command/GenerateDoctrineCrudCommand.php create mode 100644 src/Sedona/SBOGeneratorBundle/Datatable/Data/DatatableData.php create mode 100644 src/Sedona/SBOGeneratorBundle/Datatable/Data/DatatableDataManager.php create mode 100644 src/Sedona/SBOGeneratorBundle/Generator/DoctrineCrudGenerator.php create mode 100644 src/Sedona/SBOGeneratorBundle/Generator/DoctrineDatatableGenerator.php create mode 100644 src/Sedona/SBOGeneratorBundle/Generator/DoctrineFormGenerator.php create mode 100644 src/Sedona/SBOGeneratorBundle/Generator/DoctrineTranslationGenerator.php create mode 100644 src/Sedona/SBOGeneratorBundle/Generator/Generator.php create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/BaseCrudController.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/DefaultController.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_add.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_datatable.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_index.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_remove.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_search.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/datatable.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/edit.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/entity_search.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/index.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/new.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/show.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.xml.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.yml.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/controller.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/tests/others/full_scenario.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/tests/others/short_scenario.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/tests/test.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/default.html.twig.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/edit.html.twig.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/form.html.twig.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/index.html.twig.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/new.html.twig.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/object/index.html.twig.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/object/renderResultSelect2.html.twig.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/others/actions.html.twig.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/others/record_actions.html.twig.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/show.html.twig.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/AbstractCrudDatatableView.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/Datatable.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/collection_Datatable.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/form/FormType.php.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/translations/admin.en.yml.twig create mode 100644 src/Sedona/SBOGeneratorBundle/Resources/skeleton/translations/admin.fr.yml.twig create mode 100644 src/Sedona/SBOGeneratorBundle/SedonaSBOGeneratorBundle.php create mode 100644 src/Sedona/SBORuntimeBundle/ClassUtils.php create mode 100644 src/Sedona/SBORuntimeBundle/Controller/BaseCrudController.php create mode 100644 src/Sedona/SBORuntimeBundle/Datatable/Data/DatatableDataManager.php create mode 100644 src/Sedona/SBORuntimeBundle/Datatable/Data/DatatableQuery.php create mode 100644 src/Sedona/SBORuntimeBundle/DependencyInjection/Configuration.php create mode 100644 src/Sedona/SBORuntimeBundle/DependencyInjection/SedonaSBORuntimeExtension.php create mode 100644 src/Sedona/SBORuntimeBundle/Event/AdminAssociationActionEvent.php create mode 100644 src/Sedona/SBORuntimeBundle/Event/AdminCrudEvent.php create mode 100644 src/Sedona/SBORuntimeBundle/Form/DataTransformer/EntityToIdTransformer.php create mode 100644 src/Sedona/SBORuntimeBundle/Form/Type/CollectionSelect2Type.php create mode 100644 src/Sedona/SBORuntimeBundle/Form/Type/ColorPickerType.php create mode 100644 src/Sedona/SBORuntimeBundle/Form/Type/EntitySelect2Type.php create mode 100644 src/Sedona/SBORuntimeBundle/Form/Type/EntityTextType.php create mode 100644 src/Sedona/SBORuntimeBundle/Form/Type/Wysihtml5Type.php create mode 100644 src/Sedona/SBORuntimeBundle/Resources/config/services.xml create mode 100644 src/Sedona/SBORuntimeBundle/Resources/views/Breadcrumb/breadcrumb.html.twig create mode 100644 src/Sedona/SBORuntimeBundle/Resources/views/layout/macros.html.twig create mode 100644 src/Sedona/SBORuntimeBundle/Resources/views/layout_admin.html.twig create mode 100644 src/Sedona/SBORuntimeBundle/SedonaSBORuntimeBundle.php create mode 100644 src/Sedona/SBORuntimeBundle/Twig/WidgetExtension.php diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ef10f40 --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +/web/bundles/ +/web/assetic/ +/web/css/ +/web/images/ +/web/js/ +/app/bootstrap.php.cache +/app/cache/* +/app/config/parameters.yml +/app/logs/* +!app/cache/.gitkeep +!app/logs/.gitkeep +/app/phpunit.xml +/build/ +/vendor/ +/bin/ +/composer.phar +/components/ +/.idea +*.php~ diff --git a/LICENCE b/LICENCE new file mode 100644 index 0000000..c8e6ebc --- /dev/null +++ b/LICENCE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Sedona + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..12da100 --- /dev/null +++ b/README.md @@ -0,0 +1,122 @@ +Sedona Back Office +================== + +Installation +------------ + +### Requirements + + - Symfony 2.7 + + +### Step 1: Download the Bundle + +Open a command console, enter your project directory and execute the +following command to download the latest stable version of this bundle: + +```bash +$ composer require "sedona/sbo" "~1" +``` + +This command requires you to have Composer installed globally, as explained +in the [installation chapter](https://getcomposer.org/doc/00-intro.md) +of the Composer documentation. + +### Step 2: Enable the Bundle + +Then, enable the bundle by adding it to the list of registered bundles +in the `app/AppKernel.php` file of your project, if you don't use them +already, you also need to add a few more bundles: + +```php +getEnvironment(), array('dev', 'test'), true)) { + // ... + $bundles[] = new Sedona\SBOGeneratorBundle\SedonaSBOGeneratorBundle(); + } + // ... + } + + // ... +} +``` + +### Step 3: Create a base layout + +Create a template app/Resources/views/layout_admin.html.twig + +``` + {% extends 'SedonaSBORuntimeBundle:layout:base-layout.html.twig' %} + + {% block avanzu_logo %} + + {% endblock %} + {% block title %}Administration{% endblock %} + {% block page_title %}Administration{% endblock %} + {% block page_subtitle %}Administration area{% endblock %} +``` + +### Step 4: Add some basic configuration + +Add in routing.yml + + admin: + resource: "@AppBundle/Controller/Admin" + type: annotation + prefix: /admin + + fos_js_routing: + resource: "@FOSJsRoutingBundle/Resources/config/routing/routing.xml" + + +Add in config.yml + + jms_di_extra: + locations: + all_bundles: false + bundles: [SedonaSBOTestBundle] + directories: ["%kernel.root_dir%/../src"] + + +And activate translation in it by uncommenting + + translator: { fallback: "%locale%" } + + +Generator usage +--------------- + + php app/console sbo:generate:crud --entity SedonaSBOTestBundle:Track --with-write --overwrite + +To be working, each Entity should have its __toString() method declared. + +Association fields OneToMany are created but commented by default: + +* in datatable: before uncommenting, the column name should be changed (entity.name by default) +* in form: only entities with few lines should be uncommented, for long datas, should be replaced by a select2 + + + diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..62141c7 --- /dev/null +++ b/composer.json @@ -0,0 +1,38 @@ +{ + "name": "sedona/sbo", + "type": "library", + "description": "Sedona Back-Office bundles", + "license": "MIT", + "authors": [ + { + "name": "Sedona", + "email": "contact@sedona.fr" + } + ], + "autoload": { + "psr-4": { + "": "src/" + } + }, + "require": { + "php": ">=5.3.9", + + "doctrine/orm" : "^2.4.8", + "doctrine/doctrine-bundle" : "~1.4", + "twig/extensions" : "^1.3", + "symfony/symfony" : "~2.7", + "symfony/assetic-bundle" : "~2.3", + + "symfony/monolog-bundle" : "~2.4", + "friendsofsymfony/jsrouting-bundle" : "^1.5", + "jms/di-extra-bundle" : "~1.5", + "Trsteel/ckeditor-bundle" : "~1.8", + "helios-ag/fm-elfinder-bundle" : "~5.0", + "datatables/datatables" : "^1.10", + "moment/moment" : "^2.10", + "knplabs/knp-menu-bundle" : "~2.0", + "knplabs/knp-paginator-bundle" : "~2.4", + "avanzu/admin-theme-bundle" : "~1.3", + "sg/datatablesbundle" : "^0.10.0" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..df7bc62 --- /dev/null +++ b/composer.lock @@ -0,0 +1,3206 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "abebeadbb11624a984c678b1f93f1f25", + "content-hash": "e28cb1c8b2786e3cdc368ed13d915caa", + "packages": [ + { + "name": "avanzu/admin-theme-bundle", + "version": "1.3.4", + "source": { + "type": "git", + "url": "https://github.com/avanzu/AdminThemeBundle.git", + "reference": "24a1ac93ace744b5e8cffe6681009a4cc8f3a133" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/avanzu/AdminThemeBundle/zipball/24a1ac93ace744b5e8cffe6681009a4cc8f3a133", + "reference": "24a1ac93ace744b5e8cffe6681009a4cc8f3a133", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "symfony/assetic-bundle": ">=2.3", + "symfony/event-dispatcher": ">=2.3", + "symfony/http-foundation": ">=2.3", + "symfony/http-kernel": ">=2.3", + "symfony/twig-bundle": ">=2.3" + }, + "require-dev": { + "hamcrest/hamcrest-php": "dev-master", + "mockery/mockery": "0.9.*@dev" + }, + "type": "library", + "autoload": { + "psr-4": { + "Avanzu\\AdminThemeBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marc Bach", + "email": "mail@avanzu.de" + } + ], + "description": "Admin Theme based on the AdminLTE Template for easy integration into symfony", + "time": "2015-12-29 23:17:55" + }, + { + "name": "components/elfinder", + "version": "2.1.5", + "source": { + "type": "git", + "url": "https://github.com/helios-ag/elfinder-component.git", + "reference": "beb325b84ffcf7340a4fa2a5e397575128416e59" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/helios-ag/elfinder-component/zipball/beb325b84ffcf7340a4fa2a5e397575128416e59", + "reference": "beb325b84ffcf7340a4fa2a5e397575128416e59", + "shasum": "" + }, + "require": { + "components/jquery": "^1.8", + "components/jqueryui": "~1.11.1", + "robloach/component-installer": "*" + }, + "type": "component", + "extra": { + "component": { + "scripts": [ + "dist/js/elfinder.min.js" + ], + "styles": [ + "dist/css/elfinder.min.css", + "dist/css/theme.css" + ], + "files": [ + "dist/img/*.png", + "dist/img/*.gif", + "dist/js/i18n/*.js" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Al Ganiev", + "email": "helios.ag@gmail.com" + }, + { + "name": "ElFinder Team", + "homepage": "https://github.com/Studio-42/elFinder" + } + ], + "description": "ElFinder FileManager", + "keywords": [ + "elfinder", + "file manager" + ], + "time": "2016-02-19 14:27:33" + }, + { + "name": "components/jquery", + "version": "1.12.1", + "source": { + "type": "git", + "url": "https://github.com/components/jquery.git", + "reference": "b3879795fd6fa63ef364cdf9ae6cd779d61edfb2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/components/jquery/zipball/b3879795fd6fa63ef364cdf9ae6cd779d61edfb2", + "reference": "b3879795fd6fa63ef364cdf9ae6cd779d61edfb2", + "shasum": "" + }, + "type": "component", + "extra": { + "component": { + "scripts": [ + "jquery.js" + ], + "files": [ + "jquery.min.js", + "jquery.min.map", + "jquery-migrate.js", + "jquery-migrate.min.js" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Resig", + "email": "jeresig@gmail.com" + } + ], + "description": "jQuery JavaScript Library", + "homepage": "http://jquery.com", + "time": "2016-02-24 08:51:35" + }, + { + "name": "components/jqueryui", + "version": "1.11.4", + "source": { + "type": "git", + "url": "https://github.com/components/jqueryui.git", + "reference": "c34f8dbf3ba57b3784b93f26119f436c0e8288e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/components/jqueryui/zipball/c34f8dbf3ba57b3784b93f26119f436c0e8288e1", + "reference": "c34f8dbf3ba57b3784b93f26119f436c0e8288e1", + "shasum": "" + }, + "require": { + "components/jquery": ">=1.6" + }, + "type": "component", + "extra": { + "component": { + "name": "jquery-ui", + "scripts": [ + "jquery-ui.js" + ], + "files": [ + "ui/**", + "themes/**", + "jquery-ui.min.js" + ], + "shim": { + "deps": [ + "jquery" + ], + "exports": "jQuery" + } + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "jQuery UI Team", + "homepage": "http://jqueryui.com/about" + }, + { + "name": "Joern Zaefferer", + "email": "joern.zaefferer@gmail.com", + "homepage": "http://bassistance.de" + }, + { + "name": "Scott Gonzalez", + "email": "scott.gonzalez@gmail.com", + "homepage": "http://scottgonzalez.com" + }, + { + "name": "Kris Borchers", + "email": "kris.borchers@gmail.com", + "homepage": "http://krisborchers.com" + }, + { + "name": "Mike Sherov", + "email": "mike.sherov@gmail.com", + "homepage": "http://mike.sherov.com" + }, + { + "name": "TJ VanToll", + "email": "tj.vantoll@gmail.com", + "homepage": "http://tjvantoll.com" + }, + { + "name": "Corey Frang", + "email": "gnarf37@gmail.com", + "homepage": "http://gnarf.net" + }, + { + "name": "Felix Nagel", + "email": "info@felixnagel.com", + "homepage": "http://www.felixnagel.com" + } + ], + "description": "jQuery UI is a curated set of user interface interactions, effects, widgets, and themes built on top of the jQuery JavaScript Library. Whether you're building highly interactive web applications or you just need to add a date picker to a form control, jQuery UI is the perfect choice.", + "time": "2015-03-13 14:48:12" + }, + { + "name": "datatables/datatables", + "version": "1.10.11", + "source": { + "type": "git", + "url": "https://github.com/DataTables/DataTables.git", + "reference": "d0163e24a0735ef3c0f04886be301baba67482e1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/DataTables/DataTables/zipball/d0163e24a0735ef3c0f04886be301baba67482e1", + "reference": "d0163e24a0735ef3c0f04886be301baba67482e1", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "DataTables is a plug-in for the jQuery Javascript library. It is a highly flexible tool, based upon the foundations of progressive enhancement, which will add advanced interaction controls to any HTML table.", + "homepage": "http://www.datatables.net/", + "time": "2016-02-11 14:30:12" + }, + { + "name": "doctrine/annotations", + "version": "v1.2.7", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "reference": "f25c8aab83e0c3e976fd7d19875f198ccf2f7535", + "shasum": "" + }, + "require": { + "doctrine/lexer": "1.*", + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/cache": "1.*", + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Annotations\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "time": "2015-08-31 12:32:49" + }, + { + "name": "doctrine/cache", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/cache.git", + "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/cache/zipball/f8af318d14bdb0eff0336795b428b547bd39ccb6", + "reference": "f8af318d14bdb0eff0336795b428b547bd39ccb6", + "shasum": "" + }, + "require": { + "php": "~5.5|~7.0" + }, + "conflict": { + "doctrine/common": ">2.2,<2.4" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0", + "predis/predis": "~1.0", + "satooshi/php-coveralls": "~0.6" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\Cache\\": "lib/Doctrine/Common/Cache" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Caching library offering an object-oriented API for many cache backends", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2015-12-31 16:37:02" + }, + { + "name": "doctrine/collections", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/collections.git", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/collections/zipball/6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "reference": "6c1e4eef75f310ea1b3e30945e9f06e652128b8a", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "~4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Collections\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Collections Abstraction library", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "array", + "collections", + "iterator" + ], + "time": "2015-04-14 22:21:58" + }, + { + "name": "doctrine/common", + "version": "v2.6.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/common.git", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/common/zipball/a579557bc689580c19fee4e27487a67fe60defc0", + "reference": "a579557bc689580c19fee4e27487a67fe60defc0", + "shasum": "" + }, + "require": { + "doctrine/annotations": "1.*", + "doctrine/cache": "1.*", + "doctrine/collections": "1.*", + "doctrine/inflector": "1.*", + "doctrine/lexer": "1.*", + "php": "~5.5|~7.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common Library for Doctrine projects", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "annotations", + "collections", + "eventmanager", + "persistence", + "spl" + ], + "time": "2015-12-25 13:18:31" + }, + { + "name": "doctrine/dbal", + "version": "v2.5.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/dbal.git", + "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/dbal/zipball/abbdfd1cff43a7b99d027af3be709bc8fc7d4769", + "reference": "abbdfd1cff43a7b99d027af3be709bc8fc7d4769", + "shasum": "" + }, + "require": { + "doctrine/common": ">=2.4,<2.7-dev", + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*", + "symfony/console": "2.*" + }, + "suggest": { + "symfony/console": "For helpful console commands such as SQL execution and import of files." + }, + "bin": [ + "bin/doctrine-dbal" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\DBAL\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Database Abstraction Layer", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "persistence", + "queryobject" + ], + "time": "2016-01-05 22:11:12" + }, + { + "name": "doctrine/doctrine-bundle", + "version": "1.6.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineBundle.git", + "reference": "e9c2ccf573b59b7cea566390f34254fed3c20ed9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineBundle/zipball/e9c2ccf573b59b7cea566390f34254fed3c20ed9", + "reference": "e9c2ccf573b59b7cea566390f34254fed3c20ed9", + "shasum": "" + }, + "require": { + "doctrine/dbal": "~2.3", + "doctrine/doctrine-cache-bundle": "~1.0", + "jdorn/sql-formatter": "~1.1", + "php": ">=5.3.2", + "symfony/console": "~2.3|~3.0", + "symfony/doctrine-bridge": "~2.2|~3.0", + "symfony/framework-bundle": "~2.3|~3.0" + }, + "require-dev": { + "doctrine/orm": "~2.3", + "phpunit/phpunit": "~4", + "satooshi/php-coveralls": "~0.6.1", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/validator": "~2.2|~3.0", + "symfony/yaml": "~2.2|~3.0", + "twig/twig": "~1.10" + }, + "suggest": { + "doctrine/orm": "The Doctrine ORM integration is optional in the bundle.", + "symfony/web-profiler-bundle": "to use the data collector" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\DoctrineBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org/" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony DoctrineBundle", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "dbal", + "orm", + "persistence" + ], + "time": "2016-01-10 17:21:44" + }, + { + "name": "doctrine/doctrine-cache-bundle", + "version": "1.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineCacheBundle.git", + "reference": "18c600a9b82f6454d2e81ca4957cdd56a1cf3504" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineCacheBundle/zipball/18c600a9b82f6454d2e81ca4957cdd56a1cf3504", + "reference": "18c600a9b82f6454d2e81ca4957cdd56a1cf3504", + "shasum": "" + }, + "require": { + "doctrine/cache": "^1.4.2", + "doctrine/inflector": "~1.0", + "php": ">=5.3.2", + "symfony/doctrine-bridge": "~2.2|~3.0" + }, + "require-dev": { + "instaclick/coding-standard": "~1.1", + "instaclick/object-calisthenics-sniffs": "dev-master", + "instaclick/symfony2-coding-standard": "dev-remaster", + "phpunit/phpunit": "~4", + "predis/predis": "~0.8", + "satooshi/php-coveralls": "~0.6.1", + "squizlabs/php_codesniffer": "~1.5", + "symfony/console": "~2.2|~3.0", + "symfony/finder": "~2.2|~3.0", + "symfony/framework-bundle": "~2.2|~3.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/security-acl": "~2.3|~3.0", + "symfony/validator": "~2.2|~3.0", + "symfony/yaml": "~2.2|~3.0" + }, + "suggest": { + "symfony/security-acl": "For using this bundle to cache ACLs" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\DoctrineCacheBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Fabio B. Silva", + "email": "fabio.bat.silva@gmail.com" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@hotmail.com" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org/" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony Bundle for Doctrine Cache", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "cache", + "caching" + ], + "time": "2016-01-26 17:28:51" + }, + { + "name": "doctrine/inflector", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/90b2128806bfde671b6952ab8bea493942c1fdae", + "reference": "90b2128806bfde671b6952ab8bea493942c1fdae", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Inflector\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Common String Manipulations with regard to casing and singular/plural rules.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "inflection", + "pluralize", + "singularize", + "string" + ], + "time": "2015-11-06 14:35:42" + }, + { + "name": "doctrine/instantiator", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/doctrine/instantiator.git", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/instantiator/zipball/8e884e78f9f0eb1329e445619e04456e64d8051d", + "reference": "8e884e78f9f0eb1329e445619e04456e64d8051d", + "shasum": "" + }, + "require": { + "php": ">=5.3,<8.0-DEV" + }, + "require-dev": { + "athletic/athletic": "~0.1.8", + "ext-pdo": "*", + "ext-phar": "*", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Instantiator\\": "src/Doctrine/Instantiator/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com", + "homepage": "http://ocramius.github.com/" + } + ], + "description": "A small, lightweight utility to instantiate objects in PHP without invoking their constructors", + "homepage": "https://github.com/doctrine/instantiator", + "keywords": [ + "constructor", + "instantiate" + ], + "time": "2015-06-14 21:17:01" + }, + { + "name": "doctrine/lexer", + "version": "v1.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", + "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\Common\\Lexer\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "lexer", + "parser" + ], + "time": "2014-09-09 13:34:57" + }, + { + "name": "doctrine/orm", + "version": "v2.5.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/doctrine2.git", + "reference": "bc4ddbfb0114cb33438cc811c9a740d8aa304aab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/doctrine2/zipball/bc4ddbfb0114cb33438cc811c9a740d8aa304aab", + "reference": "bc4ddbfb0114cb33438cc811c9a740d8aa304aab", + "shasum": "" + }, + "require": { + "doctrine/cache": "~1.4", + "doctrine/collections": "~1.2", + "doctrine/common": ">=2.5-dev,<2.7-dev", + "doctrine/dbal": ">=2.5-dev,<2.6-dev", + "doctrine/instantiator": "~1.0.1", + "ext-pdo": "*", + "php": ">=5.4", + "symfony/console": "~2.5|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "symfony/yaml": "~2.3|~3.0" + }, + "suggest": { + "symfony/yaml": "If you want to use YAML Metadata Mapping Driver" + }, + "bin": [ + "bin/doctrine", + "bin/doctrine.php" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Doctrine\\ORM\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Object-Relational-Mapper for PHP", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database", + "orm" + ], + "time": "2016-01-05 21:34:58" + }, + { + "name": "ezyang/htmlpurifier", + "version": "v4.7.0", + "source": { + "type": "git", + "url": "https://github.com/ezyang/htmlpurifier.git", + "reference": "ae1828d955112356f7677c465f94f7deb7d27a40" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/ae1828d955112356f7677c465f94f7deb7d27a40", + "reference": "ae1828d955112356f7677c465f94f7deb7d27a40", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "type": "library", + "autoload": { + "psr-0": { + "HTMLPurifier": "library/" + }, + "files": [ + "library/HTMLPurifier.composer.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Edward Z. Yang", + "email": "admin@htmlpurifier.org", + "homepage": "http://ezyang.com" + } + ], + "description": "Standards compliant HTML filter written in PHP", + "homepage": "http://htmlpurifier.org/", + "keywords": [ + "html" + ], + "time": "2015-08-05 01:03:42" + }, + { + "name": "friendsofsymfony/jsrouting-bundle", + "version": "1.6.0", + "source": { + "type": "git", + "url": "https://github.com/FriendsOfSymfony/FOSJsRoutingBundle.git", + "reference": "2f52d924692647db02bbcb27c159fef03bf000c9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FriendsOfSymfony/FOSJsRoutingBundle/zipball/2f52d924692647db02bbcb27c159fef03bf000c9", + "reference": "2f52d924692647db02bbcb27c159fef03bf000c9", + "shasum": "" + }, + "require": { + "php": ">=5.3.2", + "symfony/console": "~2.0|3.*", + "symfony/framework-bundle": "~2.0|3.*", + "symfony/serializer": "~2.0|3.*", + "willdurand/jsonp-callback-validator": "~1.0" + }, + "require-dev": { + "symfony/expression-language": "~2.4|3.*" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "psr-4": { + "FOS\\JsRoutingBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "FriendsOfSymfony Community", + "homepage": "https://github.com/friendsofsymfony/FOSJsRoutingBundle/contributors" + }, + { + "name": "William Durand", + "email": "william.durand1@gmail.com" + } + ], + "description": "A pretty nice way to expose your Symfony2 routing to client applications.", + "homepage": "http://friendsofsymfony.github.com", + "keywords": [ + "Js Routing", + "javascript", + "routing" + ], + "time": "2015-10-28 15:08:39" + }, + { + "name": "helios-ag/fm-elfinder-bundle", + "version": "5.3", + "source": { + "type": "git", + "url": "https://github.com/helios-ag/FMElfinderBundle.git", + "reference": "01545d6343fdeafb7f308d16e14f42e36d3ddb6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/helios-ag/FMElfinderBundle/zipball/01545d6343fdeafb7f308d16e14f42e36d3ddb6d", + "reference": "01545d6343fdeafb7f308d16e14f42e36d3ddb6d", + "shasum": "" + }, + "require": { + "components/elfinder": "~2.0", + "helios-ag/fm-elfinder-php-connector": "~2.0", + "php": ">=5.3.3", + "symfony/framework-bundle": "~2.4", + "symfony/symfony": "~2.4", + "symfony/twig-bundle": "~2.4" + }, + "require-dev": { + "matthiasnoback/symfony-config-test": "~1", + "matthiasnoback/symfony-dependency-injection-test": "~0.7", + "satooshi/php-coveralls": "dev-master", + "symfony/form": "~2.1" + }, + "suggest": { + "egeloen/ckeditor-bundle": "CKEditor Bundle by Egeloen", + "helios-ag/fm-summernote-bundle": "Summernote Bundle by me", + "stfalcon/tinymce-bundle": "TinyMCE Bundle by Stfalcon", + "trsteel/ckeditor-bundle": "CKEditor Bundle by trsteel" + }, + "type": "symfony-bundle", + "autoload": { + "psr-4": { + "FM\\ElfinderBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Al Ganiev", + "email": "helios.ag@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://github.com/helios-ag/FMElfinderBundle/contributors" + } + ], + "description": "ElFinder bundle, adds ElFinder file manager to your Symfony2 project", + "homepage": "https://github.com/helios-ag/FMElfinderBundle", + "keywords": [ + "elfinder", + "file manager" + ], + "time": "2015-11-15 18:24:14" + }, + { + "name": "helios-ag/fm-elfinder-php-connector", + "version": "2.5.3", + "source": { + "type": "git", + "url": "https://github.com/helios-ag/ElFinderPHP.git", + "reference": "1b07830417f261c9c54bbcdc953071e7234b8759" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/helios-ag/ElFinderPHP/zipball/1b07830417f261c9c54bbcdc953071e7234b8759", + "reference": "1b07830417f261c9c54bbcdc953071e7234b8759", + "shasum": "" + }, + "require": { + "php": ">=5.4" + }, + "suggest": { + "aws/aws-sdk-php": "Allows you to use AWS S3 storage", + "dropbox-php/dropbox-php": "Allows you to use Dropbox storage" + }, + "type": "library", + "autoload": { + "psr-4": { + "FM\\ElFinderPHP\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3 Clause" + ], + "authors": [ + { + "name": "Al Ganiev", + "email": "helios.ag@gmail.com" + }, + { + "name": "ElFinder authors", + "homepage": "https://github.com/Studio-42/elFinder" + }, + { + "name": "Symfony Community", + "homepage": "https://github.com/helios-ag/ElFinderPHP/contributors" + } + ], + "description": "ElFinder PHP backend, 5.4 compliant", + "homepage": "http://github.com/helios-ag/ElFinderPHP", + "keywords": [ + "elfinder", + "filemanager" + ], + "time": "2016-04-26 02:19:13" + }, + { + "name": "ircmaxell/password-compat", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/ircmaxell/password_compat.git", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ircmaxell/password_compat/zipball/5c5cde8822a69545767f7c7f3058cb15ff84614c", + "reference": "5c5cde8822a69545767f7c7f3058cb15ff84614c", + "shasum": "" + }, + "require-dev": { + "phpunit/phpunit": "4.*" + }, + "type": "library", + "autoload": { + "files": [ + "lib/password.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Anthony Ferrara", + "email": "ircmaxell@php.net", + "homepage": "http://blog.ircmaxell.com" + } + ], + "description": "A compatibility library for the proposed simplified password hashing algorithm: https://wiki.php.net/rfc/password_hash", + "homepage": "https://github.com/ircmaxell/password_compat", + "keywords": [ + "hashing", + "password" + ], + "time": "2014-11-20 16:49:30" + }, + { + "name": "jdorn/sql-formatter", + "version": "v1.2.17", + "source": { + "type": "git", + "url": "https://github.com/jdorn/sql-formatter.git", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "lib" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "http://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/jdorn/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ], + "time": "2014-01-12 16:20:24" + }, + { + "name": "jms/aop-bundle", + "version": "1.2.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/JMSAopBundle.git", + "reference": "78000d007e74283cc564a58e184d7f62548ad394" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/JMSAopBundle/zipball/78000d007e74283cc564a58e184d7f62548ad394", + "reference": "78000d007e74283cc564a58e184d7f62548ad394", + "shasum": "" + }, + "require": { + "jms/cg": "^1.1", + "php": ">=5.3.9", + "symfony/framework-bundle": "^2.3|^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^2.7" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "JMS\\AopBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Adds AOP capabilities to Symfony2", + "keywords": [ + "annotations", + "aop" + ], + "time": "2015-12-09 16:30:46" + }, + { + "name": "jms/cg", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/cg-library.git", + "reference": "0af1113c7409b8636c5244bbae10b2e0ff792e9c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/cg-library/zipball/0af1113c7409b8636c5244bbae10b2e0ff792e9c", + "reference": "0af1113c7409b8636c5244bbae10b2e0ff792e9c", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-0": { + "CG\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache2" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Toolset for generating PHP code", + "keywords": [ + "code generation" + ], + "time": "2015-09-13 08:54:43" + }, + { + "name": "jms/di-extra-bundle", + "version": "1.7.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/JMSDiExtraBundle.git", + "reference": "27c3fc7150550ccc0731290b2c1ceb57449f909d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/JMSDiExtraBundle/zipball/27c3fc7150550ccc0731290b2c1ceb57449f909d", + "reference": "27c3fc7150550ccc0731290b2c1ceb57449f909d", + "shasum": "" + }, + "require": { + "jms/aop-bundle": "~1.1", + "jms/metadata": "~1.0", + "php": "~5.3|~7.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/finder": "~2.3|~3.0", + "symfony/framework-bundle": "~2.3|~3.0", + "symfony/http-kernel": "^2.3.24|~3.0", + "symfony/process": "~2.3|~3.0", + "symfony/routing": "~2.3|~3.0" + }, + "require-dev": { + "doctrine/doctrine-bundle": "~1.5", + "doctrine/orm": "~2.3", + "jms/security-extra-bundle": "~1.0", + "phpcollection/phpcollection": ">=0.2,<0.3-dev", + "sensio/framework-extra-bundle": "~2.0|~3.0", + "symfony/browser-kit": "~2.3|~3.0", + "symfony/class-loader": "~2.3|~3.0", + "symfony/form": "~2.3|~3.0", + "symfony/phpunit-bridge": "~2.7", + "symfony/security-bundle": "~2.3", + "symfony/twig-bundle": "~2.3|~3.0", + "symfony/validator": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.7-dev" + } + }, + "autoload": { + "psr-4": { + "JMS\\DiExtraBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Allows to configure dependency injection using annotations", + "homepage": "http://jmsyst.com/bundles/JMSDiExtraBundle", + "keywords": [ + "annotations", + "dependency injection" + ], + "time": "2016-04-18 22:27:09" + }, + { + "name": "jms/metadata", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/metadata.git", + "reference": "22b72455559a25777cfd28c4ffda81ff7639f353" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/metadata/zipball/22b72455559a25777cfd28c4ffda81ff7639f353", + "reference": "22b72455559a25777cfd28c4ffda81ff7639f353", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "doctrine/cache": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5.x-dev" + } + }, + "autoload": { + "psr-0": { + "Metadata\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache" + ], + "authors": [ + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh", + "role": "Developer of wrapped JMSSerializerBundle" + } + ], + "description": "Class/method/property metadata management in PHP", + "keywords": [ + "annotations", + "metadata", + "xml", + "yaml" + ], + "time": "2014-07-12 07:13:19" + }, + { + "name": "knplabs/knp-components", + "version": "1.3.2", + "source": { + "type": "git", + "url": "https://github.com/KnpLabs/knp-components.git", + "reference": "4503275e4f1a0e9667aa65b0ebed842ef2d8d8ba" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KnpLabs/knp-components/zipball/4503275e4f1a0e9667aa65b0ebed842ef2d8d8ba", + "reference": "4503275e4f1a0e9667aa65b0ebed842ef2d8d8ba", + "shasum": "" + }, + "require": { + "php": ">=5.3.2" + }, + "require-dev": { + "doctrine/mongodb-odm": "~1.0@beta", + "doctrine/orm": "~2.4", + "phpunit/phpunit": "~4.2", + "ruflin/elastica": "~1.0", + "symfony/event-dispatcher": "~2.5" + }, + "suggest": { + "doctrine/common": "to allow usage pagination with Doctrine ArrayCollection", + "doctrine/mongodb-odm": "to allow usage pagination with Doctrine ODM MongoDB", + "doctrine/orm": "to allow usage pagination with Doctrine ORM", + "propel/propel1": "to allow usage pagination with Propel ORM", + "ruflin/Elastica": "to allow usage pagination with ElasticSearch Client", + "solarium/solarium": "to allow usage pagination with Solarium Client" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "psr-0": { + "Knp\\Component": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KnpLabs Team", + "homepage": "http://knplabs.com" + }, + { + "name": "Symfony Community", + "homepage": "http://github.com/KnpLabs/knp-components/contributors" + } + ], + "description": "Knplabs component library", + "homepage": "http://github.com/KnpLabs/knp-components", + "keywords": [ + "components", + "knp", + "knplabs", + "pager", + "paginator" + ], + "time": "2015-09-01 10:54:53" + }, + { + "name": "knplabs/knp-menu", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/KnpLabs/KnpMenu.git", + "reference": "9917b999a3c3d3901386d60c4888b07679291031" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KnpLabs/KnpMenu/zipball/9917b999a3c3d3901386d60c4888b07679291031", + "reference": "9917b999a3c3d3901386d60c4888b07679291031", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "pimple/pimple": "~1.0", + "silex/silex": "~1.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/routing": "~2.3|~3.0", + "twig/twig": "~1.16|~2.0" + }, + "suggest": { + "pimple/pimple": "for the built-in implementations of the menu provider and renderer provider", + "silex/silex": "for the integration with your silex application", + "twig/twig": "for the TwigRenderer and the integration with your templates" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "psr-4": { + "Knp\\Menu\\": "src/Knp/Menu" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + }, + { + "name": "Knplabs", + "homepage": "http://knplabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://github.com/KnpLabs/KnpMenu/contributors" + } + ], + "description": "An object oriented menu library", + "homepage": "http://knplabs.com", + "keywords": [ + "menu", + "tree" + ], + "time": "2016-01-08 15:42:54" + }, + { + "name": "knplabs/knp-menu-bundle", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/KnpLabs/KnpMenuBundle.git", + "reference": "90aff8e39274d1225dfa3bb7b1dd4e47b7312dca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KnpLabs/KnpMenuBundle/zipball/90aff8e39274d1225dfa3bb7b1dd4e47b7312dca", + "reference": "90aff8e39274d1225dfa3bb7b1dd4e47b7312dca", + "shasum": "" + }, + "require": { + "knplabs/knp-menu": "~2.1", + "symfony/framework-bundle": "~2.3|~3.0" + }, + "require-dev": { + "symfony/expression-language": "~2.4|~3.0", + "symfony/phpunit-bridge": "~2.7|~3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Knp\\Bundle\\MenuBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Christophe Coevoet", + "email": "stof@notk.org" + }, + { + "name": "Knplabs", + "homepage": "http://knplabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://github.com/KnpLabs/KnpMenuBundle/contributors" + } + ], + "description": "This bundle provides an integration of the KnpMenu library", + "keywords": [ + "menu" + ], + "time": "2015-12-15 12:06:23" + }, + { + "name": "knplabs/knp-paginator-bundle", + "version": "2.5.3", + "source": { + "type": "git", + "url": "https://github.com/KnpLabs/KnpPaginatorBundle.git", + "reference": "c988761005504007c6c87d6a557641281194a0e5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/KnpLabs/KnpPaginatorBundle/zipball/c988761005504007c6c87d6a557641281194a0e5", + "reference": "c988761005504007c6c87d6a557641281194a0e5", + "shasum": "" + }, + "require": { + "knplabs/knp-components": "~1.2", + "php": ">=5.3.3", + "symfony/framework-bundle": "~2.3|~3.0", + "twig/twig": "~1.12|~2" + }, + "require-dev": { + "symfony/expression-language": "~2.4|~3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.5.x-dev" + } + }, + "autoload": { + "psr-4": { + "Knp\\Bundle\\PaginatorBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KnpLabs Team", + "homepage": "http://knplabs.com" + }, + { + "name": "Symfony2 Community", + "homepage": "http://github.com/KnpLabs/KnpPaginatorBundle/contributors" + } + ], + "description": "Paginator bundle for Symfony2 to automate pagination and simplify sorting and other features", + "homepage": "http://github.com/KnpLabs/KnpPaginatorBundle", + "keywords": [ + "Symfony2", + "bundle", + "knp", + "knplabs", + "pager", + "pagination", + "paginator" + ], + "time": "2016-04-20 11:40:30" + }, + { + "name": "kriswallsmith/assetic", + "version": "v1.3.2", + "source": { + "type": "git", + "url": "https://github.com/kriswallsmith/assetic.git", + "reference": "9928f7c4ad98b234e3559d1049abd13387f86db5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/kriswallsmith/assetic/zipball/9928f7c4ad98b234e3559d1049abd13387f86db5", + "reference": "9928f7c4ad98b234e3559d1049abd13387f86db5", + "shasum": "" + }, + "require": { + "php": ">=5.3.1", + "symfony/process": "~2.1|~3.0" + }, + "conflict": { + "twig/twig": "<1.23" + }, + "require-dev": { + "cssmin/cssmin": "3.0.1", + "joliclic/javascript-packer": "1.1", + "kamicane/packager": "1.0", + "leafo/lessphp": "^0.3.7", + "leafo/scssphp": "~0.1", + "mrclay/minify": "~2.2", + "patchwork/jsqueeze": "~1.0|~2.0", + "phpunit/phpunit": "~4.8", + "psr/log": "~1.0", + "ptachoire/cssembed": "~1.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "twig/twig": "~1.8|~2.0" + }, + "suggest": { + "leafo/lessphp": "Assetic provides the integration with the lessphp LESS compiler", + "leafo/scssphp": "Assetic provides the integration with the scssphp SCSS compiler", + "leafo/scssphp-compass": "Assetic provides the integration with the SCSS compass plugin", + "patchwork/jsqueeze": "Assetic provides the integration with the JSqueeze JavaScript compressor", + "ptachoire/cssembed": "Assetic provides the integration with phpcssembed to embed data uris", + "twig/twig": "Assetic provides the integration with the Twig templating engine" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4-dev" + } + }, + "autoload": { + "psr-0": { + "Assetic": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Asset Management for PHP", + "homepage": "https://github.com/kriswallsmith/assetic", + "keywords": [ + "assets", + "compression", + "minification" + ], + "time": "2015-11-12 13:51:40" + }, + { + "name": "moment/moment", + "version": "2.13.0", + "source": { + "type": "git", + "url": "https://github.com/moment/moment.git", + "reference": "d6651c21c6131fbb5db891b60971357739015688" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/moment/moment/zipball/d6651c21c6131fbb5db891b60971357739015688", + "reference": "d6651c21c6131fbb5db891b60971357739015688", + "shasum": "" + }, + "require": { + "robloach/component-installer": "*" + }, + "type": "component", + "extra": { + "component": { + "scripts": [ + "moment.js" + ], + "files": [ + "min/*.js", + "locale/*.js" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tim Wood", + "email": "washwithcare@gmail.com" + } + ], + "description": "Parse, validate, manipulate, and display dates in JavaScript.", + "homepage": "http://github.com/moment/moment/", + "keywords": [ + "date", + "ender", + "format", + "i18n", + "l10n", + "moment", + "parse", + "time", + "validate" + ], + "time": "2016-04-18 07:29:18" + }, + { + "name": "monolog/monolog", + "version": "1.19.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/monolog.git", + "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5f56ed5212dc509c8dc8caeba2715732abb32dbf", + "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf", + "shasum": "" + }, + "require": { + "php": ">=5.3.0", + "psr/log": "~1.0" + }, + "provide": { + "psr/log-implementation": "1.0.0" + }, + "require-dev": { + "aws/aws-sdk-php": "^2.4.9", + "doctrine/couchdb": "~1.0@dev", + "graylog2/gelf-php": "~1.0", + "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", + "php-console/php-console": "^3.1.3", + "phpunit/phpunit": "~4.5", + "phpunit/phpunit-mock-objects": "2.3.0", + "raven/raven": "^0.13", + "ruflin/elastica": ">=0.90 <3.0", + "swiftmailer/swiftmailer": "~5.3" + }, + "suggest": { + "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", + "doctrine/couchdb": "Allow sending log messages to a CouchDB server", + "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", + "ext-mongo": "Allow sending log messages to a MongoDB server", + "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", + "php-console/php-console": "Allow sending log messages to Google Chrome", + "raven/raven": "Allow sending log messages to a Sentry server", + "rollbar/rollbar": "Allow sending log messages to Rollbar", + "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Monolog\\": "src/Monolog" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Sends your logs to files, sockets, inboxes, databases and various web services", + "homepage": "http://github.com/Seldaek/monolog", + "keywords": [ + "log", + "logging", + "psr-3" + ], + "time": "2016-04-12 18:29:35" + }, + { + "name": "paragonie/random_compat", + "version": "v1.4.1", + "source": { + "type": "git", + "url": "https://github.com/paragonie/random_compat.git", + "reference": "c7e26a21ba357863de030f0b9e701c7d04593774" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paragonie/random_compat/zipball/c7e26a21ba357863de030f0b9e701c7d04593774", + "reference": "c7e26a21ba357863de030f0b9e701c7d04593774", + "shasum": "" + }, + "require": { + "php": ">=5.2.0" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "autoload": { + "files": [ + "lib/random.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "pseudorandom", + "random" + ], + "time": "2016-03-18 20:34:03" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" + }, + { + "name": "robloach/component-installer", + "version": "0.2.3", + "source": { + "type": "git", + "url": "https://github.com/RobLoach/component-installer.git", + "reference": "908a859aa7c4949ba9ad67091e67bac10b66d3d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/RobLoach/component-installer/zipball/908a859aa7c4949ba9ad67091e67bac10b66d3d7", + "reference": "908a859aa7c4949ba9ad67091e67bac10b66d3d7", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0", + "kriswallsmith/assetic": "1.*", + "php": ">=5.3.2" + }, + "require-dev": { + "composer/composer": "1.*@alpha", + "phpunit/phpunit": "4.*" + }, + "type": "composer-plugin", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + }, + "class": "ComponentInstaller\\ComponentInstallerPlugin" + }, + "autoload": { + "psr-0": { + "ComponentInstaller": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Rob Loach", + "homepage": "http://robloach.net" + } + ], + "description": "Allows installation of Components via Composer.", + "time": "2015-08-10 12:35:38" + }, + { + "name": "sg/datatablesbundle", + "version": "v0.10", + "target-dir": "Sg/DatatablesBundle", + "source": { + "type": "git", + "url": "https://github.com/stwe/DatatablesBundle.git", + "reference": "671ea19b314c6d254798cb8de551b847902a5398" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/stwe/DatatablesBundle/zipball/671ea19b314c6d254798cb8de551b847902a5398", + "reference": "671ea19b314c6d254798cb8de551b847902a5398", + "shasum": "" + }, + "require": { + "friendsofsymfony/jsrouting-bundle": "~1.6", + "php": ">=5.3.3", + "symfony/framework-bundle": "~2.6|~3.0" + }, + "require-dev": { + "sensio/generator-bundle": "~2.3" + }, + "suggest": { + "components/jquery": "~1.12", + "datatables/datatables": "1.10.10", + "liip/imagine-bundle": "^1.3", + "moment/moment": "~2.11" + }, + "type": "symfony-bundle", + "autoload": { + "psr-0": { + "Sg\\DatatablesBundle": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "stwe" + }, + { + "name": "SgDatatablesBundle contributors", + "homepage": "https://github.com/stwe/DatatablesBundle/graphs/contributors" + } + ], + "description": "Symfony Datatable Bundle For Doctrine2 Entities", + "homepage": "https://github.com/stwe/DatatablesBundle", + "keywords": [ + "Symfony2", + "admin", + "datagrid", + "datatable", + "datatables", + "grid", + "pagination", + "symfony3", + "table" + ], + "time": "2016-02-25 20:30:25" + }, + { + "name": "symfony/assetic-bundle", + "version": "v2.8.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/assetic-bundle.git", + "reference": "aa5b4f8b712f38745928fa845ddb73300bb2af6d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/assetic-bundle/zipball/aa5b4f8b712f38745928fa845ddb73300bb2af6d", + "reference": "aa5b4f8b712f38745928fa845ddb73300bb2af6d", + "shasum": "" + }, + "require": { + "kriswallsmith/assetic": "~1.3", + "php": ">=5.3.0", + "symfony/console": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/framework-bundle": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "conflict": { + "kriswallsmith/spork": "<=0.2", + "twig/twig": "<1.20" + }, + "require-dev": { + "kriswallsmith/spork": "~0.3", + "patchwork/jsqueeze": "~1.0", + "symfony/class-loader": "~2.3|~3.0", + "symfony/css-selector": "~2.3|~3.0", + "symfony/dom-crawler": "~2.3|~3.0", + "symfony/phpunit-bridge": "~2.7|~3.0", + "symfony/twig-bundle": "~2.3|~3.0" + }, + "suggest": { + "kriswallsmith/spork": "to be able to dump assets in parallel", + "symfony/twig-bundle": "to use the Twig integration" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\AsseticBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kris Wallsmith", + "email": "kris.wallsmith@gmail.com", + "homepage": "http://kriswallsmith.net/" + } + ], + "description": "Integrates Assetic into Symfony2", + "homepage": "https://github.com/symfony/AsseticBundle", + "keywords": [ + "assets", + "compression", + "minification" + ], + "time": "2015-12-28 13:12:39" + }, + { + "name": "symfony/monolog-bundle", + "version": "2.11.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/monolog-bundle.git", + "reference": "e7caf4936c7be82bc6d68df87f1d23a0d5bf6e00" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/monolog-bundle/zipball/e7caf4936c7be82bc6d68df87f1d23a0d5bf6e00", + "reference": "e7caf4936c7be82bc6d68df87f1d23a0d5bf6e00", + "shasum": "" + }, + "require": { + "monolog/monolog": "~1.18", + "php": ">=5.3.2", + "symfony/config": "~2.3|~3.0", + "symfony/dependency-injection": "~2.3|~3.0", + "symfony/http-kernel": "~2.3|~3.0", + "symfony/monolog-bridge": "~2.3|~3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8", + "symfony/console": "~2.3|~3.0", + "symfony/yaml": "~2.3|~3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bundle\\MonologBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + }, + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Symfony MonologBundle", + "homepage": "http://symfony.com", + "keywords": [ + "log", + "logging" + ], + "time": "2016-04-13 16:21:01" + }, + { + "name": "symfony/polyfill-apcu", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-apcu.git", + "reference": "0c901e4e65a2f7ece68f0fd249b56d6ad3adc214" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-apcu/zipball/0c901e4e65a2f7ece68f0fd249b56d6ad3adc214", + "reference": "0c901e4e65a2f7ece68f0fd249b56d6ad3adc214", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting apcu_* functions to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "apcu", + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-03-03 16:49:40" + }, + { + "name": "symfony/polyfill-intl-icu", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-icu.git", + "reference": "8328069d9f5322f0e7b3c3518485acfdc94c3942" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-icu/zipball/8328069d9f5322f0e7b3c3518485acfdc94c3942", + "reference": "8328069d9f5322f0e7b3c3518485acfdc94c3942", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/intl": "~2.3|~3.0" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's ICU-related data and classes", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "icu", + "intl", + "polyfill", + "portable", + "shim" + ], + "time": "2016-02-26 16:18:12" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "1289d16209491b584839022f29257ad859b8532d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/1289d16209491b584839022f29257ad859b8532d", + "reference": "1289d16209491b584839022f29257ad859b8532d", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-20 09:13:37" + }, + { + "name": "symfony/polyfill-php54", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php54.git", + "reference": "9ba741ca01c77282ecf5796c2c1d667f03454ffb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php54/zipball/9ba741ca01c77282ecf5796c2c1d667f03454ffb", + "reference": "9ba741ca01c77282ecf5796c2c1d667f03454ffb", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php54\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.4+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-25 19:13:00" + }, + { + "name": "symfony/polyfill-php55", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php55.git", + "reference": "b4f3f07d91702f8f926339fc4fcf81671d8c27e6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php55/zipball/b4f3f07d91702f8f926339fc4fcf81671d8c27e6", + "reference": "b4f3f07d91702f8f926339fc4fcf81671d8c27e6", + "shasum": "" + }, + "require": { + "ircmaxell/password-compat": "~1.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php55\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.5+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-20 09:13:37" + }, + { + "name": "symfony/polyfill-php56", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php56.git", + "reference": "4d891fff050101a53a4caabb03277284942d1ad9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php56/zipball/4d891fff050101a53a4caabb03277284942d1ad9", + "reference": "4d891fff050101a53a4caabb03277284942d1ad9", + "shasum": "" + }, + "require": { + "php": ">=5.3.3", + "symfony/polyfill-util": "~1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php56\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 5.6+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-20 09:13:37" + }, + { + "name": "symfony/polyfill-php70", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php70.git", + "reference": "386c1be9cad3ab531425211919e78c37971be4ce" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/386c1be9cad3ab531425211919e78c37971be4ce", + "reference": "386c1be9cad3ab531425211919e78c37971be4ce", + "shasum": "" + }, + "require": { + "paragonie/random_compat": "~1.0", + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Php70\\": "" + }, + "files": [ + "bootstrap.php" + ], + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "time": "2016-01-28 22:42:02" + }, + { + "name": "symfony/polyfill-util", + "version": "v1.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-util.git", + "reference": "8de62801aa12bc4dfcf85eef5d21981ae7bb3cc4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-util/zipball/8de62801aa12bc4dfcf85eef5d21981ae7bb3cc4", + "reference": "8de62801aa12bc4dfcf85eef5d21981ae7bb3cc4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Util\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony utilities for portability of PHP codes", + "homepage": "https://symfony.com", + "keywords": [ + "compat", + "compatibility", + "polyfill", + "shim" + ], + "time": "2016-01-20 09:13:37" + }, + { + "name": "symfony/security-acl", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/security-acl.git", + "reference": "053b49bf4aa333a392c83296855989bcf88ddad1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/security-acl/zipball/053b49bf4aa333a392c83296855989bcf88ddad1", + "reference": "053b49bf4aa333a392c83296855989bcf88ddad1", + "shasum": "" + }, + "require": { + "php": ">=5.5.9", + "symfony/security-core": "~2.8|~3.0" + }, + "require-dev": { + "doctrine/common": "~2.2", + "doctrine/dbal": "~2.2", + "psr/log": "~1.0", + "symfony/phpunit-bridge": "~2.8|~3.0" + }, + "suggest": { + "doctrine/dbal": "For using the built-in ACL implementation", + "symfony/class-loader": "For using the ACL generateSql script", + "symfony/finder": "For using the ACL generateSql script" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Security\\Acl\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Security Component - ACL (Access Control List)", + "homepage": "https://symfony.com", + "time": "2015-12-28 09:39:46" + }, + { + "name": "symfony/symfony", + "version": "v2.8.5", + "source": { + "type": "git", + "url": "https://github.com/symfony/symfony.git", + "reference": "39ddd2383f4113cf67f8b28cde2c9d3fa340c3c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/symfony/zipball/39ddd2383f4113cf67f8b28cde2c9d3fa340c3c2", + "reference": "39ddd2383f4113cf67f8b28cde2c9d3fa340c3c2", + "shasum": "" + }, + "require": { + "doctrine/common": "~2.4", + "php": ">=5.3.9", + "psr/log": "~1.0", + "symfony/polyfill-apcu": "~1.1", + "symfony/polyfill-intl-icu": "~1.0", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php54": "~1.0", + "symfony/polyfill-php55": "~1.0", + "symfony/polyfill-php56": "~1.0", + "symfony/polyfill-php70": "~1.0", + "symfony/polyfill-util": "~1.0", + "symfony/security-acl": "~2.7|~3.0.0", + "twig/twig": "~1.23|~2.0" + }, + "conflict": { + "phpdocumentor/reflection": "<1.0.7" + }, + "replace": { + "symfony/asset": "self.version", + "symfony/browser-kit": "self.version", + "symfony/class-loader": "self.version", + "symfony/config": "self.version", + "symfony/console": "self.version", + "symfony/css-selector": "self.version", + "symfony/debug": "self.version", + "symfony/debug-bundle": "self.version", + "symfony/dependency-injection": "self.version", + "symfony/doctrine-bridge": "self.version", + "symfony/dom-crawler": "self.version", + "symfony/event-dispatcher": "self.version", + "symfony/expression-language": "self.version", + "symfony/filesystem": "self.version", + "symfony/finder": "self.version", + "symfony/form": "self.version", + "symfony/framework-bundle": "self.version", + "symfony/http-foundation": "self.version", + "symfony/http-kernel": "self.version", + "symfony/intl": "self.version", + "symfony/ldap": "self.version", + "symfony/locale": "self.version", + "symfony/monolog-bridge": "self.version", + "symfony/options-resolver": "self.version", + "symfony/process": "self.version", + "symfony/property-access": "self.version", + "symfony/property-info": "self.version", + "symfony/proxy-manager-bridge": "self.version", + "symfony/routing": "self.version", + "symfony/security": "self.version", + "symfony/security-bundle": "self.version", + "symfony/security-core": "self.version", + "symfony/security-csrf": "self.version", + "symfony/security-guard": "self.version", + "symfony/security-http": "self.version", + "symfony/serializer": "self.version", + "symfony/stopwatch": "self.version", + "symfony/swiftmailer-bridge": "self.version", + "symfony/templating": "self.version", + "symfony/translation": "self.version", + "symfony/twig-bridge": "self.version", + "symfony/twig-bundle": "self.version", + "symfony/validator": "self.version", + "symfony/var-dumper": "self.version", + "symfony/web-profiler-bundle": "self.version", + "symfony/yaml": "self.version" + }, + "require-dev": { + "doctrine/data-fixtures": "1.0.*", + "doctrine/dbal": "~2.4", + "doctrine/doctrine-bundle": "~1.2", + "doctrine/orm": "~2.4,>=2.4.5", + "egulias/email-validator": "~1.2", + "monolog/monolog": "~1.11", + "ocramius/proxy-manager": "~0.4|~1.0|~2.0", + "phpdocumentor/reflection": "^1.0.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Bridge\\Doctrine\\": "src/Symfony/Bridge/Doctrine/", + "Symfony\\Bridge\\Monolog\\": "src/Symfony/Bridge/Monolog/", + "Symfony\\Bridge\\ProxyManager\\": "src/Symfony/Bridge/ProxyManager/", + "Symfony\\Bridge\\Swiftmailer\\": "src/Symfony/Bridge/Swiftmailer/", + "Symfony\\Bridge\\Twig\\": "src/Symfony/Bridge/Twig/", + "Symfony\\Bundle\\": "src/Symfony/Bundle/", + "Symfony\\Component\\": "src/Symfony/Component/" + }, + "classmap": [ + "src/Symfony/Component/Intl/Resources/stubs" + ], + "exclude-from-classmap": [ + "**/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "The Symfony PHP framework", + "homepage": "https://symfony.com", + "keywords": [ + "framework" + ], + "time": "2016-04-29 15:34:08" + }, + { + "name": "trsteel/ckeditor-bundle", + "version": "v1.10.4", + "source": { + "type": "git", + "url": "https://github.com/trsteel88/TrsteelCkeditorBundle.git", + "reference": "fcf6bea3eaedd8e7b00df27bfd8c9dc043202562" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/trsteel88/TrsteelCkeditorBundle/zipball/fcf6bea3eaedd8e7b00df27bfd8c9dc043202562", + "reference": "fcf6bea3eaedd8e7b00df27bfd8c9dc043202562", + "shasum": "" + }, + "require": { + "ezyang/htmlpurifier": "~4.0", + "php": ">=5.3.0", + "symfony/form": "~2.1|~3.0", + "symfony/framework-bundle": "~2.1|~3.0", + "twig/twig": "~1.1" + }, + "require-dev": { + "phpunit/phpunit": "~4.0", + "symfony/phpunit-bridge": "~2.7", + "symfony/twig-bundle": "~2.1|~3.0", + "symfony/yaml": "~2.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "1.10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Trsteel\\CkeditorBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Trent Steel", + "email": "trsteel88@gmail.com" + } + ], + "description": "Symfony2 bundle for easy integration of the CKEditor WYSIWYG", + "keywords": [ + "CKEditor", + "editor", + "wysiwyg" + ], + "time": "2016-01-31 22:04:11" + }, + { + "name": "twig/extensions", + "version": "v1.3.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig-extensions.git", + "reference": "449e3c8a9ffad7c2479c7864557275a32b037499" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig-extensions/zipball/449e3c8a9ffad7c2479c7864557275a32b037499", + "reference": "449e3c8a9ffad7c2479c7864557275a32b037499", + "shasum": "" + }, + "require": { + "twig/twig": "~1.20|~2.0" + }, + "require-dev": { + "symfony/translation": "~2.3" + }, + "suggest": { + "symfony/translation": "Allow the time_diff output to be translated" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_Extensions_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "Common additional features for Twig that do not directly belong in core", + "homepage": "http://twig.sensiolabs.org/doc/extensions/index.html", + "keywords": [ + "i18n", + "text" + ], + "time": "2015-08-22 16:38:35" + }, + { + "name": "twig/twig", + "version": "v1.24.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8", + "reference": "3e5aa30ebfbafd5951fb1b01e338e1800ce7e0e8", + "shasum": "" + }, + "require": { + "php": ">=5.2.7" + }, + "require-dev": { + "symfony/debug": "~2.7", + "symfony/phpunit-bridge": "~2.7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.24-dev" + } + }, + "autoload": { + "psr-0": { + "Twig_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + }, + { + "name": "Armin Ronacher", + "email": "armin.ronacher@active-4.com", + "role": "Project Founder" + }, + { + "name": "Twig Team", + "homepage": "http://twig.sensiolabs.org/contributors", + "role": "Contributors" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "http://twig.sensiolabs.org", + "keywords": [ + "templating" + ], + "time": "2016-01-25 21:22:18" + }, + { + "name": "willdurand/jsonp-callback-validator", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/willdurand/JsonpCallbackValidator.git", + "reference": "1a7d388bb521959e612ef50c5c7b1691b097e909" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/willdurand/JsonpCallbackValidator/zipball/1a7d388bb521959e612ef50c5c7b1691b097e909", + "reference": "1a7d388bb521959e612ef50c5c7b1691b097e909", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "~3.7" + }, + "type": "library", + "autoload": { + "psr-0": { + "JsonpCallbackValidator": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "William Durand", + "email": "william.durand1@gmail.com", + "homepage": "http://www.willdurand.fr" + } + ], + "description": "JSONP callback validator.", + "time": "2014-01-20 22:35:06" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.3.9" + }, + "platform-dev": [] +} diff --git a/src/.htaccess b/src/.htaccess new file mode 100644 index 0000000..fb1de45 --- /dev/null +++ b/src/.htaccess @@ -0,0 +1,7 @@ + + Require all denied + + + Order deny,allow + Deny from all + diff --git a/src/Sedona/SBOGeneratorBundle/Command/GenerateDoctrineCrudCommand.php b/src/Sedona/SBOGeneratorBundle/Command/GenerateDoctrineCrudCommand.php new file mode 100644 index 0000000..0b6e391 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Command/GenerateDoctrineCrudCommand.php @@ -0,0 +1,344 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBOGeneratorBundle\Command; + +use Doctrine\ORM\Mapping\ClassMetadataFactory; +use Sedona\SBOGeneratorBundle\Generator\DoctrineDatatableGenerator; +use Sedona\SBOGeneratorBundle\Generator\DoctrineTranslationGenerator; +use Sedona\SBOGeneratorBundle\Generator\DoctrineCrudGenerator; +use Sedona\SBOGeneratorBundle\Generator\DoctrineFormGenerator; +use Sensio\Bundle\GeneratorBundle\Command\AutoComplete\EntitiesAutoCompleter; +use Sensio\Bundle\GeneratorBundle\Command\GenerateDoctrineCommand; +use Sensio\Bundle\GeneratorBundle\Command\Validators; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Question\ConfirmationQuestion; +use Symfony\Component\Console\Question\Question; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; + + +/** + * Generates a CRUD for a Doctrine entity. + * + */ +class GenerateDoctrineCrudCommand extends GenerateDoctrineCommand +{ + private $formGenerator; + private $datatableGenerator; + private $translationsGenerator; + + /** + * @see Command + */ + protected function configure() + { + $this + ->setDefinition(array( + new InputOption('entity', '', InputOption::VALUE_REQUIRED, 'The entity class name to initialize (shortcut notation)'), + new InputOption('route-prefix', '', InputOption::VALUE_REQUIRED, 'The route prefix'), + new InputOption('with-write', '', InputOption::VALUE_NONE, 'Whether or not to generate create, new and delete actions'), + new InputOption('overwrite', '', InputOption::VALUE_NONE, 'Do not stop the generation if crud controller already exist, thus overwriting all generated files'), + )) + ->setDescription('Generates a CRUD based on a Doctrine entity') + ->setHelp(<<doctrine:generate:crud command generates a CRUD based on a Doctrine entity. + +The default command only generates the list and show actions. + +php app/console doctrine:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin + +Using the --with-write option allows to generate the new, edit and delete actions. + +php app/console doctrine:generate:crud --entity=AcmeBlogBundle:Post --route-prefix=post_admin --with-write + +Every generated file is based on a template. There are default templates but they can be overriden by placing custom templates in one of the following locations, by order of priority: + +BUNDLE_PATH/Resources/SensioGeneratorBundle/skeleton/crud +APP_PATH/Resources/SensioGeneratorBundle/skeleton/crud + +And + +__bundle_path__/Resources/SensioGeneratorBundle/skeleton/form +__project_root__/app/Resources/SensioGeneratorBundle/skeleton/form + +You can check https://github.com/sensio/SensioGeneratorBundle/tree/master/Resources/skeleton +in order to know the file structure of the skeleton +EOT + ) + ->setName('sbo:generate:crud') + ; + } + + /** + * @see Command + */ + protected function execute(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + + if ($input->isInteractive()) { + $question = new ConfirmationQuestion($questionHelper->getQuestion('Do you confirm generation', 'yes', '?'), true); + if (!$questionHelper->ask($input, $output, $question)) { + $output->writeln('Command aborted'); + + return 1; + } + } + + $entity = Validators::validateEntityName($input->getOption('entity')); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + $format = 'annotation'; //Validators::validateFormat($input->getOption('format')); + $prefix = $this->getRoutePrefix($input, $entity); + $withWrite = $input->getOption('with-write'); + $forceOverwrite = $input->getOption('overwrite'); + + $questionHelper->writeSection($output, 'CRUD generation'); + + try { + $entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace($bundle).'\\'.$entity; + $metadata = $this->getEntityMetadata($entityClass); + } catch (\Exception $e) { + throw new \RuntimeException(sprintf('Entity "%s" does not exist in the "%s" bundle. Create it with the "doctrine:generate:entity" command and then execute this command again.', $entity, $bundle)); + } + + $bundle = $this->getContainer()->get('kernel')->getBundle($bundle); + + $generator = $this->getGenerator($bundle); + $generator->generate($bundle, $entity, $metadata[0], $format, $prefix, $withWrite, $forceOverwrite); + + $output->writeln('Generating the CRUD code: OK'); + + $errors = array(); + $runner = $questionHelper->getRunner($output, $errors); + + // form + if ($withWrite) { + $this->generateForm($bundle, $entity, $metadata, $forceOverwrite); + $output->writeln('Generating the Form code: OK'); + } + + // datatable + $this->generateDatatable($bundle, $entity, $metadata, $forceOverwrite); + $output->writeln('Generating the Datatable code: OK'); + + // translations + $this->generateTranslations($bundle, $entity, $metadata, $forceOverwrite); + $output->writeln('Generating the translations json: OK'); + + $questionHelper->writeGeneratorSummary($output, $errors); + } + + protected function interact(InputInterface $input, OutputInterface $output) + { + $questionHelper = $this->getQuestionHelper(); + $questionHelper->writeSection($output, 'Welcome to the Sedona SBO CRUD generator'); + + // namespace + $output->writeln(array( + '', + 'This command helps you generate CRUD controllers and templates.', + '', + 'First, you need to give the entity for which you want to generate a CRUD.', + 'You can give an entity that does not exist yet and the wizard will help', + 'you defining it.', + '', + 'You must use the shortcut notation like AcmeBlogBundle:Post.', + '', + )); + + if ($input->hasArgument('entity') && $input->getArgument('entity') != '') { + $input->setOption('entity', $input->getArgument('entity')); + } + + $question = new Question($questionHelper->getQuestion('The Entity shortcut name', $input->getOption('entity')), $input->getOption('entity')); + $question->setValidator(array('Sensio\Bundle\GeneratorBundle\Command\Validators', 'validateEntityName')); + + $autocompleter = new EntitiesAutoCompleter($this->getContainer()->get('doctrine')->getManager()); + $autocompleteEntities = $autocompleter->getSuggestions(); + $question->setAutocompleterValues($autocompleteEntities); + $entity = $questionHelper->ask($input, $output, $question); + + $input->setOption('entity', $entity); + list($bundle, $entity) = $this->parseShortcutNotation($entity); + + try { + $entityClass = $this->getContainer()->get('doctrine')->getAliasNamespace($bundle).'\\'.$entity; + $metadata = $this->getEntityMetadata($entityClass); + } catch (\Exception $e) { + throw new \RuntimeException(sprintf('Entity "%s" does not exist in the "%s" bundle. You may have mistyped the bundle name or maybe the entity doesn\'t exist yet (create it first with the "doctrine:generate:entity" command).', $entity, $bundle)); + } + + // write? + $withWrite = $input->getOption('with-write') ?: false; + $output->writeln(array( + '', + 'By default, the generator creates two actions: list and show.', + 'You can also ask it to generate "write" actions: new, update, and delete.', + '', + )); + $question = new ConfirmationQuestion($questionHelper->getQuestion('Do you want to generate the "write" actions', $withWrite ? 'yes' : 'no', '?', $withWrite), $withWrite); + + $withWrite = $questionHelper->ask($input, $output, $question); + $input->setOption('with-write', $withWrite); + + // route prefix + $prefix = $this->getRoutePrefix($input, $entity); + $output->writeln(array( + '', + 'Determine the routes prefix (all the routes will be "mounted" under this', + 'prefix: /prefix/, /prefix/new, ...).', + '', + )); + $prefix = $questionHelper->ask($input, $output, new Question($questionHelper->getQuestion('Routes prefix', '/'.$prefix), '/'.$prefix)); + $input->setOption('route-prefix', $prefix); + + // summary + $output->writeln(array( + '', + $this->getHelper('formatter')->formatBlock('Summary before generation', 'bg=blue;fg=white', true), + '', + sprintf('You are going to generate a CRUD controller for "%s:%s"', $bundle, $entity), + //sprintf('using the "%s" format.', $format), + '', + )); + } + + /** + * Tries to generate forms if they don't exist yet and if we need write operations on entities. + */ + protected function generateForm($bundle, $entity, $metadata, $forceOverwrite) + { + try { + $this->getFormGenerator($bundle)->generate($bundle, $entity, $metadata[0], $forceOverwrite); + } catch (\RuntimeException $e ) { + // form already exists + } + } + + /** + * Tries to generate forms if they don't exist yet and if we need write operations on entities. + */ + protected function generateDatatable($bundle, $entity, $metadata, $forceOverwrite) + { + try { + $this->getDatatableGenerator($bundle)->generate($bundle, $entity, $metadata[0], $forceOverwrite); + } catch (\RuntimeException $e ) { + // form already exists + } + } + + /** + * Tries to generate translations if they don't exist yet and if we need write operations on entities. + */ + protected function generateTranslations($bundle, $entity, $metadata, $forceOverwrite) + { + try { + $this->getTranslationsGenerator($bundle)->generate($bundle, $entity, $metadata[0], $forceOverwrite); + } catch (\RuntimeException $e ) { + // form already exists + } + } + + protected function getRoutePrefix(InputInterface $input, $entity) + { + $prefix = $input->getOption('route-prefix') ?: strtolower(str_replace(array('\\', '/'), '_', $entity)); + + if ($prefix && '/' === $prefix[0]) { + $prefix = substr($prefix, 1); + } + + return $prefix; + } + + protected function createGenerator($bundle = null) + { + return new DoctrineCrudGenerator( + $this->getContainer()->get('filesystem'), + $this->getContainer()->getParameter('kernel.root_dir') + ); + } + + protected function getFormGenerator($bundle = null) + { + if (null === $this->formGenerator) { + $this->formGenerator = new DoctrineFormGenerator($this->getContainer()->get('filesystem')); + $this->formGenerator->setSkeletonDirs($this->getSkeletonDirs($bundle)); + } + + return $this->formGenerator; + } + + public function setFormGenerator(DoctrineFormGenerator $formGenerator) + { + $this->formGenerator = $formGenerator; + } + + protected function getDatatableGenerator($bundle = null) + { + if (null === $this->datatableGenerator) { + $this->datatableGenerator = new DoctrineDatatableGenerator($this->getContainer()->get('filesystem')); + $this->datatableGenerator->setSkeletonDirs($this->getSkeletonDirs($bundle)); + } + + return $this->datatableGenerator; + } + + public function setDatatableGenerator(DoctrineDatatableGenerator $datatableGenerator) + { + $this->translationsGenerator = $datatableGenerator; + } + + protected function getTranslationsGenerator($bundle = null) + { + if (null === $this->translationsGenerator) { + $this->translationsGenerator = new DoctrineTranslationGenerator($this->getContainer()->get('filesystem')); + $this->translationsGenerator->setSkeletonDirs($this->getSkeletonDirs($bundle)); + } + + return $this->translationsGenerator; + } + + public function setTranslationsGenerator(DoctrineTranslationGenerator $datatableGenerator) + { + $this->translationsGenerator = $datatableGenerator; + } + + protected function getEntityMetadata($entity) + { + $factory = new ClassMetadataFactory(); + $factory->setEntityManager($this->getContainer()->get("doctrine.orm.entity_manager")); + $metadata = $factory->getMetadataFor($entity); + + return array($metadata); + } + + protected function getSkeletonDirs(BundleInterface $bundle = null) + { + $skeletonDirs = array(); + + if (isset($bundle) && is_dir($dir = $bundle->getPath().'/Resources/SedonaSBOGenerator/skeleton')) { + $skeletonDirs[] = $dir; + } + + if (is_dir($dir = $this->getContainer()->get('kernel')->getRootdir().'/Resources/SedonaSBOGenerator/skeleton')) { + $skeletonDirs[] = $dir; + } + + $skeletonDirs[] = __DIR__.'/../Resources/skeleton'; + $skeletonDirs[] = __DIR__.'/../Resources'; + + return $skeletonDirs; + } + +} diff --git a/src/Sedona/SBOGeneratorBundle/Datatable/Data/DatatableData.php b/src/Sedona/SBOGeneratorBundle/Datatable/Data/DatatableData.php new file mode 100644 index 0000000..ddda8f6 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Datatable/Data/DatatableData.php @@ -0,0 +1,98 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBOGeneratorBundle\Datatable\Data; + +use Doctrine\ORM\Tools\Pagination\Paginator; +use Symfony\Component\HttpFoundation\Response; + +/** + * Class DatatableData + * @package Sedona\SBOGeneratorBundle\Datatable\Data + */ +class DatatableData extends \Sg\DatatablesBundle\Datatable\Data\DatatableData +{ + + /** + * Set columns. + * + * @return $this + */ + private function setColumns() + { + $this->datatableQuery->setSelectColumns($this->selectColumns); + $this->datatableQuery->setAllColumns($this->allColumns); + $this->datatableQuery->setJoins($this->joins); + $this->datatableQuery->setSearchColumns($this->searchColumns); + $this->datatableQuery->setOrderColumns($this->orderColumns); + + return $this; + } + + /** + * Build query. + * + * @return $this + */ + private function buildQuery() + { + $this->datatableQuery->setSelectFrom(); + $this->datatableQuery->setLeftJoins(); + $this->datatableQuery->setWhere(); + $this->datatableQuery->setWhereCallbacks(); + $this->datatableQuery->setOrderBy(); + $this->datatableQuery->setLimit(); + + return $this; + } + + + + //------------------------------------------------- + // DatatableDataInterface + //------------------------------------------------- + + /** + * {@inheritdoc} + */ + public function getResponse() + { + $this->setColumns(); + $this->buildQuery(); + + $fresults = new Paginator($this->datatableQuery->execute(), true); + $output = array("data" => array()); + + $outputHeader = array( + "draw" => (integer) $this->requestParams["draw"], + "recordsTotal" => (integer) $this->datatableQuery->getCountAllResults($this->rootEntityIdentifier), + "recordsFiltered" => (integer) $this->datatableQuery->getCountFilteredResults($this->rootEntityIdentifier) + ); + + foreach ($fresults as $item) { + if (is_callable($this->lineFormatter)) { + $callable = $this->lineFormatter; + $item = call_user_func($callable, $item, $outputHeader); + } + + $output["data"][] = $item; + } + + $this->response = array_merge($outputHeader, $output); + + $json = $this->serializer->serialize($this->response, "json"); + $response = new Response($json); + $response->headers->set("Content-Type", "application/json"); + + return $response; + } + +} diff --git a/src/Sedona/SBOGeneratorBundle/Datatable/Data/DatatableDataManager.php b/src/Sedona/SBOGeneratorBundle/Datatable/Data/DatatableDataManager.php new file mode 100644 index 0000000..dc250be --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Datatable/Data/DatatableDataManager.php @@ -0,0 +1,114 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBOGeneratorBundle\Datatable\Data; + +use Sg\DatatablesBundle\Datatable\Data\DatatableQuery; +use Sg\DatatablesBundle\Datatable\View\DatatableViewInterface; +use Symfony\Bridge\Doctrine\RegistryInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Serializer\Serializer; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * Class DatatableDataManager + * @package Sedona\SBOGeneratorBundle\Datatable\Data + */ +class DatatableDataManager extends \Sg\DatatablesBundle\Datatable\Data\DatatableDataManager +{ + /** + * The doctrine service. + * + * @var RegistryInterface + */ + private $doctrine; + + /** + * The request service. + * + * @var Request + */ + private $request; + + /** + * The serializer service. + * + * @var Serializer + */ + private $serializer; + + /** + * Holds request parameters. + * + * @var ParameterBag + */ + private $parameterBag; + + + //------------------------------------------------- + // Ctor. + //------------------------------------------------- + + /** + * Ctor. + * + * @param RegistryInterface $doctrine The doctrine service + * @param Request $request The request service + * @param Serializer $serializer The serializer service + */ + public function __construct(RegistryInterface $doctrine, Request $request, Serializer $serializer) + { + $this->doctrine = $doctrine; + $this->request = $request; + $this->serializer = $serializer; + $this->parameterBag = null; + } + + /** + * Get Datatable. + * + * @param DatatableViewInterface $datatableView + * + * @return DatatableData + */ + public function getDatatable(DatatableViewInterface $datatableView) + { + $type = $datatableView->getAjax()->getType(); + $entity = $datatableView->getEntity(); + + if ("GET" === strtoupper($type)) { + $this->parameterBag = $this->request->query; + } + + if ("POST" === strtoupper($type)) { + $this->parameterBag = $this->request->request; + } + + $params = $this->parameterBag->all(); + + /** + * @var \Doctrine\ORM\Mapping\ClassMetadata $metadata + */ + $metadata = $this->doctrine->getManager()->getClassMetadata($entity); + + /** + * @var \Doctrine\ORM\EntityManager $em + */ + $em = $this->doctrine->getManager(); + + $datatableQuery = new DatatableQuery($params, $metadata, $em); + $virtualColumns = $datatableView->getColumnBuilder()->getVirtualColumnNames(); + $datatableData = new DatatableData($params, $metadata, $em, $this->serializer, $datatableQuery, $virtualColumns); + $datatableData->setLineFormatter($datatableView->getLineFormatter()); + + return $datatableData; + } +} diff --git a/src/Sedona/SBOGeneratorBundle/Generator/DoctrineCrudGenerator.php b/src/Sedona/SBOGeneratorBundle/Generator/DoctrineCrudGenerator.php new file mode 100644 index 0000000..a75b333 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Generator/DoctrineCrudGenerator.php @@ -0,0 +1,357 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBOGeneratorBundle\Generator; + +use Doctrine\Common\Inflector\Inflector; +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; + +/** + * Generates a CRUD controller. + * + */ +class DoctrineCrudGenerator extends Generator +{ + protected $filesystem; + protected $routePrefix; + protected $routeNamePrefix; + protected $bundle; + protected $entity; + protected $metadata; + protected $format; + protected $actions; + + /** + * Constructor. + * + * @param Filesystem $filesystem A Filesystem instance + */ + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + /** + * Generate the CRUD controller. + * + * @param BundleInterface $bundle A bundle object + * @param string $entity The entity relative class name + * @param ClassMetadataInfo $metadata The entity class metadata + * @param string $format The configuration format (xml, yaml, annotation) + * @param string $routePrefix The route name prefix + * @param array $needWriteActions Wether or not to generate write actions + * + * @throws \RuntimeException + */ + public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $format, $routePrefix, $needWriteActions, $forceOverwrite) + { + $this->routePrefix = $routePrefix; + $this->routeNamePrefix = str_replace('/', '_', $routePrefix); + $this->actions = $needWriteActions ? array('index', 'datatable', 'show', 'new', 'edit', 'delete', 'search') : array('index', 'datatable', 'show'); + + if (count($metadata->identifier) > 1) { + throw new \RuntimeException('The CRUD generator does not support entity classes with multiple primary keys.'); + } + + if (!in_array('id', $metadata->identifier)) { + throw new \RuntimeException('The CRUD generator expects the entity object has a primary key field named "id" with a getId() method.'); + } + + $this->entity = $entity; + $this->bundle = $bundle; + $this->metadata = $metadata; + $this->setFormat($format); + + $this->generateControllerClass($forceOverwrite); + + $dir = sprintf('%s/Resources/views/Admin/%s', $this->bundle->getPath(), str_replace('\\', '/', $this->entity)); + + if (!file_exists($dir)) { + $this->filesystem->mkdir($dir, 0777); + } + + // Default view (home) if not exists + $this->generateDefaultView(sprintf('%s/Resources/views/Admin/Default', $this->bundle->getPath())); + + $this->generateIndexView($dir); + + if (in_array('new', $this->actions) || in_array('new', $this->actions)) { + $this->generateFormView($dir); + } + + if (in_array('show', $this->actions)) { + $this->generateShowView($dir); + } + + if (in_array('new', $this->actions)) { + $this->generateNewView($dir); + } + + if (in_array('edit', $this->actions)) { + $this->generateEditView($dir); + } + + //$this->generateTestClass(); + $this->generateConfiguration(); + } + + /** + * Sets the configuration format. + * + * @param string $format The configuration format + */ + private function setFormat($format) + { + switch ($format) { + case 'yml': + case 'xml': + case 'php': + case 'annotation': + $this->format = $format; + break; + default: + $this->format = 'yml'; + break; + } + } + + /** + * Generates the routing configuration. + * + */ + protected function generateConfiguration() + { + if (!in_array($this->format, array('yml', 'xml', 'php'))) { + return; + } + + $target = sprintf( + '%s/Resources/config/routing/%s.%s', + $this->bundle->getPath(), + strtolower(str_replace('\\', '_', $this->entity)), + $this->format + ); + + $this->renderFile('crud/config/routing.'.$this->format.'.twig', $target, array( + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + )); + } + + /** + * Generates the controller class only. + * + */ + protected function generateControllerClass($forceOverwrite) + { + $dir = $this->bundle->getPath(); + + $parts = explode('\\', $this->entity); + $entityClass = array_pop($parts); + $entityNamespace = implode('\\', $parts); + + $target = sprintf( + '%s/Controller/Admin/%s/%sController.php', + $dir, + str_replace('\\', '/', $entityNamespace), + $entityClass + ); + + if (!$forceOverwrite && file_exists($target)) { + throw new \RuntimeException('Unable to generate the controller as it already exists.'); + } + + $params = array( + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'fields' => $this->getFieldsFromMetadata($this->metadata), + 'entity_class' => $entityClass, + 'namespace' => $this->bundle->getNamespace(), + 'entity_namespace' => $entityNamespace, + 'format' => $this->format, + 'dir' => $dir + ); + + $this->renderFile('crud/controller.php.twig', $target, $params); + + // Generate base generator if not exists + $baseControllerFile = $dir.'/Controller/Admin/BaseCrudController.php'; + if (!file_exists($baseControllerFile)) { + $this->renderFile('crud/BaseCrudController.php.twig', $baseControllerFile, $params); + } + + // Generate home controller if not exists + $baseControllerFile = $dir.'/Controller/Admin/DefaultController.php'; + if (!file_exists($baseControllerFile)) { + $this->renderFile('crud/DefaultController.php.twig', $baseControllerFile, $params); + } + } + + /** + * Generates the functional test class only. + * + */ + protected function generateTestClass() + { + $parts = explode('\\', $this->entity); + $entityClass = array_pop($parts); + $entityNamespace = implode('\\', $parts); + + $dir = $this->bundle->getPath() .'/Tests/Controller'; + $target = $dir .'/'. str_replace('\\', '/', $entityNamespace).'/'. $entityClass .'ControllerTest.php'; + + $this->renderFile('crud/tests/test.php.twig', $target, array( + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'entity' => $this->entity, + 'bundle' => $this->bundle->getName(), + 'entity_class' => $entityClass, + 'namespace' => $this->bundle->getNamespace(), + 'entity_namespace' => $entityNamespace, + 'actions' => $this->actions, + 'form_type_name' => strtolower(str_replace('\\', '_', $this->bundle->getNamespace()).($parts ? '_' : '').implode('_', $parts).'_'.$entityClass), + )); + } + + /** + * Generates the index.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + protected function generateDefaultView($dir) + { + if(!file_exists($dir.'/index.html.twig')) { // only if not exists + $this->renderFile('crud/views/default.html.twig.twig', $dir.'/index.html.twig', array( + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'entities_name' => strtolower(Inflector::pluralize($this->entity)), + 'fields' => $this->metadata->fieldMappings, + 'actions' => $this->actions, + 'record_actions' => $this->getRecordActions(), + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'dir' => $dir + )); + } + } + + /** + * Generates the index.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + protected function generateIndexView($dir) + { + $this->renderFile('crud/views/index.html.twig.twig', $dir.'/index.html.twig', array( + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'entities_name' => strtolower(Inflector::pluralize($this->entity)), + 'fields' => $this->metadata->fieldMappings, + 'actions' => $this->actions, + 'record_actions' => $this->getRecordActions(), + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'dir' => $dir + )); + } + + /** + * Generates the show.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + protected function generateShowView($dir) + { + $this->renderFile('crud/views/show.html.twig.twig', $dir.'/show.html.twig', array( + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'fields' => $this->getFieldsFromMetadata($this->metadata), + 'actions' => $this->actions, + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'dir' => $dir + )); + } + + /** + * Generates the new.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + protected function generateNewView($dir) + { + $this->renderFile('crud/views/new.html.twig.twig', $dir.'/new.html.twig', array( + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'fields' => $this->getFieldsFromMetadata($this->metadata), + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'actions' => $this->actions, + 'dir' => $dir + )); + } + + /** + * Generates the edit.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + protected function generateFormView($dir) + { + $this->renderFile('crud/views/form.html.twig.twig', $dir.'/form.html.twig', array( + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'entity' => $this->entity, + 'fields' => $this->getFieldsFromMetadata($this->metadata), + 'bundle' => $this->bundle->getName(), + 'actions' => $this->actions, + 'dir' => $dir + )); + } + + /** + * Generates the new.html.twig template in the final bundle. + * + * @param string $dir The path to the folder that hosts templates in the bundle + */ + protected function generateEditView($dir) + { + $this->renderFile('crud/views/edit.html.twig.twig', $dir.'/edit.html.twig', array( + 'bundle' => $this->bundle->getName(), + 'entity' => $this->entity, + 'fields' => $this->getFieldsFromMetadata($this->metadata), + 'route_prefix' => $this->routePrefix, + 'route_name_prefix' => $this->routeNamePrefix, + 'actions' => $this->actions, + 'dir' => $dir + )); + } + + /** + * Returns an array of record actions to generate (edit, show). + * + * @return array + */ + protected function getRecordActions() + { + return array_filter($this->actions, function ($item) { + return in_array($item, array('show', 'update')); + }); + } +} diff --git a/src/Sedona/SBOGeneratorBundle/Generator/DoctrineDatatableGenerator.php b/src/Sedona/SBOGeneratorBundle/Generator/DoctrineDatatableGenerator.php new file mode 100644 index 0000000..5ec661b --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Generator/DoctrineDatatableGenerator.php @@ -0,0 +1,95 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBOGeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Generates a datatable class based on a Doctrine entity. + * + */ +class DoctrineDatatableGenerator extends Generator +{ + private $filesystem; + private $className; + private $classPath; + + /** + * Constructor. + * + * @param Filesystem $filesystem A Filesystem instance + */ + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + public function getClassName() + { + return $this->className; + } + + public function getClassPath() + { + return $this->classPath; + } + + /** + * Generates the entity form class if it does not exist. + * + * @param BundleInterface $bundle The bundle in which to create the class + * @param string $entity The entity relative class name + * @param ClassMetadataInfo $metadata The entity metadata class + */ + public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $forceOverwrite) + { + $parts = explode('\\', $entity); + $entityClass = array_pop($parts); + + $this->className = $entityClass.'Datatable'; + $dirPath = $bundle->getPath().'/Datatables'; + $this->classPath = $dirPath.'/'.str_replace('\\', '/', $entity).'Datatable.php'; + + if (file_exists($this->classPath) && !$forceOverwrite) { + throw new \RuntimeException(sprintf('Unable to generate the %s datatable class as it already exists under the %s file', $this->className, $this->classPath)); + } + + if (count($metadata->identifier) > 1) { + throw new \RuntimeException('The datatable generator does not support entity classes with multiple primary keys.'); + } + + $parts = explode('\\', $entity); + array_pop($parts); + + $params = array( + 'fields' => $this->getFieldsFromMetadata($metadata), + 'namespace' => $bundle->getNamespace(), + 'entity_namespace' => implode('\\', $parts), + 'entity_class' => $entityClass, + 'entity' => $entityClass, + 'bundle' => $bundle->getName(), + 'form_class' => $this->className, + 'form_type_name' => strtolower('admin_'.substr($this->className, 0, -4)) //str_replace('\\', '_', $bundle->getNamespace()).($parts ? '_' : '').implode('_', $parts).'_'.substr($this->className, 0, -4)), + ); + + // Generate base generator if not exists + $baseControllerFile = $dirPath.'/AbstractCrudDatatableView.php'; + if (!file_exists($baseControllerFile)) { + $this->renderFile('datatable/AbstractCrudDatatableView.php.twig', $baseControllerFile, $params); + } + + $this->renderFile('datatable/Datatable.php.twig', $this->classPath, $params); + } + +} diff --git a/src/Sedona/SBOGeneratorBundle/Generator/DoctrineFormGenerator.php b/src/Sedona/SBOGeneratorBundle/Generator/DoctrineFormGenerator.php new file mode 100644 index 0000000..6213c03 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Generator/DoctrineFormGenerator.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBOGeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Generates a form class based on a Doctrine entity. + */ +class DoctrineFormGenerator extends Generator +{ + private $filesystem; + private $className; + private $classPath; + + /** + * Constructor. + * + * @param Filesystem $filesystem A Filesystem instance + */ + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + public function getClassName() + { + return $this->className; + } + + public function getClassPath() + { + return $this->classPath; + } + + /** + * Generates the entity form class if it does not exist. + * + * @param BundleInterface $bundle The bundle in which to create the class + * @param string $entity The entity relative class name + * @param ClassMetadataInfo $metadata The entity metadata class + */ + public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $forceOverwrite) + { + $parts = explode('\\', $entity); + $entityClass = array_pop($parts); + + $this->className = $entityClass.'Type'; + $dirPath = $bundle->getPath().'/Form'; + $this->classPath = $dirPath.'/Admin/'.str_replace('\\', '/', $entity).'Type.php'; + + if (file_exists($this->classPath) && !$forceOverwrite) { + throw new \RuntimeException(sprintf('Unable to generate the %s form class as it already exists under the %s file', $this->className, $this->classPath)); + } + + if (count($metadata->identifier) > 1) { + throw new \RuntimeException('The form generator does not support entity classes with multiple primary keys.'); + } + + $parts = explode('\\', $entity); + array_pop($parts); + + $this->renderFile('form/FormType.php.twig', $this->classPath, array( + 'fields' => $this->getFieldsFromMetadata($metadata), + 'namespace' => $bundle->getNamespace(), + 'entity_namespace' => implode('\\', $parts), + 'entity_class' => $entityClass, + 'bundle' => $bundle->getName(), + 'form_class' => $this->className, + 'form_type_name' => strtolower('admin_'.substr($this->className, 0, -4)) //str_replace('\\', '_', $bundle->getNamespace()).($parts ? '_' : '').implode('_', $parts).'_'.substr($this->className, 0, -4)), + )); + } + +} diff --git a/src/Sedona/SBOGeneratorBundle/Generator/DoctrineTranslationGenerator.php b/src/Sedona/SBOGeneratorBundle/Generator/DoctrineTranslationGenerator.php new file mode 100644 index 0000000..93387c2 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Generator/DoctrineTranslationGenerator.php @@ -0,0 +1,105 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBOGeneratorBundle\Generator; + +use Symfony\Component\Filesystem\Filesystem; +use Symfony\Component\HttpKernel\Bundle\BundleInterface; +use Doctrine\ORM\Mapping\ClassMetadataInfo; + +/** + * Generates a YML translation based on a Doctrine entity. + */ +class DoctrineTranslationGenerator extends Generator +{ + private $filesystem; + private $className; + private $classPath; + + /** + * Constructor. + * + * @param Filesystem $filesystem A Filesystem instance + */ + public function __construct(Filesystem $filesystem) + { + $this->filesystem = $filesystem; + } + + public function getClassName() + { + return $this->className; + } + + public function getClassPath() + { + return $this->classPath; + } + + /** + * Generates the entity form class if it does not exist. + * + * @param BundleInterface $bundle The bundle in which to create the class + * @param string $entity The entity relative class name + * @param ClassMetadataInfo $metadata The entity metadata class + */ + public function generate(BundleInterface $bundle, $entity, ClassMetadataInfo $metadata, $forceOverwrite) + { + $parts = explode('\\', $entity); + $entityClass = array_pop($parts); + + $langs = ['fr','en']; + + $this->className = $entityClass.'Type'; + $dirPath = $bundle->getPath().'/Resources/translations'; + + foreach($langs as $lang) { + $this->classPath = $dirPath.'/'.'admin.'.$lang.'.yml'; + $translations = ""; + $entityHeader = str_repeat(" ",4).strtolower($entityClass).":\n"; + + if (file_exists($this->classPath)) { + $translations = file_get_contents($this->classPath); + + if(strpos($translations, $entityHeader) !== false) { + throw new \RuntimeException(sprintf('Unable to generate the %s entity translation as it already exists under the %s file', $this->className, $this->classPath)); + } + + } + else { + $translations = $this->render('translations/admin.'.$lang.'.yml.twig',[]); + } + + if (count($metadata->identifier) > 1) { + throw new \RuntimeException('The form generator does not support entity classes with multiple primary keys.'); + } + + // add a trailing \n if not exists + if(substr($translations,-1,1) != "\n") { + $translations .= "\n"; + } + + $parts = explode('\\', $entity); + array_pop($parts); + + $translations .= $entityHeader; + $translations .= str_repeat(" ",8)."entity_name: ".str_replace('_',' ',ucfirst(strtolower($entityClass)))."\n"; + + foreach($this->getFieldsFromMetadata($metadata) as $field => $meta ) { + $translations .= str_repeat(" ",8).$field.": ".str_replace('_',' ',ucfirst(strtolower($field)))."\n"; + } + + file_put_contents($this->classPath, $translations); + } + + } + +} diff --git a/src/Sedona/SBOGeneratorBundle/Generator/Generator.php b/src/Sedona/SBOGeneratorBundle/Generator/Generator.php new file mode 100644 index 0000000..3074b96 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Generator/Generator.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBOGeneratorBundle\Generator; + +use Doctrine\ORM\Mapping\ClassMetadataInfo; +use Sensio\Bundle\GeneratorBundle\Generator\Generator as BaseGenerator; + +/** + * Class Generator + * @package Sedona\SBOGeneratorBundle\Generator + */ +class Generator extends BaseGenerator +{ + private $SBOskeletonDirs; + + /** + * Sets an array of directories to look for templates. + * + * The directories must be sorted from the most specific to the most + * directory. + * + * @param array $skeletonDirs An array of skeleton dirs + */ + public function setSkeletonDirs($skeletonDirs) + { + $this->SBOskeletonDirs = is_array($skeletonDirs) ? $skeletonDirs : array($skeletonDirs); + } + + /** + * Returns an array of fields. Fields can be both column fields and + * association fields. + * + * @return array $fields + */ + protected function getFieldsFromMetadata($metadata) + { + $fields = $metadata->fieldMappings; + + // Remove the primary key field if it's not managed manually + if (!$metadata->isIdentifierNatural()) { + foreach($metadata->identifier as $oneId) { + unset($fields[$oneId]); + } + } + + foreach ($metadata->associationMappings as $fieldName => $relation) { + if (in_array($relation['type'], [ClassMetadataInfo::MANY_TO_ONE, ClassMetadataInfo::ONE_TO_MANY ])) { + $fields[$fieldName] = $relation; + } elseif ($relation['type'] == ClassMetadataInfo::MANY_TO_MANY && (empty($relation['mappedBy']) == false || empty($relation['inversedBy']) == false)) { + /*|| $relation['targetEntity'] == $relation['sourceEntity']*/ + $fields[$fieldName] = $relation; + } + } + + return $fields; + } + + /** + * @param $template + * @param $parameters + * @return string + */ + protected function render($template, $parameters) + { + $option = array( + 'debug' => true, + 'cache' => false, + 'strict_variables' => true, + 'autoescape' => false, + ); + + $self = $this; + $twig = new \Twig_Environment(new \Twig_Loader_Filesystem($this->SBOskeletonDirs), $option); + + $twig->addFunction(new \Twig_SimpleFunction( + "renderFile", + function($template, $target, $subParameters = []) use($self, $parameters) { + $self->renderFile($template, $target, array_merge($parameters,$subParameters) ); + return ""; + }, + $option + )); + + $twig->addFunction(new \Twig_SimpleFunction( + "method_exists", + function($object, $methodeName) { + return method_exists($object,$methodeName); + }, + $option + )); + + return $twig->render($template, $parameters); + } +} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/BaseCrudController.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/BaseCrudController.php.twig new file mode 100644 index 0000000..45e1f43 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/BaseCrudController.php.twig @@ -0,0 +1,14 @@ +render("{{ bundle }}:Admin/Default:index.html.twig", array()); + } + +} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_add.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_add.php.twig new file mode 100644 index 0000000..b943892 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_add.php.twig @@ -0,0 +1,25 @@ +{%- set targetClassName = metadata['targetEntity']|split('\\')|last %} +{%- set objectname = '$'~(targetClassName|lower != entity|lower ? targetClassName : field)|lower %} + + /** +{% block phpdoc_method_header %} + * Add relation {{ entity }} to {{ field }}. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{%- if 'annotation' == format %} + * @Route("/{id}/add{{ field|capitalize ~ '/{' ~ targetClassName|lower }}_id}", name="admin_{{ entity|lower }}_{{ field|lower }}_add", options={"expose"=true}) + * @ParamConverter("{{ targetClassName|lower }}", class="{{ metadata['targetEntity'] }}", options={"id" = "{{ targetClassName|lower }}_id"}) +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function add{{ field|capitalize }}Action({{ entity }} ${{ entity|lower }}, {{ targetClassName }} {{ objectname }}) +{% endblock method_definition %} + { +{% block method_body %} +{% endblock method_body %} +{% block method_return %} + return $this->manageJsonAction(${{ entity|lower }}, {{ objectname }}, '{{ field }}', '{{ 'add'~field|capitalize }}', false); +{% endblock method_return %} + } \ No newline at end of file diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_datatable.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_datatable.php.twig new file mode 100644 index 0000000..f0ad297 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_datatable.php.twig @@ -0,0 +1,28 @@ +{%- set targetClassName = metadata['targetEntity']|split('\\')|last %} + + /** +{% block phpdoc_method_header %} + * JSON call for datatable to list all {{ targetClassName }} entities for property {{ field }} of entity {{ entity }}. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/{id}/datatable{{ field|capitalize }}", name="admin_{{ entity|lower }}_{{ field|lower }}_datatable", options={"expose"=true}) + * @Method("GET") +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function datatable{{ field|capitalize }}Action({{ entity }} ${{ entity|lower }}) +{% endblock method_definition %} + { +{% block method_body %} +{% endblock method_body %} +{% block method_return %} +{% if metadata['type'] == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_MANY') %} + return $this->manageFieldDatatableJson(${{ entity|lower }}, '{{ field }}', '{{ metadata['inversedBy']|default(metadata['mappedBy']) }}', 'many'); +{% else %} + return $this->manageFieldDatatableJson(${{ entity|lower }}, '{{ field }}', '{{ metadata['inversedBy']|default(metadata['mappedBy']) }}', 'one'); +{% endif %} +{% endblock method_return %} + } diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_index.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_index.php.twig new file mode 100644 index 0000000..210ca90 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_index.php.twig @@ -0,0 +1,24 @@ +{%- set targetClassName = metadata['targetEntity']|split('\\')|last %} + + /** +{% block phpdoc_method_header %} + * Lists all {{ targetClassName }} entities for property {{ field }} of entity {{ entity }}. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/{id}/list{{ field|capitalize }}", name="admin_{{ entity|lower }}_{{ field|lower }}_list", options={"expose"=true}) + * @Method("GET") +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function index{{ field|capitalize }}Action({{ entity }} ${{ entity|lower }}) +{% endblock method_definition %} + { +{% block method_body %} +{% endblock method_body %} +{% block method_return %} + return $this->manageFieldIndex(${{ entity|lower }}, '{{ field }}'); +{% endblock method_return %} + } diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_remove.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_remove.php.twig new file mode 100644 index 0000000..d9ca4da --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_remove.php.twig @@ -0,0 +1,25 @@ +{%- set targetClassName = metadata['targetEntity']|split('\\')|last %} +{%- set objectname = '$'~(targetClassName|lower != entity|lower ? targetClassName : field)|lower %} + + /** +{% block phpdoc_method_header %} + * Remove relation {{ entity }} to {{ field }}. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{%- if 'annotation' == format %} + * @Route("/{id}/remove{{ field|capitalize ~ '/{' ~ targetClassName|lower }}_id}", name="admin_{{ entity|lower }}_{{ field|lower }}_remove", options={"expose"=true}) + * @ParamConverter("{{ targetClassName|lower }}", class="{{ metadata['targetEntity'] }}", options={"id" = "{{ targetClassName|lower }}_id"}) +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function remove{{ field|capitalize }}Action({{ entity }} ${{ entity|lower }}, {{ targetClassName }} {{ objectname }}) +{% endblock method_definition %} + { +{% block method_body %} +{% endblock method_body %} +{% block method_return %} + return $this->manageJsonAction(${{ entity|lower }}, {{ objectname }}, '{{ field }}', '{{ 'remove'~field|capitalize }}', true); +{% endblock method_return %} + } \ No newline at end of file diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_search.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_search.php.twig new file mode 100644 index 0000000..d1e41ad --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/collection_search.php.twig @@ -0,0 +1,35 @@ +{%- set targetClassName = metadata['targetEntity']|split('\\')|last %} + + /** +{% block phpdoc_method_header %} + * Search {{ field }} for entity {{ entity }}. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{%- if 'annotation' == format %} + * @Route("/{id}/search{{ field|capitalize }}", name="admin_{{ entity|lower }}_{{ field|lower }}_search", options={"expose"=true}) +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function search{{ field|capitalize }}Action(Request $request, {{ entity }} ${{ entity|lower }}) +{% endblock method_definition %} + { +{% block method_body %} +{% endblock method_body %} +{% block method_return %} +{% set property = null %} +{% if method_exists(metadata['targetEntity'] ,'get'~'name'|capitalize ) %} + {%- set property = 'name' %} +{% elseif method_exists(metadata['targetEntity'] ,'get'~'title'|capitalize ) %} + {%- set property = 'title' %} +{% endif %} +{% set getter = null %} +{% if method_exists(metadata['sourceEntity'] ,'get'~field|capitalize~'s' ) %} + {%- set getter = 'get'~field|capitalize~'s' %} +{% elseif method_exists(metadata['sourceEntity'] ,'get'~field|capitalize ) %} + {%- set getter = 'get'~field|capitalize %} +{% endif %} + return $this->manageSearchFieldMany($request, ${{ entity|lower }}, '{{ metadata['targetEntity'] }}', '{{ field }}', '{{ property }}'); +{% endblock method_return %} + } \ No newline at end of file diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/datatable.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/datatable.php.twig new file mode 100644 index 0000000..31f2fd9 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/datatable.php.twig @@ -0,0 +1,23 @@ + + /** +{% block phpdoc_method_header %} + * JSON call for datatable to list all {{ entity }} entities. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/datatable", name="admin_{{ entity|lower }}_datatable") + * @Method("GET") +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function datatableAction() +{% endblock method_definition %} + { +{% block method_body %} +{% endblock method_body %} +{% block method_return %} + return $this->manageDatatableJson(); +{% endblock method_return %} + } diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/edit.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/edit.php.twig new file mode 100644 index 0000000..ef53b38 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/edit.php.twig @@ -0,0 +1,22 @@ + + /** +{% block phpdoc_method_header %} + * Edit a {{ entity }}. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/{id}/edit", name="admin_{{ entity|lower }}_edit", options={"expose"=true}) +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function editAction({{ entity }} $entity, Request $request) +{% endblock method_definition %} + { +{% block method_body %} +{% endblock method_body %} +{% block method_return %} + return $this->manageEdit($entity, $request, new {{ entity }}Type()); +{% endblock method_return %} + } diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/entity_search.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/entity_search.php.twig new file mode 100644 index 0000000..a0ff024 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/entity_search.php.twig @@ -0,0 +1,31 @@ +{%- set targetClassName = metadata['targetEntity']|split('\\')|last %} + + /** +{% block phpdoc_method_header %} + * search {{ entity }}. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/search{{ field|capitalize }}", name="admin_{{ entity|lower }}_{{ field|lower }}_search", options={"expose"=true}) +{% endif %} +{% endblock phpdoc_method_annotations %} + * + * @return JsonResponse + */ +{% block method_definition %} + public function search{{ field|capitalize }}Action(Request $request) +{% endblock method_definition %} + { +{% block method_body %} +{% set property = null %} +{% if method_exists(metadata['targetEntity'] ,'get'~'name'|capitalize ) %} + {%- set property = 'name' %} +{% elseif method_exists(metadata['targetEntity'] ,'get'~'title'|capitalize ) %} + {%- set property = 'title' %} +{% endif %} + return $this->searchSelect2($request, '{{ metadata['targetEntity'] }}', '{{ property }}'); +{% endblock method_body %} +{% block method_return %} +{% endblock method_return %} + } \ No newline at end of file diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/index.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/index.php.twig new file mode 100644 index 0000000..b82a429 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/index.php.twig @@ -0,0 +1,23 @@ + + /** +{% block phpdoc_method_header %} + * Lists all {{ entity }} entities. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/", name="admin_{{ entity|lower }}_list") + * @Method("GET") +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function indexAction() +{% endblock method_definition %} + { +{% block method_body %} +{% endblock method_body %} +{% block method_return %} + return $this->manageIndex(); +{% endblock method_return %} + } diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/new.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/new.php.twig new file mode 100644 index 0000000..5feae2d --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/new.php.twig @@ -0,0 +1,22 @@ + + /** +{% block phpdoc_method_header %} + * Create a new {{ entity }}. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/new", name="admin_{{ entity|lower }}_new", options={"expose"=true}) +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function newAction(Request $request) +{% endblock method_definition %} + { +{% block method_body %} +{% endblock method_body %} +{% block method_return %} + return $this->manageNew(new {{ entity }}(), $request, new {{ entity }}Type()); +{% endblock method_return %} + } diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/show.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/show.php.twig new file mode 100644 index 0000000..62b0a97 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/actions/show.php.twig @@ -0,0 +1,23 @@ + + /** +{% block phpdoc_method_header %} + * Show a {{ entity }}. +{% endblock phpdoc_method_header %} + * +{% block phpdoc_method_annotations %} +{% if 'annotation' == format %} + * @Route("/{id}", name="admin_{{ entity|lower }}_show", options={"expose"=true}) + * @Method("GET") +{% endif %} +{% endblock phpdoc_method_annotations %} + */ +{% block method_definition %} + public function showAction({{ entity }} $entity) +{% endblock method_definition %} + { +{% block method_body %} +{% endblock method_body %} +{% block method_return %} + return $this->manageShow($entity); +{% endblock method_return %} + } diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.php.twig new file mode 100644 index 0000000..a3db646 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.php.twig @@ -0,0 +1,60 @@ +add('{{ route_name_prefix }}', new Route('/', array( + '_controller' => '{{ bundle }}:{{ entity }}:index', +))); +{% endif %} + +{% if 'show' in actions %} +$collection->add('{{ route_name_prefix }}_show', new Route('/{id}/show', array( + '_controller' => '{{ bundle }}:{{ entity }}:show', +))); +{% endif %} + +{% if 'new' in actions %} +$collection->add('{{ route_name_prefix }}_new', new Route('/new', array( + '_controller' => '{{ bundle }}:{{ entity }}:new', +))); + +$collection->add('{{ route_name_prefix }}_create', new Route( + '/create', + array('_controller' => '{{ bundle }}:{{ entity }}:create'), + array('_method' => 'post') +)); +{% endif %} + +{% if 'edit' in actions %} +$collection->add('{{ route_name_prefix }}_edit', new Route('/{id}/edit', array( + '_controller' => '{{ bundle }}:{{ entity }}:edit', +))); + +$collection->add('{{ route_name_prefix }}_update', new Route( + '/{id}/update', + array('_controller' => '{{ bundle }}:{{ entity }}:update'), + array('_method' => 'post|put') +)); +{% endif %} + +{% if 'delete' in actions %} +$collection->add('{{ route_name_prefix }}_delete', new Route( + '/{id}/delete', + array('_controller' => '{{ bundle }}:{{ entity }}:delete'), + array('_method' => 'post|delete') +)); +{% endif %} +{% endblock body %} + +{% block return %} +return $collection; +{% endblock return %} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.xml.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.xml.twig new file mode 100644 index 0000000..c8b0d4d --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.xml.twig @@ -0,0 +1,46 @@ + + + + +{% block body %} + + {{ bundle }}:{{ entity }}:index + + + + {{ bundle }}:{{ entity }}:show + + +{% if 'new' in actions %} + + {{ bundle }}:{{ entity }}:new + + + + {{ bundle }}:{{ entity }}:create + post + +{% endif %} + +{% if 'edit' in actions %} + + {{ bundle }}:{{ entity }}:edit + + + + {{ bundle }}:{{ entity }}:update + post|put + +{% endif %} + +{% if 'delete' in actions %} + + {{ bundle }}:{{ entity }}:delete + post|delete + +{% endif %} +{% endblock body %} + + diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.yml.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.yml.twig new file mode 100644 index 0000000..fc4f446 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/config/routing.yml.twig @@ -0,0 +1,40 @@ +{% if 'index' in actions %} +{{ route_name_prefix }}: + path: / + defaults: { _controller: "{{ bundle }}:{{ entity }}:index" } +{% endif %} + +{% if 'show' in actions %} +{{ route_name_prefix }}_show: + path: /{id}/show + defaults: { _controller: "{{ bundle }}:{{ entity }}:show" } +{% endif %} + +{% if 'new' in actions %} +{{ route_name_prefix }}_new: + path: /new + defaults: { _controller: "{{ bundle }}:{{ entity }}:new" } + +{{ route_name_prefix }}_create: + path: /create + defaults: { _controller: "{{ bundle }}:{{ entity }}:create" } + requirements: { _method: post } +{% endif %} + +{% if 'edit' in actions %} +{{ route_name_prefix }}_edit: + path: /{id}/edit + defaults: { _controller: "{{ bundle }}:{{ entity }}:edit" } + +{{ route_name_prefix }}_update: + path: /{id}/update + defaults: { _controller: "{{ bundle }}:{{ entity }}:update" } + requirements: { _method: post|put } +{% endif %} + +{% if 'delete' in actions %} +{{ route_name_prefix }}_delete: + path: /{id}/delete + defaults: { _controller: "{{ bundle }}:{{ entity }}:delete" } + requirements: { _method: post|delete } +{% endif %} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/controller.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/controller.php.twig new file mode 100644 index 0000000..f847f1c --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/controller.php.twig @@ -0,0 +1,93 @@ +request('GET', '/{{ route_prefix }}/'); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /{{ route_prefix }}/"); + $crawler = $client->click($crawler->selectLink('Create a new entry')->link()); + + // Fill in the form and submit it + $form = $crawler->selectButton('Create')->form(array( + '{{ form_type_name|lower }}[field_name]' => 'Test', + // ... other fields to fill + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check data in the show view + $this->assertGreaterThan(0, $crawler->filter('td:contains("Test")')->count(), 'Missing element td:contains("Test")'); + + // Edit the entity + $crawler = $client->click($crawler->selectLink('Edit')->link()); + + $form = $crawler->selectButton('Update')->form(array( + '{{ form_type_name|lower }}[field_name]' => 'Foo', + // ... other fields to fill + )); + + $client->submit($form); + $crawler = $client->followRedirect(); + + // Check the element contains an attribute with value equals "Foo" + $this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), 'Missing element [value="Foo"]'); + + // Delete the entity + $client->submit($crawler->selectButton('Delete')->form()); + $crawler = $client->followRedirect(); + + // Check the entity has been delete on the list + $this->assertNotRegExp('/Foo/', $client->getResponse()->getContent()); + } diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/tests/others/short_scenario.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/tests/others/short_scenario.php.twig new file mode 100644 index 0000000..9e1b1a4 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/tests/others/short_scenario.php.twig @@ -0,0 +1,14 @@ + + public function testCompleteScenario() + { + // Create a new client to browse the application + $client = static::createClient(); + + // Go to the list view + $crawler = $client->request('GET', '/{{ route_prefix }}/'); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /{{ route_prefix }}/"); + + // Go to the show view + $crawler = $client->click($crawler->selectLink('show')->link()); + $this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code"); + } diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/tests/test.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/tests/test.php.twig new file mode 100644 index 0000000..c07a8fc --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/tests/test.php.twig @@ -0,0 +1,24 @@ + +
+
+
+ Welcome +
+ +
+
+ + {{ "{% endblock %}" }} +{% endblock body %} \ No newline at end of file diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/edit.html.twig.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/edit.html.twig.twig new file mode 100644 index 0000000..7299a04 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/edit.html.twig.twig @@ -0,0 +1,37 @@ +{% block extends %} +{{ "{% extends '::layout_admin.html.twig' %}" }} +{% endblock extends %} + +{% block body %} + +{{ "{% block page_title %}" }} +{{ " {{ \"crud.title.edit\"|trans([], 'admin') }}" }} +{{ "{% endblock %}" }} + +{{ "{% block page_subtitle %}" }} +{{ ' {{ "admin.' ~ entity|lower ~ '.entity_name"|trans([], "admin") }}' }} +{{ "{% endblock %}" }} + +{{ "{% block content %}" }} +
+
+

{{ '{{ entity }}' }}

+
+ {{ "{{ form_start(form) }}" }} +
+
+
+ {{ "{% include '" ~ bundle ~ ":Admin/" ~ entity ~ ":" ~ "form.html.twig' %}" }} +
+
+
+ + {{ "{{ form_end(form) }}" }} +
+{{ "{% endblock %}" }} +{% endblock body %} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/form.html.twig.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/form.html.twig.twig new file mode 100644 index 0000000..d06292f --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/form.html.twig.twig @@ -0,0 +1,14 @@ +{{ "{{ form_errors(form) }}" }} + +{% for field, metadata in fields %} +{% if field == 'id' %} +{% elseif metadata.type in [constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_ONE')] %}{# 2 == MANY_TO_ONE #} +{%- set targetClassName = metadata['targetEntity']|split('\\')|last %} +{{ renderFile('crud/views/object/renderResultSelect2.html.twig.twig', dir~'/../'~targetClassName~'/renderResultSelect2.html.twig',{'tabname':field,'field': field ,'metadata': metadata }) }} +{{ "{{ form_row(form." ~ field ~ ", {'label': 'admin." ~ entity|lower() ~ "." ~ field ~ "'|trans([], 'admin')} ) }}" }} +{% elseif metadata.type in [constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::ONE_TO_MANY'),constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_MANY')] %}{# 2 == MANY_TO_ONE #} +{{ "{#" ~ "{{ form_row(form." ~ field ~ ", {'label': 'admin." ~ entity|lower() ~ "." ~ field ~ "'|trans([], 'admin')} ) }}" ~ "#}" }} +{% else %} +{{ "{{ form_row(form." ~ field ~ ", {'label': 'admin." ~ entity|lower() ~ "." ~ field ~ "'|trans([], 'admin')} ) }}" }} +{% endif %} +{% endfor %} \ No newline at end of file diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/index.html.twig.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/index.html.twig.twig new file mode 100644 index 0000000..da769ef --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/index.html.twig.twig @@ -0,0 +1,32 @@ +{% block extends %} +{{ "{% extends '::layout_admin.html.twig' %}" }} +{% endblock extends %} + +{% block body %} + +{{ "{% block page_title %}" }} +{{ " {{ \"crud.title.index\"|trans([], 'admin') }}" }} +{{ "{% endblock %}" }} + +{{ "{% block page_subtitle %}" }} +{{ ' {{ "admin.' ~ entity|lower ~ '.entity_name"|trans([], "admin") }}' }} +{{ "{% endblock %}" }} + +{{ "{% block content %}" }} +
+
+
+
+ {{ "{{ datatable_render(datatable) }}" }} +
+ +
+
+
+{{ "{% endblock %}" }} +{% endblock body %} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/new.html.twig.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/new.html.twig.twig new file mode 100644 index 0000000..434537c --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/new.html.twig.twig @@ -0,0 +1,33 @@ +{% block extends %} +{{ "{% extends '::layout_admin.html.twig' %}" }} +{% endblock extends %} + +{% block body %} + +{{ "{% block page_title %}" }} +{{ " {{ \"crud.title.new\"|trans([], 'admin') }}" }} +{{ "{% endblock %}" }} + +{{ "{% block page_subtitle %}" }} +{{ ' {{ "admin.' ~ entity|lower ~ '.entity_name"|trans([], "admin") }}' }} +{{ "{% endblock %}" }} + +{{ "{% block content %}" }} +
+ {{ "{{ form_start(form) }}" }} +
+
+
+ {{ "{% include '" ~ bundle ~ ":Admin/" ~ entity ~ ":" ~ "form.html.twig' %}" }} +
+
+
+ + {{ "{{ form_end(form) }}" }} +
+{{ "{% endblock %}" }} +{% endblock body %} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/object/index.html.twig.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/object/index.html.twig.twig new file mode 100644 index 0000000..b28d6ca --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/object/index.html.twig.twig @@ -0,0 +1,18 @@ +{% block extends %} +{% endblock extends %} + +{% block body -%} +{{ "{% block content %}" }} + +
+
+ +
+
+ +
+ {{ "{{ datatable_render(datatable) }}" }} +
+ +{{ "{% endblock %}" }} +{%- endblock body %} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/object/renderResultSelect2.html.twig.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/object/renderResultSelect2.html.twig.twig new file mode 100644 index 0000000..6ace108 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/object/renderResultSelect2.html.twig.twig @@ -0,0 +1 @@ +{{ "{{ object }}
({{ object.id }})" }} \ No newline at end of file diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/others/actions.html.twig.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/others/actions.html.twig.twig new file mode 100644 index 0000000..a795a28 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/others/actions.html.twig.twig @@ -0,0 +1,12 @@ + + diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/others/record_actions.html.twig.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/others/record_actions.html.twig.twig new file mode 100644 index 0000000..b74c69d --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/others/record_actions.html.twig.twig @@ -0,0 +1,17 @@ + diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/show.html.twig.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/show.html.twig.twig new file mode 100644 index 0000000..0bcc122 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/crud/views/show.html.twig.twig @@ -0,0 +1,122 @@ +{% block extends %} +{{ "{% extends '::layout_admin.html.twig' %}" }} +{% endblock extends %} + +{% block body %} + +{{ "{% block page_title %}" }} +{{ " {{ \"crud.title.show\"|trans([], 'admin') }}" }} +{{ "{% endblock %}" }} + +{{ "{% block page_subtitle %}" }} +{{ ' {{ "admin.' ~ entity|lower ~ '.entity_name"|trans([], "admin") }}' }} +{{ "{% endblock %}" }} + +{{ "{% block content %}" }} + + {%- set tabList = ["entity_name"] %} + {%- for field, metadata in fields %} + {%- if metadata.type in [constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::ONE_TO_MANY'),constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_MANY')] %} + {% set tabList = tabList|merge([field ]) %} + {%- endif %} + {%- endfor %} + {%- set data -%} + + + + {%- for field, metadata in fields if metadata.type not in [constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::ONE_TO_MANY'),constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_MANY')] %} + + + + + {%- if metadata.type in ['date', 'datetime', 'time'] %} + + {%- if metadata.type == 'date' %} + {%- set date_format='long' %} + {%- set time_format='none' %} + {%- elseif metadata.type == 'datetime' %} + {%- set date_format='long' %} + {%- set time_format='medium' %} + {%- else %} + {%- set date_format='none' %} + {%- set time_format='medium' %} + {%- endif %} + + + + {%- elseif metadata.type == 'array' %} + + + {%- elseif metadata.type == 'text' %} + + + {%- else %} + + + + {%- endif %} + + + + {%- endfor %} + + +
{{ '{{ "admin.' ~ entity|lower() ~ '.' ~ field ~ '"|trans([], "admin") }}' }}{{ '{{ entity.' ~ field|replace({'_': ''}) ~ '|localizeddate("' ~ date_format ~ '","' ~ time_format ~ '") }}' }}{{ '{{ entity.' ~ field|replace({'_': ''}) ~ '|join(",") }}' }}{{ '{{ entity.' ~ field ~ '|raw }}' }}{{ '{{ entity.' ~ field|replace({'_': ''}) ~ ' }}' }}
+ {%- endset -%} + {%- set button -%} + {% if ('edit' in actions) -%}{{ "{{ 'crud.form.edit'|trans([], 'admin')|addGlyphicon(\"edit\") }}" }}{%- endif %} + {{ "{{ 'crud.form.list'|trans([], 'admin')|addGlyphicon(\"list\") }}" }} + {%- endset -%} + + {#%- if tabList|length>1 %#} + + + {# %- else %} +
+
+

{{ "{{ entity }}" }}

+
+
{{ data|raw }}
+ + +
+ {%- endif %#} + +{{ "{% endblock %}" }} +{% endblock body %} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/AbstractCrudDatatableView.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/AbstractCrudDatatableView.php.twig new file mode 100644 index 0000000..a91af19 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/AbstractCrudDatatableView.php.twig @@ -0,0 +1,68 @@ +initLineFormatter(); + } + + public function addLineFormatter(callable $lineFormatter = null) + { + $this->linesFormatter[] = $lineFormatter; + } + + + protected function initLineFormatter() { + } + + /** + * {@inheritdoc} + */ + public function getLineFormatter() + { + $formatters = $this->linesFormatter; + + $formatter = function($line) use ($formatters) { + foreach($formatters as $callable) { + if (is_callable($callable)) { + $line = call_user_func($callable, $line); + } + } + return $line; + }; + + return $formatter; + } +} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/Datatable.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/Datatable.php.twig new file mode 100644 index 0000000..feddc67 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/Datatable.php.twig @@ -0,0 +1,104 @@ +setParameters(); + $this->setColumns(); + + $this->ajax->set(['url' => $this->router->generate('admin_{{ entity|lower }}_datatable')]); + + //$this->options->set(['individual_filtering' => true]); // Uncomment it to have a search for each field + + $actions = []; + if ($this->router->getRouteCollection()->get('admin_{{ entity|lower }}_show') != null) { + $actions[] = [ + 'route' => 'admin_{{ entity|lower }}_show', + 'route_parameters' => array('id' => 'id'), + 'label' => $this->translator->trans('crud.title.show', [], 'admin'), + 'icon' => 'glyphicon glyphicon-eye-open', + 'attributes' => array( + 'rel' => 'tooltip', + 'title' => 'Show', + 'class' => 'btn btn-default btn-xs', + 'role' => 'button' + ) + ]; + } + if(count($actions)>0) { + $this->getColumnBuilder() + ->add(null, 'action', array( + 'title' => 'Actions', + 'actions' => $actions + )); + } + } + + protected function setParameters() { + $this->features->set([ + 'server_side' => true, + 'processing' => true, + ]); + $this->options->set([ + 'class' => Style::BOOTSTRAP_3_STYLE, + 'use_integration_options' => true, + ]); + } + + + /** + * {@inheritdoc} + */ + protected function setColumns() { + + $this->getColumnBuilder() +{% for field, metadata in fields if metadata.type not in [constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::ONE_TO_MANY'),constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_MANY')] %} +{% if metadata.type == 'date' %} + ->add('{{ field }}', 'datetime', array('title' => $this->translator->trans('admin.{{ entity|lower }}.{{ field }}', [], 'admin'), 'date_format' => 'L')) +{% elseif metadata.type == 'datetime' %} + ->add('{{ field }}', 'datetime', array('title' => $this->translator->trans('admin.{{ entity|lower }}.{{ field }}', [], 'admin'), 'date_format' => 'L LTS')) +{% elseif metadata.type == 'time' %} + ->add('{{ field }}', 'datetime', array('title' => $this->translator->trans('admin.{{ entity|lower }}.{{ field }}', [], 'admin'), 'date_format' => 'LTS')) +{% elseif metadata.type in [2] %}{# 2 == MANY_TO_ONE #} + // ->add('{{ field }}.name', 'column', array('title' => $this->translator->trans('admin.{{ entity|lower }}.{{ field }}', [], 'admin'))) Many to one, uncomment and select column to add +{% elseif metadata.type == 'text' %} + // ->add('{{ field }}', 'column', array('title' => $this->translator->trans('admin.{{ entity|lower }}.{{ field }}', [], 'admin'))) Text field, uncomment to add +{% else %} + ->add('{{ field }}', 'column', array('title' => $this->translator->trans('admin.{{ entity|lower }}.{{ field }}', [], 'admin'))) +{% endif %} +{% endfor %} + ; + } + + /** + * {@inheritdoc} + */ + public function getEntity() + { + return '{{ bundle }}:{{ entity }}'; + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return '{{ entity|lower }}_datatable'; + } +} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/collection_Datatable.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/collection_Datatable.php.twig new file mode 100644 index 0000000..1fc4bb6 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/datatable/collection_Datatable.php.twig @@ -0,0 +1,100 @@ +{%- set targetClassName = metadata['targetEntity']|split('\\')|last %} +setParameters(); + $this->setColumns(); + + $this->ajax->set(['url' => $this->router->generate('admin_{{ entity|lower }}_{{ field|lower }}_datatable',['id'=> ${{ entity|lower }}->getId()])]); + + //$this->options->set(['individual_filtering' => true]); // Uncomment it to have a search for each field + + $actions = []; + if ($this->router->getRouteCollection()->get('admin_{{ targetClassName|lower }}_show') != null) { + $actions[] = [ + 'route' => 'admin_{{ targetClassName|lower }}_show', + 'route_parameters' => array('id' => 'id'), + 'label' => $this->translator->trans('crud.title.show', [], 'admin'), + 'icon' => 'glyphicon glyphicon-eye-open', + 'attributes' => array( + 'rel' => 'tooltip', + 'title' => 'Show', + 'class' => 'btn btn-default btn-xs', + 'role' => 'button' + ) + ]; + } + if ($this->router->getRouteCollection()->get('admin_{{ entity|lower }}_{{ field|lower }}_remove') != null) { + $actions[] = [ + 'route' => 'admin_{{ entity|lower }}_{{ field|lower }}_remove', + 'route_parameters' => array('{{ targetClassName|lower }}_id' => 'id', 'id' => '{{ metadata['mappedBy'] is not empty ? metadata['mappedBy'] : metadata['inversedBy'] }}{{ metadata['type'] == constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_MANY') ? '[0]' : '' }}.id' ), + 'label' => $this->translator->trans('crud.form.delete', [], 'admin'), + 'icon' => 'glyphicon glyphicon-remove-circle', + 'attributes' => array( + 'rel' => 'tooltip', + 'title' => $this->translator->trans('crud.form.delete', [], 'admin'), + 'class' => 'btn btn-default btn-xs', + 'role' => 'button', + 'data-toggle' => 'delete', + 'data-confirm' => $this->translator->trans('crud.form.confirm', [], 'admin'), + ), +{# {% for joinColumn in metadata['joinColumns'] if joinColumn['name'] == (targetClassName|lower~'_id') and joinColumn['nullable'] == false %} + 'renderif' => array('removeAction') + {% endfor %}#} + ]; + } + if(count($actions)>0) { + // mappedBy > {{ metadata['mappedBy'] }} | inversedBy > {{ metadata['inversedBy'] }} + $this->getColumnBuilder() + ->add('{{ metadata['mappedBy'] ? metadata['mappedBy'] : metadata['inversedBy'] }}.id','column',['visible' => false]) + ->add(null, 'action', array( + 'title' => 'Actions', + 'actions' => $actions + )); + } + + } +{# {% for joinColumn in metadata['joinColumns'] if joinColumn['name'] == (targetClassName|lower~'id') and joinColumn['nullable'] == false %} + protected function initLineFormatter() { + $this->addLineFormatter(function($line, $outputHeader = null){ + $line['removeAction'] = array_key_exists('recordsTotal',$outputHeader) ? $outputHeader['recordsTotal'] > 1 : null; + return $line; + }); + + parent::initLineFormatter(); + } + {% endfor %}#} + + /** + * {@inheritdoc} + */ + public function getName() + { + return '{{ entity|lower }}_{{ field|lower }}_datatable'; + } +} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/form/FormType.php.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/form/FormType.php.twig new file mode 100644 index 0000000..99c6193 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/form/FormType.php.twig @@ -0,0 +1,87 @@ + 0 %} + /** + * @param FormBuilderInterface $builder + * @param array $options + */ + public function buildForm(FormBuilderInterface $builder, array $options) + { + /* */ + $builder +{% for field, metadata in fields %} +{% if metadata.type == 'date' %} + ->add("{{ field }}", "{{ metadata.type }}", array('widget' => 'single_text', 'format' => "dd/MM/yyyy"{% if metadata.nullable is defined %}, 'required' => {{ metadata.nullable ? 'false' : 'true' }}{% endif %})) +{% elseif metadata.type == 'datetime' %} + ->add("{{ field }}", "{{ metadata.type }}", array('widget' => 'single_text', 'format' => "dd/MM/yyyy HH:mm:ss"{% if metadata.nullable is defined %}, 'required' => {{ metadata.nullable ? 'false' : 'true' }}{% endif %})) +{% elseif metadata.type == 'time' %} + ->add("{{ field }}", "{{ metadata.type }}", array('widget' => 'single_text', 'with_seconds' => true{% if metadata.nullable is defined %}, 'required' => {{ metadata.nullable ? 'false' : 'true' }}{% endif %})) +{% elseif metadata.type in [constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_ONE')] %} +{% set property = null -%} +{% if method_exists(metadata['targetEntity'] ,'get'~'name'|capitalize ) -%} +{% set property = 'name' %} +{% elseif method_exists(metadata['targetEntity'] ,'get'~'title'|capitalize ) -%} +{% set property = 'title' %} +{% endif %} + ->add("{{ field }}", "entity_select2", [ + 'class' => '{{ namespace }}\Entity{{ entity_namespace ? '\\' ~ entity_namespace : '' }}\{{ field|capitalize }}', + 'searchRouteName' => 'admin_{{ entity_class|lower }}_{{ field|lower }}_search', + 'property' => '{{ property }}', + 'placeholder' => 'search_placeholder', + 'required' => false + ]) +{% elseif metadata.type in [constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::ONE_TO_MANY'),constant('Doctrine\\ORM\\Mapping\\ClassMetadataInfo::MANY_TO_MANY')] %} +{% set property = null -%} +{% if method_exists(metadata['targetEntity'] ,'get'~'name'|capitalize ) -%} +{% set property = 'name' %} +{% elseif method_exists(metadata['targetEntity'] ,'get'~'title'|capitalize ) -%} +{% set property = 'title' %} +{% endif %} + // ->add("{{ field }}","collection_select2",[ + // 'class' => '{{ namespace }}\Entity{{ entity_namespace ? '\\' ~ entity_namespace : '' }}\{{ field|capitalize }}', + // 'searchRouteName' => 'admin_{{ field|lower }}_search', + // 'property' => '{{ property }}', + // 'required' => false + // ]) +{% elseif metadata.type == 'text' %} + ->add('{{ field }}', 'ckeditor'{% if metadata.nullable is defined %}, ['required' => {{ metadata.nullable ? 'false' : 'true' }}]{% endif %}) +{% else %} + ->add('{{ field }}'{% if metadata.nullable is defined %}, null, ['required' => {{ metadata.nullable ? 'false' : 'true' }}]{% endif %}) +{% endif %} +{% endfor %} + ; + } + {% endif %} + + /** + * @param OptionsResolverInterface $resolver + */ + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + $resolver->setDefaults(array( + 'data_class' => '{{ namespace }}\Entity{{ entity_namespace ? '\\' ~ entity_namespace : '' }}\{{ entity_class }}' + )); + } + + /** + * @return string + */ + public function getName() + { + return '{{ form_type_name }}'; + } +{% endblock class_body %} +} diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/translations/admin.en.yml.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/translations/admin.en.yml.twig new file mode 100644 index 0000000..fdf84fa --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/translations/admin.en.yml.twig @@ -0,0 +1,26 @@ +crud: + form: + edit: Edit + list: Back to list + save: Save + new: Create new + cancel: Cancel + delete: Delete + required: This field is required + search_placeholder: 'Search' + confirm: Are you sure? + search_and_append: 'Search and add %entity_name%' + message: + created: The item has been created + deleted: The item has been deleted + saved: The item has been updated + title: + edit: Edit + index: List + new: New + show: Show + delete: Delete + properties: Properties + +admin: + edit_contents: Edit contents \ No newline at end of file diff --git a/src/Sedona/SBOGeneratorBundle/Resources/skeleton/translations/admin.fr.yml.twig b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/translations/admin.fr.yml.twig new file mode 100644 index 0000000..ec5c022 --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/Resources/skeleton/translations/admin.fr.yml.twig @@ -0,0 +1,26 @@ +crud: + form: + edit: Edition + list: Retour à la liste + save: Sauvegarde + new: Nouveau + cancel: Annuler + delete: Supprimer + required: Ce champ est obligatoire + search_placeholder: 'Recherche' + confirm: Etes vous sûr ? + search_and_append: 'Rechercher et ajouter un %entity_name%' + message: + created: L'élément a été créé + deleted: L'élément a été supprimé + saved: L'élément a été mis à jour + title: + edit: Edition + index: Liste + new: Nouveau + show: Affichage + delete: Supprimer + properties: Propriétés + +admin: + edit_contents: Edition des contenus \ No newline at end of file diff --git a/src/Sedona/SBOGeneratorBundle/SedonaSBOGeneratorBundle.php b/src/Sedona/SBOGeneratorBundle/SedonaSBOGeneratorBundle.php new file mode 100644 index 0000000..130c9ca --- /dev/null +++ b/src/Sedona/SBOGeneratorBundle/SedonaSBOGeneratorBundle.php @@ -0,0 +1,18 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBOGeneratorBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +class SedonaSBOGeneratorBundle extends Bundle +{ +} diff --git a/src/Sedona/SBORuntimeBundle/ClassUtils.php b/src/Sedona/SBORuntimeBundle/ClassUtils.php new file mode 100644 index 0000000..370f05d --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/ClassUtils.php @@ -0,0 +1,47 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle; + +/** + * Class ClassUtils + * @package Sedona\SBORuntimeBundle + */ +class ClassUtils +{ + /** + * Return true if the given object use the given trait, FALSE if not + * + * @param \ReflectionClass|string $class + * @param string $traitName + * @param boolean $isRecursive + * + * @return bool + */ + public static function hasTrait($class, $traitName, $isRecursive = false) + { + if (is_string($class)) { + $class = new \ReflectionClass($class); + } + + if (in_array($traitName, $class->getTraitNames(), true)) { + return true; + } + + $parentClass = $class->getParentClass(); + + if ((false === $isRecursive) || (false === $parentClass) || (null === $parentClass)) { + return false; + } + + return static::hasTrait($parentClass, $traitName, $isRecursive); + } +} diff --git a/src/Sedona/SBORuntimeBundle/Controller/BaseCrudController.php b/src/Sedona/SBORuntimeBundle/Controller/BaseCrudController.php new file mode 100644 index 0000000..f088898 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Controller/BaseCrudController.php @@ -0,0 +1,628 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Controller; + +use Doctrine\ORM\QueryBuilder; +use Sedona\SBORuntimeBundle\Event\AdminAssociationActionEvent; +use Sedona\SBORuntimeBundle\Event\AdminCrudEvent; +use Symfony\Bundle\FrameworkBundle\Controller\Controller; +use Symfony\Component\Form\FormTypeInterface; +use Symfony\Component\HttpFoundation\JsonResponse; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; + +/** + * Base CRUD Controller. + */ +abstract class BaseCrudController extends Controller +{ + protected $route_name = 'admin_default'; + protected $bundle_name = 'DefaultBundle'; + protected $entity_name = 'Default'; + + /* + * default URL (can be overloaded) + */ + protected function getEditUrl($entity) + { + return $this->getUrl($entity, '_edit'); + } + protected function getNewUrl($entity) + { + return $this->getUrl($entity, '_new'); + } + protected function getShowUrl($entity) + { + return $this->getUrl($entity, '_show'); + } + protected function getListUrl() + { + return $this->generateUrl($this->route_name.'_list'); + } + + /* + * default Flash messages (can be overloaded) + */ + protected function getFlashSavedMessage() + { + return 'crud.message.saved'; + } + protected function getFlashDeletedMessage() + { + return 'crud.message.deleted'; + } + protected function getFlashCreatedMessage() + { + return 'crud.message.created'; + } + + /* + * default templates (can be overloaded) + */ + protected function getEditTemplate() + { + return $this->bundle_name.':Admin/'.$this->entity_name.':'.'edit.html.twig'; + } + protected function getNewTemplate() + { + return $this->bundle_name.':Admin/'.$this->entity_name.':'.'new.html.twig'; + } + protected function getShowTemplate() + { + return $this->bundle_name.':Admin/'.$this->entity_name.':'.'show.html.twig'; + } + + /** + * @param $entity + * @param $mode + * + * @return string + */ + protected function getUrl($entity, $mode) + { + return $this->generateUrl($this->route_name.$mode, array('id' => $entity->getId())); + } + + /** + * Generic edit controller helper. + * + * @param $entity + * @param Request $request + * + * @return string|\Symfony\Component\HttpFoundation\RedirectResponse + */ + protected function manageEdit($entity, Request $request, FormTypeInterface $form) + { + $form = $this->createForm($form, $entity, array( + 'action' => $this->getEditUrl($entity), + 'method' => 'POST', + )); + + $form + ->add('save', 'submit', array('attr' => array('class' => 'btn btn-primary'))) + ->add('delete', 'submit', array('attr' => array('class' => 'btn btn-danger'))); + + $form->handleRequest($request); + + if ($form->isSubmitted()) { + if ($form->get('save')->isClicked()) { + if ($form->isValid()) { + return $this->crudAction($entity, AdminCrudEvent::UPDATE); + } + } elseif ($form->get('delete')->isClicked()) { + return $this->crudAction($entity, AdminCrudEvent::DELETE); + } + } + + return $this->render( + $this->getEditTemplate(), + array( + 'entity' => $entity, + 'form' => $form->createView(), + )); + } + + /** + * Generic new controller helper. + * + * @param $entity + * @param Request $request + * + * @return string|\Symfony\Component\HttpFoundation\RedirectResponse + */ + protected function manageNew($entity, Request $request, FormTypeInterface $form) + { + $form = $this->createForm($form, $entity, array( + 'action' => $this->getNewUrl($entity), + 'method' => 'POST', + )); + + $form + ->add('create', 'submit', array('attr' => array('class' => 'btn btn-primary'))); + + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + return $this->crudAction($entity, AdminCrudEvent::CREATE); + } + + return $this->render( + $this->getNewTemplate(), + array( + 'entity' => $entity, + 'form' => $form->createView(), + )); + } + + /** + * Manage full action: send events, manage entity, send flash messages. + * + * @param $entity + * @param $action + * + * @return \Symfony\Component\HttpFoundation\RedirectResponse + */ + public function crudAction($entity, $action) + { + // Before, send preAction + $event = $this->dispatchCrudEventPreAction($entity, $action); + $em = $this->getDoctrine()->getManager(); + + // Update entity according to action (can be changed in event) + switch ($event->getAction()) { + case AdminCrudEvent::CREATE; + $em->persist($entity); + break; + case AdminCrudEvent::DELETE; + $em->remove($entity); + break; + } + if ($action > AdminCrudEvent::DONT_TOUCH) { + $em->flush(); + } + + // Send postAction Event + $event = $this->dispatchCrudEventPostAction($entity, $action); + + switch ($event->getAction()) { + case AdminCrudEvent::CREATE; + $this->addFlashMessage('success', $this->getFlashCreatedMessage()); + break; + case AdminCrudEvent::DELETE; + $this->addFlashMessage('success', $this->getFlashDeletedMessage()); + + return $this->redirect($this->getListUrl()); + break; + case AdminCrudEvent::UPDATE; + $this->addFlashMessage('success', $this->getFlashSavedMessage()); + break; + + } + + return $this->redirect($this->getShowUrl($entity)); + } + + /** + * Dispatch Event before action. + * + * @param $entity + * @param $action + * + * @return AdminCrudEvent + */ + public function dispatchCrudEventPreAction($entity, $action) + { + return $this->dispatchCrudEvent($entity, $action, 'sbo.crud.preAction'); + } + + /** + * Dispatch Event after action. + * + * @param $entity + * @param $action + * + * @return AdminCrudEvent + */ + public function dispatchCrudEventPostAction($entity, $action) + { + return $this->dispatchCrudEvent($entity, $action, 'sbo.crud.postAction'); + } + + /** + * Generic Event dispatcher. + * + * @param $entity + * @param $action + * @param $eventName + * + * @return AdminCrudEvent + */ + public function dispatchCrudEvent($entity, $action, $eventName) + { + $event = new AdminCrudEvent($entity, $action, $this->getUser()); + $this->get('event_dispatcher')->dispatch($eventName, $event); + + return $event; + } + + /** + * Dispatch Event before action. + * + * @param $entity + * @param $action + * + * @return AdminAssociationActionEvent + */ + public function dispatchAssociationActionEventPreAction($entity, $action, $target) + { + return $this->dispatchAssociationActionEvent($entity, $action, $target, 'sbo.association.preAction'); + } + + /** + * Dispatch Event after action. + * + * @param $entity + * @param $action + * + * @return AdminAssociationActionEvent + */ + public function dispatchAssociationActionEventPostAction($entity, $action, $target) + { + return $this->dispatchAssociationActionEvent($entity, $action, $target, 'sbo.association.postAction'); + } + + /** + * Generic Association Action dispatcher. + * + * @param $entity + * @param $action + * @param $target + * @param $eventName + * + * @return AdminAssociationActionEvent + */ + public function dispatchAssociationActionEvent($entity, $action, $target, $eventName) + { + $event = new AdminAssociationActionEvent($entity, $action, $target, $this->getUser()); + $this->get('event_dispatcher')->dispatch($eventName, $event); + + return $event; + } + + /** + * Generic show controller helper. + * + * @param $entity + * + * @return Response + */ + public function manageShow($entity) + { + return $this->render($this->getShowTemplate(), array( + 'entity' => $entity, + )); + } + + /** + * Generic index controller helper. + * + * @return Response + */ + public function manageIndex() + { + $postDatatable = $this->get($this->route_name.'_datatable'); + $postDatatable->buildDatatable(); + + return $this->render($this->getIndexTemplate(), array( + 'datatable' => $postDatatable, + )); + } + + /** + * Generic index controller helper. + * + * @param $entity + * @param $field + * + * @return Response + * + * @throws \Exception + */ + public function manageFieldIndex($entity, $field) + { + if ($entity == null || $field == null || $this->has($this->route_name.'_'.$field.'_datatable') == false) { + throw new \Exception('All the parameters are not correctly set'); + } + $postDatatable = $this->get($this->route_name.'_'.$field.'_datatable'); + $postDatatable->buildDatatable(['entity' => $entity]); + + return $this->render($this->getIndexTemplate(strtolower($field)), array( + 'datatable' => $postDatatable, + 'entity' => $entity, + )); + } + + /** + * @return string + */ + public function getIndexTemplate($field = null) + { + if ($field != null) { + return $this->bundle_name.':Admin/'.$this->entity_name.'/'.$field.':'.'index.html.twig'; + } + + return $this->bundle_name.':Admin/'.$this->entity_name.':'.'index.html.twig'; + } + + /** + * @return Response + */ + public function manageDatatableJson() + { + $postDatatable = $this->get($this->route_name.'_datatable'); + $postDatatable->buildDatatable(); + $datatable = $this->get('sg_datatables.query')->getQueryFrom($postDatatable); + + return $datatable->getResponse(); + } + + /** + * @param $entity + * @param $field + * @param $reversedField + * + * @return Response + * + * @throws \Exception + */ + public function manageFieldDatatableJson($entity, $field, $reversedField, $type = 'one') + { + if ($entity == null || $field == null || $this->has($this->route_name.'_'.$field.'_datatable') == false) { + throw new \Exception('All the parameters are not correctly set'); + } + + $postDatatable = $this->get($this->route_name.'_'.$field.'_datatable'); + $postDatatable->buildDatatable(); + + if (method_exists($postDatatable, 'addLineFormatter') && method_exists($entity, 'getId')) { + $postDatatable->addLineFormatter(function ($ligne) use ($entity) { + $ligne['entity_id'] = $entity->getId(); + + return $ligne; + }); + } + + $datatable = $this->get('sg_datatables.query')->getQueryFrom($postDatatable); + + $entityName = $this->entity_name; + + if ($type === 'one') { + $datatable->addWhereAll(function (QueryBuilder $qb) use ($entity, $entityName, $reversedField) { + $qb + ->andWhere($qb->getRootAliases()[0].'.'.$reversedField." = :$entityName") + ->setParameter("$entityName", $entity) + ; + }); + } elseif ($type === 'many') { + $datatable->addWhereAll(function (QueryBuilder $qb) use ($entity, $entityName, $reversedField) { + $qb + ->join($qb->getRootAliases()[0].'.'.$reversedField, 'reverseField') + ->andWhere('reverseField.id = :'.$entityName.'_id') + ->setParameter($entityName.'_id', $entity->getId()) + ; + }); + } + + return $datatable->getResponse(); + } + + /** + * @param $type + * @param $message + */ + protected function addFlashMessage($type, $message) + { + $this->addFlash( + $type, + $this->get('translator')->trans($message, [], 'admin') + ); + } + + /** + * @param Request $request + * @param $class + * @param closure|string $filerFunction + * @param closure|null $renderResult + * + * @return JsonResponse + */ + public function searchSelect2(Request $request, $class, $fieldSearchFunction = 'title', $renderResult = null) + { + // 3rd parameter: use the following code to have a more flexible search + // $querySearch = function(\Doctrine\ORM\QueryBuilder $queryBuilder, $query) { + // $queryBuilder + // ->andWhere("o.{{ property }} LIKE :{{ property }}") + // ->setParameter("{{ property }}","%$query%"); + // }; + + // 4th parameter: use the following code to customize rendering of select2 + // $twig = $this->get('twig'); + // $html = $this->bundle_name.":Admin/".strtolower(substr($class,strripos($class,'\\')+1)).":renderResultSelect2.html.twig"; + // $appendResult = function( $entity, $query) use ($twig, $html) { + // return [ + // 'renderValue' => $twig->render($html, ['entity' => $entity, 'query' => $query]), + // 'text' => $entity->__toString(), + // 'id' => $entity->getId() + // ]; + // }; + + $query = $request->get('q', null); + $limit = (int) $request->get('page_limit', 10); + $page = (int) $request->get('page', 1); + + $res = ['term' => $query, 'more' => false, 'results' => []]; + + // creation du queryBulider + $queryBuilder = $this->get('doctrine.orm.entity_manager') + ->getRepository($class) + ->createQueryBuilder('o') + ; + + // add filters + if (is_string($fieldSearchFunction)) { + $queryBuilder + ->andWhere("o.$fieldSearchFunction LIKE :$fieldSearchFunction") + ->setParameter("$fieldSearchFunction", "%$query%") + ->orderBy("o.$fieldSearchFunction") + ; + } elseif (is_callable($fieldSearchFunction)) { + $fieldSearchFunction($queryBuilder, $query); + } + + // page & limit + if (is_int($page) && $page > 0 && is_int($limit) && $limit > 0) { + $queryBuilder = $queryBuilder + ->setFirstResult(($page - 1) * $limit) + ->setMaxResults($limit + 1) + ; + } + + // get results + $result = $queryBuilder + ->getQuery() + ->getResult() + ; + + if ($renderResult == null) { + $getter = is_string($fieldSearchFunction) ? $this->getGetter($fieldSearchFunction) : '__toString'; + $renderResult = function ($entity, $query) use ($getter) { + return [ + 'text' => $entity->$getter(), + 'id' => $entity->getId(), + ]; + }; + } + + // create response + foreach ($result as $object) { + $res['results'][] = $renderResult($object, $query); + } + + // check if not more result + if (count($result) > $limit) { + $res['more'] = true; + $res['results'] = array_slice($res['results'], 0, $limit); + } + + $response = new JsonResponse($res); + $response->setCallback($request->get('callback')); + + // send data into JSON + return $response; + } + + /** + * Manage search to add in datatable. + * + * @param Request $request + * @param $entity + * @param $fieldClass + * @param $field + * @param $fieldSearch + * + * @return JsonResponse + */ + protected function manageSearchFieldMany(Request $request, $entity, $fieldClass, $field, $fieldSearch) + { + $twig = $this->get('twig'); + + $getTitle = $this->getGetter($fieldSearch); + + $querySearch = function (\Doctrine\ORM\QueryBuilder $queryBuilder, $query) use ($entity, $field, $fieldSearch, $fieldClass) { + + $queryBuilder + ->andWhere($queryBuilder->expr()->notIn('o.id', + 'SELECT field.id FROM '.get_class($entity).' entity JOIN entity.'.$field.' field WHERE entity.id = :entity_id')) + ->setParameter('entity_id', $entity->getId()) + ->andWhere('o.'.$fieldSearch.' LIKE :'.$fieldSearch) + ->setParameter($fieldSearch, "%$query%") + ->orderBy('o.'.$fieldSearch) + ; + }; + + $fieldClassName = substr($fieldClass, strripos($fieldClass, '\\') + 1); + if ($this->get('templating')->exists($this->bundle_name.':Admin/'.$fieldClassName.':'.'renderResultSelect2.html.twig')) { + $appendResult = function ($subentity, $query) use ($entity, $field, $twig, $getTitle, $fieldClassName) { + return [ + 'renderValue' => $twig->render($this->bundle_name.':Admin/'.$fieldClassName.':'.'renderResultSelect2.html.twig', ['entity' => $subentity, 'query' => $query]), + 'confirme' => $this->generateUrl($this->route_name.'_'.strtolower($field).'_add', ['id' => $entity->getId(), strtolower($fieldClassName).'_id' => $subentity->getId()]), + 'text' => $subentity->$getTitle(), + 'id' => $subentity->getId(), + ]; + }; + } else { + $appendResult = function ($subentity, $query) use ($entity, $field, $twig, $getTitle, $fieldClassName) { + return [ + 'confirme' => $this->generateUrl($this->route_name.'_'.strtolower($field).'_add', ['id' => $entity->getId(), strtolower($fieldClassName).'_id' => $subentity->getId()]), + 'text' => $subentity->$getTitle(), + 'id' => $subentity->getId(), + ]; + }; + } + + return $this->searchSelect2($request, $fieldClass, $querySearch, $appendResult); + } + + /** + * @param $source + * @param $target + * @param $field + * @param $action + * @param bool $contains + * + * @return JsonResponse + */ + protected function manageJsonAction($source, $target, $field, $action, $contains = true) + { + $res = ['result' => true, 'message' => '']; + + $getter = $this->getGetter($field); + + if ($source->$getter()->contains($target) == $contains) { + $event = $this->dispatchAssociationActionEventPreAction($source, $action, $target); + + if ($event->getAction() != AdminAssociationActionEvent::DONT_TOUCH) { + $source2 = $event->getItem(); + $target2 = $event->getTarget(); + $action2 = $event->getAction(); + + $source2->$action2($target2); + $this->get('doctrine.orm.entity_manager')->flush(); + } + //$res['html'] = $this->get("twig")->render("SedonaSBOTestBundle:Admin/Artist:_renderAlbum.html.twig", ['object'=>$target]); + + $event = $this->dispatchAssociationActionEventPostAction($source, $action, $target); + } + + return new JsonResponse($res); + } + + /** + * @param $field + * + * @return string + */ + protected function getGetter($field) + { + return 'get'.ucfirst($field); + } +} diff --git a/src/Sedona/SBORuntimeBundle/Datatable/Data/DatatableDataManager.php b/src/Sedona/SBORuntimeBundle/Datatable/Data/DatatableDataManager.php new file mode 100644 index 0000000..30da5fa --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Datatable/Data/DatatableDataManager.php @@ -0,0 +1,141 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Datatable\Data; + +use Sg\DatatablesBundle\Datatable\View\DatatableViewInterface; +use Sg\DatatablesBundle\Datatable\Data\DatatableDataManager as DatatableDataManagerBase; + +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\Serializer\Serializer; + +/** + * Class DatatableDataManager + * + * @package Sg\DatatablesBundle\Datatable\Data + */ +class DatatableDataManager extends DatatableDataManagerBase +{ + /** + * The request. + * + * @var Request + */ + private $request; + + /** + * The serializer service. + * + * @var Serializer + */ + private $serializer; + + /** + * Configuration settings. + * + * @var array + */ + private $configs; + + /** + * True if the LiipImagineBundle is installed. + * + * @var boolean + */ + private $imagineBundle; + + /** + * True if GedmoDoctrineExtensions installed. + * + * @var boolean + */ + private $doctrineExtensions; + + /** + * The locale. + * + * @var string + */ + private $locale; + + //------------------------------------------------- + // Ctor. + //------------------------------------------------- + + /** + * Ctor. + * + * @param RequestStack $requestStack + * @param Serializer $serializer + * @param array $configs + * @param array $bundles + */ + public function __construct(RequestStack $requestStack, Serializer $serializer, array $configs, array $bundles) + { + $this->request = $requestStack->getCurrentRequest(); + $this->serializer = $serializer; + $this->configs = $configs; + $this->imagineBundle = false; + $this->doctrineExtensions = false; + + if (true === class_exists('Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker')) { + $this->doctrineExtensions = true; + } + + if (true === array_key_exists('LiipImagineBundle', $bundles)) { + $this->imagineBundle = true; + } + + $this->locale = $this->request->getLocale(); + } + + //------------------------------------------------- + // Public + //------------------------------------------------- + + /** + * Get query. + * + * @param DatatableViewInterface $datatableView + * + * @return DatatableQuery + */ + public function getQueryFrom(DatatableViewInterface $datatableView) + { + $twig = $datatableView->getTwig(); + + $type = $datatableView->getAjax()->getType(); + $parameterBag = null; + + if ('GET' === strtoupper($type)) { + $parameterBag = $this->request->query; + } + + if ('POST' === strtoupper($type)) { + $parameterBag = $this->request->request; + } + + $params = $parameterBag->all(); + $query = new DatatableQuery( + $this->serializer, + $params, + $datatableView, + $this->configs, + $twig, + $this->imagineBundle, + $this->doctrineExtensions, + $this->locale + ); + + return $query; + } +} diff --git a/src/Sedona/SBORuntimeBundle/Datatable/Data/DatatableQuery.php b/src/Sedona/SBORuntimeBundle/Datatable/Data/DatatableQuery.php new file mode 100644 index 0000000..39a2aa9 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Datatable/Data/DatatableQuery.php @@ -0,0 +1,1051 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Datatable\Data; + +use Sedona\SBORuntimeBundle\ClassUtils; +use Sg\DatatablesBundle\Datatable\View\DatatableViewInterface; +use Sg\DatatablesBundle\Datatable\Column\AbstractColumn; +use Sg\DatatablesBundle\Datatable\Column\ImageColumn; +use Sg\DatatablesBundle\Datatable\Column\GalleryColumn; +use Sg\DatatablesBundle\Datatable\Data\DatatableQuery as DatatableQueryBase; + +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\PropertyAccess\PropertyAccess; +use Symfony\Component\PropertyAccess\PropertyAccessorInterface; +use Symfony\Component\Serializer\Serializer; +use Symfony\Component\PropertyAccess\Exception\InvalidArgumentException; +use Doctrine\ORM\Mapping\ClassMetadata; +use Doctrine\ORM\Mapping\MappingException; +use Doctrine\ORM\EntityManager; +use Doctrine\ORM\QueryBuilder; +use Doctrine\ORM\Query\Expr\Andx; +use Doctrine\ORM\Query; +use Doctrine\ORM\Tools\Pagination\Paginator; + +class DatatableQuery extends DatatableQueryBase +{ + /** + * @var Serializer + */ + private $serializer; + + /** + * @var array + */ + private $requestParams; + + /** + * @var DatatableViewInterface + */ + private $datatableView; + + /** + * @var string + */ + private $entity; + + /** + * @var boolean + */ + private $individualFiltering; + + /** + * @var EntityManager + */ + private $em; + + /** + * @var ClassMetadata + */ + private $metadata; + + /** + * @var string + */ + private $tableName; + + /** + * @var mixed + */ + private $rootEntityIdentifier; + + /** + * @var QueryBuilder + */ + private $qb; + + /** + * @var array + */ + private $selectColumns; + + /** + * @var array + */ + private $virtualColumns; + + /** + * @var array + */ + private $joins; + + /** + * @var array + */ + private $searchColumns; + + /** + * @var array + */ + private $orderColumns; + + /** + * @var array + */ + private $callbacks; + + /** + * @var callable + */ + private $lineFormatter; + + /** + * @var AbstractColumn[] + */ + private $columns; + + /** + * @var array + */ + private $configs; + + /** + * @var Twig_Environment + */ + private $twig; + + /** + * @var boolean + */ + private $imagineBundle; + + /** + * @var boolean + */ + private $doctrineExtensions; + + /** + * The locale. + * + * @var string + */ + private $locale; + + /** + * @var PropertyAccessorInterface + */ + private $propertyAccessor; + + //------------------------------------------------- + // Ctor. + //------------------------------------------------- + + /** + * Ctor. + * + * @param Serializer $serializer + * @param array $requestParams + * @param DatatableViewInterface $datatableView + * @param array $configs + * @param \Twig_Environment $twig + * @param boolean $imagineBundle + * @param boolean $doctrineExtensions + * @param string $locale + * + * @throws \Exception + */ + public function __construct( + Serializer $serializer, + array $requestParams, + DatatableViewInterface $datatableView, + array $configs, + \Twig_Environment $twig, + $imagineBundle, + $doctrineExtensions, + $locale + ) + { + $this->propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()->enableMagicCall()->getPropertyAccessor(); + + $this->serializer = $serializer; + $this->requestParams = $requestParams; + $this->datatableView = $datatableView; + + $this->individualFiltering = $this->datatableView->getOptions()->getIndividualFiltering(); + + $this->entity = $this->datatableView->getEntity(); + $this->em = $this->datatableView->getEntityManager(); + $this->metadata = $this->getMetadata($this->entity); + $this->tableName = $this->getTableName($this->metadata); + $this->rootEntityIdentifier = $this->getIdentifier($this->metadata); + $this->qb = $this->em->createQueryBuilder(); + + $this->selectColumns = array(); + $this->virtualColumns = $datatableView->getColumnBuilder()->getVirtualColumns(); + $this->joins = array(); + $this->searchColumns = array(); + $this->orderColumns = array(); + $this->callbacks = array(); + $this->columns = $datatableView->getColumnBuilder()->getColumns(); + + $this->configs = $configs; + + $this->twig = $twig; + $this->imagineBundle = $imagineBundle; + $this->doctrineExtensions = $doctrineExtensions; + $this->locale = $locale; + + $this->setLineFormatter(); + $this->setupColumnArrays(); + } + + //------------------------------------------------- + // Setup query + //------------------------------------------------- + + /** + * Setup column arrays. + * + * @return $this + */ + private function setupColumnArrays() + { + /* Example: + SELECT + partial fos_user.{id}, + partial posts_comments.{title,id}, + partial posts.{id,title} + FROM + AppBundle\Entity\User fos_user + LEFT JOIN + fos_user.posts posts + LEFT JOIN + posts.comments posts_comments + ORDER BY + posts_comments.title asc + */ + + $this->selectColumns[$this->tableName][] = $this->rootEntityIdentifier; + + foreach ($this->columns as $key => $column) { + $data = $column->getDql(); + + if (true === $this->isSelectColumn($data)) { + if (false === $this->isAssociation($data)) { + if (!$this->metadata->hasField($data)) { + $reflection = $this->metadata->getReflectionClass(); + if ( + ClassUtils::hasTrait($reflection, 'Knp\DoctrineBehaviors\Model\Translatable\Translatable', true) and + $this->em->getClassMetadata(call_user_func([$reflection->getName(), 'getTranslationEntityClass']))->hasField($data) + ) { + $this->setIdentifierFromAssociation('translations'); + + $this->selectColumns['translations'][] = $data; + $this->joins[$this->tableName . '.' . 'translations'] = 'translations'; + $this->addSearchOrderColumn($key, 'translations', $data); + + continue; + } + } + + $this->addSearchOrderColumn($key, $this->tableName, $data); + if ($data !== $this->rootEntityIdentifier) { + $this->selectColumns[$this->tableName][] = $data; + } + } else { + $array = explode('.', $data); + $count = count($array); + + if ($count > 2) { + $replaced = str_replace('.', '_', $data); + $parts = explode('_', $replaced); + $last = array_pop($parts); + $select = implode('_', $parts); + $join = str_replace('_', '.', $select); + + // id root-table + if (false === array_key_exists($array[0], $this->selectColumns)) { + $this->setIdentifierFromAssociation($array[0]); + } + $this->joins[$this->tableName . '.' . $array[0]] = $array[0]; + + // id association + if (false === array_key_exists($select, $this->selectColumns)) { + $this->setIdentifierFromAssociation($parts, $select); + } + $this->joins[$join] = $select; + + $this->selectColumns[$select][] = $last; + $this->addSearchOrderColumn($key, $select, $last); + } else { + if (false === array_key_exists($array[0], $this->selectColumns)) { + $this->setIdentifierFromAssociation($array[0]); + } + + $this->selectColumns[$array[0]][] = $array[1]; + $this->joins[$this->tableName . '.' . $array[0]] = $array[0]; + $this->addSearchOrderColumn($key, $array[0], $array[1]); + } + } + } else { + $this->orderColumns[] = null; + $this->searchColumns[] = null; + } + + } + + return $this; + } + + /** + * Build query. + * + * @return $this + */ + public function buildQuery() + { + $this->setSelectFrom(); + $this->setLeftJoins($this->qb); + $this->setWhere($this->qb); + $this->setWhereResultCallback($this->qb); + $this->setWhereAllCallback($this->qb); + $this->setOrderBy(); + $this->setLimit(); + + return $this; + } + + /** + * Get query. + * + * @return QueryBuilder + */ + public function getQuery() + { + return $this->qb; + } + + /** + * Set query. + * + * @param QueryBuilder $qb + * + * @return $this + */ + public function setQuery(QueryBuilder $qb) + { + $this->qb = $qb; + + return $this; + } + + //------------------------------------------------- + // Callbacks + //------------------------------------------------- + + /** + * Add the where-result function. + * + * @param $callback + * + * @return $this + */ + public function addWhereResult($callback) + { + $this->callbacks['WhereResult'][] = $callback; + + return $this; + } + + /** + * Add the where-all function. + * + * @param $callback + * + * @return $this + */ + public function addWhereAll($callback) + { + $this->callbacks['WhereAll'][] = $callback; + + return $this; + } + + /** + * Set the line formatter function. + * + * @return $this + */ + private function setLineFormatter() + { + $this->lineFormatter = $this->datatableView->getLineFormatter(); + + return $this; + } + + /** + * Set where result callback. + * + * @param QueryBuilder $qb + * + * @return $this + */ + private function setWhereResultCallback(QueryBuilder $qb) + { + if (!empty($this->callbacks['WhereResult'])) { + foreach ($this->callbacks['WhereResult'] as $callback) { + $callback($qb); + } + } + + return $this; + } + + /** + * Set where all callback. + * + * @param QueryBuilder $qb + * + * @return $this + */ + private function setWhereAllCallback(QueryBuilder $qb) + { + if (!empty($this->callbacks['WhereAll'])) { + foreach ($this->callbacks['WhereAll'] as $callback) { + $callback($qb); + } + } + + return $this; + } + + //------------------------------------------------- + // Build a query + //------------------------------------------------- + + /** + * Set select from. + * + * @return $this + */ + private function setSelectFrom() + { + foreach ($this->selectColumns as $key => $value) { + $this->qb->addSelect($key); + } + + $this->qb->from($this->entity, $this->tableName); + + return $this; + } + + /** + * Set leftJoins. + * + * @param QueryBuilder $qb + * + * @return $this + */ + private function setLeftJoins(QueryBuilder $qb) + { + foreach ($this->joins as $key => $value) { + $qb->leftJoin($key, $value); + } + + return $this; + } + + /** + * Searching / Filtering. + * Construct the WHERE clause for server-side processing SQL query. + * + * @param QueryBuilder $qb + * + * @return $this + */ + private function setWhere(QueryBuilder $qb) + { + $globalSearch = $this->requestParams['search']['value']; + + // global filtering + if ('' != $globalSearch) { + + $orExpr = $qb->expr()->orX(); + + foreach ($this->columns as $key => $column) { + if (true === $this->isSearchColumn($column)) { + $searchField = $this->searchColumns[$key]; + $orExpr->add($qb->expr()->like($searchField, '?' . $key)); + $qb->setParameter($key, '%' . $globalSearch . '%'); + } + } + + $qb->where($orExpr); + } + + // individual filtering + if (true === $this->individualFiltering) { + $andExpr = $qb->expr()->andX(); + + $i = 100; + + foreach ($this->columns as $key => $column) { + + if (true === $this->isSearchColumn($column)) { + $searchType = $column->getSearchType(); + $searchField = $this->searchColumns[$key]; + $searchValue = $this->requestParams['columns'][$key]['search']['value']; + $searchRange = $this->requestParams['columns'][$key]['name'] === 'daterange'; + if ('' != $searchValue && 'null' != $searchValue) { + if ($searchRange) { + list($_dateStart, $_dateEnd) = explode(' - ', $searchValue); + $dateStart = new \DateTime($_dateStart); + $dateEnd = new \DateTime($_dateEnd); + $dateEnd->setTime(23, 59, 59); + + $k = $i + 1; + $andExpr->add($qb->expr()->between($searchField, '?' . $i, '?' . $k)); + $qb->setParameter($i, $dateStart->format('Y-m-d H:i:s')); + $qb->setParameter($k, $dateEnd->format('Y-m-d H:i:s')); + $i += 2; + } else { + $andExpr = $this->addCondition($andExpr, $qb, $searchType, $searchField, $searchValue, $i); + $i++; + } + } + } + } + + if ($andExpr->count() > 0) { + $qb->andWhere($andExpr); + } + } + + return $this; + } + + /** + * Add a condition. + * + * @param Andx $andExpr + * @param QueryBuilder $pivot + * @param string $searchType + * @param string $searchField + * @param string $searchValue + * @param integer $i + * + * @return Andx + */ + private function addCondition(Andx $andExpr, QueryBuilder $pivot, $searchType, $searchField, $searchValue, $i) + { + switch ($searchType) { + case 'like': + $andExpr->add($pivot->expr()->like($searchField, '?' . $i)); + $pivot->setParameter($i, '%' . $searchValue . '%'); + break; + case 'notLike': + $andExpr->add($pivot->expr()->notLike($searchField, '?' . $i)); + $pivot->setParameter($i, '%' . $searchValue . '%'); + break; + case 'eq': + $andExpr->add($pivot->expr()->eq($searchField, '?' . $i)); + $pivot->setParameter($i, $searchValue); + break; + case 'neq': + $andExpr->add($pivot->expr()->neq($searchField, '?' . $i)); + $pivot->setParameter($i, $searchValue); + break; + case 'lt': + $andExpr->add($pivot->expr()->lt($searchField, '?' . $i)); + $pivot->setParameter($i, $searchValue); + break; + case 'lte': + $andExpr->add($pivot->expr()->lte($searchField, '?' . $i)); + $pivot->setParameter($i, $searchValue); + break; + case 'gt': + $andExpr->add($pivot->expr()->gt($searchField, '?' . $i)); + $pivot->setParameter($i, $searchValue); + break; + case 'gte': + $andExpr->add($pivot->expr()->gte($searchField, '?' . $i)); + $pivot->setParameter($i, $searchValue); + break; + case 'in': + $andExpr->add($pivot->expr()->in($searchField, '?' . $i)); + $pivot->setParameter($i, explode(',', $searchValue)); + break; + case 'notIn': + $andExpr->add($pivot->expr()->notIn($searchField, '?' . $i)); + $pivot->setParameter($i, explode(",", $searchValue)); + break; + case 'isNull': + $andExpr->add($pivot->expr()->isNull($searchField)); + break; + case 'isNotNull': + $andExpr->add($pivot->expr()->isNotNull($searchField)); + break; + } + + return $andExpr; + } + + /** + * Ordering. + * Construct the ORDER BY clause for server-side processing SQL query. + * + * @return $this + */ + private function setOrderBy() + { + if (isset($this->requestParams['order']) && count($this->requestParams['order'])) { + + $counter = count($this->requestParams['order']); + + for ($i = 0; $i < $counter; $i++) { + $columnIdx = (integer) $this->requestParams['order'][$i]['column']; + $requestColumn = $this->requestParams['columns'][$columnIdx]; + + if ('true' == $requestColumn['orderable']) { + $this->qb->addOrderBy( + $this->orderColumns[$columnIdx], + $this->requestParams['order'][$i]['dir'] + ); + } + } + } + + return $this; + } + + /** + * Paging. + * Construct the LIMIT clause for server-side processing SQL query. + * + * @return $this + */ + private function setLimit() + { + if (isset($this->requestParams['start']) && -1 != $this->requestParams['length']) { + $this->qb->setFirstResult($this->requestParams['start'])->setMaxResults($this->requestParams['length']); + } + + return $this; + } + + //------------------------------------------------- + // Results + //------------------------------------------------- + + /** + * Query results before filtering. + * + * @param integer $rootEntityIdentifier + * + * @return int + */ + private function getCountAllResults($rootEntityIdentifier) + { + $qb = $this->em->createQueryBuilder(); + $qb->select('count(distinct ' . $this->tableName . '.' . $rootEntityIdentifier . ')'); + $qb->from($this->entity, $this->tableName); + + $this->setLeftJoins($qb); + $this->setWhereAllCallback($qb); + + return (int) $qb->getQuery()->getSingleScalarResult(); + } + + /** + * Query results after filtering. + * + * @param integer $rootEntityIdentifier + * + * @return int + */ + private function getCountFilteredResults($rootEntityIdentifier) + { + $qb = $this->em->createQueryBuilder(); + $qb->select('count(distinct ' . $this->tableName . '.' . $rootEntityIdentifier . ')'); + $qb->from($this->entity, $this->tableName); + + $this->setLeftJoins($qb); + $this->setWhere($qb); + $this->setWhereAllCallback($qb); + + return (int) $qb->getQuery()->getSingleScalarResult(); + } + + /** + * Constructs a Query instance. + * + * @return Query + * @throws \Exception + */ + private function execute() + { + $query = $this->qb->getQuery(); + + if (true === $this->configs['translation_query_hints']) { + if (true === $this->doctrineExtensions) { + $query->setHint( + \Doctrine\ORM\Query::HINT_CUSTOM_OUTPUT_WALKER, + 'Gedmo\\Translatable\\Query\\TreeWalker\\TranslationWalker' + ); + + $query->setHint( + \Gedmo\Translatable\TranslatableListener::HINT_TRANSLATABLE_LOCALE, + $this->locale + ); + + $query->setHint( + \Gedmo\Translatable\TranslatableListener::HINT_FALLBACK, + 1 + ); + } else { + throw new \Exception('execute(): "DoctrineExtensions" does not exist.'); + } + } + + $query->setHydrationMode(Query::HYDRATE_OBJECT); + + return $query; + } + + //------------------------------------------------- + // Response + //------------------------------------------------- + + /** + * Normalizes an entity path into an array. + * Each entity of each level will have its id set. + * All values are retrieved using a property accessor with magic call activated + * + * @param array $data + * @param object $entity Entity to normalize + * @param string $path Path to set in the array + * + * @return array Normalized data + */ + private function entityFieldToArray(array $data, $entity, $path) + { + $parts = explode('.', $path, 2); + + if (count($parts) === 1) { + if ($entity instanceof \Traversable) { + $data = []; + + foreach ($entity as $item) { + $id = $this->em->getClassMetadata(get_class($item))->getSingleIdentifierColumnName(); + + $data[] = [ + $id => $this->propertyAccessor->getValue($item, $id), + $parts[1] => $this->propertyAccessor->getValue($item, $parts[1]), + ]; + } + + } else { + if (count($data) === 0) { + $data = []; + $id = $this->em->getClassMetadata(get_class($entity))->getSingleIdentifierColumnName(); + $data[$id] = $this->propertyAccessor->getValue($entity, $id); + } + + $data[$parts[0]] = $this->propertyAccessor->getValue($entity, $parts[0]); + } + + return $data; + } else { + $data[$parts[0]] = $this->entityFieldToArray( + isset($data[$parts[0]]) ? $data[$parts[0]] : [], + $this->propertyAccessor->getValue($entity, $parts[0]), + $parts[1] + ); + + return $data; + } + } + + public function getResponse($buildQuery = true) + { + false === $buildQuery ? : $this->buildQuery(); + + $fresults = new Paginator($this->execute(), true); + $fresults->setUseOutputWalkers(true); + $output = array('data' => array()); + + foreach ($fresults as $result) { + $item = []; + $item[$this->rootEntityIdentifier] = $this->propertyAccessor->getValue($result, $this->rootEntityIdentifier); + foreach ($this->columns as $column) { + $data = $column->getDql(); + if (!$this->isSelectColumn($data)) { + continue; + } + + $item = $this->entityFieldToArray($item, $result, $data); + } + + // Line formatter + if (is_callable($this->lineFormatter)) { + $callable = $this->lineFormatter; + $item = call_user_func($callable, $item); + } + + // Images + foreach ($this->columns as $column) { + $data = $column->getDql(); + + /** @var ImageColumn $column */ + if ('image' === $column->getAlias()) { + if (true === $this->imagineBundle) { + $item[$data] = $this->renderImage($item[$data], $column); + } else { + $item[$data] = $this->twig->render( + 'SgDatatablesBundle:Helper:render_image.html.twig', + array( + 'image_name' => $item[$data], + 'path' => $column->getRelativePath() + ) + ); + } + } + + /** @var GalleryColumn $column */ + if ('gallery' === $column->getAlias()) { + $fields = explode('.', $data); + + if (true === $this->imagineBundle) { + $galleryImages = ''; + $counter = 0; + $images = count($item[$fields[0]]); + if (0 === $images) { + $item[$fields[0]] = $this->renderImage(null, $column); + } else { + foreach ($item[$fields[0]] as $image) { + $galleryImages = $galleryImages . $this->renderImage($image[$fields[1]], $column); + if (++$counter == $column->getViewLimit()) break; + } + $item[$fields[0]] = $galleryImages; + } + } else { + throw new InvalidArgumentException('getResponse(): Bundle "LiipImagineBundle" does not exist or it is not enabled.'); + } + } + } + + $output['data'][] = $item; + } + + $outputHeader = array( + 'draw' => (int) $this->requestParams['draw'], + 'recordsTotal' => (int) $this->getCountAllResults($this->rootEntityIdentifier), + 'recordsFiltered' => (int) $this->getCountFilteredResults($this->rootEntityIdentifier) + ); + + $json = $this->serializer->serialize(array_merge($outputHeader, $output), 'json'); + $response = new Response($json); + $response->headers->set('Content-Type', 'application/json'); + + return $response; + } + + //------------------------------------------------- + // Helper + //------------------------------------------------- + + /** + * Add search/order columns. + * + * @param integer $key + * @param string $columnTableName + * @param string $data + */ + private function addSearchOrderColumn($key, $columnTableName, $data) + { + $column = $this->columns[$key]; + + true === $column->getOrderable() ? $this->orderColumns[] = $columnTableName . '.' . $data : $this->orderColumns[] = null; + true === $column->getSearchable() ? $this->searchColumns[] = $columnTableName . '.' . $data : $this->searchColumns[] = null; + } + + /** + * Get metadata. + * + * @param string $entity + * + * @return ClassMetadata + * @throws \Exception + */ + private function getMetadata($entity) + { + try { + $metadata = $this->em->getMetadataFactory()->getMetadataFor($entity); + } catch (MappingException $e) { + throw new \Exception('getMetadata(): Given object ' . $entity . ' is not a Doctrine Entity.'); + } + + return $metadata; + } + + /** + * Get table name. + * + * @param ClassMetadata $metadata + * + * @return string + */ + private function getTableName(ClassMetadata $metadata) + { + return strtolower($metadata->getTableName()); + } + + /** + * Get identifier. + * + * @param ClassMetadata $metadata + * + * @return mixed + */ + private function getIdentifier(ClassMetadata $metadata) + { + $identifiers = $metadata->getIdentifierFieldNames(); + + return array_shift($identifiers); + } + + /** + * Set identifier from association. + * + * @param string|array $association + * @param string $key + * @param integer $i + * @param ClassMetadata|null $metadata + * + * @return $this + * @throws \Exception + */ + private function setIdentifierFromAssociation($association, $key = '', $i = 0, $metadata = null) + { + if (null === $metadata) { + $metadata = $this->metadata; + } + + if (is_string($association)) { + $targetEntityClass = $metadata->getAssociationTargetClass($association); + $targetMetadata = $this->getMetadata($targetEntityClass); + $this->selectColumns[$association][] = $this->getIdentifier($targetMetadata); + } + + if (is_array($association) && array_key_exists($i, $association)) { + $column = $association[$i]; + $count = count($association) - 1; + if (true === $metadata->hasAssociation($column)) { + $targetEntityClass = $metadata->getAssociationTargetClass($column); + $targetMetadata = $this->getMetadata($targetEntityClass); + if ($count == $i) { + $this->selectColumns[$key][] = $this->getIdentifier($targetMetadata); + } else { + $i++; + $this->setIdentifierFromAssociation($association, $key, $i, $targetMetadata); + } + } + } + + return $this; + } + + /** + * Is association. + * + * @param string $data + * + * @return bool|int + */ + private function isAssociation($data) + { + return strpos($data, '.'); + } + + /** + * Is select column. + * + * @param string $data + * + * @return bool + */ + private function isSelectColumn($data) + { + if (null !== $data && !in_array($data, $this->virtualColumns)) { + return true; + } + + return false; + } + + /** + * Is search column. + * + * @param AbstractColumn $column + * + * @return bool + */ + private function isSearchColumn(AbstractColumn $column) + { + if (false === $this->configs['search_on_non_visible_columns']) { + if (null !== $column->getDql() && true === $column->getSearchable() && true === $column->getVisible()) { + return true; + } + } else { + if (null !== $column->getDql() && true === $column->getSearchable()) { + return true; + } + } + + return false; + } + + /** + * Render image. + * + * @param string $imageName + * @param ImageColumn $column + * + * @return string + */ + private function renderImage($imageName, ImageColumn $column) + { + return $this->twig->render( + 'SgDatatablesBundle:Helper:ii_render_image.html.twig', + array( + 'image_id' => 'sg_image_' . uniqid(rand(10000, 99999)), + 'image_name' => $imageName, + 'filter' => $column->getImagineFilter(), + 'path' => $column->getRelativePath(), + 'holder_url' => $column->getHolderUrl(), + 'width' => $column->getHolderWidth(), + 'height' => $column->getHolderHeight(), + 'enlarge' => $column->getEnlarge() + ) + ); + } +} diff --git a/src/Sedona/SBORuntimeBundle/DependencyInjection/Configuration.php b/src/Sedona/SBORuntimeBundle/DependencyInjection/Configuration.php new file mode 100644 index 0000000..a34c24a --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/DependencyInjection/Configuration.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * This is the class that validates and merges configuration from your app/config files + * + * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html#cookbook-bundles-extension-config-class} + */ +class Configuration implements ConfigurationInterface +{ + /** + * {@inheritdoc} + */ + public function getConfigTreeBuilder() + { + $treeBuilder = new TreeBuilder(); + $rootNode = $treeBuilder->root('sedona_sbo_runtime'); + + // Here you should define the parameters that are allowed to + // configure your bundle. See the documentation linked above for + // more information on that topic. + + return $treeBuilder; + } +} diff --git a/src/Sedona/SBORuntimeBundle/DependencyInjection/SedonaSBORuntimeExtension.php b/src/Sedona/SBORuntimeBundle/DependencyInjection/SedonaSBORuntimeExtension.php new file mode 100644 index 0000000..bbe4000 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/DependencyInjection/SedonaSBORuntimeExtension.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\DependencyInjection; + +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\Config\FileLocator; +use Symfony\Component\HttpKernel\DependencyInjection\Extension; +use Symfony\Component\DependencyInjection\Loader; + +/** + * This is the class that loads and manages your bundle configuration + * + * To learn more see {@link http://symfony.com/doc/current/cookbook/bundles/extension.html} + */ +class SedonaSBORuntimeExtension extends Extension +{ + /** + * {@inheritdoc} + */ + public function load(array $configs, ContainerBuilder $container) + { + $configuration = new Configuration(); + $config = $this->processConfiguration($configuration, $configs); + + $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.xml'); + } +} diff --git a/src/Sedona/SBORuntimeBundle/Event/AdminAssociationActionEvent.php b/src/Sedona/SBORuntimeBundle/Event/AdminAssociationActionEvent.php new file mode 100644 index 0000000..5edbc6c --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Event/AdminAssociationActionEvent.php @@ -0,0 +1,107 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Event; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * Class AdminAssociationActionEvent + * @package Sedona\SBORuntimeBundle\Event + */ +class AdminAssociationActionEvent extends Event +{ + const DONT_TOUCH = 'none'; + + /** + * @var + */ + private $item; + + /** + * @var + */ + private $action; + + /** + * @var \Symfony\Component\Security\Core\User\UserInterface + */ + private $user; + + /** + * @var + */ + private $target; + + /** + * @param $item + * @param $action + * @param $target + * @param \Symfony\Component\Security\Core\User\UserInterface $user + */ + public function __construct($item, $action, $target, UserInterface $user = null) + { + $this->item = $item; + $this->action = $action; + $this->user = $user; + $this->target = $target; + } + + /** + * @return mixed + */ + public function getAction() + { + return $this->action; + } + + /** + * @param $action + * @return mixed + */ + public function setAction($action) + { + return $this->action = $action; + } + + /** + * @return mixed + */ + public function getItem() + { + return $this->item; + } + + /** + * @return \Symfony\Component\Security\Core\User\UserInterface + */ + public function getUser() + { + return $this->user; + } + + /** + * @return mixed + */ + public function getTarget() + { + return $this->target; + } + + /** + * @param mixed $target + */ + public function setTarget($target) + { + $this->target = $target; + } +} diff --git a/src/Sedona/SBORuntimeBundle/Event/AdminCrudEvent.php b/src/Sedona/SBORuntimeBundle/Event/AdminCrudEvent.php new file mode 100644 index 0000000..0eba486 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Event/AdminCrudEvent.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Event; + +use Symfony\Component\EventDispatcher\Event; +use Symfony\Component\Security\Core\User\UserInterface; + +/** + * Class AdminCrudEvent + * @package Sedona\SBORuntimeBundle\Event + */ +class AdminCrudEvent extends Event +{ + const DONT_TOUCH = 0; + const CREATE = 1; + const UPDATE = 2; + const DELETE = 3; + + /** + * @var + */ + private $item; + /** + * @var + */ + private $action; + /** + * @var \Symfony\Component\Security\Core\User\UserInterface + */ + private $user; + + /** + * @param $item + * @param $action + * @param \Symfony\Component\Security\Core\User\UserInterface $user + */ + public function __construct($item, $action, UserInterface $user = null) + { + $this->item = $item; + $this->action = $action; + $this->user = $user; + } + + /** + * @return mixed + */ + public function getAction() + { + return $this->action; + } + + /** + * @param $action + * @return mixed + */ + public function setAction($action) + { + return $this->action = $action; + } + + /** + * @return mixed + */ + public function getItem() + { + return $this->item; + } + + /** + * @return \Symfony\Component\Security\Core\User\UserInterface + */ + public function getUser() + { + return $this->user; + } +} diff --git a/src/Sedona/SBORuntimeBundle/Form/DataTransformer/EntityToIdTransformer.php b/src/Sedona/SBORuntimeBundle/Form/DataTransformer/EntityToIdTransformer.php new file mode 100644 index 0000000..c8f5df5 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Form/DataTransformer/EntityToIdTransformer.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Form\DataTransformer; + +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Component\Form\DataTransformerInterface; +use Symfony\Component\Form\Exception\TransformationFailedException; + +/** + * Class EntityToIdTransformer + * @package Sedona\SBORuntimeBundle\Form\DataTransformer + */ +class EntityToIdTransformer implements DataTransformerInterface +{ + /** + * @var ObjectManager + */ + private $om; + + /** + * @var string + */ + private $class; + + /** + * @var string + */ + private $primaryKey; + + /** + * @var boolean + */ + private $multiple; + + /** + * @var string + */ + private $glue; + + /** + * @param \Doctrine\Common\Persistence\ObjectManager $om + * @param string $class Class : 'AcmeTaskBundle:Issue' + * @param string $primaryKey Property used as primary key + * @param boolean $multiple + * @param string $glue + */ + public function __construct(ObjectManager $om, $class, $primaryKey = 'code', $multiple = false, $glue = ',') + { + $this->om = $om; + $this->class = $class; + $this->primaryKey = $primaryKey; + $this->multiple = $multiple; + $this->glue = $glue; + } + + /** + * Transforms an object (issue) to a string (number). + * + * @param Object|array|null $item + * @return string + */ + public function transform($item) + { + if (null === $item) { + return ''; + } + + $getter = 'get'.ucfirst($this->primaryKey); + if ($this->multiple && (is_array($item) || $item instanceof \Traversable)) { + $result = []; + + foreach ($item as $it) { + if (method_exists($it, $getter)) { + $result[] = $it->$getter(); + } elseif (method_exists($it, 'getId')) { + $result[] = $it->getId(); + } + } + + return implode($this->glue, $result); + } + + if (method_exists($item, $getter)) { + return $item->$getter(); + } + if (method_exists($item, 'getId')) { + return $item->getId(); + } + + return ''; + } + + /** + * Transforms a string (number) to an object (issue). + * + * @param string $number + * @return Object|array|null + * @throws TransformationFailedException if object (issue) is not found. + */ + public function reverseTransform($key) + { + + if (!$key) { + return null; + } + + if ($this->multiple) { + $keys = explode($this->glue, $key); + $items = $this->om + ->getRepository($this->class) + ->findBy(array($this->primaryKey => $keys)) + ; + + if (count($keys) != count($items)) { + throw new TransformationFailedException(sprintf("Can't find all items in %s this keys", $key)); + } + + return $items; + } + + $item = $this->om + ->getRepository($this->class) + ->findOneBy(array($this->primaryKey => $key)) + ; + + if (null === $item) { + throw new TransformationFailedException(sprintf("Can't find the %s item", $key)); + } + + return $item; + } + +} diff --git a/src/Sedona/SBORuntimeBundle/Form/Type/CollectionSelect2Type.php b/src/Sedona/SBORuntimeBundle/Form/Type/CollectionSelect2Type.php new file mode 100644 index 0000000..cffbf36 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Form/Type/CollectionSelect2Type.php @@ -0,0 +1,118 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Form\Type; + +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\Routing\RouterInterface; + +/** + * Class CollectionSelect2Type + * @package Sedona\SBORuntimeBundle\Form\Type + */ +class CollectionSelect2Type extends EntityTextType { + + /** + * @var RouterInterface + */ + protected $router; + + protected $copyProperty = [ + 'multiple' => 'multiple', + 'placeholder' => 'placeholder', + 'minimumInputLength' => 'data-minimumInputLength', + 'maximumInputLength' => 'data-maximumInputLength', + 'maximumSelectionSize' => 'data-maximumSelectionSize' + ]; + + /** + * @param ObjectManager $om + */ + public function __construct(ObjectManager $om, RouterInterface $router) + { + parent::__construct($om, $this->getName()); + $this->router = $router; + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + parent::setDefaultOptions($resolver); + $resolver + ->setDefaults(array( + 'multiple' => true, + 'attr' => [ + "data-toggle" => 'select2-remote' + ], + 'placeholder' => null // Initial value that is selected if no other selection is made. + )) + ->setOptional([ + 'minimumInputLength', // Number of characters necessary to start a search. + 'maximumInputLength', // Maximum number of characters that can be entered for an input. + 'maximumSelectionSize' // The maximum number of items that can be selected in a multi-select control. If this number is less than 1 selection is not limited. + ]) + ->setRequired([ + 'class', + 'searchRouteName', // route de recherche... + 'property', // proprrété sur lr quelle est fait la recheche + ]) + ->setAllowedTypes([ + 'class' => ['String'], + 'searchRouteName' => ['String'], + 'property' => ['String'] + ]) + ; + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['attr']['autocomplete'] = 'off'; + if (array_key_exists('searchRouteName',$options)) { + $view->vars['attr']['data-source'] = $this->router->generate($options['searchRouteName'])."?property=".$options['property']; + } + + $collection = $form->getData(); + if ($collection != null && (is_array($collection) || $collection instanceof \Traversable)) { + + $getterI = 'get'.ucfirst($options['primaryKey']); + $getterP = 'get'.ucfirst($options['property']); + $result = []; + foreach ($collection as $object) { + if($object instanceof $options['class'] && method_exists($object, $getterI) && method_exists($object, $getterP)) { + $result[] = ["id"=>$object->$getterI(),"text"=> $object->$getterP() ]; + } + } + + $view->vars['attr']['data-initSelection'] = json_encode($result); + } + + foreach ($this->copyProperty as $optionProperty => $attrProperty) { + if (array_key_exists($optionProperty,$options) && empty($options[$optionProperty]) == false) { + $view->vars['attr'][$attrProperty] = $options[$optionProperty]; + } + } + + parent::buildView($view,$form,$options); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'collection_select2'; + } +} diff --git a/src/Sedona/SBORuntimeBundle/Form/Type/ColorPickerType.php b/src/Sedona/SBORuntimeBundle/Form/Type/ColorPickerType.php new file mode 100644 index 0000000..bde4117 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Form/Type/ColorPickerType.php @@ -0,0 +1,81 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Form\Type; + +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * Class ColorPickerType + * @package Sedona\SBORuntimeBundle\Form\Type + */ +class ColorPickerType extends TextType { + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + parent::setDefaultOptions($resolver); + $resolver + ->setDefaults(array( + 'color-withaddon' => true, + 'color-format' => null, // If not false, forces the color format to be hex, rgb or rgba, otherwise the format is automatically detected. + //container string or jQuery Element false If not false, the picker will be contained inside this element, otherwise it will be appended to the document body. + //component string or jQuery Element '.add-on, .input-group-addon' Children selector for the component or element that trigger the colorpicker and which background color will change (needs an inner element). + //input string or jQuery Element 'input' Children selector for the input that will store the picker selected value. + 'color-horizontal'=> null, // If true, the hue and alpha channel bars will be rendered horizontally, above the saturation selector. + 'color-template' => null // Customizes the default colorpicker HTML template. + )) + ->setOptional([ + ]) + ->setRequired([ + 'color-withaddon' + ]) + ->setAllowedValues([ + 'color-format' => [null,'hex','rgb','rgba'], + 'color-horizontal'=> [null,true,false] + ]) + ->setAllowedTypes([ +// 'color-withaddon' => ['boolean'] +// 'color-template' => [null,'string'] + ]) + ; + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $optionsColorPicker = []; + if (array_key_exists('color-format',$options) && empty($options['color-format']) == false) { + $optionsColorPicker['format'] = $options['color-format']; + } + if (array_key_exists('color-horizontal',$options) && empty($options['color-horizontal']) == false) { + $optionsColorPicker['horizontal'] = $options['color-horizontal']; + } + if (array_key_exists('color-template',$options) && empty($options['color-template']) == false) { + $optionsColorPicker['template'] = $options['color-template']; + } + + $view->vars['optionsColorPicker'] = json_encode($optionsColorPicker); + $view->vars['withAddon'] = $options['color-withaddon'] == true; + parent::buildView($view,$form,$options); + } + + + public function getName() + { + return 'colorpicker'; + } + +} diff --git a/src/Sedona/SBORuntimeBundle/Form/Type/EntitySelect2Type.php b/src/Sedona/SBORuntimeBundle/Form/Type/EntitySelect2Type.php new file mode 100644 index 0000000..52b49dc --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Form/Type/EntitySelect2Type.php @@ -0,0 +1,115 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Form\Type; + +use Doctrine\Common\Persistence\ObjectManager; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; +use Symfony\Component\Routing\RouterInterface; + +/** + * Class EntitySelect2Type + * @package Sedona\SBORuntimeBundle\Form\Type + */ +class EntitySelect2Type extends EntityTextType { + + /** + * @var RouterInterface + */ + protected $router; + + protected $copyProperty = [ + 'placeholder' => 'placeholder', + 'minimumInputLength' => 'data-minimumInputLength', + 'maximumInputLength' => 'data-maximumInputLength' + ]; + + /** + * @param ObjectManager $om + */ + public function __construct(ObjectManager $om, RouterInterface $router) + { + parent::__construct($om, $this->getName()); + $this->router = $router; + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + parent::setDefaultOptions($resolver); + $resolver + ->setDefaults(array( + 'attr' => [ + "data-toggle" => 'select2-remote' + ], + 'placeholder' => null, // Initial value that is selected if no other selection is made. + 'searchRouteParams' => [] // parametre de la route de recherche... + )) + ->setOptional([ + 'minimumInputLength', // Number of characters necessary to start a search. + 'maximumInputLength' // Maximum number of characters that can be entered for an input. + ]) + ->setRequired([ + 'class', + 'searchRouteName', // route de recherche... + 'property', // proprrété sur lr quelle est fait la recheche + ]) + ->setAllowedTypes([ + 'class' => ['String'], + 'searchRouteName' => ['String'], + 'property' => ['String'] + ]) + ; + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['attr']['autocomplete'] = 'off'; + if (array_key_exists('searchRouteName',$options)) { + $view->vars['attr']['data-source'] = $this->router->generate($options['searchRouteName'], $options['searchRouteParams'])."?property=".$options['property']; + } + + $object = $form->getData(); + if ($object != null && $object instanceof $options['class']) { + $getterI = 'get'.ucfirst($options['primaryKey']); + $getterP = 'get'.ucfirst($options['property']); + if(method_exists($object, $getterI) && method_exists($object, $getterP)) { + $view->vars['attr']['data-initSelection'] = '{"id":"'.$object->$getterI().'","text":"'.str_replace('"','\"',$object->$getterP()).'"}' ; + } else if(method_exists($object, $getterI) && method_exists($object, '__toString')) { + $view->vars['attr']['data-initSelection'] = '{"id":"'.$object->$getterI().'","text":"'.str_replace('"','\"',''.$object).'"}' ; + } + } + + if (array_key_exists('required',$options) && $options['required'] == false && array_key_exists('placeholder',$options) && empty($options['placeholder'])) { + throw new \Exception('Le placeholder est obligatoire pour pouvoir supprimer la valeur'); + } + + foreach ($this->copyProperty as $optionProperty => $attrProperty) { + if (array_key_exists($optionProperty,$options)) { + $view->vars['attr'][$attrProperty] = $options[$optionProperty]; + } + } + + parent::buildView($view,$form,$options); + } + + /** + * {@inheritdoc} + */ + public function getName() + { + return 'entity_select2'; + } +} diff --git a/src/Sedona/SBORuntimeBundle/Form/Type/EntityTextType.php b/src/Sedona/SBORuntimeBundle/Form/Type/EntityTextType.php new file mode 100644 index 0000000..20eff4c --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Form/Type/EntityTextType.php @@ -0,0 +1,69 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Form\Type; + +use Doctrine\Common\Persistence\ObjectManager; +use JMS\DiExtraBundle\Annotation as DI; +use Sedona\SBORuntimeBundle\Form\DataTransformer\EntityToIdTransformer; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * Class EntityTextType + * @package Sedona\SBORuntimeBundle\Form\Type + */ +class EntityTextType extends TextType +{ + + /** + * @var ObjectManager + */ + protected $om; + + /** + * @var string + */ + protected $name; + + /** + * @param ObjectManager $om + */ + public function __construct(ObjectManager $om, $name = 'entity_text') + { + $this->om = $om; + $this->name = $name; + } + + public function buildForm(FormBuilderInterface $builder, array $options) + { + parent::buildForm($builder, $options); + $transformer = new EntityToIdTransformer($this->om, $options['class'], $options['primaryKey'], $options['multiple']); + $builder->addModelTransformer($transformer); + } + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + parent::setDefaultOptions($resolver); + $resolver->setDefaults(array( + 'primaryKey' => 'id', + 'class' => null, + 'invalid_message' => 'The selected item does not exist', + 'multiple' => false, + )); + } + + public function getName() + { + return $this->name; + } +} diff --git a/src/Sedona/SBORuntimeBundle/Form/Type/Wysihtml5Type.php b/src/Sedona/SBORuntimeBundle/Form/Type/Wysihtml5Type.php new file mode 100644 index 0000000..0ceb265 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Form/Type/Wysihtml5Type.php @@ -0,0 +1,49 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Form\Type; + +use Symfony\Component\Form\Extension\Core\Type\TextareaType; +use Symfony\Component\Form\AbstractType; +use Symfony\Component\Form\FormView; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\OptionsResolver\OptionsResolverInterface; + +/** + * Class Wysihtml5Type + * @package Sedona\SBORuntimeBundle\Form\Type + */ +class Wysihtml5Type extends TextareaType { + + public function setDefaultOptions(OptionsResolverInterface $resolver) + { + parent::setDefaultOptions($resolver); +// $resolver +// ->setDefaults(array( +// )) +// ; + } + + /** + * {@inheritdoc} + */ + public function buildView(FormView $view, FormInterface $form, array $options) + { + $view->vars['attr']["data-wysihtml5"] = json_encode([]); + parent::buildView($view,$form,$options); + } + + + public function getName() + { + return 'wysihtml5'; + } +} diff --git a/src/Sedona/SBORuntimeBundle/Resources/config/services.xml b/src/Sedona/SBORuntimeBundle/Resources/config/services.xml new file mode 100644 index 0000000..e611365 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Resources/config/services.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + %sg_datatables.datatable.query% + %kernel.bundles% + + + + + + + diff --git a/src/Sedona/SBORuntimeBundle/Resources/views/Breadcrumb/breadcrumb.html.twig b/src/Sedona/SBORuntimeBundle/Resources/views/Breadcrumb/breadcrumb.html.twig new file mode 100644 index 0000000..5493208 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Resources/views/Breadcrumb/breadcrumb.html.twig @@ -0,0 +1,14 @@ + \ No newline at end of file diff --git a/src/Sedona/SBORuntimeBundle/Resources/views/layout/macros.html.twig b/src/Sedona/SBORuntimeBundle/Resources/views/layout/macros.html.twig new file mode 100644 index 0000000..8b09ee9 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Resources/views/layout/macros.html.twig @@ -0,0 +1,44 @@ +{% extends '@AvanzuAdminTheme/layout/macros.html.twig' %} + +{% macro avatar(image, alt, class) %} + {% if image %} + {{ alt }} + {% else %} + {{ alt }} + {% endif %} +{% endmacro %} + +{% macro menu_item(item) %} + {% if item.route or item.hasChildren %} +
  • + + {% if item.icon %} {% endif %} + {{ item.label|trans([], 'admin') }} + {% if item.badge %} + {{ item.badge }} + {% endif %} + {% if item.hasChildren %}{% endif %} + + + {% if item.hasChildren %} + + {% endif %} +
  • + {% else %} +
  • + {{ item.label }} + {% if item.badge %} + {{ item.badge }} + {% endif %} +
  • + {% endif %} +{% endmacro %} diff --git a/src/Sedona/SBORuntimeBundle/Resources/views/layout_admin.html.twig b/src/Sedona/SBORuntimeBundle/Resources/views/layout_admin.html.twig new file mode 100644 index 0000000..b407a00 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Resources/views/layout_admin.html.twig @@ -0,0 +1,52 @@ +{% extends 'AvanzuAdminThemeBundle:layout:base-layout.html.twig' %} + +{% block javascripts_head %} + + + + + +{% endblock %} + +{% block title %} + Sedona SBO +{% endblock title %} + +{% block page_content %} + {% for label, flashes in app.session.flashbag.all %} + {% for flash in flashes %} +
    + + + {{ flash }} +
    + {% endfor %} + {% endfor %} + {% block content %}{% endblock content %} +{% endblock page_content %} + +{% block javascripts %} + + + + + {% if app.request.locale != 'en' %} + + + {% endif %} +{% endblock %} + +{% block avanzu_admin_footer %} +
    + + Footer +
    +{% endblock %} diff --git a/src/Sedona/SBORuntimeBundle/SedonaSBORuntimeBundle.php b/src/Sedona/SBORuntimeBundle/SedonaSBORuntimeBundle.php new file mode 100644 index 0000000..f2fe2d1 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/SedonaSBORuntimeBundle.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * Class SedonaSBORuntimeBundle + * @package Sedona\SBORuntimeBundle + */ +class SedonaSBORuntimeBundle extends Bundle +{ + public function getParent() + { + return 'AvanzuAdminThemeBundle'; + } +} diff --git a/src/Sedona/SBORuntimeBundle/Twig/WidgetExtension.php b/src/Sedona/SBORuntimeBundle/Twig/WidgetExtension.php new file mode 100644 index 0000000..3fa74b6 --- /dev/null +++ b/src/Sedona/SBORuntimeBundle/Twig/WidgetExtension.php @@ -0,0 +1,89 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Sedona\SBORuntimeBundle\Twig; + +use Twig_Environment; + +/** + * Class WidgetExtension + * @package Sedona\SBORuntimeBundle\Twig + */ +class WidgetExtension extends \Twig_Extension +{ + /** + * @var Twig_Environment + */ + protected $env; + + public function defaultDateFormat() + { + return 'DD/MM/YYYY'; + } + + public function defaultDateTimeFormat() + { + return 'DD/MM/YYYY HH:mm:ss'; + } + + public function defaultTimeFormat() + { + return 'HH:mm:ss'; + } + + public function renderWidget() + { + + } + + public function addGlyphicon($text, $icon) + { + return ' '.$text; + } + + public function getFilters() + { + return array( + 'addGlyphicon' => new \Twig_SimpleFilter( + 'addGlyphicon', + array($this, 'addGlyphicon'), + array('pre_escape' => 'html', 'is_safe' => array('html'))) + ); + } + + public function getFunctions() + { + return array( + 'widget_box' => new \Twig_SimpleFunction('widget_box', + array($this, 'renderWidget'), + array('is_safe' => array('html'))), + 'default_date_format' => new \Twig_SimpleFunction( + 'default_date_format', + array($this, 'defaultDateFormat')), + 'default_datetime_format' => new \Twig_SimpleFunction( + 'default_datetime_format', + array($this, 'defaultDateTimeFormat')), + 'default_time_format' => new \Twig_SimpleFunction( + 'default_time_format', + array($this, 'defaultTimeFormat')), + ); + } + + public function initRuntime(Twig_Environment $environment) + { + $this->env = $environment; + } + + public function getName() + { + return 'sbo_widget'; + } +}